# 专题

返回:Java开发

🌰 各种类型互转及在线文件图片处理 🌰 并发、线程、各种锁
注解 java时间处理
正则 上传下载
定时任务 图片处理
容器(集合、列表) 注释
切面 日志管理
其他类-Enum、FTP、反射、断言等 各种路径问题
常用类库-日志、JSON解析、EXCEL、单元测试等 java数组操作
字符串操作 消息中间件Kafka、RocketMQ
编程字体 JNA
各种安全相关 缓存
参数校验 java导出
格式化输出 webSocket
java设计模式 IO流
web java调用外部命令
算法 工作流

# 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));
1
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());
1
  • 两个集合类结合
List<String> listInteger = new ArrayList<>(Arrays.asList("dsds","sdfasfd","oio"));
1

# 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;
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());
        // 输出结果:[我, 必须知道, 陈莉敏, 妥妥的最好看]
1
2
3
4
5
6
7
8

由于asList产生的集合元素是直接引用作为参数的数组,所以当外部数组或集合改变时,
数组和集合会同步变化,这在平时我们编码时可能产生莫名的问题。

  • 错误三:数组转换为集合后,进行增删元素
listsStr.add("who");
        listsStr.remove(1);
        System.out.println(listsStr.toString());
1
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);
1
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);
1
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();
1

# 语法糖可能要遭遇的坑

# 泛型——当泛型遇到重载

public void method(List<String> list){
        System.out.println(list);
    }
    public void method(List<Integer> list){
        System.out.println(list);
    }
1
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);

    }
1
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;
    }
}
1
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);
    }
1
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;//变量
1
2

# 回调

# Lambda表达式

# 接口调用

public interface CallBackInterface {
    void processResponse();
}

public class CallBackInterfaceImpl implements CallBackInterface {
    @Override
    public void processResponse() {
        System.out.println("[Interface_CallBack]:处理响应");
    }
}
1
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();
    }
1
2
3
4
5
6
CallBackInterfaceImpl callBackInterface = new CallBackInterfaceImpl();
        new Thread(() -> {
            try {
                request.interfaceSend(callBackInterface);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
1
2
3
4
5
6
7
8

# 直接调用

public void directSend(CallBack callBack) throws Exception {
        // 模拟等待响应
        Thread.sleep(3000);
        System.out.println("[directSend_Request]:收到响应");
        callBack.directProcessResponse();
    }
1
2
3
4
5
6
CallBack callBack = new CallBack();
        new Thread(() -> {
            try {
                request.directSend(callBack);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
1
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());
    }
}
1
2
3
4
5
6
7
8
public class CallBack {
    public void processResponse() {
        System.out.println("[CallBack]:处理响应");
    }
}
1
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);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Main]:我开个线程去异步发请求
[Main]:请求发完了,我去干点别的
[Request]:收到响应
[CallBack]:处理响应
1
2
3
4