1. final 成员变量
final 关键字修饰类、变量和方法不可改变。(一旦获得了初始值)
一旦获得了初始值就不能重新被赋值。
final 修饰的成员变量必须由程序员显示地指定初始值。
final 修饰的类变量、实例变量能指定初始值的地方如下:
- 类变量: 必须在静态初始化块中指定初始值或声明该类变量时指定初始值,而且只能在两个地方的其中之一指定。
- 实例变量: 必须在非静态初始化块、声明该实例变量或构造器中指定初始值,而且只能在三个地方的其中之一指定。
final 成员变量在显示初始化之前不能直接访问,但可以通过方法来访问。—》设计缺陷
避免在final成员变量显示初始化之前访问。
public class FinalVariableTest
{
// 定义成员变量时指定 默认值,合法
final int a = 6;
// 下面变量将在构造器或初始化块中分配初始值
final String str;
final int c;
final static double d;
// 既没有指定默认值,又没有在初始化块、构造器中指定初始值
// 下面定义的ch实例变量是不合法的
// final char ch;
// 初始化块,可对没有指定默认值的实例变量指定初始值
{
//在初始化块中为实例变量指定初始值,合法
str = "Hello";
// 定义a实例变量时已经指定了默认值
// 不能为a重新赋值,因此 a = 9 ; 非法
}
// 静态初始化块,可对没有指定默认值的类变量指定初始值
static
{
// 在静态初始化块中为类变量指定初始值,合法
d = 5.6;
}
// 构造器,可对既没有指定默认值,又没有在初始化块中
// 指定初始值的实例变量指定初始值
2. final 局部变量
系统不会对局部变量进行初始化,局部变量必须由程序员显示初始化。
因此使用final修饰局部变量时,即可以在定义时指定默认值,也可以不指定默认值。
public class FinalLocalVariableTest
{
public void test(final int a)
{
// 不能对final 修饰的形参赋值
// a = 5 ; 非法
// final修饰形参,形参在调用该方法时,由系统根据传入的参数来完成初始化
// 因此使用final修饰的形参不能被赋值
}
public static void main(String[] args)
{
// 定义final局部变量时指定默认值,则str变量无法重新赋值
final var str = "hello";
final double d;
d = 5.6;
// 不可再次赋值
}
3. final 修饰基本类型变量和引用类型变量的区别
基本类型变量,不能重新赋值
引用变量仅仅保存一个引用
final 只保证这个引用类型变量所引用的地址不会改变
即一直引用同一个对象,但这个对象完全可以发生改变
class Person
{
private int age;
public Person(){}
// 有参数的构造器
public Person(int age)
{
this.age = age;
}
}
public class FinalReferenceTest
{
public static void mian(String[] args)
{
// final 修饰数组变量,iArr 是一个引用变量
final int[] iArr = (5,6,12,9);
System.out.println ( Arrays.toString ( iArr ));
// 对数组元素进行排序,合法
Arrays.sort(iArr);
System.out.println(Arrays.toString(iArr));
// 对数组元素赋值,合法
iArr[2] = -8;
System.out.println(Arrays.toString(iArr));
// 下面语句对iArr重新赋值,非法
// iArr = null;
// final 修饰Person变量,p是一个引用变量
final var p = new Person(45);
// 改变person对象的age实例变量,合法
p.setAge(23);
System.out.println(p.getAge());
// 下面语句对p重新赋值,非法
// p = null;
}
}
使用final 修饰的引用类型变量不能被重新赋值,但可以改变引用类型变量所引用对象的内容。
4. 可执行“宏替换”的final 变量
满足一下三个条件,相当于一个直接量。
- 使用final 修饰符修饰
- 在定义该final 变量时指定了初始值
- 该初始值可以在编译时就被确定下来
final 变量的实质就是“宏变量”
编译器会把程序中所有用到该变量的地方替换成该变量的值。
5. final 方法
- final 修饰的方法不可以被重写( 不希望子类重写父类的某个方法,可以使用)但可以被重载。
- 对于一个private 方法,因为它仅在当前类中可见,其子类无法访问该方法,所以子类无法重写该方法。
即使使用final 修饰一个private 访问权限的方法,依然可以在其子类中定义与该方法具有相同方法名、相同形参列表、相同返回值类型的方法。
6. final 类
final 修饰的类不可以有子类
7.不可变类
不可变类的意思是创建该类的实例后,该实例的实例变量是不可改变的。
8. 缓存实例的不可变类
class CacheImmutale
{
private static int MAX_SIZE = 10;
// 使用数组来缓存已有的实例
private static CacheImmulate[] cache = new CacheImmutale[MAX_SIZE];
// 记录缓存实例在缓存中的位置,cache[pos-1] 是最新缓存的实例
private static int pos = 0;
private final String name;
private CacheImmutale(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public static CacheImmutale valueOf(String name)
{
// 遍历已经缓存的对象
for (var i = 0; i < MAX_SIZE ; i++)
{
// 如果已有相同实例,则直接返回该缓存的实例
if (cache[i] != null && cache[i].getName().equals(name))
{
return cache[i];
}
}
// 如果缓存池已满
if ( pos == MAX_SIZE)
{
// 把缓存的第一个对象覆盖,即把刚刚生成的对象放在缓存池的最开始位置
cache[0] = new CacheImmutale(name);
// 把pos设为1
pos = 1;
}
else
{
// 把新创建的对象缓存起来,pos 加1
cache[pos++] = new CacheImmutale(name);
}
return cache[pos-1];
}
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj != null && obj.getClass() == CacheImmutale.class)
{
var ci = (CacheImmutale) obj;
return name.equals(ci.getName());
}
return false;
}
public int hashCode()
{
return name.hashCode();
}
}
public class CacheImmutaleTest
{
public static void main(String[] args)
{
var c1 = CacheImmutale.valueOf("hello");
var c2 = CacheImmutale.valueOf("helo");
// 下面代码将输出true
System.out.ptintln(c1 == c2);
}
}