JVM知识-Strings=newString(111)会创建⼏个对象?
String s = new String("111")会创建⼏个对象?
⼀、引⼊
String字符串的不可变性:常量池中⼀定不存在两个相同的字符串。
public class App {
public static void main(String[] args) {
String a = "111";
a = "222";
System.out.println(a);
}
}
输出结果为:?
分析:String在JVM中的存储
字符串的分配和其他对象分配⼀样,是需要消耗⾼昂的时间和空间的,⽽且字符串我们使⽤的⾮常多。JVM为了提⾼性能和减少内存的开销,在实例化字符串的时候进⾏了⼀些优化:使⽤字符串常量池
当创建⼀个字符串常量时:
JVM会⾸先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引⽤。
如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。
所以在上⾯的代码中:
String a = "111";==>先在常量池中,不到,创建字符串对象,并将该对象的引⽤地址赋给a
a = "222";==>不到"222",将"222"的引⽤地址赋给a
所以最终的输出结果为"222";
引⽤地址
引⽤类型声明的变量是指该变量在内存中实际存储的是⼀个引⽤地址,实体在堆中。
⽐如:User user = new User()
user是⼀个User 类型的引⽤变量,它表⽰的是User这个对象在内存中的地址。
所以,这⾥的变量a是"111"这个对象的引⽤地址,变量可变,不可变的是"111";
⼆、String为什么不可变
1. 查看String类的源码
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
public String() {
this.value = "".value;
}
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
public String(char value[]) {
this.value = pyOf(value, value.length);
}
}
可以看到三点:
String类为final修饰
String存储内容使⽤的是char数组
char数组是final修饰
所以:
//String a = "111";相当于
char data [] ={'1','1','1'};
Stirng a = new String(data);
//a = "222";
char data [] ={'2','2','2'};
a = new String(data);
final修饰符的作⽤
final修饰⼀个类:表明这个类不能被继承。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员⽅法都会被隐式地指定为final⽅法
当final修饰的⽅法表⽰此⽅法已经是“最后的、最终的”含义,亦即此⽅法不能被重写,但可以重载多个final修饰的⽅法。重写的前提是⼦类可以从⽗类中继承此⽅法,如果⽗类中final修饰的⽅法同时访问控制权限为private,将会导致⼦类中不能直接继承到此⽅法,因此,此时可以在⼦类中定义相同的⽅法名和参数,此时不再产⽣重写与final的⽭盾,⽽是在⼦类中重新定义了新的⽅法。(注:类的private⽅法会隐式地被指定为final⽅法。)
当final修饰⼀个基本数据类型时,表⽰该基本数据类型的值⼀旦在初始化后便不能发⽣变化。
如果final修饰⼀个引⽤类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引⽤所指向的对象的内容是可以发⽣变化的。本质上是⼀回事,因为引⽤的值是⼀个地址,final要求值,即地址的值不发⽣变化。
另外final修饰⼀个成员变量(属性),必须要显⽰初始化。在申明的时候给其赋值,否则必须在其类的所有构造⽅法中都要为其赋值2. String中⼏个常⽤⽅法源码字符常量池是什么意思
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
//啥都没有,就直接把当前字符串给你
return this;
}
int len = value.length;
char buf[] = pyOf(value, len + otherLen);
//看到了吗?返回的居然是新的String对象
return new String(buf, true);
}
void getChars(char dst[], int dstBegin) {
System.arraycopy(value, 0, dst, dstBegin, value.length);
}
public String replace(char oldChar, char newChar) {
//如果两个是⼀样的,那就没必要替换了,所以返回this
if (oldChar != newChar) {
int len = value.length;
int i = -1;
//把当前的char数组复制给val,然后下⾯基于val来操作
char[] val = value;
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
//创建⼀个新的char数组
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
//创建⼀个新的String对象
return new String(buf, true);
}
}
return this;
}
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
int subLen = endIndex - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
//正常返回的都是新new出来的String对象
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
public String trim() {
int len = value.length;
int st = 0;
char[] val = value; /* avoid getfield opcode */
while ((st < len) && (val[st] <= ' ')) {
st++;
}
while ((st < len) && (val[len - 1] <= ' ')) {
len--;
}
/
/如果是该字符串中包含了空格,调⽤substring⽅法,否则就是啥都没⼲原本返回
//就是如果字符串⾥有空格,那么还是新⽣⼀个String对象返回
return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
总结:
String对象⼀旦被创建就是固定不变的了,对String对象的任何改变都不影响到原对象,相关的任何变化性的操作都会⽣成新的对象。
String对象每次有变化性操作的时候,都会重新new⼀个String对象(这⾥指的是有变化的情况)。
3. 简单的测试
public class App {
public static void main(String[] args) {
String a = "111";
String a1 = "111";
String b = new String("111");
String c = new String("111");
//对象地址是同⼀个
System.out.println(a==a1);
//对象内容是⼀样的
System.out.println(a.equals(a1));
//对象地址不⼀样
//我觉得是:因为对象类型不⼀样
System.out.println(a==b);
//false:显然是两个不同的对象
System.out.println(b==c);
//对象内容是⼀样的
System.out.println(a.equals(b));
}
}
总结:
String a = "111"; 在JVM申请内存存放"111"对应的对象,并将对象保存起来。当String a1="1111";的时候,会先去JVM的那块地⾥寻是否存在"111",刚好前⾯保存过,所以到,然后直接把对象的引⽤地址给了a1。所以此时的a和a1都保存着同⼀个引⽤地址。
String b = new String("111");就是创建⼀个对象然后把对象引⽤地址赋给变量b。但是这⾥有个特殊点,那就是(“111”),这⾥会先去JVM⾥的那块地⾥,到了直接存放引⽤地址。不到创建⼀个对象然后把引⽤地址给String的有参构造⽅法⾥。
因为a和b所保存的对象引⽤是不⼀样的。(⼀个引⽤的是字符串常量,⼀个引⽤的是字符串变量)
a和a1放在栈上,存放着对象的引⽤地址。
new的对象是在堆中。
常量其实是要看jdk版本的。
4. 最终的答案
String s = new String("111")会创建⼏个对象?
如果常量池中存在,只需创建⼀个对象
不存在,创建两个对象
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论