public class LambdaTest{
@Test
public void test1(){
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("我爱北京天安门");
}
};
r1.run();
System.out.println("***********************");
//使用Lambda表达式
Runnable r2 = () -> System.out.println("我爱北京故宫");
r2.run();
}
@Test
public void test2(){
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
int compare1 = com1.compare(12,21);
System.out.println(compare1);
//使用Lambda表达式
System.out.println("***********************");
Comparator<Integer> com2 = (o1,o2) -> Integer.compare(o1,o2);
int compare2 = com1.compare(32,21);
System.out.println(compare2);
//方法引用
System.out.println("***********************");
Comparator<Integer> com3 = Integer :: compare;
int compare3 = com1.compare(32,21);
System.out.println(compare3);
}
}
/*
lambda形参列表 -> lambda体
-> :lambda操作符
-> 的左边 lambda形参列表,对应着要重写的接口方法的形参列表
-> 的右边 lamda体,对应着接口的实现类要重写的方法的方法体
*/
Lambda表达式的使用,分六种情况介绍
public class LambdaTest1 {
//语法格式1 无参无返回值
@Test
public void test1() {
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("我爱北京天安门");
}
};
r1.run();
System.out.println("***********************");
//使用Lambda表达式
Runnable r2 = () -> System.out.println("我爱北京故宫");
r2.run();
}
//语法格式 2 Lambda需要1个参数,但是没有返回值
@Test
public void test2() {
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("谎言和誓言的区别是什么?");
System.out.println("***********************");
//使用Lambda表达式
Consumer<String> con1 = (String s) -> System.out.println(s);
con1.accept("一个是说的人当真了,一个是听的人当真了");
}
//语法格式3 数据类型可以省略,可以由编译器推断得出,称为"类型推断"
@Test
public void test3() {
Consumer<String> con1 = (String s) -> System.out.println(s);
con1.accept("如果大学可以重来,最想重来的事是啥?");
Consumer<String> con2 = (s) -> System.out.println(s);
con1.accept("好好努力考研!");
}
//语法格式4 Lmabda只需要一个参数的时候,小括号可以省略
@Test
public void test4() {
Consumer<String> con1 = (s) -> System.out.println(s);
con1.accept("世界那么大,我想去看看");
System.out.println("***********************");
Consumer<String> con2 = s -> System.out.println(s);
con1.accept("世界那么大,我想去看看");
}
//语法格式5 Lmabda需要两个以上的参数,多条执行语句,并且能够有返回值
@Test
public void test5() {
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println(com1.compare(12,21));
System.out.println("***********************");
Comparator<Integer> com2 = (o1,o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com2.compare(12,21));
}
//语法格式6 Lmabda体只有一条语句,return 与 大括号若有,都可省略
@Test
public void test6() {
Comparator<Integer> com1 = (o1,o2) -> {
return o1.compareTo(o2);
};
System.out.println(com1.compare(12,6));
Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
}
}
Lambda表达式的本质:
//定义函数式接口
@FunctionalInterface
public interface MyFunctionalInterfaceTest {
void method();
}
//测试
public class MyFunctionalInterfaceTest {
@Test
public void test1(){
MyFunctionalInterface m = () -> System.out.println("hello");
m.method();
}
}
函数式接口 | 称谓 | 参数类型 | 用途 |
---|---|---|---|
Consumer |
消费型接口 | T | 对类型为T的对象应用操作,包含方法: void accept(T t) |
Supplier |
供给型接口 | 无 | 返回类型为T的对象,包含方法:T get() |
Function |
函数型接口 | T | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t) |
Predicate |
判断型接口 | T | 确定类型为T的对象是否满足某约束,并返回 boolean 值。包含方法:boolean test(T t) |
Lambda表达式是可以简化函数式接口的变量或形参赋值的语法。而方法引用和构造器引用是为了简化Lambda表达式的。
格式:使用方法引用操作符 “::
” 将类(或对象) 与 方法名分隔开来。
如下三种主要使用情况:
对象 :: 实例方法名
类 :: 静态方法名
类 :: 实例方法名
public class MethodRefTest {
// 情况一:对象 :: 实例方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)
/*函数式接口总的抽象方法a与内部实现时调用的对象的某个方法b的形参列表和返回值类型都相同(或一致),此时可以考虑方法b对方法a的替换、覆盖。即为方法引用 --- b为非静态方法,需要对象调用*/
@Test
public void test1() {
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("hello");
System.out.println("*******************");
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con2.accept("hello");
}
//Supplier中的T get()
//Employee中的String getName()
@Test
public void test2() {
Employee emp = new Employee(1001,"Tom",23,5600);
Supplier<String> sup1 = () -> emp.getName();
System.out.println(sup1.get());
System.out.println("*******************");
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
}
// 情况二:类 :: 静态方法
//Comparator中的int compare(T t1,T t2)
//Integer中的int compare(T t1,T t2)
/*
函数式接口总的抽象方法a与内部实现时调用的类的某个静态方法b的形参列表和返回值类型都相同(或一致),此时可以考虑方法b对方法a的替换、覆盖。即为方法引用 --- b为静态方法,需要类调用
*/
@Test
public void test3() {
Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
System.out.println(com1.compare(12,21));
System.out.println("*******************");
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(12,3));
}
//Function中的R apply(T t)
//Math中的Long round(Double d)
@Test
public void test4() {
Function<Double,Long> func = new Function<Double, Long>() {
@Override
public Long apply(Double d) {
return Math.round(d);
}
};
System.out.println("*******************");
Function<Double,Long> func1 = d -> Math.round(d);
System.out.println(func1.apply(12.3));
System.out.println("*******************");
Function<Double,Long> func2 = Math::round;
System.out.println(func2.apply(12.6));
}
// 情况三:类 :: 实例方法 (有难度)
// Comparator中的int comapre(T t1,T t2)
// String中的int t1.compareTo(t2)
/*函数式接口总的抽象方法a与内部实现时调用的对象的某个方法b的返回值类型都相同(或一致),同时抽象方法a有n个参数,方法b中有n-1个参数,且抽象方法a的第一个参数作为方法b的调用者,且抽象方法a的后n-1个参数,与方法b的n-1个参数类型相同或一致。此时可以考虑方法b对方法a的替换、覆盖。即为方法引用 --- b为非静态方法,需要对象调用。形式上是使用对象a所属的类*/
@Test
public void test5() {
Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abc","abd"));
System.out.println("*******************");
Comparator<String> com2 = String :: compareTo;
System.out.println(com2.compare("abd","abm"));
}
//BiPredicate中的boolean test(T t1, T t2);
//String中的boolean t1.equals(t2)
@Test
public void test6() {
BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
System.out.println(pre1.test("abc","abc"));
System.out.println("*******************");
BiPredicate<String,String> pre2 = String :: equals;
System.out.println(pre2.test("abc","abd"));
}
// Function中的R apply(T t)
// Employee中的String getName();
@Test
public void test7() {
Employee employee = new Employee(1001, "Jerry", 23, 6000);
Function<Employee,String> func1 = e -> e.getName();
System.out.println(func1.apply(employee));
System.out.println("*******************");
Function<Employee,String> func2 = Employee::getName;
System.out.println(func2.apply(employee));
}
}
当Lambda表达式是创建一个对象,并且满足Lambda表达式形参,正好是给创建这个对象的构造器的实参列表,就可以使用构造器引用。
格式:类名::new
public class ConstructorRefTest {
//构造器引用
//Supplier中的T get()
//Employee的空参构造器:Employee()
@Test
public void test1(){
Supplier<Employee> sup = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
System.out.println("*******************");
Supplier<Employee> sup1 = () -> new Employee();
System.out.println(sup1.get());
System.out.println("*******************");
//构造器引用
Supplier<Employee> sup2 = Employee :: new;
System.out.println(sup2.get());
}
//Function中的R apply(T t)
@Test
public void test2(){
Function<Integer,Employee> func1 = id -> new Employee(id);
Employee employee = func1.apply(1001);
System.out.println(employee);
System.out.println("*******************");
Function<Integer,Employee> func2 = Employee :: new;
Employee employee1 = func2.apply(1002);
System.out.println(employee1);
}
//BiFunction中的R apply(T t,U u)
@Test
public void test3(){
BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);
System.out.println(func1.apply(1001,"Tom"));
System.out.println("*******************");
BiFunction<Integer,String,Employee> func2 = Employee :: new;
System.out.println(func2.apply(1002,"Tom"));
}
}
当Lambda表达式是创建一个数组对象,并且满足Lambda表达式形参,正好是给创建这个数组对象的长度,就可以数组构造引用。
格式:数组类型名::new
//数组引用
//Function中的R apply(T t)
@Test
public void test4(){
Function<Integer,String[]> func1 = length -> new String[length];
String[] arr1 = func1.apply(5);
System.out.println(Arrays.toString(arr1));
System.out.println("*******************");
Function<Integer,String[]> func2 = String[] :: new;
String[] arr2 = func2.apply(10);
System.out.println(Arrays.toString(arr2));
}
1.Stream API 关注的是多个数据的计算,面向CPU;
2.集合关注的是数据的存储,面向内存的;
3.Stream API–>集合 == SQL -->数据表的查询;
1- 创建 Stream(实例化)
一个数据源(如:集合、数组),获取一个流
2- 中间操作
每次处理都会返回一个持有结果的新Stream,即中间操作的方法返回值仍然是Stream类型的对象。因此中间操作可以是个操作链
,可对数据源的数据进行n次处理,但是在终结操作前,并不会真正执行。
3- 终止操作(终端操作)
终止操作的方法返回值类型就不再是Stream了,因此一旦执行终止操作,就结束整个Stream操作了。一旦执行终止操作,就执行中间操作链,最终产生结果并结束Stream。
方式一:通过集合
Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:
default Stream stream() : 返回一个顺序流
default Stream parallelStream() : 返回一个并行流
@Test
public void test01(){
List<Integer> list = Arrays.asList(1,2,3,4,5);
//JDK1.8中,Collection系列集合增加了方法
Stream<Integer> stream = list.stream();
}
方式二:通过数组
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
@Test
public void test02(){
String[] arr = {"hello","world"};
Stream<String> stream = Arrays.stream(arr);
}
@Test
public void test03(){
int[] arr = {1,2,3,4,5};
IntStream stream = Arrays.stream(arr);
}
方式三:通过Stream的of()
可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。
@Test
public void test04(){
Stream<Integer> stream = Stream.of(1,2,3,4,5);
stream.forEach(System.out::println);
}
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
1-筛选与切片
方 法 | 描 述 |
---|---|
filter(Predicatep) | 接收 Lambda , 从流中排除某些元素 |
distinct() | 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量 |
skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。 若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 |
2-映 射
方法 | 描述 |
---|---|
map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。 |
mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。 |
mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。 |
flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
3-排序
方法 | 描述 |
---|---|
sorted() | 产生一个新流,其中按自然顺序排序 |
sorted(Comparator com) | 产生一个新流,其中按比较器顺序排序 |
代码举例:
public class StreamAPITest {
//1- 筛选与切片
@Test
public void test1(){
// filter(Predicate p) 接收 Lambda,从流中排除某些元素
//遍历员工表薪资大于7000的员工信息
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
stream.filter(emp -> emp.getSalary() > 7000).forEach(System.out::println);
System.out.println();
// limit(long maxSize) 截断流,使其元素不超过给定数量
list.stream().limit(4).forEach(System.out::println);
System.out.println();
// skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足n个,则返回一个空流。与 limit(n) 互补
list.stream().skip(5).forEach(System.out::println);
System.out.println();
// distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
list.add(new Employee(1009, "马斯克", 35, 2500.32));
list.add(new Employee(1009, "马斯克", 35, 2500.32));
list.add(new Employee(1009, "马斯克", 35, 2500.32));
list.add(new Employee(1009, "马斯克", 35, 2500.32));
list.stream().distinct().forEach(System.out::println);
}
//2 - 映射
@Test
public void test2(){
// map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
//转化为大写
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
list.stream().map(String::toUpperCase).forEach(System.out::println);
//获取员工姓名长度大于3的员工
List<Employee> employees = EmployeeData.getEmployees();
employees.stream().filter(emp -> emp.getName().length() > 3).forEach(System.out::println);
//获取员工姓名长度大于3的员工姓名
employees.stream().filter(emp -> emp.getName().length() > 3).map(emp -> emp.getName()).forEach(System.out::println);
}
//3 - 排序
@Test
public void test3(){
//sorted 自然排序
Integer[] arr = new Integer[]{213,434,2,232,0,-434,432,43,21};
String[] arr1 = new String[]{"GG","DD","MM","SS","JJ"};
Arrays.stream(arr).sorted().forEach(System.out::println);
System.out.println("***********************************");
Arrays.stream(arr1).sorted().forEach(System.out::println);
//sorted(Comparator com) 定制排序
List<Employee> list = EmployeeData.getEmployees();
list.stream().sorted((e1,e2) -> e1.getAge() - e2.getAge()).forEach(System.out::println);
//针对字符串从大到小排序
Arrays.stream(arr1).sorted((s1,s2) -> -s1.compareTo(s2)).forEach(System.out::println);
}
}
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
流进行了终止操作后,不能再次使用。
1-匹配与查找
方法 | 描述 |
---|---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
**anyMatch(Predicate p) ** | 检查是否至少匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。 相反,Stream API 使用内部迭代——它帮你把迭代做了) |
2-归约
方法 | 描述 |
---|---|
reduce(T identity, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 T |
reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 Optional |
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
3-收集
方 法 | 描 述 |
---|---|
collect(Collector c) | 将流转换为其他形式。接收一个 Collector接口的实现, 用于给Stream中元素做汇总的方法 |
Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。
另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
方法 | 返回类型 | 作用 |
---|---|---|
toList | Collector |
把流中元素收集到List |
List<Employee> emps= list.stream().collect(Collectors.toList());
方法 | 返回类型 | 作用 |
---|---|---|
toSet | Collector |
把流中元素收集到Set |
Set<Employee> emps= list.stream().collect(Collectors.toSet());
方法 | 返回类型 | 作用 |
---|---|---|
toCollection | Collector |
把流中元素收集到创建的集合 |
Collection<Employee> emps =list.stream().collect(Collectors.toCollection(ArrayList::new));
方法 | 返回类型 | 作用 |
---|---|---|
counting | Collector |
计算流中元素的个数 |
long count = list.stream().collect(Collectors.counting());
方法 | 返回类型 | 作用 |
---|---|---|
summingInt | Collector |
对流中元素的整数属性求和 |
int total=list.stream().collect(Collectors.summingInt(Employee::getSalary));
方法 | 返回类型 | 作用 |
---|---|---|
averagingInt | Collector |
计算流中元素Integer属性的平均值 |
double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));
方法 | 返回类型 | 作用 |
---|---|---|
summarizingInt | Collector |
收集流中Integer属性的统计值。如:平均值 |
int SummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
方法 | 返回类型 | 作用 |
---|---|---|
joining | Collector |
连接流中每个字符串 |
String str= list.stream().map(Employee::getName).collect(Collectors.joining());
方法 | 返回类型 | 作用 |
---|---|---|
maxBy | Collector |
根据比较器选择最大值 |
Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
方法 | 返回类型 | 作用 |
---|---|---|
minBy | Collector |
根据比较器选择最小值 |
Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
方法 | 返回类型 | 作用 |
---|---|---|
reducing | Collector |
从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值 |
int total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
方法 | 返回类型 | 作用 |
---|---|---|
collectingAndThen | Collector |
包裹另一个收集器,对其结果转换函数 |
int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
方法 | 返回类型 | 作用 |
---|---|---|
groupingBy | Collector |
根据某属性值对流分组,属性为K,结果为V |
Map<Emp.Status, List<Emp>> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus));
方法 | 返回类型 | 作用 |
---|---|---|
partitioningBy | Collector |
根据true或false进行分区 |
Map<Boolean,List<Emp>> vd = list.stream().collect(Collectors.partitioningBy(Employee::getManage));
举例:
public class StreamAPITest2 {
//1 匹配与查找
@Test
public void test1(){
// allMatch(Predicate p) 检查是否匹配所有元素
//练习:是否所有员工的年龄都大于18
List<Employee> list = EmployeeData.getEmployees();
System.out.println(list.stream().allMatch(emp -> emp.getAge() > 18));
System.out.println(list.stream().anyMatch(emp -> emp.getAge() > 18));
// anyMatch(Predicate p) 检查是否至少匹配一个元素
//练习:是否存在员工的工资大于 10000
System.out.println(list.stream().anyMatch(emp -> emp.getSalary() > 10000));
// findFirst() 返回第一个元素
System.out.println(list.stream().findFirst().get());
}
@Test
public void test2(){
// count() 返回流中元素总数
List<Employee> list = EmployeeData.getEmployees();
System.out.println(list.stream().filter(emp -> emp.getSalary() > 7000).count());
// max(Comparator c) 返回流中最大值
//返回最高的工资
System.out.println(list.stream().map(emp -> emp.getSalary()).max((salary1, salary2) -> Double.compare(salary1, salary2)));
System.out.println(list.stream().map(emp -> emp.getSalary()).max(Double::compare).get());
// min(Comparator c) 返回流中最小值
//返回最低的工资的员工
System.out.println(list.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
// forEach(Consumer c)内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)
list.stream().forEach(System.out::println);
}
//2 - 归约
@Test
public void test3(){
// reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
//练习 计算1-10的自然数的和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(list.stream().reduce(0, (x1, x2) -> x1 + x2));
System.out.println(list.stream().reduce(10, (x1, x2) -> x1 + x2));
// reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional
//备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google用它来进行网络搜索而出名。
//计算公司所有员工的工资总和
List<Employee> employeeList = EmployeeData.getEmployees();
System.out.println(employeeList.stream().map(emp -> emp.getSalary()).reduce((salary1, salary2) -> Double.sum(salary1, salary2)));
System.out.println(employeeList.stream().map(emp -> emp.getSalary()).reduce(Double::sum));
}
//3-收集
@Test
public void test4(){
List<Employee> list = EmployeeData.getEmployees();
// collect(Collector c) 将流转换为其他形式。接收一个 Collector 接口的实现,用于给Stream中元素做汇总 的方法
//练习1 查找工资大于6000的员工 结果返回List或者Set
List<Employee> list1 = list.stream().filter(emp -> emp.getSalary() > 6000).collect(Collectors.toList());
list1.forEach(System.out::println);
System.out.println("*************************************");
list.forEach(System.out::println);
//练习2 按照员工年龄进行排序,返回一个新的List中
System.out.println("*************************************");
List<Employee> list2 = list.stream().sorted((e1, e2) -> e1.getAge() - e2.getAge()).collect(Collectors.toList());
list2.forEach(System.out::println);
}
}
Java 终于拥有了像Python 和 Scala 之类语言的REPL工具(交互式编程环境,read - evaluate - print - loop):
jShell
。以交互式的方式对语句和表达式进行求值。即写即得
、快速运行
。利用jShell在没有创建类的情况下,在命令行里直接声明变量,计算表达式,执行语句。无需跟人解释”public static void main(String[] args)”这句"废话"。
try的前面可以定义流对象,try后面的()中可以直接引用流对象的名称。在try代码执行完毕后,流对象也可以释放掉,也不用写finally了。
格式:
A a = new A();
B b = new B();
try(a;b){
可能产生的异常代码
}catch(异常类名 变量名){
异常处理的逻辑
}
举例:
@Test
public void test04() {
InputStreamReader reader = new InputStreamReader(System.in);
OutputStreamWriter writer = new OutputStreamWriter(System.out);
try (reader; writer) {
//reader是final的,不可再被赋值
// reader = null;
} catch (IOException e) {
e.printStackTrace();
}
}
局部变量的显示类型声明,常常被认为是不必须的,给一个好听的名字反而可以很清楚的表达出下面应该怎样继续。本新特性允许开发人员省略通常不必要的局部变量类型声明,以增强Java语言的体验性、可读性。(var)
使用举例
//1.局部变量的实例化
var list = new ArrayList<String>();
var set = new LinkedHashSet<Integer>();
//2.增强for循环中的索引
for (var v : list) {
System.out.println(v);
}
//3.传统for循环中
for (var i = 0; i < 100; i++) {
System.out.println(i);
}
//4. 返回值类型含复杂泛型结构
var iterator = set.iterator();
//Iterator> iterator = set.iterator();
/*
不适用场景
- 声明一个成员变量
- 声明一个数组变量,并为数组静态初始化(省略new的情况下)
- 方法的返回值类型
- 方法的参数类型
- 没有初始化的方法内的局部变量声明
- 作为catch块中异常类型
- Lambda表达式中函数式接口的类型
- 方法引用中函数式接口的类型
*/
JDK12中预览特性:
case L ->
来替代以前的break;
,省去了 break 语句,避免了因少写 break 而出错。 :
,但是同一个 switch 结构里不能混用 ->
和 :
,否则编译错误。public class SwitchTest2 {
public static void main(String[] args) {
Fruit fruit = Fruit.GRAPE;
int numberOfLetters = switch(fruit){
case PEAR -> 4;
case APPLE,MANGO,GRAPE -> 5;
case ORANGE,PAPAYA -> 6;
default -> throw new IllegalStateException("No Such Fruit:" + fruit);
};
System.out.println(numberOfLetters);
}
}
JDK13中二次预览特性:
JDK13中引入了yield语句,用于返回值。这意味着,switch表达式(返回值)应该使用yield,switch语句(不返回值)应该使用break。
yield和return的区别在于:return会直接跳出当前循环或者方法,而yield只会跳出当前switch块。
@Test
public void testSwitch2(){
String x = "3";
int i = switch (x) {
case "1" -> 1;
case "2" -> 2;
default -> {
yield 3;
}
};
System.out.println(i);
}
//或者
@Test
public void testSwitch3() {
String x = "3";
int i = switch (x) {
case "1":
yield 1;
case "2":
yield 2;
default:
yield 3;
};
System.out.println(i);
}
JDK17的预览特性:switch的模式匹配
旧写法:
static String formatter(Object o) {
String formatted = "unknown";
if (o instanceof Integer i) {
formatted = String.format("int %d", i);
} else if (o instanceof Long l) {
formatted = String.format("long %d", l);
} else if (o instanceof Double d) {
formatted = String.format("double %f", d);
} else if (o instanceof String s) {
formatted = String.format("String %s", s);
}
return formatted;
}
模式匹配新写法:
static String formatterPatternSwitch(Object o) {
String formatted = switch (o) {
case Integer i :
yield "int" + i;
case Long l:
yield "long"+ l;
case Double d:
yield "double"+ d ;
case String s:
yield "String" + s;
default:
yield o.toString();
};
return formatted;
}
/*
直接在 switch 上支持 Object 类型,这就等于同时支持多种类型,使用模式匹配得到具体类型,大大简化了语法量,这个功能很实用。
*/
使用"""作为文本块的开始符和结束符,在其中就可以放置多行的字符串,不需要进行任何转义。因此,文本块将提高Java程序的可读性和可写性。
使用:
"""
line1
line2
line3
"""
//相当于
"line1\nline2\nline3\n"
"line1\n" +
"line2\n" +
"line3\n"
JDK14中二次预览特性
JDK14的版本主要增加了两个escape sequences,分别是 \(取消换行)
与\s (空格)
。
instanceof 模式匹配通过提供更为简便的语法,来提高生产力。有了该功能,可以减少Java程序中显式强制转换的数量,实现更精确、简洁的类型安全的代码。instanceof 模式匹配通过提供更为简便的语法,来提高生产力。有了该功能,可以减少Java程序中显式强制转换的数量,实现更精确、简洁的类型安全的代码。JDK16中转正。
Java 14之前旧写法:
if(obj instanceof String){
String str = (String)obj; //需要强转
.. str.contains(..)..
}else{
...
}
Java 14新特性写法:
if(obj instanceof String str){//加变量名,不用显示强转
.. str.contains(..)..
}else{
...
}
record
是一种全新的类型,它本质上是一个
final类,同时所有的属性都是
final修饰,它会自动编译出
public get、
hashcode、
equals、
toString`、构造器等结构,减少了代码编写量。具体来说:当你用
record
声明一个类时,该类将自动拥有以下功能:
- 获取成员变量的简单方法。注意区别于我们平常getter()的写法。
- 一个 equals 方法的实现,执行比较时会比较该类的所有成员属性。
- 重写 hashCode() 方法。
- 一个可以打印该类所有成员属性的 toString() 方法。
- 只有一个构造方法。
此外:
还可以在record声明的类中定义静态字段、静态方法、构造器或实例方法。
不能在record声明的类中定义实例字段;类不能声明为abstract;不能声明显式的父类等。
record的设计目标是提供一种将数据建模为数据的好方法。它也不是 JavaBeans 的直接替代品,因为record的方法不符合 JavaBeans 的 get 标准。另外 JavaBeans 通常是可变的,而记录是不可变的。尽管它们的用途有点像,但记录并不会以某种方式取代 JavaBean。
在 Java 中如果想让一个类不能被继承和修改,这时我们应该使用
final
关键字对类进行修饰。不过这种要么可以继承,要么不能继承的机制不够灵活,有些时候我们可能想让某个类可以被某些类型继承,但是又不能随意继承,是做不到的。Java 15 尝试解决这个问题,引入了sealed
类,被sealed
修饰的类可以指定子类。这样这个类就只能被指定的类继承。
JDK15的预览特性:
通过密封的类和接口来限制超类的使用,密封的类和接口限制其它可能继承或实现它们的其它类或接口。
具体使用:
使用修饰符sealed
,可以将一个类声明为密封类。密封的类使用保留关键字permits
列出可以直接扩展(即extends)它的类。
sealed
修饰的类的机制具有传递性,它的子类必须使用指定的关键字进行修饰,且只能是 final
、sealed
、non-sealed
三者之一。
public abstract sealed class Shape permits Circle, Rectangle, Square {...}//sealed指定类才可被继承
public final class Circle extends Shape {...} //final表示Circle不能再被继承了
public sealed class Rectangle extends Shape permits TransparentRectangle{...}//sealed只能被指定的类继承
public non-sealed class Square extends Shape {...} //non-sealed表示可以允许任何类继承
到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google在著名的Guava项目引入了Optional类,通过检查空值的方式避免空指针异常。受到Google的启发,Optional类已经成为Java 8类库的一部分。
Optional
类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。如果值存在,则isPresent()方法会返回true,调用get()方法会返回该对象。
/*
1.为什么需要Optional类?
为了避免空指针异常
2.如何实例化
static Optional ofNullable(T value) :用来创建一个Optional实例,value可能是空,也可能非空
3.常用方法
T orElse(T other) :orElse(T other) 与ofNullable(T value)配合使用,如果Optional容器中非空,就返回所包装值,如果为空,就用orElse(T other)other指定的默认值(备胎)代替
*/
public class OptionalTest {
@Test
public void test() {
String value = "小明";
value = null;
//Optional类避免空指针异常
//1.实例化
//ofNullable(T value) :用来创建一个Optional实例,value可能是空,也可能非空
Optional<String> optional = Optional.ofNullable(value);
//orElse(T other) 与ofNullable(T value)配合使用,
// 如果Optional的value不为null,就返回所value,如果为空,就返回other
String other = "小洪";
String s = optional.orElse(other);
System.out.println(s.toString());
}
@Test
public void test2(){
String value = "小明";
Optional<String> optional = Optional.ofNullable(value);
//get()取出value值
System.out.println(optional.get());
}
}
String、StringBuffer 与 StringBuilder 再也不用 char[] 来存储,改成了 byte[] 加上编码标记,节约了一些空间。
JDK11新特性:新增了一系列字符串处理方法
描述 | 举例 |
---|---|
判断字符串是否为空白 | " ".isBlank(); // true |
去除首尾空白 | " Javastack ".strip(); // “Javastack” |
去除尾部空格 | " Javastack “.stripTrailing(); // " Javastack” |
去除首部空格 | " Javastack ".stripLeading(); // "Javastack " |
复制字符串 | “Java”.repeat(3);// “JavaJavaJava” |
行数统计 | “A\nB\nC”.lines().count(); // 3 |
JDK12新特性:String 实现了 Constable 接口
Keep Going!