知识点太混乱,一段一个理解,最后我理解透了
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
关于 String a=new String(“abc”); 究竟产生几个String对象实例问题的讨论?
首先说明:问此题的人,其实是草包一个,是半瓶水而已,就像问中国究竟有多少人一样?中国究竟有多少人呢?这问题本身就有问题,你是说中国现在有多少人,还是新中国成立的时候有多少人?还是全球有多少中国人?
问问题之前先要明白环境是什么?上下文是什么?那么java 也一样,你不了解java语言本身,或者jvm 本质很难回答看似简单,其实并非简单的问题。往往简单的问题,比较难回答,为什么呢?因为简单的问题没有环境约束,它没有限制出环境。任何东西脱离了周围环境,就没有它存在的价值,就好比 1+1 等于2 如何证明它一样。怎么证明呢?这个问题,你首先考虑的并不是证明不证明的问题。我们要考虑它是怎么来的?这个还真难考虑。不过怎么去证明它,首先你要知道,1+1 是否是规定就等于2,如果规定的东西,不要证明,我规定了1+1 等于二 那么证明它就显得很苍白,原因很简单规定必证明的优先权高,没有方法啊,你总不能越过规定吧,这是自然规律。首先你要这自然规律的约束下发展其自己,就像你活着地球上一样,你要看清地球本身,你光这地球上是不行的。
好废话少说,开始我们的讨论:
我定义了两个类
类一:StringDemo1
public class StringDemo1 {
public static void main(String[] args) {
String a=new String("abc");
}
}
类二:StringDemo2
public class StringDemo2 {
public static void main(String[] args) {
String b="abc";
String a=new String("abc");
}
}
这是很简单的两个类,没有什么可解释的,唯一不同是StringDemo2 比StringDemo1多加了一行代码 String b="abc"; 换句话说是加了一个上文的环境。
有人就会问我这有什么关系呢?那么请看我下面的分析,很简单,我不会像论坛上的一些人一样,故意那么研究得深刻,把一些不明白的人往火坑里推,显得自己很高深似的。
开始动手:
步骤一:
Javac 编译两个类(各位自己这命令行编译,这里就不列出了了)
步骤二:
javap -c 使用此命令,这个命令可能一般人就没有使用过,自己上网查查,都是jdk命令;此命令使用的例子:javap -c StringDemo1 javap -c StringDemo2 分别这命令行敲上面的两条命令
好,下面的是我cmd 命令行的输出(你自己的输出应该也差不多)
注:我用红色的字体来注释行:
D:\String>javac StringDemo1.java
D:\String>javac StringDemo2.java
D:\String>javap -c StringDemo1
Compiled from "StringDemo1.java"
public class StringDemo1 extends java.lang.Object{
public StringDemo1();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2; //class java/lang/String 此句new 了一个对象,产生了一个对象,毫无疑问
3: dup
4: ldc #3; //String abc 此句将String abc常量值从常量池中推送至栈顶 ,那这算不算对象,当然算了,只不过时创建时间可能不是现在哦 “#” 后面的为索引地址,我们可以从索引地址可以看到,这里有#2,#3,那么String a=new String("abc");
这是一句代码哦,可以看出此题目中这此句产生了两个String 对象。
6: invokespecial #4; //Method java/lang/String."<init>":(Ljava/lang/Strin
g;)V
9: astore_1
10: return
}
D:\String>javap -c StringDemo2
Compiled from "StringDemo2.java"
public class StringDemo2 extends java.lang.Object{
public StringDemo2();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2; //String abc 此句将String abc常量值从常量池中推送至栈顶
2: astore_1 //存储变量,也就是赋值操作
3: new #3; //class java/lang/String 此句产生一个对像
6: dup
7: ldc #2; //String abc 这里很有意思哦,我们看到 #2 索引地址已经出现过来,对了,上面 code 0: 就出现过了 他只不过是把地址,引用要过来了,并没有产生String对象,也就是从Stirng常量池里面要了个地址,自己知道地址就知道他的家了哦,
那么这里就有一个问题呀?什么问题?那这样的话,多个Sting变量有可能引用一个Strng实例,那么会产生String共享不安全的问题,多线程的情况下。这个不要担心了,String是线程安全的,因为它是final 类型的,没有办法改变!哦,恍然大悟!原来这样,怪不得sun这样做,有利于减少内存浪费啊(此问题其实还会产生String碎片,那就扯远了和StringBuffere有关系了,暂不讨论)。那么这么说来String a=new String("abc"); 此句产生了一个String对象。
9: invokespecial #4; //Method java/lang/String."<init>":(Ljava/lang/Strin
g;)V
12: astore_2
13: return
}
总结一下吧:
String a=new String("abc");这样的问题没有什么意义,脱离上下文的环境本身就没有意思了。从上面的分析,我们得出两个结论,第一个结论,也就是StringDemo1 中的请求,此句产生两个对象,其中一个特殊的String对象在String常量池里,另外一个就是对象堆中的(这个普通的Java类一样)
第二个结论,也就是StringDemo2, 那一句只产生了一个对象,只有堆中的一个对象,因为上网已经产生了一个String常量对象,后面就不产生了,直接使用地址索引了。
String 是一个特殊的数据类型,说白了它是从简单到对象类型过渡的一种类型;它既有简单类型那样的快速又有对象类型的特性。对应String的不足,那么String家族里面已经有了补充的类,完善的类,StringBuffere 各位不用担心,StringBuffere完全面向对象了,怎么证明呢?很简单
String a=“abc”; 这样可以直接复制常量值个String类型的。就好像简单类型的可以使用字面量来直接初始化 一样。例如 int a=123;
StringBuffere a=“abc”; 这样就不行啦
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
String a = "av"; //生成一个字符串对象,放在常量池中。
String b = "av"; //不会在池中生成对象
String c = new String("av"); //生成一个av对象,放在堆中
String d = new String("av"); //又会在堆中生成个对象
String e = st.intern(); //他会从常量池中找字符串对象的值是否等于st这个值,如果有,就返回池中对象,没有就往池中生成一个。程序中,已经有了,所以不会在生成
话说String是一个神奇的东西,又能在常量池中生成对象(String s = "av"),又能在堆中生成对象(String s = new String("av"))
在池中生成对象的时候,还要先看看池中有没值一样的,有就不生成了,直接指向就行,而堆中生成,永远都新生成,绝不含糊。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
String s = new String("av") 生成了几个对象啊?
这个第一段就解释了。总的来说, 要看上下文,也就是看下 av 在池中有木有存在。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
http://rednaxelafx.iteye.com/blog/774673 这个连接讨论的激烈,评论是亮点。
博主有趣,把String s = new String(“av”) 分得更细,av在类加载的时候就已经生成一个实例,运行时只生成了堆中的那个,所以得出了
String s = new String(“av”)
String s1 = new String(“av”)
只生成2个对象的结论。(下段是原话)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1. String s1 = new String("xyz");
2. String s2 = new String("xyz");
在运行时这两句只创建2个实例,能讲的简单的吗?
最初我认为是3个,xyz共享一个,s1,s2各一个, 但是太笨还是没看懂只有2个实例的的创建过程..谢谢赐教.wait
“在运行时”要分为两部分看:类加载阶段和实际执行时。
类加载对一个类只会进行一次。"xyz"在类加载时就已经创建并驻留了(如果该类被加载之前已经有"xyz"字符串被驻留过则不需要重复创建用于驻留的"xyz"实例)。驻留的字符串是放在全局共享的字符串常量池里的。
- String s1 = new String("xyz");
- String s2 = new String("xyz");
在这段代码后续被执行的时候,"xyz"字面量对应的String实例已经固定了,不会再被重复创建,所以这段代码无论执行多少次都只会在原本的基础上 新创建2个String实例而已。这是从概念上的Java/JVM来说。