# 专题
# List中获取最大值
List<Double> currencyList = new ArrayList<>();
currencyList.add(1.2);
currencyList.add(2.5);
currencyList.add(3.6);
System.out.println(Collections.max(currencyList));
// 3.6
List<String> currencyList = new ArrayList<>();
currencyList.add("ghrte");
currencyList.add("ythty");
currencyList.add("rte");
System.out.println(Collections.max(currencyList));
2
3
4
5
6
7
8
9
10
11
12
# 数组遍历转ArrayList
for循环数组
- 工具类
Collections.addAll(list2,232,3434,1312);
addAll()方法的实现就是用的上面遍历的方式。
- 工具类
java8
List<Integer> list2 = Arrays.stream(intArray).boxed().collect(Collectors.toList());
- 两个集合类结合
List<String> listInteger = new ArrayList<>(Arrays.asList("dsds","sdfasfd","oio"));
# Arrays.asList
WARNING
使用Arrays.asList()的原因无非是想将数组或一些元素转为集合,而你得到的集合并不一定是你想要的那个集合。
而一开始asList的设计时用于打印数组而设计的,但jdk1.5开始,有了另一个比较更方便的打印函数Arrays.toString(),于是打印不再使用asList(),而asList()恰巧可用于将数组转为集合。
# 错误用法
- 错误一:将
基本类型数组作为asList的参数
int[] intArray = {1,2,3};
List listsInt = Arrays.asList(intArray);
System.out.println(listsInt.size());
//输出结果为:1;
2
3
4
由于Arrays.ArrayList参数为可变长泛型,而基本类型是无法泛型化的,
所以它把int[] arr数组当成了一个泛型对象,所以集合中最终只有一个元素arr。
- 错误二:将数组作为asList参数后,修改数组或List
String[] strArray = {"我","知道","陈莉敏","最好看"};
List<String> listsStr = Arrays.asList(strArray);
strArray[1]="必须知道";
listsStr.set(3,"妥妥的最好看");
System.out.println(Arrays.toString(strArray));
// 输出结果:[我, 必须知道, 陈莉敏, 妥妥的最好看]
System.out.println(listsStr.toString());
// 输出结果:[我, 必须知道, 陈莉敏, 妥妥的最好看]
2
3
4
5
6
7
8
由于asList产生的集合元素是直接引用作为参数的数组,所以当外部数组或集合改变时,
数组和集合会同步变化,这在平时我们编码时可能产生莫名的问题。
- 错误三:数组转换为集合后,进行增删元素
listsStr.add("who");
listsStr.remove(1);
System.out.println(listsStr.toString());
2
3
直接异常,没有重写add和remove方法
由于asList产生的集合并没有重写add,remove等方法,所以它会调用父类AbstractList的方法,
而父类的方法中抛出的却是异常信息。
# 不同之处
Arrays.ArrayList 是工具类 Arrays 的一个内部静态类,它没有完全实现List的方法,而 ArrayList直接实现了List 接口,实现了List所有方法。
- 1.长度不同 和 实现的方法不同
Arrays.ArrayList是一个定长集合,因为它没有重写add,remove方法,所以一旦初始化元素后,集合的size就是不可变的。
- 2.参数赋值方式不同
Arrays.ArrayList将外部数组的引用直接通过“=”赋予内部的泛型数组,所以本质指向同一个数组。
# 正确姿势
- 如果使用Spring
int[] intArray = {2,3,4};
//此处返回的还是Arrays.asList
List list1 = CollectionUtils.arrayToList(intArray);
// list1.add(6);
System.out.println(list1);
2
3
4
5
- 如果使用Java 8
List<Integer> list2 = Arrays.stream(intArray).boxed().collect(Collectors.toList());
// for (Integer i:list2){
// if (i==2){
// list2.remove(i);
// }
// }
list2.add(7);
2
3
4
5
6
7
注释掉的部分,会报错。
此时可以随意增减的。
# 14个Java并发容器
TIP
不考虑多线程并发的情况下,容器类一般使用ArrayList、HashMap等线程不安全的类,效率更高
- ConcurrentHashMap:并发版HashMap
- CopyOnWriteArrayList:并发版ArrayList
- CopyOnWriteArraySet:并发Set
- ConcurrentLinkedQueue:并发队列(基于链表)
- ConcurrentLinkedDeque:并发队列(基于双向链表)
- ConcurrentSkipListMap:基于跳表的并发Map
- ConcurrentSkipListSet:基于跳表的并发Set
- ArrayBlockingQueue:阻塞队列(基于数组)
- LinkedBlockingQueue:阻塞队列(基于链表)
- LinkedBlockingDeque:阻塞队列(基于双向链表)
- PriorityBlockingQueue:线程安全的优先队列
- SynchronousQueue:读写成对的队列
- LinkedTransferQueue:基于链表的数据交换队列
- DelayQueue:延时队列
# 删除操作
Collection.removeIf();
# 语法糖可能要遭遇的坑
# 泛型——当泛型遇到重载
public void method(List<String> list){
System.out.println(list);
}
public void method(List<Integer> list){
System.out.println(list);
}
2
3
4
5
6
上面这段代码,有两个重载的函数,因为他们的参数类型不同,一个是List另一个是List,但是,这段代码是编译通不过的。
因为我们前面讲过,参数List和List编译之后都被擦除了,变成了一样的原生类型List,擦除动作导致这两个方法的特征签名变得一模一样。
# 泛型——当泛型遇到catch
泛型的类型参数不能用在Java异常处理的catch语句中。因为异常处理是由JVM在运行时刻来进行的。由于类型信息被擦除,
JVM是无法区分两个异常类型MyException<String>和MyException<Integer>的
# 泛型——当泛型内包含静态变量
@Test
public void method(){
PersonModel<String> pst1 = new PersonModel<>("male",18,"hu");
pst1.varS=1;
PersonModel<Integer> pst2 = new PersonModel<>("male",18,"hu");
pst2.varS =3;
System.out.println(pst1.varS);
}
2
3
4
5
6
7
8
9
public class PersonModel<T> {
public static int varS = 0;
private String sex;
private String firstName;
private String lastName;
private Integer age;
private String country;
public PersonModel(String sex, Integer age,String lastName) {
this.sex = sex;
this.age = age;
this.lastName = lastName;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
以上代码输出结果为:3!
由于经过类型擦除,所有的泛型类实例都关联到同一份字节码上,泛型类的所有静态变量是共享的。
# 自动装箱与拆箱——对象相等比较
/**
* <p>基本知识:我们知道,如果两个引用指向同一个对象,用==表示它们是相等的。</p>
* <p>如果两个引用指向不同的对象,用==表示它们是不相等的,即使它们的内容相同。</p>
*/
@Test
public void testUsualEqual(){
int a =1000,b=1000;
int c=100,d=100;
//true
System.out.println(a==b);
//true
System.out.println(c==d);
Integer e=1000,f=1000;
// 如果你看去看 Integer.Java 类,你会发现有一个内部私有类,IntegerCache.java,
// 它缓存了从-128到127之间的所有的整数对象。
// Integer g = 100;它实际上在内部做的是:Integer i = Integer.valueOf(100);
// 如果值的范围在-128到127之间,它就从高速缓存返回实例。
Integer g=100,h=100;
//false
System.out.println(e==f);
//本来这个也应该返回false,但是返回了true
System.out.println(g==h);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 导入静态方法或者变量
import static org.apache.commons.lang3.ObjectUtils.allNotNull;//方法
import static org.apache.commons.lang3.ObjectUtils.NULL;//变量
2
# 回调
# Lambda表达式
# 接口调用
public interface CallBackInterface {
void processResponse();
}
public class CallBackInterfaceImpl implements CallBackInterface {
@Override
public void processResponse() {
System.out.println("[Interface_CallBack]:处理响应");
}
}
2
3
4
5
6
7
8
9
10
public void interfaceSend(CallBackInterface callBack) throws Exception {
// 模拟等待响应
Thread.sleep(3000);
System.out.println("[interface_Request]:收到响应");
callBack.processResponse();
}
2
3
4
5
6
CallBackInterfaceImpl callBackInterface = new CallBackInterfaceImpl();
new Thread(() -> {
try {
request.interfaceSend(callBackInterface);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
2
3
4
5
6
7
8
# 直接调用
public void directSend(CallBack callBack) throws Exception {
// 模拟等待响应
Thread.sleep(3000);
System.out.println("[directSend_Request]:收到响应");
callBack.directProcessResponse();
}
2
3
4
5
6
CallBack callBack = new CallBack();
new Thread(() -> {
try {
request.directSend(callBack);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
2
3
4
5
6
7
8
这种实现方式十分简单,但是存在的问题是不符合修改封闭原则。也就是说当我们想要换一种“处理响应”的方法时,将必须去修改 CallBack 类的 processRequest()方法。而如果将 CallBack 类改为接口,我们就可以仅更换 CallBack 的实现了。
# 反射
Java 的反射机制允许我们获取类的信息,其中包括类的方法。我们将以
Method类型去获取回调函数,然后传递给请求函数
public class RequestCallBack {
public void send(Class clazz, Method method) throws Exception {
// 模拟等待响应
Thread.sleep(3000);
System.out.println("[Request]:收到响应");
method.invoke(clazz.newInstance());
}
}
2
3
4
5
6
7
8
public class CallBack {
public void processResponse() {
System.out.println("[CallBack]:处理响应");
}
}
2
3
4
5
public class MainCallBack {
public static void main(String[] args) throws Exception {
RequestCallBack request = new RequestCallBack();
System.out.println("[Main]:我开个线程去异步发请求");
new Thread(() -> {
try {
request.send(CallBack.class, CallBack.class.getMethod("processResponse"));
} catch (Exception e) {
e.printStackTrace();
}
}).start();
System.out.println("[Main]:请求发完了,我去干点别的");
Thread.sleep(100000);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Main]:我开个线程去异步发请求
[Main]:请求发完了,我去干点别的
[Request]:收到响应
[CallBack]:处理响应
2
3
4