1. final 成员变量

final 关键字修饰类、变量和方法不可改变。(一旦获得了初始值)
一旦获得了初始值就不能重新被赋值。

final 修饰的成员变量必须由程序员显示地指定初始值。

final 修饰的类变量、实例变量能指定初始值的地方如下:

  • 变量: 必须在静态初始化块中指定初始值或声明该类变量时指定初始值,而且只能在两个地方的其中之一指定。
  • 实例变量: 必须在非静态初始化块、声明该实例变量或构造器中指定初始值,而且只能在三个地方的其中之一指定。

final 成员变量在显示初始化之前不能直接访问,但可以通过方法来访问。—》设计缺陷
避免在final成员变量显示初始化之前访问。

  1. public class FinalVariableTest
  2. {
  3. // 定义成员变量时指定 默认值,合法
  4. final int a = 6;
  5. // 下面变量将在构造器或初始化块中分配初始值
  6. final String str;
  7. final int c;
  8. final static double d;
  9. // 既没有指定默认值,又没有在初始化块、构造器中指定初始值
  10. // 下面定义的ch实例变量是不合法的
  11. // final char ch;
  12. // 初始化块,可对没有指定默认值的实例变量指定初始值
  13. {
  14. //在初始化块中为实例变量指定初始值,合法
  15. str = "Hello";
  16. // 定义a实例变量时已经指定了默认值
  17. // 不能为a重新赋值,因此 a = 9 ; 非法
  18. }
  19. // 静态初始化块,可对没有指定默认值的类变量指定初始值
  20. static
  21. {
  22. // 在静态初始化块中为类变量指定初始值,合法
  23. d = 5.6;
  24. }
  25. // 构造器,可对既没有指定默认值,又没有在初始化块中
  26. // 指定初始值的实例变量指定初始值

2. final 局部变量

系统不会对局部变量进行初始化,局部变量必须由程序员显示初始化。
因此使用final修饰局部变量时,即可以在定义时指定默认值,也可以不指定默认值。

  1. public class FinalLocalVariableTest
  2. {
  3. public void test(final int a)
  4. {
  5. // 不能对final 修饰的形参赋值
  6. // a = 5 ; 非法
  7. // final修饰形参,形参在调用该方法时,由系统根据传入的参数来完成初始化
  8. // 因此使用final修饰的形参不能被赋值
  9. }
  10. public static void main(String[] args)
  11. {
  12. // 定义final局部变量时指定默认值,则str变量无法重新赋值
  13. final var str = "hello";
  14. final double d;
  15. d = 5.6;
  16. // 不可再次赋值
  17. }

3. final 修饰基本类型变量和引用类型变量的区别

基本类型变量,不能重新赋值
引用变量仅仅保存一个引用
final 只保证这个引用类型变量所引用的地址不会改变
即一直引用同一个对象,但这个对象完全可以发生改变

  1. class Person
  2. {
  3. private int age;
  4. public Person(){}
  5. // 有参数的构造器
  6. public Person(int age)
  7. {
  8. this.age = age;
  9. }
  10. }
  11. public class FinalReferenceTest
  12. {
  13. public static void mian(String[] args)
  14. {
  15. // final 修饰数组变量,iArr 是一个引用变量
  16. final int[] iArr = (5,6,12,9);
  17. System.out.println ( Arrays.toString ( iArr ));
  18. // 对数组元素进行排序,合法
  19. Arrays.sort(iArr);
  20. System.out.println(Arrays.toString(iArr));
  21. // 对数组元素赋值,合法
  22. iArr[2] = -8;
  23. System.out.println(Arrays.toString(iArr));
  24. // 下面语句对iArr重新赋值,非法
  25. // iArr = null;
  26. // final 修饰Person变量,p是一个引用变量
  27. final var p = new Person(45);
  28. // 改变person对象的age实例变量,合法
  29. p.setAge(23);
  30. System.out.println(p.getAge());
  31. // 下面语句对p重新赋值,非法
  32. // p = null;
  33. }
  34. }

使用final 修饰的引用类型变量不能被重新赋值,但可以改变引用类型变量所引用对象的内容。

4. 可执行“宏替换”的final 变量

满足一下三个条件,相当于一个直接量。

  • 使用final 修饰符修饰
  • 在定义该final 变量时指定了初始值
  • 该初始值可以在编译时就被确定下来

final 变量的实质就是“宏变量”
编译器会把程序中所有用到该变量的地方替换成该变量的值。

5. final 方法

  • final 修饰的方法不可以被重写( 不希望子类重写父类的某个方法,可以使用)但可以被重载。
  • 对于一个private 方法,因为它仅在当前类中可见,其子类无法访问该方法,所以子类无法重写该方法。

即使使用final 修饰一个private 访问权限的方法,依然可以在其子类中定义与该方法具有相同方法名、相同形参列表、相同返回值类型的方法。

6. final 类

final 修饰的类不可以有子类

7.不可变类

不可变类的意思是创建该类的实例后,该实例的实例变量是不可改变的。

8. 缓存实例的不可变类

  1. class CacheImmutale
  2. {
  3. private static int MAX_SIZE = 10;
  4. // 使用数组来缓存已有的实例
  5. private static CacheImmulate[] cache = new CacheImmutale[MAX_SIZE];
  6. // 记录缓存实例在缓存中的位置,cache[pos-1] 是最新缓存的实例
  7. private static int pos = 0;
  8. private final String name;
  9. private CacheImmutale(String name)
  10. {
  11. this.name = name;
  12. }
  13. public String getName()
  14. {
  15. return name;
  16. }
  17. public static CacheImmutale valueOf(String name)
  18. {
  19. // 遍历已经缓存的对象
  20. for (var i = 0; i < MAX_SIZE ; i++)
  21. {
  22. // 如果已有相同实例,则直接返回该缓存的实例
  23. if (cache[i] != null && cache[i].getName().equals(name))
  24. {
  25. return cache[i];
  26. }
  27. }
  28. // 如果缓存池已满
  29. if ( pos == MAX_SIZE)
  30. {
  31. // 把缓存的第一个对象覆盖,即把刚刚生成的对象放在缓存池的最开始位置
  32. cache[0] = new CacheImmutale(name);
  33. // 把pos设为1
  34. pos = 1;
  35. }
  36. else
  37. {
  38. // 把新创建的对象缓存起来,pos 加1
  39. cache[pos++] = new CacheImmutale(name);
  40. }
  41. return cache[pos-1];
  42. }
  43. public boolean equals(Object obj)
  44. {
  45. if (this == obj)
  46. {
  47. return true;
  48. }
  49. if (obj != null && obj.getClass() == CacheImmutale.class)
  50. {
  51. var ci = (CacheImmutale) obj;
  52. return name.equals(ci.getName());
  53. }
  54. return false;
  55. }
  56. public int hashCode()
  57. {
  58. return name.hashCode();
  59. }
  60. }
  61. public class CacheImmutaleTest
  62. {
  63. public static void main(String[] args)
  64. {
  65. var c1 = CacheImmutale.valueOf("hello");
  66. var c2 = CacheImmutale.valueOf("helo");
  67. // 下面代码将输出true
  68. System.out.ptintln(c1 == c2);
  69. }
  70. }