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