java final关键字
final关键字的定义
java中final关键字一旦被赋值就无法改变。final关键字能修饰类和方法,变量(包括对象)。
final Person perso;
key = new Person("a");
key = new Person("b");【错误】 已经在上一步赋值了,final修饰的key不可以再赋值
不能改变是key对应"123"的引用不改变,对象里面的数据是可以改变。别误解。
- 用final修饰的变量不能被修改
- 用final修饰的类不能够被继承
- 用final修饰的方法不能被重写
所以用final修饰的变量,方法,类都是确定的。不会发生变化的。
String上的final陷阱
有一段代码如下:
public class Main {
public static void main(String[] args) {
final String finalStr = "1234";
String normalStr = "1234";
String testStr = "123400";
String a = finalStr + "00";
String b = normalStr + "00";
System.out.println("r0 =" + (finalStr == normalStr));
System.out.println("r1 =" + (a == testStr));
System.out.println("r2 =" + (b == testStr));
}
}
输出的结果是?
答案是:
r0 = true
r1 = true
r2 = false
解释之前需要说明java如何赋值对象的。
java对象一般都是存在堆上面,但是有一个例外就是String,为了减少频繁创建String的内存开销,设计者希望在定义一个字符串时,如何恰好曾有一个相同的,那么就使用这个已经存在的,于是java设计了常量池,因此String即可以像普通对象一样存在堆上,也能存在常量池上。
- 直接用String a = “XXX”这种格式会存在常量池,实例化之前会找是否存在一个相同的(equal)的对象,有就直接赋值过来,没有就在常量池创建,因此String对象设计成无法修改里面的值的,如果要改值就得重新实例化对象。
- 如果使用String a = new String(“xxx”)的方式,就会存储在堆上。这个也是其他对像的存储方式。
- 如果就是 String a = b + “XXX”,这种方式。我们可以确定“xxx”存储在常量池里面,但是我们不能确定a是否也在,如果a是final修饰的,且是存在常量池(final String b = “123”),java会定义在常量池,其他情况在堆上。
分析上述代码:
- 赋值normalStr和finalStr java会在常量池中找,如果没有,就申请一个,所以normalStr与finalStr是一个对象,所以==,
- 对于finalStr,由于finalStr是确定的,因为用final修饰的变量不能被修改,java在拼接成“123400”后会在常量池中找,然后找到testStr,所以后面的a实际上就是testStr,类似于直接String a = “123400”
- 对于normalStr,normalStr不确定是在堆还是常量池上,因为normalStr可以在后面修改,可以再赋值一次,出于最安全考虑会在堆上创建对象