一、Java7
1、try-with-resources
JDK7之前的写法:
String path = "/Users/hanxiantao/Desktop/hello.txt";
OutputStream out = null;
try {
out = new FileOutputStream(path);
out.write("hello world".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
使用try-with-resources:
在try后的括号中初始化资源,可以实现资源⾃动关闭
String path = "/Users/hanxiantao/Desktop/hello.txt";
try (OutputStream out = new FileOutputStream(path);) {
out.write("hello world".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
注意点:
- 实现了AutoCloseable接⼝的类,在try()⾥声明该类实例的时候,try结束后⾃动调⽤的
close()
⽅法,这个动作会早于finally⾥调⽤的⽅法 - 不管是否出现异常,try()⾥的实例都会被调⽤
close()
⽅法 - try()⾥⾯可以声明多个⾃动关闭的对象,越早声明的对象,会越晚被close掉
二、Java8
1、接口的增强
在JDK1.8以前接⼝⾥⾯是只能有抽象⽅法,不能有任何⽅法的实现的
JDK1.8引⼊了新的关键字default,使⽤default修饰⽅法,可以在接⼝⾥⾯定义具体的⽅法实现
默认⽅法:接⼝⾥⾯定义⼀个默认⽅法,又叫做扩展方法,在实现该接口时,该默认扩展方法在子类上可以直接使用
public interface Animal {
void run();
default void breath() {
System.out.println("使用氧气呼吸");
}
}
Animal dog = new Animal() {
@Override
public void run() {
System.out.println("狗run");
}
};
dog.breath();
dog.run();
静态方法:通过接⼝名.静态⽅法
来访问接⼝中的静态⽅法
public interface Animal {
void run();
default void breath() {
System.out.println("使用氧气呼吸");
}
static void eat() {
System.out.println("吃");
}
}
Animal.eat();
2、base64加解密API
1)、什么是Base64编码
Base64是⽹络上最常⻅的⽤于传输8Bit字节码的编码⽅式之⼀,Base64就是⼀种基于64个可打印字符来表示⼆进制数据的⽅法,基于64个字符A-Z、a-z、0-9、+、/的编码⽅式(在日常使用中我们还会看到=
或==
号出现在Base64的编码结果中,=
在此是作为填充字符出现),是⼀种能将任意⼆进制数据⽤64种元字符组合成字符串的⽅法,⽽这个⼆进制数据和字符串之间是可以互相转换的
2)、JDK1.8之前相关API
使⽤JDK⾥sun.misc
下的BASE64Encoder和BASE64Decoder这两个类
public static void main(String[] args) throws IOException {
BASE64Encoder encoder = new BASE64Encoder();
BASE64Decoder decoder = new BASE64Decoder();
String text = "hello world";
byte[] bytes = text.getBytes("UTF-8");
//编码
String encodedText = encoder.encode(bytes);
System.out.println(encodedText);
//解码
byte[] bytes2 = decoder.decodeBuffer(encodedText);
System.out.println(new String(bytes2, "UTF-8"));
}
执行结果:
aGVsbG8gd29ybGQ=
hello world
缺点:编码和解码的效率⽐较差
3)、JDK1.8相关API
Base64.Encoder encoder = Base64.getEncoder();
Base64.Decoder decoder = Base64.getDecoder();
String text = "hello world";
byte[] bytes = text.getBytes("UTF-8");
//编码
String encodedText = encoder.encodeToString(bytes);
System.out.println(encodedText);
//解码
byte[] bytes2 = decoder.decode(encodedText);
System.out.println(new String(bytes2, "UTF-8"));
3、时间日期处理类
参考之前的博客开发中常用Java8日期和时间相关API
4、Optional类
Optional类主要解决的是空指针异常,本质是⼀个包含有可选值的包装类,这意味着Optional类既可以含有对象也可以为空
1)、创建Optional类
of()
:null值作为参数传递进去,则会抛异常
Student student = null;
Optional<Student> optional = Optional.of(student);
ofNullable()
:如果对象即可能是null也可能是⾮null
Student student = null;
Optional<Student> optional = Optional.ofNullable(student);
2)、访问Optional对象的值
get()
:获取Optional对象的值
isPresent()
:如果值存在会返回true,调⽤get()
⽅法会返回该对象⼀般使⽤get()
之前需要先验证是否有值,不然还会抛出异常java.util.NoSuchElementException: No value present
Student student = null;
Optional<Student> optional = Optional.ofNullable(student);
if (optional.isPresent()) {
System.out.println("optional不为空");
Student s = optional.get();
} else {
System.out.println("optional为空");
}
3)、兜底方法
orElse()
:如果Optional对象有值则返回该值,否则返回传递给它的参数值
Student s1 = null;
Student s2 = new Student("小明", 22);
Student student = Optional.ofNullable(s1).orElse(s2);
System.out.println(student);
Student s1 = null;
Integer age = Optional.ofNullable(s1).map(obj -> obj.getAge()).orElse(6);
System.out.println(age);
5、Lambda表达式
函数编程:即可理解是将⼀个函数(也称为⾏为)作为⼀个参数进⾏传递, ⾯向对象编程是对数据的抽象(各种各样的POJO类),⽽函数式编程则是对⾏为的抽象(将⾏为作为⼀个参数进⾏传递)
List<Integer> list = Arrays.asList(22, 30, 19);
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);//降序
}
});
list.stream().forEach(e -> System.out.println(e));
或者:
List<Integer> list = Arrays.asList(22, 30, 19);
Collections.sort(list, Comparator.reverseOrder());//降序
list.stream().forEach(e -> System.out.println(e));
使用Lambda表达式:
List<Integer> list = Arrays.asList(22, 30, 19);
Collections.sort(list, (a, b) -> b.compareTo(a));//降序
list.stream().forEach(e -> System.out.println(e));
Lambda表达式使⽤场景:⼀个接⼝中只包含⼀个⽅法,则可以使⽤Lambda表达式,这样的接⼝称之为函数接⼝语法 (params) -> expression
第⼀部分为括号内⽤逗号分隔的形式参数,参数是函数式接⼝⾥⾯⽅法的参数;第⼆部分为⼀个箭头符号->;第三部分为⽅法体,可以是表达式和代码块
参数列表:
括号中参数列表的数据类型可以省略不写
括号中的参数只有⼀个,那么参数类型和()都可以省略不写
⽅法体:
如果{}中的代码只有⼀⾏,⽆论是否返回值,可以省略{},return、分号要⼀起省略;其他则需要加上
Lambda表达式的实现⽅式在本质是以匿名内部类的⽅式进⾏实现
6、@FunctionalInterface注解
@FunctionalInterface
:声明该接口为函数式接口
函数式接口:当然首先是一个接口,然后就是在这个接口里面只能有一个抽象方法
public static void main(String[] args) {
System.out.println(operator(5, 20, (x, y) -> x + y));
System.out.println(operator(5, 20, (x, y) -> x - y));
System.out.println(operator(5, 20, (x, y) -> x * y));
System.out.println(operator(5, 20, (x, y) -> x / y));
}
public static Integer operator(Integer x, Integer y,
OperFunction<Integer, Integer> of) {
return of.operator(x, y);
}
7、函数式编程
1)、Function接口
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
传⼊⼀个值经过apply()
的计算返回另⼀个值,T为入参类型,R为出参类型
将转换逻辑提取出来,解耦合
Function<Integer, Integer> func = p -> p + 100;
System.out.println(func.apply(100));//200
map()
方法:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
2)、BiFunction接口
Function只能接收⼀个参数,如果要传递两个参数,则⽤BiFunction
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
BiFunction<Integer, Integer, Integer> biFunction = (a, b) -> a + b;
System.out.println(biFunction.apply(10, 20));//30
3)、Consumer接口
Consumer为消费型接⼝:有⼊参,⽆返回值
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
⽤途: 因为没有出参,常⽤于打印、发送短信等消费动作
Consumer<String> consumer = a -> System.out.println(a);
consumer.accept("hello world");
forEach()
方法:
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
List<String> list = Arrays.asList("hello", "world");
list.forEach(e -> System.out.println(e));
4)、Supplier接口
Supplier为供给型接⼝:⽆⼊参,有返回值
@FunctionalInterface
public interface Supplier<T> {
T get();
}
⽤途:泛型⼀定和⽅法的返回值类型是⼀种类型,如果需要获得⼀个数据,并且不需要传⼊参数,可以使⽤Supplier接⼝,例如:⽆参的⼯⼚⽅法,即⼯⼚设计模式创建对象,简单来说就是提供者
public static void main(String[] args) {
Student student = newStudent();
System.out.println(student.getName());
}
public static Student newStudent() {
Supplier<Student> supplier = () -> {
Student student = new Student();
student.setName("小明");
return student;
};
return supplier.get();
}
5)、Predicate接口
Predicate为断⾔型接⼝:有⼊参,有返回值,返回值类型确定是boolean
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
⽤途:接收⼀个参数,⽤于判断是否满⾜⼀定的条件,过滤数据
public static void main(String[] args) {
List<String> list = Arrays.asList("aaa", "xsda", "a2weqe", "akkk", "eeq");
List<String> result = filter(list, obj -> obj.startsWith("a"));
System.out.println(result);
}
public static List<String> filter(List<String> list, Predicate<String> predicate) {
List<String> result = new ArrayList<>();
for (String str : list) {
if (predicate.test(str)) {
result.add(str);
}
}
return result;
}
filter()
方法:
Stream<T> filter(Predicate<? super T> predicate);
6)、⽅法引⽤与构造函数引⽤
//静态函数
Function<String, Integer> func = Integer::parseInt;
System.out.println(func.apply("1024"));
//非静态函数
String context = "hello world";
Function<Integer, String> func2 = context::substring;
System.out.println(func2.apply(1));
//构造函数 多个参数
BiFunction<String, Integer, Student> biFunction = Student::new;
System.out.println(biFunction.apply("小明", 22));
//构造函数 单个参数
Function<String, Student> fuc3 = Student::new;
System.out.println(fuc3.apply("小明"));
8、集合框架
1)、流stream的使⽤
1)什么是stream
stream中⽂称为流,通过将集合转换为⼀种叫做流的元素队列,通过声明性⽅式,能够对集合中的每个元素进⾏⼀系列并⾏或串⾏的流⽔线操作
元素是特定类型的对象,所以元素集合看作⼀种流,流在管道中传输,且可以在管道的节点上进⾏处理,⽐如排序、聚合、过滤等操作
2)操作详情
- 数据元素:原始集合,如List、Set、Map等
- ⽣成流:可以是串⾏流
stream()
或者并⾏流parallelStream()
- 中间操作:可以是排序、聚合、过滤、转换等
- 终端操作:很多流操作本身就会返回⼀个流,所以多个操作可以直接连接起来,最后统⼀进⾏收集
2)、map()
方法
将流中的每⼀个元素T映射为R(类似类型转换)
场景:转换对象,如JavaWeb开发中集合⾥⾯的DO对象转换为DTO对象
List<User> list = Arrays.asList(new User(1, "Tom", "123"),
new User(2, "Jack", "123456"));
List<Object> userDTOList = list.stream().map(obj -> {
UserDTO dto = new UserDTO(obj.getId(), obj.getUsername());
return dto;
}).collect(Collectors.toList());
System.out.println(userDTOList);
3)、filter()
方法
⽤于通过设置的条件过滤出元素
需求:过滤出字符串⻓度⼤于5的字符串
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
List<String> resultList = list.stream().filter(obj -> obj.length() > 5)
.collect(Collectors.toList());
System.out.println(resultList);
4)、sorted()
方法
sorted()
对流进⾏⾃然排序,其中的元素必须实现Comparable接⼝
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
List<String> resultList = list.stream().sorted()
.collect(Collectors.toList());
System.out.println(resultList);
sorted(Comparator<? super T> comparator)
⽤来⾃定义升降序
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
//根据长度进行生序排序
List<String> resultList = list.stream().sorted(Comparator.comparing(obj -> obj.length()))
.collect(Collectors.toList());
System.out.println(resultList);
//根据长度进行降序排序
// List<String> resultList2 = list.stream().sorted(Comparator.comparing(obj -> obj.length(), Comparator.reverseOrder()))
// .collect(Collectors.toList());
List<String> resultList2 = list.stream().sorted(Comparator.comparing(String::length).reversed())
.collect(Collectors.toList());
System.out.println(resultList2);
5)、limit()
方法
截断流使其最多只包含指定数量的元素
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
//截取长度前三的元素
List<String> resultList = list.stream().sorted(Comparator.comparing(String::length).reversed())
.limit(3).collect(Collectors.toList());
System.out.println(resultList);
6)、allMatch()
方法
检查是否匹配所有元素,只有全部符合才返回true
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
boolean flag = list.stream().allMatch(obj -> obj.length() > 1);
System.out.println(flag);//true
boolean flag2 = list.stream().allMatch(obj -> obj.length() > 5);
System.out.println(flag2);//false
7)、anyMatch()
方法
检查是否⾄少匹配⼀个元素
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
boolean flag = list.stream().anyMatch(obj -> obj.length() > 5);
System.out.println(flag);//true
8)、max()
方法
最大值
List<Student> list = Arrays.asList(new Student(10), new Student(12),
new Student(11), new Student(9));
Optional<Student> optional = list.stream().max(Comparator.comparingInt(Student::getAge));
System.out.println(optional.get().getAge());
9)、min()
方法
最小值
List<Student> list = Arrays.asList(new Student(10), new Student(12),
new Student(11), new Student(9));
Optional<Student> optional = list.stream().min(Comparator.comparingInt(Student::getAge));
System.out.println(optional.get().getAge());
10)、并行流
集合做重复的操作,如果使⽤串⾏执⾏会相当耗时,因此⼀般会采⽤多线程来加快,Java8的parallelStream()
⽤Fork/Join框架提供了并发执⾏能⼒
底层原理:线程池(ForkJoinPool)维护⼀个线程队列,分割任务,将⽗任务拆分成⼦任务
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
//顺序输出
numbers.stream().forEach(System.out::println);
System.out.println("--------------------");
//并⾏乱序输出
numbers.parallelStream().forEach(System.out::println);
1)parallelStream()
并⾏是否⼀定⽐Stream串⾏快吗?
错误,数据量少的情况,可能串⾏更快,ForkJoin会耗性能
2)多数情况下并⾏⽐串⾏快,是否可以都⽤并⾏
不⾏,部分情况会有线程安全问题,parallelStream()
⾥⾯使⽤的外部变量,⽐如集合⼀定要使⽤线程安全集合,不然就会引发多线程安全问题
for (int i = 0; i < 10; ++i) {
List<Integer> list = new ArrayList<>();
IntStream.range(0, 100).parallel().forEach(list::add);
System.out.println(list.size());
}
执行抛出异常java.lang.ArrayIndexOutOfBoundsException
for (int i = 0; i < 10; ++i) {
List<Integer> list = new CopyOnWriteArrayList<>();
IntStream.range(0, 100).parallel().forEach(list::add);
System.out.println(list.size());
}
size均为100
11)、reduce()
方法
1)reduce(BinaryOperator<T> accumulator)
方法
//累加器
Integer result = Stream.of(1, 2, 3, 4, 5)
.reduce((item1, item2) -> item1 + item2).get();
System.out.println(result);
2)reduce(T identity, BinaryOperator<T> accumulator)
方法
//初始值为10,在此基础上进行累加
Integer result = Stream.of(1, 2, 3, 4, 5)
.reduce(10, (sum, item) -> sum + item);
System.out.println(result);
12)、forEach()
方法
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
注意点:
- 不能修改包含外部变量的值
- 不能⽤break或者return或者continue等关键词结束或者跳过循环
9、收集器和集合统计
1)、收集器collector
collect()
方法是一个终端操作,⽤于对流中的数据进⾏归集操作,collect()
方法接收的参数是⼀个Collector
<R, A> R collect(Collector<? super T, A, R> collector);
Collector:收集器,也是⼀个接⼝,它的⼯具类Collectors提供了很多⼯⼚⽅法
Collectors:⼯具类,提供了很多常⻅的收集器实现
Collectors.toList()
Collectors.toMap()
Collectors.toSet()
Collectors.toCollection():⽤⾃定义的实现Collection的数据结构收集
1)Collectors.toCollection(LinkedList::new)
2)Collectors.toCollection(CopyOnWriteArrayList::new)
3)Collectors.toCollection(TreeSet::new)
案例:
List<User> list = Arrays.asList(new User(1, "Tom"),
new User(2, "Jack"), new User(1, "Lucky"));
//如果有重复的key,则保留key1,舍弃key2
Map<Integer, User> map = list.stream().collect(
Collectors.toMap(User::getId, e -> e, (k1, k2) -> k1));
System.out.println(map);
2)、joining()
方法
拼接函数joining()
public static Collector<CharSequence, ?, String> joining()
//参数1:元素之间的连接符
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter)
//参数1:元素之间的连接符 参数2:前缀 参数3:后缀
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix)
案例:
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
String result = list.stream().collect(Collectors.joining(",", "[", "]"));
System.out.println(result);
3)、partitioningBy()
方法
public static <T>
Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
return partitioningBy(predicate, toList());
}
需求:根据list⾥⾯进⾏分组,字符串⻓度⼤于4的为⼀组,其他为另外⼀组
List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
"Redis", "Netty", "Java", "Docker");
Map<Boolean, List<String>> map = list.stream().collect(Collectors.partitioningBy(obj -> obj.length() > 4));
System.out.println(map);
4)、groupingBy()
方法
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier) {
return groupingBy(classifier, toList());
}
需求:根据学⽣所在的省份,进⾏分组
List<Student> students = Arrays.asList(new Student("⼴东", 23),
new Student("⼴东", 24), new Student("⼴东", 23),
new Student("北京", 22), new Student("北京", 20),
new Student("北京", 20), new Student("海南", 25));
Map<String, List<Student>> map = students.stream().collect(Collectors.groupingBy(obj -> obj.getProvince()));
map.forEach((key, value) -> {
System.out.println(key);
value.stream().forEach(e -> System.out.println(e.getAge()));
System.out.println("--------------------");
});
需求:统计各个省份的⼈数
List<Student> students = Arrays.asList(new Student("⼴东", 23),
new Student("⼴东", 24), new Student("⼴东", 23),
new Student("北京", 22), new Student("北京", 20),
new Student("北京", 20), new Student("海南", 25));
Map<String, Long> map = students.stream().collect(Collectors.groupingBy(Student::getProvince, Collectors.counting()));
map.forEach((key, value) -> {
System.out.println(key);
System.out.println(value);
System.out.println("--------------------");
});
5)、summarizing()
集合统计
summarizing()
可以⼀个⽅法把统计相关的基本上都完成,主要有如下几种:
summarizingInt()
summarizingDouble()
summarizingLong()
List<Student> students = Arrays.asList(new Student("⼴东", 23),
new Student("⼴东", 24), new Student("⼴东", 23),
new Student("北京", 22), new Student("北京", 20),
new Student("北京", 20), new Student("海南", 25));
IntSummaryStatistics summaryStatistics = students.stream().collect(Collectors.summarizingInt(Student::getAge));
System.out.println("平均值:" + summaryStatistics.getAverage());
System.out.println("⼈数:" + summaryStatistics.getCount());
System.out.println("最⼤值:" + summaryStatistics.getMax());
System.out.println("最⼩值:" + summaryStatistics.getMin());
System.out.println("总和:" + summaryStatistics.getSum());