电子说
String对象有三种创建方式:
第一种方式是直接通过赋值语句,将字符串赋值给String类型的变量。
例如:
String str = “Hello”;
第二种方式是通过new运算符,实例化一个String对象,并将对象引用赋值给String类型的变量。
例如:
String str = new String(“Hello”);
第三种方式是通过String对象的intern()方法返回一个String对象的引用。
例如:
String str = “Hello”。 intern();
前面String对象的三种创建方式,虚拟机对其内存分配上是有所区别的,先来看第一种创建方式。
第一种创建方式是通过赋值语句直接将字符串赋值给String类型的变量。在这种创建方式中,虚拟机会在方法区的常量池中判断是否存在具有和字符串(如Hello)内容相同的String对象:如果常量池不存在和赋值字符串内容相同的对象,虚拟机就在常量池中分配内存并创建该String对象,并将String对象的引用赋值给String类型的变量;如果常量池存在与赋值字符串内容相同的对象,虚拟机会直接将该对象的引用赋值给String类型的变量。这种创建方式对连续创建同一字符串内容的String对象特别有用,内存利用效率非常高效。
第二种创建方式是通过new运算符实例化String对象,并将new运算符返回的对象引用赋值给String类型的变量。在这种创建方式中,虚拟机会创建两个String对象:一个String对象是在常量池中创建,如果常量池中已有字符串内容相同的对象,则不创建;一个String对象是在运行数据区的堆中创建,将在常量池中创建的String对象的字符数组复制到在堆中创建的String对象。
String类型的变量接收new运算符返回的对象引用后,如果使用赋值语句对该String类型的变量重新赋予不同的字符串内容,该变量将会指向一个新的String对象,该String对象会在常量池中创建。
案例1:建立StringTest1类,在类的main()方法内部,使用new运算符实例化一个String对象,返回的对象引用赋值给String类型的变量str,输出str指向对象的哈希码,然后使用赋值语句将新的字符串内容赋值给str,输出str指向对象的哈希码,验证哈希码是否一致。
在memory包下新建StringTest1类。代码如下:
publicclassStringTest1 {
/**
* @Title: main
* @Description: Java程序入口main方法
* @param@paramargs 参数
* @returnvoid 返回类型
* @throws
*/
publicstaticvoidmain(String[] args) {
// 实例化String对象
String str = newString(“Hello”);
System.out.println(“str对象的哈希码:” + str.hashCode());
// 修改str对象的内容
str = “Hello World”;
System.out.println(“str对象修改后的哈希码:” + str.hashCode());
}
}
程序执行结果如下图所示:
从程序的执行结果可以看出,当对str重新赋值不同的内容后,虚拟机会在常量池创建一个新的String对象,并将该对象的引用赋值给str。
第三种创建方式是通过String对象的intern()方法来返回一个String对象的引用。在这种创建方式中,虚拟机会首先判断在常量池中是否存在“Hello”字符串对象,如果存在就直接返回该对象的引用,否则就在常量池创建该对象,并返回对象的引用。
前面String对象的内存分配经常用到常量池,常量池是虚拟机从运行数据区的方法区划分出来的一块内存区域,JDK1.8将常量池放置到运行数据区的堆区域。常量池主要用来存储字面常量、使用final修饰的变量以及符号引用。字面常量包括数值常量(如36、100等)、字符串常量(如“123”、“abc”等)。符号引用是指用一组符号来描述引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。例如编译器会把对象的引用作为一个符号引用,因为编译器不知道对象引用在内存的实际地址,当虚拟机加载类到运行数据区并初始化类后,虚拟机会把这些符号引用转换为直接引用(指向目标的内存地址,如对象在堆中的内存地址)。
全部0条评论
快来发表一下你的评论吧 !