public class LambdaTest {
@Test
public void test1(){
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("test1");
}
};
r1.run();
//Lambda表达式的写法
Runnable r2 = () ->{
System.out.println("test2");
};
r2.run();
}
}
->:lambda操作符或箭头操作符
->的左边:lambda形参列表,对应着要重写的接口中的抽象方法的形参列表。
->的右边:lambda体,对应着接口的实现类要重写的方法的方法体。
//1.无参且无返回值
@Test
public void test2(){
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("test1");
}
};
//Lambda表达式的写法
Runnable r2 = () ->{
System.out.println("test2");
};
}
//2.需要一个参数但无返回值
@Test
public void test3(){
Consumer<String> con = (String s) ->{
System.out.println(s);
};
}
//3.数据类型可以省略,因为可由编译器通过类型推断得出
@Test
public void test4(){
Consumer<String> con = (s) ->{
System.out.println(s);
};
}
//4.lambda若只需要一个参数,参数的小括号可以省略
@Test
public void test5(){
Consumer<String> con = s ->{
System.out.println(s);
};
}
//5.lambda需要两个或以上的参数,多条执行语句并且具有返回值
@Test
public void test6(){
Comparator<Integer> com = (o1,o2) ->{
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
}
//6.当lambda体只有一条语句时,return与大括号若有,都可以省略
@Test
public void test7(){
Comparator<Integer> com = (o1,o2) -> o1.compareTo(o2);
}
如果接口中只声明有一个抽象方法,则此接口就称为函数式接口。
因为只有给函数式接口提供实现类的对象时,我们才可以使用lambda表达式。
JDK8中声明的函数式接口都在java.util.function包下。
Integer :: compare;
类(或对象) :: 方法名
//方法引用1 对象 :: 实例方法
@Test
public void test1() {
PrintStream ps = System.out;
Consumer<String> con = ps::println;
Supplier<String> sup = emp::getName;
}
//方法引用2 类 :: 静态方法
@Test
public void test2() {
Comparator<Integer> com = Integer::compare;
Function<Double,Long> fun = Math::round;
}
//方法引用3 类 :: 实例方法
@Test
public void test4() {
Comparator<String> com = String::compareTo;
BiPredicate<String,String> bp = String::equals;
}
类名 :: new
@Test
public void test1(){
//调用的是Employee类中的空参构造器
Supplier<Employee> sup = Employee::new;
//调用的是Employee类中参数是Integer/int的构造器
Function<Integer,Employee> fun = Employee::new;
//调用的是Employee类中参数是Integer/int,String的构造器
BiFunction<Integer,String,Employee> bfun = Employee::new;
}
数组名[] :: new
@Test
public void test2(){
Function<Integer,Employee[]> fun = Employee[]::new;
}
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
//方式1:通过集合
@Test
public void test1(){
List<Employee> list = EmployeeData.getEmployees();
//返回一个顺序流
Stream<Employee> stream = list.stream();
//返回一个并行流
Stream<Employee> stream1 = list.parallelStream();
}
//方式2:通过数组
@Test
public void test2(){
//返回一个流
Integer[] arr = new Integer[]{1,2,3};
Stream<Integer> stream = Arrays.stream(arr);
int[] arr1 = new int[]{1,2,3};
IntStream stream1 = Arrays.stream(arr1);
}
//方式3:通过Stream的of()
@Test
public void test3(){
Stream<String> stream = Stream.of("AA", "BB");
}
//1-筛选与切片
@Test
public void test1(){
//filter(Predicate p)-接收Lambda,从流中排除某些元素
//练习:查询员工表中薪资大于7000的员工信息
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
stream.filter(employee -> emp.getSalary() > 7000).forEach(System.out::println);
//limit(n)-使其元素不超过指定的数量
Stream<Employee> stream1 = list.stream();
stream1.limit(5).forEach(System.out::println);
//skip(n)-跳过元素,返回一个去掉前n个元素的流,若流中元素不足n个,返回一个空流
Stream<Employee> stream2 = list.stream();
stream2.skip(5).forEach(System.out::println);
//distinct()-筛选元素,通过流所生成元素的hashCode()和equals()去除重复元素
Stream<Employee> stream3 = list.stream();
stream3.distinct().forEach(System.out::println);
}
//2-映射
@Test
public void test2(){
//map(Function f)-接收一个函数作为参数,将元素转换为其他形式或提取信息,该函数会被应用到每一个元素上
//练习:转换为大写
List<String> list = Arrays.asList("aa", "bb", "cc");
Stream<String> stream = list.stream();
//方式1
stream.map(str -> str.toUpperCase()).forEach(System.out::println);
//方式2
stream.map(String :: toUpperCase()).forEach(System.out::println);
}
//3-排序
@Test
public void test3(){
//sorted()-自然排序
Integer[] arr = new Integer[]{213,432,5435,6456};
Arrays.stream(arr).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);
}
//1-匹配与查找
@Test
public void test1(){
//allMatch(Predicate p)-检查是否匹配所有元素
//练习:是否所有员工的年龄都大于18
List<Employee> list = EmployeeData.getEmployees();
System.out.println(list.stream().allMatch(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());
//count-返回流中元素的总个数
List<Employee> list = EmployeeData.getEmployees();
System.out.println(list.stream().count());
//max(Comparator c)/min(Comparator c)-返回流中的最大值/最小值
//练习:返回最高的工资
System.out.println(list.stream().map(emp -> emp.getSalary()).max((salary1, salary2) -> Double.compare(salary1, salary2)).get());
//forEach(Consumer c)-内部迭代
list.stream().forEach(System.out::println)
}
//2-归约
@Test
public void test2(){
//reduce(T identity,BinaryOperator)-可以将流中的元素反复结合起来,得到一个值,返回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));
//reduce(BinaryOperator)-可以将流中的元素反复结合起来,得到一个值。返回Optional
//练习:计算公司所有员工工资的总和
List<Employee> list = EmployeeData.getEmployees();
System.out.println(list.stream().map(emp -> emp.getSalary()).reduce(Double::sum));
}
//3-收集
@Test
public void test3(){
//collect(Collector c)-将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
//练习:查找工资大于6000的员工,结果返回为一个list或set
List<Employee> list = EmployeeData.getEmployees();
List<Boolean> list1 = list.stream().map(emp -> emp.getSalary() > 6000).collect(Collectors.toList());
list1.forEach(System.out::println);
}
JDK9 的新特性。jShell。以交互式的方式对语句和表达
式进行求值。即写即得、快速运行。
在 try 的后面可以增加一个(),在括号中可以声明流对象并初化。try 中的代码执行完毕,会自动把流对象释放,就不用写 finally了。
try(资源对象的声明和初始化){
业务逻辑代码,可能会产生异常
}catch(异常类型 1 e){
处理异常代码
}catch(异常类型 2 e){
处理异常代码
}
//JDK7
@Test
public void test1() {
try (FileWriter fw = new FileWriter("hello.txt"); BufferedWriter bw = new BufferedWriter(fw);) {
bw.write("hello");
} catch (IOException e) {
e.printStackTrace();
}
}
try 的前面可以定义流对象,try 后面的()中可以直接引用流对象的名称。在 try代码执行完毕后,流对象也可以释放掉,也不用写 finally 了。
A a = new A();
B b = new B();
try(a;b){
可能产生的异常代码
}catch(异常类名 变量名){
异常处理的逻辑
}
//JDK9
@Test
public void test2() {
InputStreamReader reader = new InputStreamReader(System.in);
OutputStreamWriter writer = new OutputStreamWriter(System.out);
try (reader; writer) {
} catch (IOException e) {
e.printStackTrace();
}
}
JDK10的新特性。局部变量的显示类型声明,常常被认为是不必须的,给一个好听的名字反而可以很清楚的表达出下面应该怎样继续。本新特性允许开发人员省略通常不必要的局部变量类型声明,以增强 Java 语言的体验性、可读性。
//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();
不适用场景:
JDK14中的预览特性,在JDK15中未进行修改,在JDK16中正式使用。instanceof 模式匹配通过提供更为简便的语法,来提高生产力。有了该功能,可以减少 Java 程序中显式强制转换的数量,实现更精确、简洁的类型安全的代码。
@Test
public void test1() {
Object obj = new String("hello");
if (obj instanceof String str) {
System.out.println(str.contains("Java"));
} else {
System.out.println("error");
}
}
}
class Computer {
private String model;
private double price;
@Override
public boolean equals(Object obj) {
return obj instanceof Computer other &&this.model.equals(other.model) && this.price == other.price;
}
}
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);
}
}
@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);
}
static String formatter(Object o) {
String formatted = switch (o) {
case Integer i:
yield "int" + i;
case Long l:
yield "long" + l;
case Double d:
yield "double" + d;
default:
yield o.toString();
};
return formatted;
}
在 Java 中,通常需要使用 String 类型表达 HTML,XML,SQL 或 JSON 等格式的字符串,在进行字符串赋值时需要进行转义和连接操作,然后才能编译该代码,这种表达方式难以阅读并且难以维护。
使用"""作为文本块的开始符和结束符,在其中就可以放置多行的字符串,不需要进行任何转义。因此,文本块将提高 Java 程序的可读性和可写性。
String text2 = """
The Sound of silence
Hello darkness, my old friend
I've come to talk with you again
Because a vision softly creeping
Left its seeds while I was sleeping
And the vision that was planted in my brain
Still remains
Within the sound of silence
""";
System.out.println(text2);
JDK14 的版本主要增加了两个 escape sequences,分别是
String sql2 = """
SELECT id,NAME,email \
FROM customers\s\
WHERE id > 4 \
ORDER BY email DESC
""";
System.out.println(sql2);
record 是一种全新的类型,它本质上是一个 final 类,同时所有的属性都是final 修饰,它会自动编译出 public get 、hashcode 、equals、toString、构造器等结构,减少了代码编写量。
public record Order1(int orderId,String orderName) {
}
用 record 声明一个类时,该类将自动拥有以下功能:
此外:
在 Java 中如果想让一个类不能被继承和修改,这时我们应该使用 final 关键字对类进行修饰。不过这种要么可以继承,要么不能继承的机制不够灵活,有些时候我们可能想让某个类可以被某些类型继承,但是又不能随意继承,是做不到的。Java 15 尝试解决这个问题,引入了 sealed 类,被 sealed 修饰的类可以指定子类。这样这个类就只能被指定的类继承。
具体使用:
- 使用修饰符 sealed,可以将一个类声明为密封类。密封的类使用保留关键字permits 列出可以直接扩展(即 extends)它的类。
- sealed 修饰的类的机制具有传递性,它的子类必须使用指定的关键字进行修饰,且只能是 final、sealed、non-sealed 三者之一。
JDK8的新特性。
到目前为止,臭名昭著的空指针异常是导致 Java 应用程序失败的最常见原因。以前,为了解决空指针异常,Google 在著名的 Guava 项目引入了 Optional类,通过检查空值的方式避免空指针异常。受到 Google 的启发,Optional 类已经成为 Java 8 类库的一部分。
为了避免代码中出现空指针异常。
Optional 类(java.util.Optional) 是一个容器类,它可以保存类型 T 的值,代表这个值存在。或者仅仅保存 null,表示这个值不存在。如果值存在,则isPresent()方法会返回 true,调用 get()方法会返回该对象。
@Test
public void test1(){
String str = "hello";
str = null;
//使用Optional避免空指针异常
Optional<String> optional = Optional.ofNullable(str);
String other = "你好";
String finalStr = optional.orElse(other);
System.out.println(finalStr.toString());
}