final可以用来修饰类,成员变量和方法,用于表示他修饰的类,成员变量和方法是不可改变的。
用final修饰,并不是不能赋值,而是赋值后不能改变或者说,只能赋值一次。
修饰成员变量 final修饰的成员变量必须由程序员显示的指定初始值。
原因:当类初始化的时候,系统会为该类的类变量分配内存,并设置默认值;当创建对象的时候,系统会为对象的实例变量分配内存,并设置默认值。对于final修饰的成员变量而言,一旦有了初始值,就不能被重新赋值,如果既没有在定义的地方指定初始值,也没有在初始化块,构造器中指定初始值,那么这些成员变量的值,将一直是系统默认分配的0,‘\u0000’,false或null,这些成员变量就完全失去了存在的意义。所以java规定:final修饰的成员变量必须由程序员显示的指定初始值。
final修饰的成员变量可以指定初始值的地方:
类变量:声明的时候或者是静态初始化块中,只能是二选一。
实例变量:声明的时候,普通初始化块或者构造器,只能是三选一。
静态初始化块不能对实例变量赋值,因为静态成员不能访问非静态成员。类变量不能在普通初始化块中赋值,因为类变量已经在静态代码块中被赋值了,所以不能重新赋值。
在final修饰的成员变量被初始化之前,不要访问成员变量,会报错。
修饰局部变量 系统不会对局部变量进行显示的初始化,局部变量必须由程序员显示的进行初始化。因此使用final修饰局部变量的时候,既可以在定义的时候指定默认值,也可以不指定默认值。
如果final修饰的局部变量在定义的时候没有指定初始值,则可以在后面的代码中对该final变量赋初始值,但只能一次,不能重赋值;如果final修饰的局部变量在定义的时候已经指定了初始值,则后面的代码中不能再赋值。
1 2 3 4 5 6 7 8 9 10 11 public void testOne(final int a){ a=5;//不能对final修饰的形参赋值 } public void testTwo(){ final Stirng str = "hello"; str = "";//出错 final double d; d = 0.5; d = 0.6;//出错 }
修饰基本类型变量和引用类型变量的区别 修饰基本类型的变量的时候,不能对基本类型的变量重新赋值,此基本类型的变量不会发生改变。当修饰引用类型的变量的时候,这个引用变量的值不可以改变,但是这个引用变量所引用的对像是可能会发生改变的。
1 2 3 4 5 6 7 8 9 10 11 12 class Person{ private int age; public Person(){} public Person(int age){this.age=age;} ....getter和setter方法.... } public static void main(String[] args){ final Person p = new Person(28); p.setAge(29);//合法 p=null;//报错 }
宏替换 final修饰的变量,只要满足三个条件,就不再是变量,而是相当于一个直接量。
使用final修饰符
在定义final变量的时候,指定了初始值
该初始值在编译的时候就可以确定下来
如果一个变量满足了上面的三个条件,这个变量就相当于一个“宏变量”,编译器会把程序中所有用到该变量的地方直接替换成这个变量的值。
1 2 final int a = 10; System.out.print(a);
以上代码实际上相当于变量a不存在,System.out.print(a)相当于System.out.print(5);
如果赋值表达式只是基本的算数表达式或者字符串连接运算,没有访问普通变量,调用方法,java编译器同样会把这种变量当成“宏变量”处理。
1 2 3 4 5 6 7 final int a = 10+10;//“宏变量” final int b = 10/10;//“宏变量” final String strA = "ha"+"ha";//“宏变量” final String strB = "haha"+"10";//“宏变量” final String strC = "haha"+String.valueof(10);//非“宏变量” System.out.print(strB == "haha10");//true System.out.print(strC =="haha10");//false
深入
1 2 3 4 5 6 7 String s1= "我擦嘞"; String s2 = "我擦"+"嘞"; String strA = "我擦"; String strB = "嘞"; String s3 = strA+strB; System.out.print(s1 == s2);//true System.out.print(s1 == s3);//false
strA和strB只是两个普通的变量,编译的时不会执行宏替换,编译的时候无法确定s3的值,s3无法指向字符串常量池中的“我擦嘞”。
如果想要输出true,只要让strA和strB执行宏替换即可,即将它们声明为final类型即可。
修饰方法 final方法不可以被重写
修饰类 final类不能被继承
不可变类 创建实例后,实例变量不可以改变的类,称为不可变类。
Java中的不可变类:8个包装类和String类。
自定义不可变类,遵循如下规则:
使用final和private修饰该类的成员变量
提供带参数的构造器,用于根据传入的参数来初始化类里的成员变量
仅为该类的成员变量提供getter方法
如果有必要,重写该类的hashCode方法和equals方法。equals方法根据关键字成员变量来作为两个对象是否相等的标准,除此以外还应该保证两个用equals方法判断相等的对象,hashCode值也相等
特殊情况,不可变类中的成员变量是一个引用类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Name{ private String xing; private String ming; public Name(){} public Name(String xing,String ming){ this.xing = xing ; this.ming = ming; } ....省略getter和seter方法 } public class Person{ private final Name name; public Person(){} public Person(Name name){ this.name = new Name(name.getXing(),name.getMing()); } pulic Name getName(){ return new Name(name.getXing(),name.getMing()); } }
缓存实例的不可变类 不可变类的实例状态不可以改变,可以很方便的被多个对象所共享。如果程序经常需要使用相同的不可变类实例,则应该考虑缓存这种不可变类的实例。毕竟重复创建相同的对象没有太大的意义,而且加大系统的开销。如果可能,应该将已经创建的不可变类的实例进行缓存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 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修饰符来隐藏构造器,是外部无法通过new 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; }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){ 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"); //true System.out.println(c1==c2); } }
Java中的Integer类就采用了与CacheImmutale类相同的处理策略,如果采用new构造器来创建对象,则每次返回都是新的;使用valueof()方法创建对象则会缓存该方法创建的对象
上一篇:Android WebSocket 的一个Demo
下一篇:Java内存模型-final