`

从JVM 内部看String 类型的问题

阅读更多

    关于String 类型,在面试题或者实际编程中都会经常遇到,有很多的网友也曾做过大量的分析。在看完深入Java 虚拟机这本书后,对JVM 处理Java 程序的流程有了一个大概的认识,所以总结一下。

   下面的分析从我们遇到的一些问题实例进行,我觉得这样是最好理解的。

 

1、String 类型对象的生成

     

String s=new String("zhxing");
String s1="zhxing";
Object o=new Object();

    下面来对比下这三条代码反编译后生成的字节码:

 

 (1)

   0:   new     #2; //class java/lang/String
   3:   dup
   4:   ldc     #3; //String zhxing
   6:   invokespecial   #4; //Method java/lang/String."<init>":(Ljava/lang/Strin
g;)V
   9:   astore_1

 

(2)

   10:  ldc     #3; //String zhxing
   12:  astore_2

 (3)

   13:  new     #5; //class java/lang/Object
   16:  dup
   17:  invokespecial   #1; //Method java/lang/Object."<init>":()V
   20:  astore_3

 

   涉及到得指令集的解析:

new指令格式:

   new indexbyte1,indexbyte2

 

执行过程:

   要执行new指令,Jvm通过计算(indextype1<<8)|indextype2生成一个指向常量池的无符号16位索引。然后JVM根据计算出的索引查找JVM常量池入口。该索引所指向的常量池入口必须为CONSTANT_Class_info。如果该入口尚不存在,那么JVM将解析这个常量池入口,该入口类型必须是类。JVM从堆中为新对象映像分配足够大的空间,并将对象的实例变量设为默认值。最后JVM将指向新对象的引用objectref压入操作数栈。

 

dup指令格式:

  dup

 

执行过程:

  要执行dup指令,JVM复制了操作数栈顶部一个字长的内容,然后再将复制内容压入栈。本指令能够从操作数栈顶部复制任何单位字长的值。但绝对不要使用它来复制操作数栈顶部任何两个字长(long型或double型)中的一个字长。上面例中,即复制引用objectref,这时在操作数栈存在2个引用。

 

ldc指令格式:

  ldc,index

 

执行过程:

   要执行ldc指令,JVM首先查找index所指定的常量池入口,在index指向的JVM常量池入口,JVM将会查找CONSTANT_Integer_info,CONSTANT_Float_info和CONSTANT_String_info入口。如果还没有这些入口,JVM会解析它们。而对于上面的haha,JVM会找到CONSTANT_String_info入口,同时,将把指向被拘留String对象(由解析该入口的进程产生)的引用压入操作数栈。

 

invokespecial指令格式:
  invokespecial,indextype1,indextype2

 

执行过程:
  对于该类而言,该指令是用来进行实例初始化方法的调用。上面例子中,即通过其中一个引用调用String类的构造器,初始化对象实例,让另一个相同的引用指向这个被初始化的对象实例,然后前一个引用弹出操作数栈。

 

astore_1指令格式:(astore_2、astore_3类似只是局部变量不同
  astore_1

 

astore_1指令过程:
  要执行astore_1指令,JVM从操作数栈顶部弹出一个引用类型或者returnAddress类型值,然后将该值存入由索引1指定的局部变量中,即将引用类型或者returnAddress类型值存入局部变量1。

 

JVM常量池:

   虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,包括直接常量(string,integer和floating point常量)和对其他类型,字段和方法的符号引用。对于String常量,它的值是在常量池中的。而JVM常量池在内存当中是以表的形式存在的,特别要注意的是对于String类型,JVM维护着一张表用来存储文字字符串值(被拘留的字符串值)。--这里有一点不明白的是:到底这张表是保存String 引用还是直接保存String 值(像int 常量池是直接保存值的),这点还不明确。规范上也没有说明,如有大虾知道的话,麻烦短信告诉下。

 

总结:

    看了上面的介绍后,可以知道,String 类型在编码中字面上的对象,是会进行查询String 常量池表的(如果没有就创建一个String 对象,然后再保存在表中)。而new String() 生成的对象是不会查询String 常量池表的,它不管表中是否存在,都会创建一个新的String 对象,所以内存地址是肯定不同的。而如果调用String 方法中的intern()方法,则会在String常量池表中查找,然后把该表中的引用返回。

 

这里有几篇String 相关的文章值得一看的 :

http://blog.csdn.net/ZangXT/archive/2009/08/05/4410246.aspx

http://www.iteye.com/topic/522167

 

 

2、String 类型参数的传递

   记得有篇文章是相关的Java-String类型的参数传递问题 ,(在一年前我还转载过)。

   该文章中的有几点我不大认同:

(1)String的存储实际上通过char[]来实现的。
(2)String就相当于是char[]的包装类。包装类的特质之一就是在对其值进行操作时会体现出其对应的基本类型的性质。

  

  看了JVM规范可以知道,数组在JVM 是一个对象来的,如果改变数组,对其操作是会改变它的值的。所以这和String 作为参数传递不会改变它的值是没有关系的。具体测试看下面:

public class Test {
	public static void main(String[] args) {
		char[] c={'b','a'};
		test(c);
		System.out.println(c[0]);
	}
	
	public static void test(char[] c){
		c[0]='a';
	}

}

 

   那为什么String 作为一个对象引用传递,而不会改变它的值。这是因为String 中成员变量,也就是保存值的char[] 数组是final 类型,而且没有提供任何关于它的修改的方法,当然没办法修改到String 的引用的内容啦。看下面模拟String 的例子:

public class Test {
	public static void main(String[] args) {
		StringTemp s=new StringTemp("bbbb");
		test2(s);
		System.out.println(s);
	}
	public static void test2(StringTemp s1){
		s1=new StringTemp("zhxing");
	}
}
//模拟String类型
public final class StringTemp {
	private final char[] value;
	public StringTemp(String s){
		value=s.toCharArray();
	}
	@Override
	public String toString() {
		return new String(value).toString();
	}
	

}

   在main 方法的s 和test2 方法的参数s1 都只是指向new StringTemp("bbbb"); 这个的引用,当s1 改变时(指向了另外的一个引用new StringTemp("zhxing");),是不会影响到s 的引用的。同理String 类型也是一样,是不会改变的。

  

 

  对于String 类型就了解到这里,不知道还有没相关的应用问题,如果有提出再来补上。。感觉自己写文章讲解有点犯晕,呵呵,文笔不好吧。。

1
1
分享到:
评论

相关推荐

    深入理解Java:String

    按照官方的说法:Java虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。 JVM主要管理两种类型...简单来说,非堆包含方法区、JVM内部处理或优化所需的内存(如JITCompiler,Just-in-time

    cyc学习笔记.pdf

    注:包装类型中一般设有缓冲池,比如Integer、String。 1、Integer缓存池范围-128~127都是同一个地址,在缓存池范围内赋值不会创建新的对象,且不开辟新内存空间。该缓存池由源码Integer.class中的IntegerCache这个...

    深入Java虚拟机(原书第2版).pdf【附光盘内容】

    14.2 inner float:揭示java float类型内部性质的applet 14.3 浮点模式 14.3.1 浮点值集合 14.3.2 浮点值集的转换 14.3.3 相关规则的本质 14.4 浮点操作码 14.5 一个模拟:“circle of squares” ...

    java面试题,180多页,绝对良心制作,欢迎点评,涵盖各种知识点,排版优美,阅读舒心

    180多页面试题,前前后后不间断的更新了两年,准备换工作时,总是拿来看看,有比较好的面试题,也不间断的更新,面试题目录如下: 【基础】面向对象的特征有哪些方面 13 抽象 13 继承 13 封装 13 多态性 13 【基础】...

    Java内存分配分析/栈内存、堆内存

    首先学习JVM相关需要需要内存的组成。  基本内容  · 堆  java动态创建对象,即对于new的一个实例对象。但是需要注意的是该实例对象的...  即JVM为每个已加载的类型开辟一块区域,包括基本类型和String类型(其

    java笔试题

    3、String 是最基本的数据类型吗? 4、float f=3.4;是否正确? 5、short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗? 6、Java有没有goto? 7、int和Integer有什么区别? 8、&和&&的区别? 9、解释...

    JAVA面试题最全集

    1.Java有那些基本数据类型,String是不是基本数据类型,他们有何区别。 2.字符串的操作: 写一个方法,实现字符串的反转,如:输入abc,输出cba 写一个方法,实现字符串的替换,如:输入bbbwlirbbb,输出...

    Big-Data-Interview:大数据面试知识点

    Big-Data-InterviewJava开发、大数据...封装、继承和多态Java语言数据类型Java的自动类型转换,强制类型转换String的不可变性、虚拟机的常量池中的String字符串、String.intern()的底层原理Java语言中的关键字:finalJa

    Java面向对象程序设计笔记

    2.对String对象操作的类 24 3. 对时间处理的类 25 4. 对系统处理的类: 26 5. 数据运算的类 27 6. 正则表达式 27 第七章 异常处理 32 第八章Oracle数据库基础 32 第九章JDBC编 32 第十章GUI编程 ...

    java 面试题 总结

    从内存方面来看, Stateful Session Bean 与 Stateless Session Bean 比较, Stateful Session Bean 会消耗 J2EE Server 较多的内存,然而 Stateful Session Bean 的优势却在于他可以维持使用者的状态。 9、...

    Java 高级特性.doc

    学习心得:先从这四个知识点来看,张老师的确很让人敬佩!以上的一些程序代码均为张老师课堂即兴发挥所写,也可以看出,张老师对JAVA特性的深刻理解能力!现在说说我对这些程序代码的理解,说实话,才开始听张老师讲...

    java面试宝典2012版.pdf

    13、是否可以从一个static方法内部发出对非static方法的调用? 14、Integer与int的区别 15、Math.round(11.5)等於多少? Math.round(-11.5)等於多少? 16、下面的代码有什么不妥之处? 17、请说出作用域public,...

    千方百计笔试题大全

    3、String 是最基本的数据类型吗? 8 4、float 型float f=3.4是否正确? 8 5、语句float f=1.3;编译能否通过? 8 6、short s1 = 1; s1 = s1 + 1;有什么错? 8 7、Java 有没有goto? 8 8、int 和Integer 有什么区别? 9 9...

    java面试宝典

    3、String 是最基本的数据类型吗? 8 4、float 型float f=3.4是否正确? 8 5、语句float f=1.3;编译能否通过? 8 6、short s1 = 1; s1 = s1 + 1;有什么错? 8 7、Java 有没有goto? 8 8、int 和Integer 有什么区别? 9 9...

    【05-面向对象(下)】

    基本数据类型的包装类 •八大数据类型的包装类分别为:Byte、Short、Integer、Long、Character、 Float、Double、Boolean。 把基本数据类型变量包装类实例是通过对应包装类的构造器来实现的,不仅如此,8个...

    涵盖了90%以上的面试题

    一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 如何在main方法执行前输出”hello world” java程序的初始化顺序 请说出作用域public,private,protected,以及不写时的区别 为什么java中有些...

    疯狂JAVA讲义

    9.3.2 String、StringBuffer和StringBuilder类 322 9.3.3 Math类 327 9.3.4 Random类 328 9.3.5 BigDecimal类 330 9.4 处理日期的类 333 9.4.1 Date类 333 9.4.2 Calendar类 334 9.4.3 TimeZone类 337 9.5 ...

    open:开源CGTA库

    CGTA /塞尔维亚最近更新了序列化库,以支持我们在jvm上内部使用多年的scala-js。 这是一个非常简单的使用示例: (可选)将cgta.serland.SerlandExports混合到您的包对象中,这将在具有SerClass的任何对象上将...

Global site tag (gtag.js) - Google Analytics