final关键字浅析

/**
* @author jack.chen
* @version 创建时间:2018年7月9日 下午12:46:47
* 类说明:final关键字修饰成员变量
*/
public class TestFinal
{
	final int a = 6;
	final String str;
	final int c;
	final static double d;
	final char ch;
	static 
	{
		d=5.6;
	}
	{
		str="hello";	
		//a=9;	//非法,不能重新赋值	
	}
	public TestFinal()
	{
		//str="abc";//非法,str已经在初始化块中赋值
		c=2;
		ch='a';
	}
	public void changeFinal()
	{
		//普通方法不能为final”成员变量“指定初始值,因为成员变量是随类初始化或对象初始化而初始化的,普通方法需通过对象调用
		//d=1.2;	
		//ch='a';
	}
	//如果没有在初始化块、构造器中指定初始化值,系统不会自动为final修饰的成员变量分配初始值,会为没有用final修饰的成员变量分配初始值
	public static void main(String[] args)
	{
		TestFinal tf = new TestFinal();
		System.out.println(tf.a);
		System.out.println(tf.c);
	}
}
/**
* @author jack.chen
* @version 创建时间:2018年7月9日 下午12:59:17
* 类说明:final修饰局部变量
*/
public class TestFinalLocal {
	public void test(final int a) {
		//a=5;//非法,不能为final修饰的形参赋值
	}
	public static void main(String[] args) {
		final String str = "hello";	
		//str = "java";  //非法,不能重新赋值	
	}
}

/**
* @author jack.chen
* @version 创建时间:2018年7月9日 下午1:04:08
* 类说明:final修饰引用类型变量
*/
import java.util.Arrays;
class Per
{
	private int age;
	public Per() {}
	public Per(int age) {
		this.age=age;
	}
	void setter(int m_age) {
		this.age=m_age;
	}
	int getter(){
		return age;
	}
}
public class TestFinalReference
{
	public static void main(String[] args){
	final int[] iArr = {5,2,13,4};	//iArr是一个引用变量,被final修饰的是iArr引用变量,iArr不能被重新赋值,数组元素可以被重新赋值
	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 = null;	//非法
	final Per p = new Per(45);	//Per类对象p(引用变量)被final修饰,不能被重新赋值,但是p变量所引用的Per对象的属性可以被改变
	p.setter(23);
	System.out.println(p.getter());
	//p = null;	//非法
	}
}

常量命名规则:

如果final修饰的变量是基本数据类型,且在编译时就可以确定该变量的值,于是可以将该变量当成常量处理。根据Java的可读性命名规范:常量名由多个有意义的单词连缀而成,而每个单词的所有字母都大写,单词与单词之间以下划线来分割,例如:MAX_TAX_RATE=20;

反之,如果final修饰的是引用数据类型,final变量无法在编译时就获得值,而必须在运行时才能得到值,例如:final Aclass aInstance=new Aclass();

编译时系统不会创建一个Aclass对象来赋给aInstance变量,所以aInstance变量无需使用常量命名规则;


此外,还有final修饰某个方法

则其它任何类都不能重写这个方法;(如果final修饰了某个private方法,因为其只在当前类可见,子类其他类不可见,如果在子类中定义了一个跟这个private方法完全一样的方法(可以被public、private修饰)不能称为重写,而是重新定义)

final类(不可变类):即创建了类的某个实例后,该实例的属性是不可改变的,用private final修饰成员变量即可,但是,如果成员变量中有某个引用类型变量,就产生了一个问题:当创建不可变类时,如果它包含属性的类型是可变的,那么其对象的属性值依然是可改变的,那么这个类就不是不可变类了:

所以要保护好包含类的属性,如下面类Name的name属性,使用临时对象可以解决问题

