Java常用知识补充
目录
- Java常用知识补充
- 1、sort与comparator
- 2、java lamda
- 3、java集合
- 4、java集合初始化
- 5、java反射
- 6、java注解
- 7、Fastjson(序列化与反序列化)
- 8、java常用注解(lombok)
- 9、Java常用包及其类总结
- 10、Java 什么是动态代理
- 11、Java注解和Python装饰器
- 12、Java StringUtils和CollectionUtil
- 13、collection.stream()以及collect()方法
- 14、BeanUtils
- 15、String类常用方法
- 16、异步任务:CompletableFuture
- 17、java 枚举用法
- 18、Java中List.forEach()方法使用
- 19、Java Maps.newHashMap()使用
- 20、Java中Synchronized的用法
- 21、Arrays.asList() 和Collections.singletonList()的区别
- 22、Java Collections.emptyList()方法使用
- 23、equals使用
1、sort与comparator
1)Java的sort
常见sort:
- java.util.Arrays中的静态方法Arrays.sort()
- 主要是针对各种数据类型(基本数据类型和引用对象类型)的数组元素排序
- java.util.Collections中的静态方法的Collections.sort()
- 主要是针对集合框架中的动态数组,链表,树,哈希表等( ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap )进行排序
2)Java 的Comparator
参考:
sort函数中使用的cmp
1、通常写法
import java.util.Comparator;
class MyComparator implements Comparator<A> {
@Override
public int compare(A o1, A o2) {
//升序
//return o1.a - o2.a;
//降序:后面会具体分析为什么降序
return o2.a - o1.a;
}
}
// 使用
Collections.sort(list, new MyComparator())
这里o1表示位于前面的对象,o2表示后面的对象
- 返回-1(或负数),表示不需要交换01和02的位置,o1排在o2前面,asc,即升序
- 返回1(或正数),表示需要交换01和02的位置,o1排在o2后面,desc,即降序
2、高级写法
lamda:
// 先按照区间起始位置排序
Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);
解释:
-
v1[0] - v2[0]即升序排列,因为v1[0]<v2[0]时为负数,不调换位置,反之调换位置
-
Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);假设传来两个值,v1与v2,那么他们的先后顺序以v1[0]比v2[0]的结果为准,即:若v1[0] < v2[0]则v1 < v2,若=则=,若>则>
-
举一反三:
Arrays.sort(intervals, (v1, v2) -> v1[0] == v2[0] ? v1[1] - v2[1] : v1[0] - v2[0]);
表示:传来两个值v1
与v2
,若[0]
相同,则按[1]
升序;若不同则按[0]
升序。- 效果等同于:
class MyComparator implements Comparator<int[]> {
@Override
public int compare(int[] a1, int[] a2) {
if (a1[0] == a2[0]) {
return a1[1] - a2[1];
} else {
return a1[0] - a2[0];
}
}
}
- 趁热打铁题目 354. 俄罗斯套娃的信封问题
2、java lamda
参考:
语法:
(parameters) -> expression
或
(parameters) ->{ statements; }
例子:
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
3、java集合
参考:
ArrayList
1、概念
ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。它还提供了快速基于索引访问元素的方式,对尾部成员的增加和删除支持较好。使用 ArrayList 创建的集合,允许对集合中的元素进行快速的随机访问,不过,向 ArrayList 中插入与删除元素的速度相对较慢。
2、常用方法
方法 | 描述 |
---|---|
add() | 将元素插入到指定位置的 arraylist 中 |
addAll() | 添加集合中的所有元素到 arraylist 中 |
clear() | 删除 arraylist 中的所有元素 |
clone() | 复制一份 arraylist |
contains() | 判断元素是否在 arraylist |
get() | 通过索引值获取 arraylist 中的元素 |
indexOf() | 返回 arraylist 中元素的索引值 |
removeAll() | 删除存在于指定集合中的 arraylist 里的所有元素 |
remove() | 删除 arraylist 里的单个元素 |
size() | 返回 arraylist 里元素数量 |
isEmpty() | 判断 arraylist 是否为空 |
subList() | 截取部分 arraylist 的元素 |
set() | 替换 arraylist 中指定索引的元素 |
sort() | 对 arraylist 元素进行排序 |
forEach() | 遍历 arraylist 中每一个元素并执行特定操作(常使用lambda函数配合) |
LinkedList
LinkedList 类采用链表结构保存对象,这种结构的优点是便于向集合中插入或者删除元素。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高,但是 LinkedList 类随机访问元素的速度则相对较慢。这里的随机访问是指检索集合中特定索引位置的元素。
HashSet
非线程安全的
HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。HashSet 是无序的,即不会记录插入的顺序。
HashMap
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
方法 | 描述 |
---|---|
clear() | 删除 hashMap 中的所有键/值对 |
clone() | 复制一份 hashMap |
isEmpty() | 判断 hashMap 是否为空 |
size() | 计算 hashMap 中键/值对的数量 |
put() | 将键/值对添加到 hashMap 中 |
putAll() | 将所有键/值对添加到 hashMap 中 |
putIfAbsent() | 如果 hashMap 中不存在指定的键,则将指定的键/值对插入到 hashMap 中。 |
remove() | 删除 hashMap 中指定键 key 的映射关系 |
containsKey() | 检查 hashMap 中是否存在指定的 key 对应的映射关系。 |
containsValue() | 检查 hashMap 中是否存在指定的 value 对应的映射关系。 |
replace() | 替换 hashMap 中是指定的 key 对应的 value。 |
replaceAll() | 将 hashMap 中的所有映射关系替换成给定的函数所执行的结果。 |
get() | 获取指定 key 对应对 value |
forEach() | 对 hashMap 中的每个映射执行指定的操作。 |
---|---|
entrySet() | 返回 hashMap 中所有映射项的集合集合视图。 |
keySet() | 返回 hashMap 中所有 key 组成的集合视图。 |
values() | 返回 hashMap 中存在的所有 value 值。 |
关于hash有序无序的问题:
参考:
HashSet、LinkedHashSet和TreeSet三者区别与联系
三者都保证了元素的唯一性,如果无排序要求可以选用HashSet;如果想取出元素的顺序和放入元素的顺序相同,那么可以选用LinkedHashSet。如果想插入、删除立即排序或者按照一定规则排序可以选用TreeSet。
4、java集合初始化
ArrayList初始化
参考:list初始化
1)常规方式
List<String> languages = new ArrayList<>();
languages.add("Java");
languages.add("PHP");
2)Arrays.asList
List<String> numbers = new ArrayList<>(Arrays.asList("1", "2", "3"));
numbers.add("4");
3)匿名内部类——双括号初始化
List<String> names = new ArrayList<>() {{
add("Tom");
add("Sally");
add("John");
}};
4)List.of
List<String> cups = List.of("A", "B", "C");
HashMap初始化
参考:map初始化
1、常规添加
HashMap<String, String> map = new HashMap<String, String>();
map.put("name", "test");
map.put("age", "20");
2、双括号
HashMap<String, String> map = new HashMap<String, String>() {
{
put("name", "test");
put("age", "20");
}
};
3、Map.of
HashMap<String, Integer> map = Map.of("Hello", 1, "World", 2);//不可变集合
5、java反射
参考:
反射:反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。反射的目的是为了获得某个实例的信息。
简单例子
public class Apple {
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public static void main(String[] args) throws Exception{
//正常的调用
Apple apple = new Apple();
apple.setPrice(5);
System.out.println("Apple Price:" + apple.getPrice());
//使用反射调用
// 1、获取CLass对象实例
Class clz = Class.forName("com.chenshuyi.api.Apple");
// 2、获取方法的 Method 对象
Method setPriceMethod = clz.getMethod("setPrice", int.class);
// 3、根据 Class 对象实例获取 Constructor 对象
Constructor appleConstructor = clz.getConstructor();
// 4、使用 Constructor 对象的 newInstance 方法获取反射类对象
Object appleObj = appleConstructor.newInstance();
// 5、利用 invoke 方法调用方法
setPriceMethod.invoke(appleObj, 14);
Method getPriceMethod = clz.getMethod("getPrice");
System.out.println("Apple Price:" + getPriceMethod.invoke(appleObj));
}
}
常用API
获取反射中的Class对象
- 使用 Class.forName 静态方法:知道该类的全路径名
Class clz = Class.forName("java.lang.String");
- 使用.class方法:只适合在编译前就知道操作的 Class
Class clz = String.class;
- 使用类对象的getClass()方法
String str = new String("Hello");
Class clz = str.getClass();
通过反射创建类对象
两种方式:
- 通过 Class 对象的 newInstance() 方法:只能使用默认的无参构造
Class clz = Apple.class;
Apple apple = (Apple)clz.newInstance();
- 通过 Constructor 对象的 newInstance() 方法:可以使用有参数的构造函数
Class clz = Apple.class;
Constructor constructor = clz.getConstructor();
Apple apple = (Apple)constructor.newInstance();
Apple apple2 = (Apple)constructor.newInstance("红富士", 15);
通过反射获取类属性、方法、构造器
1、获得属性
- 我们通过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性。
- 使用 Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性
Class clz = Apple.class;
Field[] fields = clz.getFields();
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
// getFields结果:price
// getDeclaredFields结果:name\n price
- Field getField(name):根据字段名获取某个public的field(包括父类)
- Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
- Field[] getFields():获取所有public的field(包括父类)
- Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
2、获得方法
Method getMethod(name, Class...)
:获取某个public
的Method
(包括父类)Method getDeclaredMethod(name, Class...)
:获取当前类的某个Method
(不包括父类)Method[] getMethods()
:获取所有public
的Method
(包括父类)Method[] getDeclaredMethods()
:获取当前类的所有Method
(不包括父类)
3、获得构造方法
getConstructor(Class...)
:获取某个public
的Constructor
;getDeclaredConstructor(Class...)
:获取某个Constructor
;getConstructors()
:获取所有public
的Constructor
;getDeclaredConstructors()
:获取所有Constructor
。
6、java注解
参考:
注解(Annotation)是放在Java源码的类、方法、字段、参数前的一种特殊“注释”
7、Fastjson(序列化与反序列化)
参考:
fastJSON,使用TypeReference处理复杂的泛型对象
1)序列化与反序列化
序列化:就是将对象转化成字节序列的过程。序列化是把对象转换成有序字节流,以便在网络上传输或者保存在本地文件中。核心作用是对象状态的保存与重建。
**反序列化:**就是讲字节序列转化成对象的过程。
在实际工作场景中,序列化和反序列化是一个十分常见常用的功能,现如今大部分都在采纳Json和FastJson解析库结合的这种方式进行交互,但是对于Java中Serializable的基本性质还是需要了解。
2)为什么需要序列化和反序列化
那么为什么要去进行序列化呢?有以下两个原因
- 持久化:对象是存储在JVM中的堆区的,但是如果JVM停止运行了,对象也不存在了。序列化可以将对象转化成字节序列,可以写进硬盘文件中实现持久化。在新开启的JVM中可以读取字节序列进行反序列化成对象。
- 网络传输:网络直接传输数据,但是无法直接传输对象,可在传输前序列化,传输完成后反序列化成对象。所以所有可在网络上传输的对象都必须是可序列化的。
3)Fastjson
Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。
- 提供了 toJSONString() 和 parseObject() 方法来将 Java 对象与 JSON 相互转换。
- 调用toJSONString方法即可将对象转换成 JSON 字符串
- parseObject 方法则反过来将 JSON 字符串转换成对象。
具体例子可以参考:高性能JSON框架之FastJson的简单使用
@Test
public void whenJson_thanConvertToObjectCorrect() {
Person person = new Person(20, "John", "Doe", new Date());
// 对象装字符串
String jsonObject = JSON.toJSONString(person);
// 字符串转对象
Person newPerson = JSON.parseObject(jsonObject, Person.class);
assertEquals(newPerson.getAge(), 0); // 如果我们设置系列化为 false
assertEquals(newPerson.getFullName(), listOfPersons.get(0).getFullName());
}
//
此外,还会使用TypeReference处理复杂的泛型对象。
8、java常用注解(lombok)
参考:
1)简介
lombok到底是个什么呢,lombok是一个可以通过简单的注解的形式来帮助我们简化消除一些必须有但显得很臃肿的 Java 代码的工具,简单来说,比如我们新建了一个类,然后在其中写了几个字段,然后通常情况下我们需要手动去建立getter和setter方法啊,构造函数啊之类的。
lombok的作用就是为了省去我们手动创建这些代码的麻烦,它能够在我们编译源码的时候自动帮我们生成这些方法。
2)lombok提供的常用注解:
-
@Data : 注在类上,自动为所有字段添加@ToString, @EqualsAndHashCode, @Getter方法,为非final字段添加@Setter,和@RequiredArgsConstructor
-
@AllArgsConstructor : 注在类上,提供类的全参构造
-
@NoArgsConstructor : 注在类上,提供类的无参构造
-
@Getter / @Setter: 自动生成Getter/Setter方法
-
@EqualsAndHashCode : 注在类上,提供对应的 equals 和 hashCode 方法
-
@NonNull: 可以帮助我们避免空指针。
-
@Cleanup: 自动帮我们调用
close()
方法,用于InputStream等场景
9、Java常用包及其类总结
参考:
1.java.lang包,最基础的包、核心类库。常用类有String、Math、Thread、Object、包装类Integer、Character等,常用接口有Runnable、Iterable、Comparable。
2.java.util包,实用工具包。常用类有Arrays、Scanner、Random、HashSet、HashMap、ArrayList、Date等,常用接口有Collection、Set、List等。
3.java.io包,提供数据输入输出。常用类有File、FileInputStream、Reader等提供文件、字节输入输出的类。
4.java.net包,为网络连接提供服务。常用类有Socket、ServerSocket、URL等。
5.java.sql包,连接数据库的包。要实现jdbc类库。
6.java.awt、(javax.awt)包,创建用户界面、绘图的。常用类有Button、Panel、(JButton、JPanel)等。
10、Java 什么是动态代理
参考:# java | 什么是动态代理?
背景:
Spring对接口类型使用JDK动态代理,对普通类使用CGLIB创建子类。
-
JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
-
CGLib动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
- CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。
代理模式在 Java 领域很多地方都有应用,它分为静态代理和动态代理,其中 Spring AOP 就是动态代理的典型例子。动态代理又分为接口代理和 cglib (子类代理)。
代理模式在我们日常中很常见,生活处处有代理:
- 看张学友的演唱会很难抢票,可以找黄牛排队买
- 嫌出去吃饭麻烦,可以叫外卖
无论是黄牛、外卖骑手都得帮我们干活。但是他们不能一手包办(比如黄牛不能帮我吃饭),他们只能做我们不能或者不想做的事。
- 找黄牛可以帮我排队买上张学友的演唱会门票
- 外卖骑手可以帮我把饭送到楼下
所以,你看。代理模式其实就是当前对象不愿意做的事情,委托给别的对象做。
1)静态代理
代理类实现了抽象角色的接口,导致代理类无法通用。比如,我的狗病了,想去看医生,但是排队挂号很麻烦,我也想有个黄牛帮我的排队挂号看病,但是黄牛它不懂这只狗的特性(黄牛跟狗不是同一类型,黄牛属于 Human 但狗属于 Animal 类)但排队挂号和排队买票相对于黄牛来说它两就是一件事,这个方法是不变的,现场排队。
2)基于接口的动态代理
如静态代理的内容所描述的,静态代理受限于接口的实现。动态代理就是通过使用反射,动态地获取抽象接口的类型,从而获取相关特性进行代理。因动态代理能够为所有的委托方进行代理。
使用动态代理有三个要点,
-
必须实现 InvocationHandler 接口,表明该类是一个动态代理执行类。
-
InvocationHandler 接口内有一实现方法如下: public Object invoke(Object proxy, Method method, Object[] args) 。使用时需要重写这个方法
-
获取代理类,需要使用 Proxy.newProxyInstance(Clas loader, Class<?>[] interfaces, InvocationHandler h) 这个方法去获取Proxy对象(Proxy 类类型的实例)
3)基于子类的动态代理
与基于接口实现类不同的是:
- CGLib (基于子类的动态代理)使用的是方法拦截器 MethodInterceptor ,需要导入 cglib.jar 和 asm.jar 包
- 基于子类的动态代理,返回的是子类对象
- 方法拦截器对 protected 修饰的方法可以进行调用
11、Java注解和Python装饰器
参考:
总结
第一点:对代码块的影响
java注解:不会对所修饰的代码产生直接的影响。
python装饰器:可以对所修饰的代码产生直接的影响。
第二点:共通处
java中注解+反射 可以实现 python装饰器同样的功能,包括面向切面编程、参数校验等。
第三点:从用途看
****从用途看注解像是注释文档一样,用于生成javadoc文档(以参数形式标注)、检查等。
装饰器像是为函数提供更多的功能,并装在不同的函数身上。
第四点:从原理看
java注解:所有注解本质是继承自接口(Annotation)的接口
python装饰器:被装饰函数的返回值 作为参数传给闭包函数执行(这个闭包函数名前面加个@,就是装饰器)
12、Java StringUtils和CollectionUtil
参考:
StringUtils中isEmpty和isBlank的区别
1)StringUtils
import org.apache.commons.lang3.StringUtils;
- StringUtils.isEmpty(String str)** 判断某字符串是否为空,为空的标准是 str==null 或 str.length()==0
- StringUtils.isBlank(String str) 判断某字符串是否为空或长度为0或由空白符(whitespace) 构成
System.out.println(StringUtils.isEmpty(null)); //true
System.out.println(StringUtils.isEmpty("")); //true
System.out.println(StringUtils.isEmpty(" ")); //false
System.out.println(StringUtils.isBlank(null)); //true
System.out.println(StringUtils.isBlank("")); //true
System.out.println(StringUtils.isBlank(" ")); //true
2)CollectionUtils
1、CollectionUtils.isEmpty的用法
public static boolean isEmpty(Collection coll) {
return coll == null || coll.isEmpty();
}
CollectionUtils.isEmpty(集合) 用来对集合null和空的判断.
2)常用方法
- 并集:CollectionUtils.union(listA, listB)
- 交集:CollectionUtils.intersection(listA, listB)
- 补集:CollectionUtils.disjunction(listA, listB)
- 差集(扣除):CollectionUtils.subtract(listA, listB)
- 判空:CollectionUtils.isEmpty(first)、CollectionUtils.isNotEmpty(first)
- 相等:CollectionUtils.isEqualCollection(first,second)
13、collection.stream()以及collect()方法
参考:
collection.stream()以及collect()方法
Java 8 Lambda 表达式及 Stream 在集合中的用法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VrT3QeUw-1662296124739)(images/java编程常用知识补充.assets/image-20220824191559008.png)]
一些概念:
1)Stream
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。
流(stream)的操作类型分为两种:
- Intermediate:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
- Terminal:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。
2)流的操作
接下来,当把一个数据结构包装成 Stream 后,就要开始对里面的元素进行各类操作了。常见的操作可以归类如下。
Intermediate:
map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
Terminal:
forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
Short-circuiting:
anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
3)Stream 中主要包含如下几个方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AXeAnC8Y-1662296124740)(images/java编程常用知识补充.assets/image-20220812104656824.png)]
1、filter
filter 对原始 Stream 进行某项测试,通过测试的元素被留下来生成一个新 Stream
//留下偶数,经过条件“被 2 整除”的 filter,剩下的数字为 {2, 4, 6}。
Integer[] sixNums = {1, 2, 3, 4, 5, 6};
Integer[] evens =Stream.of(sixNums).filter(n -> n%2 == 0).toArray(Integer[]::new);
//把每行的单词用 flatMap 整理到新的 Stream,然后保留长度不为 0 的,就是整篇文章中的全部单词了。
//REGEXP为正则表达式,具体逻辑具体分析
List<String> output = reader.lines().
flatMap(line -> Stream.of(line.split(REGEXP))).
filter(word -> word.length() > 0).collect(Collectors.toList());
2、Collectors.toMap
使用toMap()函数之后,返回的就是一个Map了,自然会需要key和value。
在.collect(Collectors.toMap(Person::getId, v -> v, (a,b)->a))
中:
-
第一个参数:Person:getId表示选择Person的getId作为map的key值;
-
第二个参数:v->v表示选择将原来的对象作为Map的value值
-
第三个参数:(a,b)->a中,如果a与b的key值相同,选择a作为那个key所对应的value值。
3、findFirst、orElse、orElseThrow
总是返回 Stream 的第一个元素,如果找不到则可以利用orElse或orElseThrow返回默认值或异常。
orElseThrow
PoAdjustRecord poAdjustRecord = poAdjustRecordList.stream()
.filter(poAdjustRecord1 -> Objects.equals(AdjustDataTypeEnum.AFTER_ADJUST_VALUE.getType(), poAdjustRecord1.getAdjustDataType()))
.findFirst().orElseThrow(() -> new ErpRuntimeException(ErrorCodeConstant.SAVE_ADJUST_RECORD_INFO_ERROR))
orElse
UserDTO userDTO = users.stream().filter(u -> u.getId() == uid).findFirst().orElse(null);
min/max/distinct
min 和 max 的功能也可以通过对 Stream 元素先排序,再 findFirst 来实现,但前者的性能会更好,为 O(n),而 sorted 的成本是 O(n log n)。同时它们作为特殊的 reduce 方法被独立出来也是因为求最大最小值是很常见的操作。
使用 distinct 来找出不重复的单词
基本例子:
例子:
List<String> widgetIds = widgets.stream().map(Widget::getWidgetId).collect(Collectors.toList());
List<String> list= Arrays.asList("a", "b", "c", "d");
List<String> collect =list.stream().map(String::toUpperCase).collect(Collectors.toList());
System.out.println(collect); //[A, B, C, D]
List<Integer> num = Arrays.asList(1,2,3,4,5);
List<Integer> collect1 = num.stream().map(n -> n * 2).collect(Collectors.toList());
System.out.println(collect1); //[2, 4, 6, 8, 10]
解释下一这行代码:
- widgets:一个实体类的集合,类型为List
- Widget:实体类
- getWidgetId:实体类中的get方法,为获取Widget的id
本来想要获得wiget的id集合,按照我的思路肯定是遍历widges,依次取得widgetIds,但是此行代码更加简洁,高效
14、BeanUtils
参考:
Beanutils.copyProperties( )用法及重写提高效率
如果你有两个具有很多相同属性的JavaBean,一个很常见的情况就是Struts里的PO对象(持久对象)和对应的ActionForm。例如:一个用户注册页面,有一个User实体类和一个UserActionForm,我们一般会在Action里从ActionForm构造一个PO对象,传统的方式是使用类似下面的语句对属性逐个赋值:
// 获取 ActionForm 表单数据
UserActionForm uForm = (UserActionForm) form;
// 构造一个User对象
User user = new User();
// 逐一赋值
user.setUsername(uForm.getUsername);
user.setPassword(uForm.getPassword);
user.setAge(uForm.getAge);
...........
...........
// 然后调用JDBC、或操作Hibernate 持久化对象User到数据库
HibernateDAO.save(user);
使用 BeanUtils.copyProperties() 方法以后,代码量大大的减少,而且整体程序看着也简洁明朗,代码如下:
// 获取 ActionForm 表单数据
UserActionForm uForm = (UserActionForm) form;
// 构造一个User对象
User user = new User();
BeanUtils.copyProperties(uForm,user);
// 然后调用JDBC、或操作Hibernate 持久化对象User到数据库
HibernateDAO.save(user);
注:如果User和UserActionForm 间存在名称不相同的属性,则BeanUtils不对这些属性进行处理,需要手动处理。例如:
User类里面有个createDate 创建时间字段,而UserActionForm里面无此字段。BeanUtils.copyProperties()不会对此字段做任何处理。必须要自己手动处理。
user.setModifyDate(new Date());
15、String类常用方法
参考:菜鸟string
1)valueof
返回给定data type类型x参数的字符串表示形式。
ouble d = 1100.00;
boolean b = true;
long l = 1234567890;
char[] arr = {'r', 'u', 'n', 'o', 'o', 'b' };
System.out.println("返回值 : " + String.valueOf(d) );
System.out.println("返回值 : " + String.valueOf(b) );
System.out.println("返回值 : " + String.valueOf(l) );
System.out.println("返回值 : " + String.valueOf(arr) );
16、异步任务:CompletableFuture
参考:
Java8 CompletableFuture 用法全解(介绍的很详细、之后可以细看)
概要
在Java中CompletableFuture用于异步编程,异步编程是编写非阻塞的代码,运行的任务在一个单独的线程,与主线程隔离,并且会通知主线程它的进度,成功或者失败。
在这种方式中,主线程不会被阻塞,不需要一直等到子线程完成。主线程可以并行的执行其他任务。
使用这种并行方式,可以极大的提高程序的性能。
CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步回调、流式处理、多个Future组合处理的能力,使Java在处理多任务的协同工作时更加顺畅便利。
分类
一、创建异步任务
1、Future.submit
2、supplyAsync / runAsync
二、异步回调
1、thenApply / thenApplyAsync
2、thenAccept / thenRun
3、 exceptionally
4、whenComplete
5、handle
三、组合处理
1、thenCombine / thenAcceptBoth / runAfterBoth
2、applyToEither / acceptEither / runAfterEither
3、thenCompose
4、allOf / anyOf
其他:
1、get和join的异同点
- 相同:join()和get()方法都是用来获取CompletableFuture异步之后的返回值
- 不同点:
- join()方法抛出的是uncheck异常(即未经检查的异常),不会强制开发者抛出。
- get()方法抛出的是经过检查的异常,ExecutionException, InterruptedException 需要用户手动处理(抛出或者 try catch)
PS:
RuntimeException(运行时异常、非检查异常) 是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。 如果出现 RuntimeException,那么一定是程序员代码书写导致的错误.
CheckedException:一般是外部错误,这种异常都发生在编译阶段,Java 编译器会强制程序去捕获此类异常,即会出现要求你把这段可能出现异常的程序进行 try catch
具体介绍
创建异步任务会创建子线程。子线程是异步执行的,主线程休眠等待子线程执行完成,子线程执行完成后唤醒主线程,主线程获取任务执行结果后退出。
1、supplyAsync / runAsync(商城代码中用了runAsync)
supplyAsync表示创建带返回值的异步任务的,相当于ExecutorService submit(Callable task) 方法,runAsync表示创建无返回值的异步任务,相当于ExecutorService submit(Runnable task)方法,这两方法的效果跟submit是一样的,测试用例如下(一般使用lambda表达式):
- 使用
runAsync()
运行异步计算 如果你想异步的运行一个后台任务并且不想改任务返回任务东西,这时候可以使用CompletableFuture.runAsync()
方法,它持有一个Runnable 对象,并返回CompletableFuture<Void>
。
// Using Lambda Expression
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
// Simulate a long-running Job
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
System.out.println("I'll run in a separate thread than the main thread.");
});
CompletableFuture可以从全局的 ForkJoinPool.commonPool()获得一个线程中执行这些任务。 但是你也可以创建一个线程池并传给runAsync()
和supplyAsync()
方法来让他们从线程池中获取一个线程执行它们的任务。
Executor executor = Executors.newFixedThreadPool(10);
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "Result of the asynchronous computation";
}, executor);
商城代码:
CompletableFuture.runAsync(()->{
contractCapService.updateContractAmountByPreOrder(prePoDbList, prePoLineDbList,
ContractRecordTypeEnum.POSITIVE, ContractRecordFromEnum.PRE_SUBMIT);
}, ThreadPoolTraceExecutor.traceThreadPool()); //
-
使用
supplyAsync()
运行一个异步任务并且返回结果 当任务不需要返回任何东西的时候,CompletableFuture.runAsync()
非常有用。但是如果你的后台任务需要返回一些结果应该要怎么样?CompletableFuture.supplyAsync()
就是你的选择。它持有supplier<T>
并且返回CompletableFuture<T>
,T
是通过调用 传入的supplier取得的值的类型。
2、allof
- allOf返回的CompletableFuture是多个任务都执行完成后才会执行,只有有一个任务执行异常,则返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回null。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4kV91hGb-1662296124740)(images/java编程常用知识补充.assets/image-20220728143249473.png)]
17、java 枚举用法
参考:
用法三:向枚举中添加新方法(实习中遇到这种写法)
public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
// 普通方法
public static String getName(int index) {
for (Color c : Color.values()) {
if (c.getIndex() == index) {
return c.name;
}
}
return null;
}
// get set 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
18、Java中List.forEach()方法使用
参考:
例子:
// 创建集合
List<String> list =Lists.newArrayList("a","b","c","d");
//1、正常遍历
list.forEach(item->System.out.println(item));
//2、条件遍历
list.forEach(item->{
if("b".equals(item)){
System.out.println(item);
}
结论:
- 如果只是遍历集合或者数组,用foreach好些,快些。
- 如果对集合中的值进行修改,确定循环次数就要用for循环了。
19、Java Maps.newHashMap()使用
参考:
Guava - Maps.newHashMap 和 new HashMap 区别
Map<String, Object> result = new HashMap<String,Object>();
Map<String, Object> result = Maps.newHashMap();
- 是google的guava.jar提供的方法,不需要手动加泛型,仅此而已,简化写法,并没有性能方面的提升!
20、Java中Synchronized的用法
参考:
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
- 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
- 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
- 修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
- 修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象。
详细例子见原文,笔记只做记录。
修饰一个代码块
-
一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞
-
当一个线程访问对象的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该对象中的非synchronized(this)同步代码块
-
此时每个对象一把锁(对比修饰静态方法)
class SyncThread implements Runnable {
private static int count;
public SyncThread() {
count = 0;
}
public void run() {
synchronized(this) { /
for (int i = 0; i < 5; i++) {
try {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public int getCount() {
return count;
}
}
- 还可以指定要给某个对象加锁
class AccountOperator implements Runnable{
private Account account;
public AccountOperator(Account account) {
this.account = account;
}
public void run() {
synchronized (account) {
account.deposit(500);
account.withdraw(500);
System.out.println(Thread.currentThread().getName() + ":" + account.getBalance());
}
}
}
修饰一个方法
Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,public synchronized void method(){//todo}; synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。
Synchronized作用于整个方法的写法。
写法一:
public synchronized void method()
{
// todo
}
写法二:
public void method()
{
synchronized(this) {
// todo
}
}
修饰一个静态方法
Synchronized也可修饰一个静态方法,用法如下:
public synchronized static void method() {
// todo
}
静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。
- 此时,该类的所有对象共用一把锁
修饰一个类
synchronized作用于一个类T时,是给这个类T加锁,T的所有对象用的是同一把锁
总结
A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是**一个静态方法或一个类,则它取得的锁是对类**,该类所有的对象同一把锁。
B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
21、Arrays.asList() 和Collections.singletonList()的区别
参考:Arrays.asList() 和Collections.singletonList()的区别
两者最大区别就是可变性和不可变性
- Arrays.asList()得到的List是可变的,跟据数组大小确定;Collections.singletonList是不可变的
- Collections.singletonList()得到的List元素只能有1个
- 既然Collections.singletonList()不可变,那就不要尝试对其元素修改,一旦修改就会导致UnsupportedOperationException异常抛出
22、Java Collections.emptyList()方法使用
参考:
java Collections.emptyList方法的使用及注意事项
作用:
返回一个空的List(使用前提是不会再对返回的list进行增加和删除操作)
好处:
1,new ArrayList()创建时有初始大小10,占用内存,emptyList()不用创建一个新的对象,可以减少内存开销;
2,方法返回一个emptyList()时,不会报空指针异常,如果直接返回Null,没有进行非空判断就会报空指针异常;
注意:此List与常用的List不同,它是Collections类里的静态内部类,在继承AbstractList后并没有实现add()、remove()等方法,所以返回的List不能进行增加和删除元素操作。
23、equals使用
有两种用法说明:
(1)对于字符串变量来说,使用“==”和“equals()”方法比较字符串时,其比较方法不同。
“==”比较两个变量本身的值,即两个对象在内存中的首地址。
“equals()”比较字符串中所包含的内容是否相同。
(2)使用时为了防止空指针,一般是固定字符串在前:
"xxx".equals(f.getAttribute())