1.调用运行时类的指定结构(重点)
主要是属性和方法,构造器。属性和方法更常用。
首先是调用属性。
提示:第一个方法不常用,开发常用第二个方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| @Test public void test5() throws Exception { Class clazz = Person.class; Person p = (Person)clazz.newInstance(); Field age = clazz.getField("age"); age.set(p,12); int pAge = (int)age.get(p); System.out.println(pAge); }
@Test public void test6() throws Exception { Class clazz = Person.class; Person p = (Person)clazz.newInstance(); Field name = clazz.getDeclaredField("name"); name.setAccessible(true); name.set(p,"Newton"); System.out.println(name.get(p)); }
|
然后是方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Test public void testMethod() throws Exception { Class clazz = Person.class; Person p = (Person)clazz.newInstance(); Method showNation = clazz.getDeclaredMethod("showNation", String.class); showNation.setAccessible(true); Object chn = showNation.invoke(p, "CHN"); System.out.println(chn);
Method info = clazz.getDeclaredMethod("info"); info.setAccessible(true); info.invoke(Person.class);
}
|
最后是构造器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Test public void test7() throws Exception { Class clazz = Person.class;
Constructor constructor = clazz.getDeclaredConstructor(String.class); constructor.setAccessible(true); Person p = (Person)constructor.newInstance("Tom"); System.out.println(p); }
|
2.集合框架内容补充
Queue
队列(Queue
)是一种经常使用的集合。Queue
实际上是实现了一个先进先出(FIFO:First In First Out)的有序表。它和List
的区别在于,List
可以在任意位置添加和删除元素,而Queue
只有两个操作:
在Java的标准库中,队列接口Queue
定义了以下几个方法:
int size()
:获取队列长度;
boolean add(E)
/boolean offer(E)
:添加元素到队尾;
E remove()
/E poll()
:获取队首元素并从队列中删除;
E element()
/E peek()
:获取队首元素但并不从队列中删除。
|
throw Exception |
返回false或null |
添加元素到队尾 |
add(E e) |
boolean offer(E e) |
取队首元素并删除 |
E remove() |
E poll() |
取队首元素但不删除 |
E element() |
E peek() |
因此,两套方法可以根据需要来选择使用。
注意:不要把null
添加到队列中,否则poll()
方法返回null
时,很难确定是取到了null
元素还是队列为空。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Main { public static void main(String[] args) { Queue<String> q = new LinkedList<>(); q.offer("apple"); q.offer("pear"); q.offer("banana"); System.out.println(q.poll()); System.out.println(q.poll()); System.out.println(q.poll()); System.out.println(q.poll()); } }
|
PriorityQueue
PriorityQueue
和Queue
的区别在于,它的出队顺序与元素的优先级有关,对PriorityQueue
调用remove()
或poll()
方法,返回的总是优先级最高的元素。
要使用PriorityQueue
,我们就必须给每个元素定义“优先级”。我们以实际代码为例,先看看PriorityQueue
的行为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import java.util.PriorityQueue; import java.util.Queue;
public class Main { public static void main(String[] args) { Queue<String> q = new PriorityQueue<>(); q.offer("apple"); q.offer("pear"); q.offer("banana"); System.out.println(q.poll()); System.out.println(q.poll()); System.out.println(q.poll()); System.out.println(q.poll()); } }
|
我们放入的顺序是"apple"
、"pear"
、"banana"
,但是取出的顺序却是"apple"
、"banana"
、"pear"
,这是因为从字符串的排序看,"apple"
排在最前面,"pear"
排在最后面。
因此,放入PriorityQueue
的元素,必须实现Comparable
接口,PriorityQueue
会根据元素的排序顺序决定出队的优先级。
如果我们要放入的元素并没有实现Comparable
接口怎么办?PriorityQueue
允许我们提供一个Comparator
对象来判断两个元素的顺序。我们以银行排队业务为例,实现一个PriorityQueue
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| import java.util.Comparator; import java.util.PriorityQueue; import java.util.Queue;
public class Main { public static void main(String[] args) { Queue<User> q = new PriorityQueue<>(new UserComparator()); q.offer(new User("Bob", "A1")); q.offer(new User("Alice", "A2")); q.offer(new User("Boss", "V1")); System.out.println(q.poll()); System.out.println(q.poll()); System.out.println(q.poll()); System.out.println(q.poll()); } }
class UserComparator implements Comparator<User> { public int compare(User u1, User u2) { if (u1.number.charAt(0) == u2.number.charAt(0)) { return u1.number.compareTo(u2.number); } if (u1.number.charAt(0) == 'V') { return -1; } else { return 1; } } }
class User { public final String name; public final String number;
public User(String name, String number) { this.name = name; this.number = number; }
public String toString() { return name + "/" + number; } }
|
Deque
我们知道,Queue
是队列,只能一头进,另一头出。
如果把条件放松一下,允许两头都进,两头都出,这种队列叫双端队列(Double Ended Queue),学名Deque
。
注意到Deque
接口实际上扩展自Queue
。
Java集合提供了接口Deque
来实现一个双端队列,它的功能是:
- 既可以添加到队尾,也可以添加到队首;
- 既可以从队首获取,又可以从队尾获取。
我们来比较一下Queue
和Deque
出队和入队的方法:
|
Queue |
Deque |
添加元素到队尾 |
add(E e) / offer(E e) |
addLast(E e) / offerLast(E e) |
取队首元素并删除 |
E remove() / E poll() |
E removeFirst() / E pollFirst() |
取队首元素但不删除 |
E element() / E peek() |
E getFirst() / E peekFirst() |
添加元素到队首 |
无 |
addFirst(E e) / offerFirst(E e) |
取队尾元素并删除 |
无 |
E removeLast() / E pollLast() |
取队尾元素但不删除 |
无 |
E getLast() / E peekLast() |
Deque
是一个接口,它的实现类有ArrayDeque
和LinkedList
。
我们发现LinkedList
真是一个全能选手,它即是List
,又是Queue
,还是Deque
。
避免把null
添加到队列。
Stack
在Java中,我们用Deque
可以实现Stack
的功能:
- 把元素压栈:
push(E)
/addFirst(E)
;
- 把栈顶的元素“弹出”:
pop()
/removeFirst()
;
- 取栈顶元素但不弹出:
peek()
/peekFirst()
。
为什么Java的集合类没有单独的Stack
接口呢?因为有个遗留类名字就叫Stack
,出于兼容性考虑,所以没办法创建Stack
接口,只能用Deque
接口来“模拟”一个Stack
了。
JavaSE阶段回顾总结:
https://www.bilibili.com/video/BV1MJ411v7tJ