我们都知道,在堆中有一个方法区,但有时也称之为非堆,它是各个线程共享的内存区域。用来存储加载的类信息、常量、静态变量、即时编译器编译后的大麦等数据。这里面有一个常量池,而不是运行时常量池。这里的常量池是用来存放在jvm编译器期间,就已经确定的常量,比如以下的代码:
测试对象
在创建测试对象前,我们需要创建两个Javabean类
Student类
创建Student类:
public class Student {
public String sname;
public Integer sno;
public Student() {
super();
}
public Student(String sname) {
this.sname = sname;
}
public Student( int sno,String name) {
this.sname = name;
this.sno = sno;
}
public String getName() {
return sname;
}
public void setName(String name) {
this.sname = name;
}
public int getSno() {
return sno;
}
public void setSno(int sno) {
this.sno = sno;
}
public String getString(){
return "你好";
}
public static Student getStaticString(){
return new Student();
}
public static String getRepeatedString(){
return new Student().getString();
}
SClass类型
static class SClass {
private Map<Integer, List<Student>> maps;
private String className;
public SClass(Map<Integer, List<Student>> maps, String className) {
this.maps = maps;
this.className = className;
}
public Map<Integer, List<Student>> getMaps() {
return maps;
}
public void setMaps(Map<Integer, List<Student>> maps) {
this.maps = maps;
}
public String getClassName() {
return className;
}
public SClass() {
}
public void setClassName(String className) {
this.className = className;
}
public String getStr(){
return "你好";
}
}
测试类
public static void main(String[] args) {
Student stu1=new Student("jack");
Student stu2=new Student("tom");
String stu1Str=stu1.getString();
String stu2Str=stu1.getString();
//语句1
System.out.println("stu1Str is equals to stu2Str:\t"+(stu1Str==stu2Str));
//语句2
System.out.println("\n"+Student.getStaticString());
System.out.println(Student.getStaticString());
System.out.println(stu1.getStaticString());
System.out.println(Student.getStaticString()==stu1.getStaticString());
String strReapeatedStr1=stu1.getRepeatedString();
String strRepeatedStr2=Student.getRepeatedString();
//语句3
System.out.println("\nstrReapeatedStr1 is equal to strRepeatedStr2:\t"+(strReapeatedStr1==strRepeatedStr2));
String str=new String("jack");
String str1="jack";
//语句4
System.out.println("\nstr is equals to str1:\t"+(str1==str));
System.out.println("str1 is equals to stu.getName():"+(str1==stu1.getName()));
String thisStr1=getString();
String thisStr2="meili";
String thisStr3=getString("meili");
//语句5
System.out.println("\nthisStr1 is equal to thisStr2:\t"+(thisStr1==thisStr2));
System.out.println("\nthisStr2 is equal to thisStr3:\t"+(thisStr2==thisStr3));
//语句6
String studengStr=new Student().getString();
String classStr=new SClass().getString();
System.out.println("\nstudengStr is equal to classStr:\t"+(studengStr==classStr));
}
分析语句1
语句1的输出结果为:stu1Str is equals to stu2Str: true
我们明明是说,当我们在创建一个对象时,会在栈里面分配一个地址,栈中的地址有一个别名,即为实例对象名。地址存存储着引用堆中对象的首地址。如果两个别名存储的对象引用地址的不同,那么这两个别名对应的栈地址就不相同。
堆空间随机分配的一个地址来存储对象的信息,每个对象都有自己的属性,并不包括对象的方法和常量池。常量池是放在方法区中的,是在编译时期就已经确定的大小和类型的常量。方法是存储在类空间,每个对象都可以共享类的方法,因而,语句1中返回的是方法的中的数据。数据时在编译时期就已经确定,并且放在常量池中,那么它是按照什么规则来安放的?
比如说在声明第一句时,Student stu1=new Student(“jack”);在编译器期间时,jvm会在堆中分配一个地址空间,用来存放常量数据的,这个常量数据包括——直接常量(String)和对其他类型的初始化值、方法内中声明的局部常量、字段的符号引用。池中的数据和数组一样通过索引访问。其次,jvm就会去常量池中,查找是否存有“jack”这个堆地址,如果没有“jack”这个堆地址,它就随机分配一个地址,来存放“jack”这个常量。如果没有存放“jack”这个常量的地址,那么就会随机分配一个地址,来存放“jack”这个常量的二进制数据。如果再有类的方法、构造器、String中声明这个“jack”时,变量就可以直接指向“jack”地址的引用。诸如“你好”,也是同样的道理。
分析语句2
语句2输出结果为:
com.mybaits.entity.Student@6084fa6a
com.mybaits.entity.Student@3a5476a7
com.mybaits.entity.Student@7f39ebdb
false
为什么语句2不相等呢?正如我们知道的那样,方法区还有类信息的数据,包括静态变量、静态方法等。对象可以访问静态方法,这是为什么呢?在静态方法中不能访问私有私有方法,但在私有方法可以访问静态方法,由此,也可以说明对象可以访问静态方法。我们扯远了,把话题再绕回来。我在Student类定义的这个方法:
public static Student getStaticString(){
return new Student();
}
它返回的是一个对象,该对象信息存储在堆空间的一个随机地址中,这也是我们可以不用通过new来创建对象的一个方式。既然创建的每一个对象的堆空间的地址是随机分配的,那么每一个对象的实例变量名所引用的实例对象的首地址都是不一样的,虽然我没有声明对象名,但这是匿名对象,因而,他们他们是不相同的。
语句3
语句三的输出结果:
strReapeatedStr1 is equal to strRepeatedStr2: true
为什么语句3的输出结果为true?
你看看我在Student类中定义的方法:
public static String getRepeatedString(){
return new Student().getString();
}
虽然每个调用这个方法时,在堆空间都会生成不同地址的Student对象,但是Student对象中只包含各自的属性,而方法是在类信息中的供所有对象的来共享的。当类加载到内存空间时,“构造器,组合关系中的对象的数据,方法中的数据等”就已加载到常量池中了。因而,不论调用了多少次getRepeatedString()方法,其内部都会返回“你好”对象的引用。
因而,语句3是true
分析语句4
语句4的输出结果:
str is equals to str1: false
str1 is equals to stu.getName():true
str确实不等于str1,为什么呢?str存储的是指向new String(“jack”)的首地址,而str1存储的是指向在常量池中的对象“jack”的地址,它是在编译器就已经放进常量池中的,因而,str1不等于str。但stu1.getName()是不同的,它返回的是指向常量池中“jack”的地址,而str1存储的也是这个地址,因而,后者是相等。
分析语句5
语句5的输出结果:
thisStr1 is equal to thisStr2: true
thisStr2 is equal to thisStr3: true
同时,这个也是相等,因为各自返回的是常量池中的“meili”的地址,因而,是相等。
分析语句6
语句6的输出结果:
studengStr is equal to classStr: true
为什么也为true呢?
SClass类的该方法:
public String getString(){
return "你好";
}
Student类的方法:
public String getStr(){
return "你好";
}
常量池不是针对哪个类的,而是针对整体数据的,当类加载Student时,会在常量池中遍历,是否存有“你好”的随机地址,如果没有则分配并返回“你好”对象的引用,如果有则直接放回“你好”对象的引用,这就类似于String.intern()方法一样。既然他们返回的是同一个地址的引用,自然就想等了。
以上就是个人对常量池的分析,如果有不对的地方,还请大牛指教。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/99291.html