两种情况:如果是基本数据类型,被final修饰的变量一旦初始化就不能改变;如果是引用数据类型的变量,初始化之后不能指向另外一个对象。
基本数据类型:
package cn.yqg.day2; public class StringTest {
public static void main(String[] args) {
final int a;
int b=;
a=b;
System.out.println(a);
}
}
我们看到上面的代码a是final类型的,但是没有被初始化,所以可以给a赋值。如果a被初始化,a存储的值就不能改变了,示例如下。
package cn.yqg.day2; public class StringTest {
public static void main(String[] args) {
final int a=;
int b=;
a=b;//编译通不过,会报错建议去掉fianl System.out.println(a);
}
}
如果是引用数据类型
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
package cn.yqg.day2; public class StringTest {
public static void main(String[] args) {
final StringBuffer a=new StringBuffer("ppp");
System.out.println(a);
System.out.println(a.hashCode());
a.append("kkk");
System.out.println(a);
System.out.println(a.hashCode()); }
}
运行结果:
ppp pppkkk
我们发现变量内容变了,引用没变。
我们把String定义为final类型,那么String类不能被继承,开始创建对象ppp,并用指向ppp的存储地址,后来a=a+“kkk”会新创建一个内存来存储该对象,并把新的地址赋值给a,那么老的存储ppp对象的地址就没有变量赋值了,就会成为垃圾。
String a="ppp";
a=a+"kkk";
我们看到a的对象内容改变了
不允许String类的引用地址被改变是为了安全性着想,我们看这个例子。
package cn.yqg.day3; public class Test2 {
public static void main(String[] args) {
String a=new String("fds");
String b=new String("ddd");
String c=b;//c和b都指向ddd对象
c=c+"ds";//c会创建新的内存,内存中存储新的对象,这是ddd只有b指着,c指向新对象。
System.out.println(b);
System.out.println(c);
}
}
运行内存图:
运行结果:
ddd
dddds
我们从此可以知道,c新开辟一个地址并进行存贮,这就保证了String类的安全性,相反StringBuilder就存在安全隐患
package cn.yqg.day3; public class Test2 {
public static void main(String[] args) {
StringBuilder s=new StringBuilder("ppp");
StringBuilder r=s;
r.append("kkk");
System.out.println(r);
System.out.println(s);
}
}
运行结果:
pppkkk
pppkkk
r改变会导致s也改变,因为他们指向得内存地址一样。
------------------------------------------------------------------------------------------------------------------------------------------------------