java的内存问题(堆,常量池)

刚开始学java,今天遇到三个不太懂的地方,问题是这样的:

问题一:
堆中的方法区是用来存放类的信息,其中包括方法代码(比如study()方法)。
老师说,新建的对象的方法是使用指针指向方法区类的方法代码,那么如果用这个类新建两个对象,两个对象中的study()方法都是指向方法区的同一个地址吗?

问题二:
常量池中如果已经存在某一个字符串了(如"java"),那么再新建一个变量,为它赋值为"java",那么这个变量是不是指向之前已经存在的那个"java"的地址?这样如果常量池中有很多常量,每次新建时程序都要去常量池挨个寻找吗?会不会太麻烦。

问题三:
java中可以像C++中一样打印某一个变量的地址吗?

谢谢。

一、同一个类的方法是被各个实例对象共用的,指向的地址一样。
对比:这和属性不同,属性一般每个对象都有自己的一份,当然了静态属性也是共用的。
二、很多问题自己是可以测试的,学会自己测试很重要,比如这个问题,帮你写个junit测试一下更明了:
@Test
public void testString(){
String s1 = "java";
String s2 = "java";
System.out.println("s1 eq s2?"+s1.equals(s2));
System.out.println("s1 == s2?"+(s1==s2));
}
串口的输出为:
s1 eq s2?true
s1 == s2?true
从第一个可以看出,s1和s2值是相等的,这很显然;从第二个可以看出,s1和s2地址也是相同的。这是因为s1和s2都直接用赋值号引用"java"。
但是!别得意,再看一种情况。
@Test
public void testString(){
String s1 = "java";
String s2 = "java";
String s3 = new String("java");
System.out.println("s1 eq s2?"+s1.equals(s2));
System.out.println("s1 == s2?"+(s1==s2));
System.out.println("s1 eq s3?"+s1.equals(s3));
System.out.println("s1 == s3?"+(s1==s3));
}
结果为:
s1 eq s2?true
s1 == s2?true
s1 eq s3?true
s1 == s3?false
可以看到,s1和s3值相等,但是地址不同,这是因为s3是在其他地址上动态开辟出来的,只是初始化时值也为"java"。
三、java中打印出来的地址是哈希码,不是真正的地址。
其实就算C语言中的指针,也只是逻辑地址而不是物理地址,搞应用开发的虽然可以直接操作内存,不过也是在操作系统分配好的内存段内操作。此外操作系统中地址还有地址映射的问题。搞过单片机开发的人应该对操作物理地址和寄存器比较了解,那些就交给搞底层开发的人做吧。
最后,希望你遇到问题可以学会自己写测试程序解决问题,祝学习进步!

问题一:是的最后调用的都是一个访问,只不过在调用方法时会java虚拟机会传给study(this),就是把当前调用该方法的对象传过去。
问题二:对,引用会指向常量池的“java”的地址,至于虚拟机是怎么处理的我就不知道了。
问题三:对象是可以的,你直接打印那个对象,出来的应该就是那个对象的地址,只不过是hash地址。

1.在堆中对象中有一个指向方法区的指针,当你调用方法时,这时候指针就会到方法区找到它所对应的way方法完成了Java对象对方法的调用.所以两个对象中的study()方法都是指向方法区的同一个地址
2.常量池中如果已经存在某一个字符串了(如"java"),那么再新建一个变量,为它赋值为"java",那么这个变量指向之前已经存在的那个"java"的地址,常量赋值的时候,会根据相应的类,在JVM规范中是这样定义运行时常量池这个数据结构的:Runtime Constant Pool代表运行时每个class文件中的常量表。它包括**几种常量:编译期的数字常量、方法或者域的引用(在运行时解析)**,所有这些常量都会放在这个地方。所以当你运行时会先查表,当表没有就建一个,把索引返回去给你,这样不麻烦呀,比你每次都开一个string对象要好是吧
3.java不能让你直接操作内存,toString里面封装的也是

     public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

哈希码,是地址的一个代表,但不是地址