final

基本用法

  • 修饰类时,表示其不能被继承。final类中的所有成员方法都会被隐式指定为final方法
    final不能用来修饰抽象类
  • 修饰方法,表示不能被子类覆盖。所有private方法都被隐式指定为final方法
    final修饰的方法可以被重载
  • 修饰方法参数时,称为最终参数,其不能在方法内被修改
  • 修饰变量
    • 如果修饰基本数据类型变量,则其数值一旦被赋值就不能再修改
    • 如果修饰引用类型变量,则其不能再指向其他对象
  • 修饰成员字段,则其必须在定义时或者构造器中进行初始化赋值

    深入理解

  • 当final变量是基本数据类型或String类型时,如果能在编译期间知道它的确切值,编译器就会将它当作编译期常量使用。也就是需要访问到这个final变量的地方,相当于直接访问这个常量,而无需在运行时确认

  • 匿名内部类中使用的外部局部变量只能是final变量

    参考文档

    浅析Java中的final关键字

Var

简介

  • Java10引入,用来进行局部变量的类型自动推断
  • 事实上,var并非是关键字,它只是一个保留类型,也就是说仍然可以用var作为标识符

    1. int var = 10;

    var和继承

  • var依然能实现多态

    • var类型的子类对象能赋值给var类型的父类对象
    • var类型的父类对象不能赋值给var类型的子类对象
      1. var str = "hello";
      2. var obj = new Object();
      3. obj = str;
      4. str = obj; //不兼容的类型: java.lang.Object无法转换为java.lang.String

      var和编译时安全性

  • 一旦编译器推断出了var类型变量的值,就不能对其赋值错误的值

    1. var a = 10;
    2. a = "hello"; //不兼容的类型: java.lang.String无法转换为int
  • var类型是编译时推断出的,而不是运行时决定

    var和数组初始化

  • 使用var来初始化一个数组时,只能使用以下语法
    其他语法都会报错

    1. var arr = new int[3]; //正确
    2. var[] arr = new int[3]; // 'var' 不允许用作数组的元素类型
    3. var[] arr = {1,2,3}; //'var' 不允许用作数组的元素类型

    var和lambda

  • lambda表达式中不能捕获非final的局部变量,但因为var可以用来声明不可指类型(匿名),就可以做到类似让lambda捕获非final局部变量

    1. //错误
    2. int count = 0;
    3. List.of("ice1000", "Glavo")
    4. .forEach(e -> count += 1); //error
    5. System.out.println(count);
    6. //var
    7. var context = new Object(){
    8. int count = 0;
    9. };
    10. List.of("ice1000", "Glavo")
    11. .forEach(e -> context.count += 1); //error
    12. System.out.println(context.count);

    var的局限

  • var变量必须初始化

  • 不能进行复合声明

    1. var a = 1,b = 2,c = 3; // 'var' 不允许在复合声明中使用
  • 不能初始化为null

    1. var a = null; //无法推断本地变量 a 的类型 (变量初始化程序为 'null')
  • 不能使用var作为字段类型

  • 不能使用var作为参数类型
    • 不过Java11允许var作为lambda表达式的参数类型
  • 不能使用var作为返回值类型
  • catch捕获不能使用var做异常的类型
  • var和菱形推断一起使用时,菱形推断得到的是Object类型
    1. var arr = new ArrayList<>();
    2. //编译器将其推断为ArrayList<Object> arr = new ArrayList<Object>();

    参考文档

    Java10 var关键字详解
    Java 10 新特性之局部变量类型推断

static

static方法

  • 静态方法不依赖与任何对象就能够访问,所以它是没有this的
    且由于这个特性,在静态方法中不能访问类的非静态成员和非静态方法

但是非静态方法是可以访问静态方法和字段的

  • 构造器不属于静态方法
  • 静态方法可以通过对象去访问

    static字段

  • 静态字段被所有的对象共享,在内存中只有一个副本,在第一次访问时创建,程序结束时销毁

  • 静态字段可以通过对象去访问
  • static不允许修饰局部变量
  • static字段位于静态存储区,普通字段位于堆区
  • 对象序列化时,static字段会被排除在外

    静态内部类

  • 非静态内部类持有外部类对象的引用,而静态内部类没有

    static代码块

  • static代码块只会在构造第一个对象时执行一次,所以可以用来为static字段进行一些复杂的赋值操作

  • static代码块会在所有构造器前调用,无论超类还是子类
    而普通代码块自会在当前类的构造器前调用
  • static代码块可以出现在类中的所有非方法内部的位置
    且按照定义顺序执行
    1. package Test;
    2. public class TestCode extends Base{
    3. //子类static代码块
    4. static{
    5. System.out.println("test static");
    6. }
    7. //子类普通代码块
    8. {
    9. System.out.println("test normal");
    10. }
    11. //子类构造器
    12. public TestCode(){
    13. System.out.println("test constructor");
    14. }
    15. public static void main(String[] args) {
    16. new TestCode();
    17. }
    18. }
    19. class Base{
    20. //超类static代码块
    21. static{
    22. System.out.println("base static");
    23. }
    24. //超类普通代码块
    25. {
    26. System.out.println("base normal");
    27. }
    28. //超类构造器
    29. public Base(){
    30. System.out.println("base constructor");
    31. }
    32. }
    33. /*
    34. 输出:
    35. base static
    36. test static
    37. base normal
    38. base constructor
    39. test normal
    40. test constructor
    41. */

    参考文档

    构造器时静态方法吗
    Java static关键字为什么不能应用于局部变量
    Java中的static关键字解析
    再议Java中的static关键字

synchronized

  • 被synchronized修饰的方法同一时间只能被一个线程访问

transient

  • 被transient修饰的字段在对象序列化时会被忽略

volatile

  • volatile修饰的字段,每次访问时,都必须从共享内存中重新读取,且在发生变化时,必须将其写入共享内存

访问权限控制符

修饰外部类