【Java】6.4 final 修饰符

目录

final 成员变量

final局部变量

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

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

final方法

final类

不可变类

缓存实例的不可变类


【final】关键字可以用来修饰【类】、【变量】(包括成员变量和局部变量)、【方法】

final 成员变量

Java成员(Field)默认是可以由系统执行初始化,程序员可以不指定初始化。而final修饰过的成员变量【必须由程序员执行初始化】,final修饰变量 —— 该变量值只能赋值一次,不可改变。(原因见注释1)

【注意】若final修饰实例变量,可以再如下三个地方为final实例变量的初始值 —— 最多指定一次,不能多也不能少

  1. 定义时指定初始值
  2. 初始化块
  3. 构造器
  4. 【添加】普通方法不能为final修饰的成员变量赋值
  5. 【添加】final不会对成员变量进行隐式初始化

【注意】普通方法不能对final值修饰的实例变量赋值。若final修饰类变量,可以在如下2个地方为final类变量指定初始值

  1. 定义时指定初始值
  2. 类初始化

实例变量不能在静态初始化块中指定初始值(原因见注释2),同样的类变量也不能在普通初始化块中指定初始值

public class FinalVariableTest {
	// 定义成员变量时的初始值,合法
	final int a = 6;
	// 下面变量将在初始化块或构造器中分配初始值
	final String str;
	final int c;
	final static double d;
	// 以上既没有指定默认值,有没有在初始化块或构造器中指定初始化值
	// 下面定义的ch实例变量是不合法的
	// final char ch;
	// 初始化块,可对没有指定默认值的实例变量指定初始化值
	{
		// 在初始化块中为实例变量指定初始值
		str = "hello";
	}
	static {
		// 静态初始化块为静态变量指定初始值
		d = 55.56;
	}

	// 构造器中,可对既有没有默认初始值,又没有在初始化块中指定初始值的实例变量指定初始值
	public FinalVariableTest() {
		c = 5;
	}

	public void changeFinal() {
		// 普通方法不能为final修饰的变量赋值
		// 不能在普通方法中为final成员变量指定初始值
		// d=1.2;
		// ch='a';
	}

	public static void main(String[] args) {
		FinalVariableTest ft = new FinalVariableTest();
		System.out.println(ft.a);    //输出6
		System.out.println(ft.c);     //输出5
		System.out.println(ft.str);    //输出hello
		System.out.println(FinalVariableTest.d);     //输出55.56
	}
}

final局部变量

系统不会对局部变量初始化,所以需要有程序员显式初始化。在final修饰的局部变量中

  1. 可以在定义时就赋值
  2. 也可以在后面代码中对final修饰的局部变量赋初始值
  3. 且赋值之后不可改变

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

当使用final修饰基本类型变量的时候,基本类型变量只能被赋值一次。但是当final修饰引用类型变量的时候,他保存的仅仅只是一个引用,final只能保证这个引用变量的地址不会被改变,即一直引用同一个对象。使用final修饰的引用类型变量不能被重新赋值,但是可以改变引用类新变量所引用对象的内容

import java.util.Arrays;

class Persons{
	private int age;
	public Persons(){
		
	}
	public Persons(int age) {
		this.age=age;
	}
	
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}
public class FinalReferenceTest {
	public static void main(String[] args) {
		//final修饰数组变量,iArr是一个引用变量
		final int[] iArr = {5,6,8,2};
		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 Persons p = new Persons(45);
		//改变Person对象的age值,合法
		p.setAge(23);
		System.out.println(p.getAge());
		//下面语句对p重新赋值,非法
		//p = null;
	}
}

运行结果

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

final是在编译的时候就确定下来了。只要有变量,编译的时候就确定不下来。对于一个final变量来说,无论它是什么类型的变量,只要满足以下三个条件,这个final就不再是一个变量,而是相当于一个直接量:

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

final方法

final修饰的方法不可被重写,常用于不希望父类中的方法被子类重写重写。

Java提供Object类中就有一个final方法:getClass()

此外,在Java方法中,final和private一起使用时没有意义(注释3

public class FinalMethonTest{
	private void test() {
		
	}
}
class Sub extends FinalVariableTest{
    //会出现提示性错误
	@Override
	private void test() {
		//
	}
}

final类

final修饰的类不允许有子类。用于保护父类的内部数据和禁止重写父类的方法。。

不可变类

不可变类是指创建该类的实例之后,该实例不可被改变。比如8个包装类。 如果需要创建自定义的不可变类,需要遵守如下准则:

  1. 使用private和final修饰符来修饰类的成员变量
  2. 提供携带参数构造器,用于根据传入参数来初始化类里的成员变量
  3. 仅为该类的成员变量提供getter()方法,不要为成员变量提供setter方法,因为普通方法不能修改final修饰的成员变量的值
  4. 若有必要,重写Object类的hashCode()和equals()两个方法。equals方法根据关键成员变量来作为两个对象是否相等的标准,除此之外,还应该保证用两个equals方法判断为相等的hashCode方法也相等
class Name {
	private String firstName;
	private String lastName;

	public Name() {

	}

	public Name(String firstName, String lastName) {
		this.firstName = firstName;
		this.lastName = lastName;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

}

public class Person3 {
	private final Name name;

	public Person3(Name name) {
		// 设置name实例变量为临时创建的Name对象,该对象为firstname和lastname
		// 与传入的name参数的firstname和lastname相同
		 this.name = new Name(name.getFirstName(),name.getLastName());
		 //若换下面的语句,则可以对firstname做修改
		//this.name = name;
	}

	public Name getName() {
		 return new Name(name.getFirstName(),name.getLastName());
		//return name;
	}

	public static void main(String[] args) {
		Name n = new Name("悟空", "孙");
		Person3 p = new Person3(n);
		// Person对象的name的firstname为:悟空
		System.out.println(p.getName().getFirstName());
		// 以下为了改变Person对象的firstname值
		//无法改变值了,输出还是悟空
		n.setFirstName("八戒");
		System.out.println(p.getName().getFirstName());
	}
}

缓存实例的不可变类

若经常使用不可变类,可以将不可变类缓存下来。

class CacheImmutale{
	private static int MAX_SIZE=10;
	//用数组缓存已有实例
	private static CacheImmutale[] 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 (int 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{
			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) {
			CacheImmutale 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) {
		CacheImmutale c1 = CacheImmutale.valueOf("hello");
		CacheImmutale c2 = CacheImmutale.valueOf("hello");
		System.out.println(c1==c2);	//输出true
	}
}

 

注释1若让系统初始化,变量则会被自动赋予0/0.0/\u0000/false/null等值。final修饰的这也变量值不允许被改变,name这些就失去价值了

【注释2】因为静态初始化块是静态成员,不可以访问实例变量 —— 非静态成员。

【注释3】因为private方法不能被子类中的实例访问到,所以子类中即使有相同的名字、相同的形参列表、相同的返回值,那也只不过是定义了一个新的方法,不是重写。同时final也是不让子类重写方法,所以两者放在一起没有意义

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_之桐_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值