其实如果我们学过C语言的话,可以发现其中并没有String字符串类型,只有字符类型,String类型这是java为了方便我们操作而创建的。
我们大家如果对String有一定的了解,就应该了解,String类型创建对象有两种方式
第一种
String str1 = "abc";
这样创建是将其存放在常量池中的。
第二种
String str2 = new String("abc");
这种事通过new在堆中开辟空间存储的
然而这两种方式不光是堆中还是常量池中存储的都是字符串“abc”真正位置的地址。
字符串是由一个个字符组成的,一个个字符又组成了字符数组,在String类的底层,其实是把字符串转换成字符数组存放在堆中的。
上面不管是常量池中的,还是new在堆开辟的空间中的,实际指向的都是一个字符数组。我们该怎么证明那。
我们可以利用反射机制,对str2指向的字符数组的数据进行修改,然后分别输出str1和str2,来进行验证。
String str1 = "abc";
String str2 = new String("abc");
Class sc = str2.getClass();
Field field = sc.getDeclaredField("value");
field.setAccessible(true);//忽略访问权限修饰符
char[] a = (char[]) field.get(str2);
a[0]='c';
System.out.println(str1);
System.out.println(str2);
输出:
cbc
cbc
我们通过反射修改了str2所指向的字符数组,但是存储在常量池中的str1的值也改变了,由此可以证明我们的验证是正确的,不管是变量池中 还是new在堆中开辟空间存储的都是字符数组的引用地址。
通过这我们可以看出,不管我们怎么创建,其实指向的都是唯一的字符数组,实际的字符串“abc”是以一个一个字符的方式存储在字符数组中的。
上面的验证环节利用了反射的知识,我在之前的文章中有过介绍,有兴趣的可以看看我之前关于反射的一些介绍进行理解,我在此处就不多解释了,此处主要是告诉我们大家String字符串的真正存储。有兴趣的可以去看看String类的源码,可以研究一下