A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

  今天学到面向对象。如创建对象Person p = new Person();
  是在堆内存中开辟一个空间存放对象,栈内存中的p指向堆内存中new Person()的首地址。

  突然想到,String也是个类,是引用数据类型。
  那么问题来了,String s = "abc";是在堆内存中开辟一个空间,并把地址值传递给s,s指向堆内存吗?
  问了指导老师,回答说java有一个常量池的存在,目前先不需要了解。

  不了解怎么行!开始打一堆代码测试
  1)
  System.out.println(new Object());//结果:java.lang.Object@3e2de41d
  System.out.println(new String());//结果:什么都没输出
  为啥new String()不是打印地址值?记得打印对象,是默认调用上帝类的toString()方法。
  查看文档,发现String类重写了toString()方法,
  继续查看源码,Object类的toString()方法为:
  public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
  }
  而String类重写的方法是:
  public String toString() {
        return this;
  }
  返回调用者的字符串类型?
  因为创建对象时是空参,输出的是空。
  继续测试
  2)
  System.out.println(new String("abc"));//结果:abc
  那么大胆假设,String类的实例,就是个常量并存在常量池中?
  继续测试
  3)
  String string常量 = "abc";
  String string实例 = new String("abc");
  System.out.println(string常量 == string实例);//结果是false
  System.out.println(string常量.equals(string实例));//结果是true
  测试结果表明假设错误。
  只能说明两者值是相同的,但String的实例不是常量。
  也就是说,new String()依然是在堆中开辟内存空间。
  继续假设,既然值是存在常量池中,new String()指向常量池就行啦!

  那么常量池在哪里?不懂问度娘吧

  得出总结:常量池存在于.class文件中,在运行期间被JVM装载,并且可以用String 的intern()方法扩充。
                        1)当使用javac命令编译语句String str = "abc";时,虚拟机已知"abc"是个常量,首先查看常量池中是否有字符串"abc",有就赋值给str,
                        没有就在常量池中创建一个字符串"abc"后赋值给str。这个在编译时期就能确定。
                        2)当使用javac命令编译语句String str = new String("abc");时,用new创建的字符串不是常量,不能在编译时期就确定,
                        所以不放入常量池中,而是赋予它地址空间。
                        在使用java命令运行期间,JVM装载了常量池,new String()在堆中产生的实例(旧)自行调用String类的intern()方法,查找常量池中是否
                        有相同编码的字符串常量,找得到就算了,找不到就在常量池开辟空间保存这个字符串。
                        然后不管在常量池找不找得到,都会在堆内存重新开辟一个空间产生一个新的实例,即实例(新),存放常量池中的字符串。栈中的str就指向
                        这个新实例。而一开始创建的实例(旧)就变成垃圾等待回收了。
                        3)同理,八种基本数据类型的封装类大部分都实现了常量池技术,这些类是Byte,Short,Integer,Long,Character,Boolean。
                        另外两种浮点型的包装类没有实现,这里就不多说啦。
                        4)既然String类intern()方法这么厉害,它是怎么实现的呢?
                        查看文档如下:
                        public String intern()返回字符串对象的规范化表示形式。
                        一个初始为空的字符串池,它由类 String 私有地维护。
                        当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。
                        否则,将此 String 对象添加到池中,并返回此 String 对象的引用。
                        查看源码如下:
                        public native String intern();
                       
                        妹的这是个本地方法看不了,剧终!



0 个回复

您需要登录后才可以回帖 登录 | 加入黑马