验证不变性代码
String str = "Hello";
System.out.println(str);//输出为Hello
str.concat("World");//该方法是在原来的变量后面添加一个字符串然后变成新的字符串
System.out.println(str);//输出为Hello
str=str.concat("World");
System.out.println(str);//输出为HelloWorld
就是指引用的对象实例的是不可以改变的,但是我们可以改变引用的地址,改变地址改变值。
常量池概念:
在Java虚拟机的整个体系里面,我们加载类文件,分别存放到不同的单元,占用大部分内存空间的是常量池,大约能占到60%,为了节省内存,JVM专门用一片特殊的存储区域存储String,作为String ,面对String常量,会检查String常量池中是否存在此常量,如果存在的话,直接进行引用,避浪费多余的内存空间,因此必须确保这个具体的常量不能变,这就是我们经常讲的String的不变性。
我们可以联想到其它方式,还有其它使用的技术:
String newStr=“java”;//这个应该是放在String的常量池中的常量。
String newStrObj=new String("java");//这个放在常规的内存中。
假若字符串对象允许改变,那么将会导致各种逻辑错误,比如改变一个对象会影响到另一个独立对象. 严格来说,这种常量池的思想,是一种优化手段.
请思考: 假若代码如下所示,s1和s2还会指向同一个实际的String对象吗?
String s1= “ab” + “cd”; String s2= “abc” + “d”;
也许这个问题违反新手的直觉, 但是考虑到现代编译器会进行常规的优化, 所以他们都会指向常量池中的同一个对象. 或者,你可以用 jd-gui 之类的工具查看一下编译后的class文件。
代码实现
import java.nio.charset.StandardCharsets;
public class Experiment6 {
public static void main(String[] args) {
//验证string类的不变性
String s1 = "张李浩";
s1.concat("在");
System.out.println(s1);//输出"张李浩",说明引用对象的实例是不可以改变的
s1 = s1.concat("做实验");//输出"张李浩做实验",可以改变引用的地址,改变地址改变值
System.out.println(s1);
//下面实现用StringBuffer, StringBuilder解决String类带来的消耗内存的问题,
String s2="张";
//查看字符串的内存地址
System.out.println(s2.getBytes(StandardCharsets.UTF_8));
String s3="李";
System.out.println(s3.getBytes(StandardCharsets.UTF_8));
String s4="浩";
System.out.println(s4.getBytes(StandardCharsets.UTF_8));
s2=s2+s3+s4;
System.out.println(s2.getBytes(StandardCharsets.UTF_8));//总共占用了4个内存地址
//使用StringBuffer和StringBuilder提供的append方法解决内存消耗过大问题
StringBuffer b1=new StringBuffer("张");
System.out.println(b1);
b1.append("李浩");
//StringBuffer和StringBuilde对象调用append方法拼接好字符串后,临时字符串变量就会被当作垃圾释放掉了,有效地节省了内存空间。
System.out.println(b1);
}
}