1. lambda表达式;
- 无参数时需要写"()";
- 参数类型可推断时,不需要写类型;
- 只有一个参数时,可以不写"()";
- 返回值类型总是由上下文推导得出,所以无需指定返回类型;
- 如果存在分支,分支A有返回值,分支B无返回值是不合法的;
- 函数式接口。当需要只有一个抽象方法的接口对象是,就可以使用lambda表达式;
- lambda表达式中捕获的变量必须是事实最终变量;
- lambda表达式体与嵌套块有相同的作用域;
- lambda表达式中的this关键字,是指创建这个lambda表达式的方法的this参数;
(String first, String second) -> first.length() - second.length()
var timer = new Timer(1000, event -> System.out.println(event));
// 也可以写成
// System.out::println是一个方法引用,会由编译器生成一个函数式接口实例;
var timer = new Timer(1000, System.out::println);
// 构造器引用,例如Person类
Stream<Person> stream = names.stream().map(Person::new);
public class Application {
public void init() {
ActionListener listener = event -> {
System.out.println(this.toString()); // this指的是Application实例
// ....
}
}
}
2. 内部类会持有外部类的实例。
编译器会修改所有内部类的构造器,添加一个对应外部类引用的参数。
- 内部类可以访问外部类私有的字段;
- 只有内部类才可以声明为私有;
- 内部类中声明的所有静态字段都必须是final的;
- 内部类不能有static方法;
内部类为啥能够访问外部类的私有字段:Java 11之前,编译器会为外部类生成类似access$0的静态方法以供调用,虚拟机并没有对其有特别的了解。Java 11以上,不再生成访问方法,虚拟机会了解类之间的嵌套关系。
3. 局部内部类
- 作用域总是限定在声明这个局部类的块中;
- 不能使用访问修饰符(即public、private);
- 在内部类的基础上,还可以访问局部变量;
编译器会将使用的局部变量复制成其的一个不可变字段存储,所以即使出了局部变量的作用域,局部内部类仍然可以使用到;
4. 匿名内部类
如果构造参数列表结束小括号后面跟一个开始大括号,就是在定义匿名内部类。
- 如果将一个匿名类实例存储在用var定义的变量中,这个变量会了解增加的方法或字段;
var bob = new Object(){ String name = "Bob";}
System.out.println(bob.name); // "Bob"
- 匿名类没有构造器,但可以提供一个对象初始化块;
var count = new Person("xxxx") {
{
// initialization
}
}
- 匿名类中
getClass() != other.getClass()
这个测试会失败;
5. 静态内部类
- 只要内部类不需要访问外部类的对象,就应该使用静态内部类;
- 与常规内部类不同,静态内部类可以有静态字段和方法;
- 在接口中声明的内部类自动是public static的;
- 类中声明的接口、记录和枚举都自动是static的;
6. try-with-resources 语句
try (Resource res1 =...; Resource res2 = ... ) {
// work with res1 、res2
}
- 这个块正常退出,或者存在一个异常时,都会自动调用res1.close()、res2.close()方法关闭资源;
- 如果是异常退出,而close()若也有异常时,其异常会被自动捕获,并通过addSuppressed方法添加到原来异常中去;
- 若有catch、finally子句,这些子句会在关闭资源之后执行;
7. 通过StackWalker类可以获取实例流,其中每个实例分别描述一个栈帧;
StackWalker walker = StackWalker.getInstance();
walker.forEach(frame -> analyze frame)
walker.walk(stream -> process stream)