/**
* @author jack.chen
* @version 创建时间:2018年6月18日 下午2:40:39
* 类说明:自定义一个不可变类,如果有引用变量,则要保证引用变量所指的对象属性不会被改变,使用临时对象可以解决问题
*/
class Name
{
	private String firstname;
	private String lastname;
	public Name() {
	}
	public Name(String firstname, String lastname) {
		this.firstname = firstname;
		this.lastname = lastname;
	}
	public void setFirstName(String firstname) {
		this.firstname = firstname;
	}
	public String getFirstName() {
		return firstname;
	}
	public void setLastName(String lastname) {
		this.lastname = lastname;
	}
	public String getLastName() {
		return lastname;
	}
}
public class TestCannotChange {
	private final Name name;		//定义不可变类的标准,用private、final修饰
	public TestCannotChange(Name name) {
//		this.name = name;
		//创建临时Name对象
		this.name = new Name(name.getFirstName(),name.getLastName());
	}
	public Name getName() {
//		return name;
		//返回一个匿名对象,直接return name可能会改变对象
		return new Name(name.getFirstName(),name.getLastName());
	}
	public static void main(String[] args) {
		Name n = new Name("孙悟空","孙");
		TestCannotChange t = new TestCannotChange(n);
		System.out.println(t.getName().getFirstName());
		n.setFirstName("八戒");
		System.out.println(t.getName().getFirstName());
	}
}

缓存实例的不可变类:

不可变类的实例的状态不可改变,可以很方便地被多个对象所共享。如果程序经常需要相同地不可变类实例,则需要使用缓存实例的不可变类来减少系统开销。

/**
* @author jack.chen
* @version 创建时间:2018年6月18日 下午3:00:50
* 类说明:缓存实例的不可变类
*/
public class CacleImmutale 
{
	private final String name;
	private static CacleImmutale[] cache = new CacleImmutale[10];
	//记录缓存实例在缓存中的位置,cache[pos-1]是最新缓存的实例
	private static int pos = 0;
	public CacleImmutale(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public static CacleImmutale valueOf(String name)
	{
		//遍历已缓存的对象
		for(int i=0; i<pos; i++) 
		{
			//如果已经有相同实例,直接返回该缓存的实例
			if(cache[i]!=null&&cache[i].getName().equals(name))
				return cache[i];
		}
		if(pos==10) //如果缓存池已满
		{
			//把缓存的第一个对象覆盖,即把刚刚生成的对象放在缓存池的最开始位置,然后把pos设为1
			cache[0] = new CacleImmutale(name);
			pos = 1;
			return cache[0];
		}
		else 
		{
			//把新创建的对象缓存起来,pos加1
			cache[pos++] = new CacleImmutale(name);
			return cache[pos-1];
		}
	}
	public boolean equals(Object obj) {
		if(obj instanceof CacleImmutale) {
			CacleImmutale ci = (CacleImmutale)obj;
			if(name.equals(ci.getName())) {
				return true;
			}
		}
		return false;
	}
	public int hashCode() {
		return name.hashCode();
	}
	public static void main(String[] args) {
		CacleImmutale c1 = CacleImmutale.valueOf("hello");
		CacleImmutale c2 = CacleImmutale.valueOf("hello");
		System.out.println(c1==c2);		//返回true	
	}
}
	

实际上java.lang.Integer类就采用了这种相同的处理策略:

/**
* @author jack.chen
* @version 创建时间:2018年7月9日 下午4:31:54
* 类说明:Integer类利用new创建新对象,而利用valueOf()方法创建对象,会考虑缓存
*/
public class TestInteger {
	public static void main(String[] args) {
		Integer in1 = new Integer(6);    //生成新的Integer对象		
		Integer in2 = Integer.valueOf(6);    //生成新的Integer对象,并缓存该对象	
		Integer in3 = Integer.valueOf(6);    //直接从缓存中取出Integer对象
		System.out.println(in1==in2);	//false
		System.out.println(in2==in3);	//true
	}
}



猜你喜欢

转载自blog.csdn.net/dyd850804/article/details/80968446