/**
* @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
}
}