下面这条语句一共创建了多少个对象:String s="a"+"b"+"c"+"d"我觉得应该有 a b ab c abc d abcd七个,但是也有人说是四个,两个,还有一个五个的
我用debug下断点一看居然只有四个 a b c d,是不是测试方法有误,,求大神给出令人信服的答案
我用debug下断点一看居然只有四个 a b c d,是不是测试方法有误,,求大神给出令人信服的答案
解决方案 »
- 关于 event
- 怎么算字符串的长度?
- RMI的服务端怎样获取访问端的IP地址?
- 如何让我选择水果列表框就显示水果列表,选择蔬菜列表框就显示蔬菜列表呢
- 队列线程是什么东西?
- 给新手的java书籍选择的建议
- 用java怎样判断一个Email是否发送成功?
- 我可以访问任何的网站,就是访问不了www.sun.com,而且我能访问www.sun.com.cn。
- 这个表格如何用For循环或者别的办法实现
- 请问网站的applet应如何设置才能让其他人都能正常浏览。
- java如何抽取指定范围的随机数
- java 中 Object 作为默认父类 为什么 出现错误 incompatible operand types Person and Object
javap -c是这样的
public class tyj {
public tyj();
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 abcd
2: astore_1
3: return
}坐等楼下高手了
可以分成下面几部来看:
String s = "a";
String b = "b";
String c = "c";
String d = "d";
String s += b + c + d;因为我的理解是第一步的声明s时,已经给它赋值"a",然后按上面我写的代码这么来看,"b","c","d",这正好是三个对象,而最后的s虽然做了拼接操作,但是s还是原来的那个对象,所以最后一共正好有四个
问此语句共创建了几个对象, 答案是
就创建了一个
String s = "a" + "b" + "c" + "d" + "e";
赋值符号右边的"a"、"b"、"c"、"d"、"e"都是常量
对于常量,编译时就直接存储它们的字面值而不是它们的引用
在编译时就直接讲它们连接的结果提取出来变成了"abcde"
该语句在class文件中就相当于String s = "abcde"
然后当JVM执行到这一句的时候, 就在String pool里找
如果没有这个字符串,就会产生一个
呢 又是几个了。 就是说上面是一个是因为 "a"、"b"、"c"、"d"、"e"都是常量
但如果是变量呢? 我的答案是3个对象,但只有一个String对象:由于编译器的优化,最终代码为通过StringBuilder完成:1.StringBuilder builder = new StringBuilder(); 2.builder.append(a); 3.builder.append(b); 4.builder.append(c); 5.builder.append(d); 6.builder.append(e); 7.String s = builder.toString(); 8.我们先看看StringBuilder的构造器1. public StringBuilder() {2. super(16);3. }看下去1. AbstractStringBuilder(int capacity) {2. value = new char[capacity];3. }可见,分配了一个16自己长度的char数组我们看看append的整个过程(注意,源代码我从各个类进行了整合,他们实际上不在一个类里面的)1.2. public StringBuilder append(String str) {3. super.append(str);4. return this;5. }6.7. public AbstractStringBuilder append(String str) {8. if (str == null)9. str = "null";10. int len = str.length();11. if (len == 0)12. return this;13. int newCount = count + len;14. if (newCount > value.length)15. expandCapacity(newCount);16. str.getChars(0, len, value, count);17. count = newCount;18. return this;19. }20.21. public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {22. if (srcBegin < 0) {23. throw new StringIndexOutOfBoundsException(srcBegin);24. }25. if (srcEnd > count) {26. throw new StringIndexOutOfBoundsException(srcEnd);27. }28. if (srcBegin > srcEnd) {29. throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);30. }31. System32. .arraycopy(value, offset + srcBegin, dst, dstBegin, srcEnd - srcBegin);33. }可见,我们的代码不会超过16个,所以不会出现扩展value的情况。而append里面使用了arraycopy的复制方式,也没有产生新的对象。最后,我们再看StringBuilder的 toString()方法: 1. public String toString() {2. // Create a copy, don't share the array3. return new String(value, 0, count);4. }这里通过前面的数组生成了一个新的String。大家注意那个默认的16容量,如果题目出现了总长度超过16,则会出现如下的再次分配的情况1. void expandCapacity(int minimumCapacity) {
2. int newCapacity = (value.length + 1) * 2;
3. if (newCapacity < 0) {
4. newCapacity = Integer.MAX_VALUE;
5. } else if (minimumCapacity > newCapacity) {
6. newCapacity = minimumCapacity;
7. }
8. value = Arrays.copyOf(value, newCapacity);
9. }
10.11. public static char[] copyOf(char[] original, int newLength) {
12. char[] copy = new char[newLength];
13. System
14. .arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
15. return copy;
16. }
可见,expand容量时,增加为当前(长度+1)*2。
注意这里用了Arrays的方法,注意不是前面的 System.arraycopy方法哦。这里产生了一个新的
copy的char数组,长度为新的长度
总结:三个对象分别为1 StringBuilder2 new char[capacity]3 new String(value,0,count);如果说String对象,则为1个。
只有在将后面的几个常量组合成了abcd之后赋给s时才会去创建对象 也就是说最终还是有五个。而且String a=
“b” 的方式本来就会在串池里创建对象,怎么能说只有一个呢
就是这句
对于常量,编译时就直接存储它们的字面值而不是它们的引用
在编译时就直接讲它们连接的结果提取出来变成了"abcde"
该语句在class文件中就相当于String s = "abcde"
然后当JVM执行到这一句的时候, 就在String pool里找
如果没有这个字符串,就会产生一个
你说
对于常量,编译时就直接存储它们的字面值而不是它们的引用
在编译时就直接讲它们连接的结果提取出来变成了"abcde"
该语句在class文件中就相当于String s = "abcde"
然后当JVM执行到这一句的时候, 就在String pool里找
如果没有这个字符串,就会产生一个 也就是说最终的s在String pool是对象,那么最初的
"a" + "b" + "c" + "d"同样在string pool中,那么就不止一个对象了不是吗??
你再想想,会产生几个?
就创建了一个
String s = "a" + "b" + "c" + "d" + "e";
赋值符号右边的"a"、"b"、"c"、"d"、"e"都是常量
对于常量,编译时就直接存储它们的字面值而不是它们的引用
在编译时就直接讲它们连接的结果提取出来变成了"abcde"
该语句在class文件中就相当于String s = "abcde"
然后当JVM执行到这一句的时候, 就在String pool里找
如果没有这个字符串,就会产生一个
引用 14 楼 hc334521 的回复:引用 10 楼 lieyanfeng 的回复:
问题1: String s = "a" + "b" + "c" + "d" + "e";
问此语句共创建了几个对象, 答案是
就创建了一个
String s = "a" + "b" + "c" + "d" + "e";
赋值符号右边的"a"、"b"、"c"、……
我有点乱,你要不用debug断点看一下
[
我不是很明白
一个是s
一个是abcd
是的。java的初始化部分进行了优化,这种情况只创建一次。但是如果"abcd"本来就存在string pool里的话,恐怕就是0个了。
1.这行代码编译器对他必然会进行优化,优化的方案和编译器的版本可能有关系,高版本的会优化为一个。
像String s="abcd";但是这仅限于代码到class文件阶段,具体创建和运行的前后程序和运行的虚拟机有关系。
2.如果这行代码运行之前,内存里面已经存在“abcd”这样的对象,那么就不会再创建这个对象,意思是让我们从另一个角度看,这行代码可能运行在其他环境中,这和虚拟机内部有关系,需要去学习像string对象池这样的内部机制的存在
3.java虚拟机在运行时,如果string池中不存在该对象,创建“abcd”这个对象时,可能也会创建其他的对象来提高创建目标对象的效率,这些对象具体的实现可能也和jvm版本有关系
4.根据以上觉得,此题的目的还是第一条,是让我们理解编译器在编译阶段不会做傻瓜似的事,让虚拟机运行时去一个一个的创建对象,而是直接优化代码中的不合理的地方。
5.另lz在debug中看见的a,b,c,这样的东西,就类似于int 和Integer一样,它可能只是一个值,不是对象
一个是s
一个是abcd
http://www.iteye.com/topic/774673
http://yiliner.iteye.com/blog/207723
我看过得到的结论就是,像楼主String s="a"+"b"+"c"+"d";这语句在编译时期就可以确定的,这个语句等同于
String s="abcd";只会在常量池中存在一个对象,即abcd,楼主说在debug时看见的四个对象,其实string的内部实现是char数组,这个可以看下jdk的源码,你看到所谓的四个对象,其实就是string value值的char数组
变量不属于对象。
String 类声明的s才算一个对象。
所以只有一个对象。
http://blog.163.com/luyufen_luise/blog/static/5777392520086230367627/
s是引用,跟对象不是一个概念的,所以string a=new strin("abc");创建了几个对象,网上的答案是有错误的,无论什么情况,那个a他不是对象,只是string 对象的引用变量
System.out.printf("time:%d%n", end - start);
} /**
* java虚拟机对其进行了优化,只有一个对象,
*
* <p>
* 让我突然想到了一个线程的问题,没有同步,虚拟机将这个代码 <code>while(!done)i++;</code> 被转变成这样
* <code>if(!done)while(true)i++;</code> 这种优化称作提升(hoisting),擦!扯远了。
* </p>
*
* time:2194
*
* time:731 连续执行2次a(),第二次执行不再创建对象(除非用new), 对象已经存在于(heap)内存池中
*/
private void a() {
long start = System.nanoTime();
String s = "0" + "1" + "2" + "3" + "4" + "5" + "6" + "7" + "8" + "9";
long end = System.nanoTime();
prt(end, start);
} /**
* 和方法a的时间几乎一样,该方法更加确定了方法a在创建字符串的时候只有一个对象。
*
* time:2194
*/
private void b() {
long start = System.nanoTime();
String s = "0123456789";
long end = System.nanoTime();
prt(end, start);
} /**
* 哇塞,时间比a多了将近32倍
*
* time:69482
*/
private void c() {
// 加入一个变量
String a = "1";
//
long start = System.nanoTime();
String s = "0" + a + "2" + "3" + "4" + "5" + "6" + "7" + "8" + "9";
long end = System.nanoTime();
prt(end, start);
} /**
* 时间复杂度为n的2次方(n为相加的字符数量),NEVER DO THIS!
*
* i == 10 time:88133
*
* i == 1000 time:7454010
*/
public void d() {
long start = System.nanoTime();
String s = "0";
for (int i = 1; i < 1000; i++) {
s += i;
}
long end = System.nanoTime();
prt(end, start); } /**
* StringBuilder
*
* i == 10 time:68386
*
* i == 1000 time:529164
*/
public void e() {
long start = System.nanoTime();
StringBuilder s = new StringBuilder("0");
for (int i = 1; i < 1000; i++) {
s.append(i);
}
long end = System.nanoTime();
prt(end, start);
} /**
* 1个对象
*
* time:2194
*/
public void f() {
long start = System.nanoTime();
String s = "1 Object";
long end = System.nanoTime();
prt(end, start);
} /**
* 2个对象, "2 Object"是一个对象, new完后s句柄指向的是第二个新创建的对象, 看来new的开销挺虎人呀
*
* time:17919
*/
public void g() {
long start = System.nanoTime();
String s = new String("2 Object");
long end = System.nanoTime();
prt(end, start); } public static void main(String[] args) {
Test t = new Test();
// 一次只运行一个方法
// t.a();
// t.a(); // t.b();
// t.c();
// t.d();
// t.e();
// t.f();
t.g(); }}楼主问题结论:只有一个对象,java虚拟机对其进行了优化。
字符串对象是4个,也就是 a b c 和 d
而字符串引用对象是1个,也就是s
所以一共是5个对象。
Java常用的存储有三个地方:
栈——栈是存放引用和基本类型值的地方;
堆——存储new对象的地方(所有使用new创建的对象都存放在此);
池——也叫常量存储池,字符串的常量表达式(直接使用"..."创建的字符串)就存储在这个地方。
String类对象的创建:
1.String s="abcd";
直接在池中创建一个String类常量对象。
2.String s=new String("abcd");
首先判断池中是否有值为abcd的String类常量对象,如果没有则在池中创建一个String类常量对象,之后再在堆中创建一个String对象,引用s指向堆中的对象。
搞清楚String类对象的创建和存储就弄清了楼主问的问题了。
String s="a"+"b"+"c"+"d";
//JVM在编译时对String常量对象(这里不能是非final引用)的+操作符进行了优化
//因此上面表达式等同于s="abcd",也就是只在池中创建了一个对象;
String l="abcd";
String p=new String("abcd");
System.out.println(s==l);//true s与l都指向池中的String常量对象
System.out.println(s==p);//false p指向了堆中的String对象11楼的大侠提的问题很好,可以更好的理解String类对象的创建。
String s="ab";
String p=s+"cd";//引用s的值在编译时期无法确定,s+"cd"无法在编译时期进行优化
//JVM中对String对象的+进行了重载,其会先创建一个StringBulder对象
//之后使用appand方法连接字符串,然后使用toString方法创建为String对象
//这里至少要创建三个对象,StringBulder、池中的String对象、堆中的String对象(引用p指向此对象)
String l="abcd";
System.out.println(l==p);//false p指向了堆中的String对象final String m="ab";
//String类对象值不可变,final修饰的引用指向不可变,故引用m与String常量值"ab"绑定了
String n=m+"cd";
//此时jvm编译时会确定引用m就是String常量对象"ab"
//这里就等同于n="ab"+"cd",也就等同于n="abcd";
System.out.println(n==p);//ture n与l都指向了池中的String常量对象String类对象的创建于存储基本就是这个样子,希望大家多多拍砖。
期望楼主满意,给分吧!
这个断点的意思并不是创建了4个对象
你用断点跟踪一下String s = "abcd";
结果和String s = "a"+"b"+"c"+"d";是一样的,你总不能说String s = "abcd";创建了4个对象吧
这个断点只能说明String对象内部是用一个char []保存String的值,而且char []的长度为4