版权声明:Please make the source marked https://blog.csdn.net/qq_31807385/article/details/83030967
JVM的内存分配图:
PC寄存器:会记录各个线程的执行位置
Java方法栈:面向java方法
本地方法栈:面向本地方法(用C++ 写的native方法)
方法区:加载后的Java类会被存放在java方法区,代码实际运行的时候,虚拟机会执行方法区内的代码。
为什么字符串是不可变的?
因为字符串的底层使用的是数组存储,数组的长度是不可变的。且使用final,private修饰,不能直接方法,String也没有提供直接修改的方法。
由此我们知道:String一旦被创建,则不能被修改,所谓的修改也只是创建了新的对象。
字符串常量在内存中的位置:
字符串常量对象存储在常量池中,jdk1.6及之前在方法区中,jdk1.7之后堆中特殊的区域中;jdk1.8,在元空间中(元空间是方法区的一种体现)
String str1 = new String(“hello”);
String str2 = “hello”;
System.out.println(str1 == str2);//false;
第一句代码创建了几个对象?
字符串常量池中有一个对象,堆中有一个字符串对象;
堆中的字符串对象指向了常量池中常量对象的"hello";str1,str2在栈中;
new String("hello")在堆中;"hello"字符串常量在常量池中
为什么是false?
str1引用在栈中,指向了堆中的对象,str2引用在栈中指向常量池(方法区),两者的地址是不一样的。 请注意:字符串常量中的字符串也是对象,堆中的字符串对象都是指向了字符串常量池中的对象的。
String str1 = "hellojava";
String str2 = "hello" +"java";
System.out.println(str1 == str2);//true
为什么是true?
hellojava作为一个字符串(常量)对象在字符串常量池中存在,java编译器发现hello和java是两个常量,
是字符串的拼接,拼接之后仍然是字符串常量对象,依然存储在常量池中,发现常量池中有这个常量对象,
直接将栈中的引用str2指向该存在的常量对象。这里的注意:没有涉及到变量的运算,相同的字符串不会产生新的对象
String str1 = "hello";
String str2 = "java";
String str3 = "hellojava";
String str4 = str1 + "java";
String str5 = str1 + str2;
System.out.println(str3 == str4);//false
System.out.println(str3 == str5);//false
为什么是这个结果?
详解:str1指向常量池中的常量对象hello str2指向常量池中的常量对象java,str3指向常量池中的常量对象hellojava。str4 和 str5 都涉及到了String对象,实际会产生新的对象存储在堆中,而str3 指向的是常量池(方法区中) 总结:变量参与运算(拼接),一定会产生新的对象。对象在堆中
关于String对象的图解
(1)
(2)
任何的类型都有常量池的概念,比如说
比如下面的代码:
package hello_java;
public class Test {
public static void main(String[] args) {
Integer i1 = 127;
Integer i2 = 127;
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i1 == i2);
System.out.println(i3 == i4);
}
}
true
false
为甚呢?看下面的源码,重新创建一个Integer的对象的时候,首先看值的范围,如果在该方位内的话,就使用常量池中的值。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}