黑马程序员技术交流社区

标题: String 类的问题 [打印本页]

作者: 黑夜里的白猫    时间: 2013-6-8 15:55
标题: String 类的问题
本帖最后由 黑夜里的白猫 于 2013-6-8 19:55 编辑

String s1=new String ("a");
视频中说new 了一个对象,“a“也是一个对象;
a不是相当于一个参数么?为什么说”a“是个对象?




作者: 曹德君    时间: 2013-6-8 16:18
String 类是一个特殊类。字符串被创建的时候会在内存里面建立对象,内存为了节省空间将相同的字符串视为同一个对象。除非new的话就是一个新的对象。
  1.         String s1 = "a";
  2.                 String s2 = "a";
  3.                 String s3 = new String("a");
  4.                 System.out.println("s1==s2:"+(s1==s2));
  5.                 System.out.println("s1==s3:"+(s1==s3));
  6.                 System.out.println("s2==s3:"+(s2==s3));
复制代码
s1==s2:true
s1==s3:false
s2==s3:false
作者: 张歆明    时间: 2013-6-8 16:18
本帖最后由 张歆明 于 2013-6-8 16:20 编辑

你的问题 我也不确定 没办法 我去看了Java String类的源码
我加上了汉语注释:
  1. public final class String  implements java.io.Serializable, Comparable<String>, CharSequence
  2. {
  3.     /** The value is used for character storage. */
  4.     private final char value[];//存储字符串中每个字符的数组
  5.     private final int offset;//存储字符数组的指针的偏移量
  6.     /** The count is the number of characters in the String. */
  7.     private final int count;//表示真正的字符串在数组中占了多少位
  8.     private int hash; // Default to 0
  9.     private static final long serialVersionUID = -6849794470754667710L;//实现序列化用的

  10.     private static final ObjectStreamField[] serialPersistentFields =
  11.         new ObjectStreamField[0];
  12. //这是字符串的第一种构造方式 没有参数的   String s =new String();  可以看出来  指针 计数器和数组三个成员都是初始化为0  其中字符数组 char[] value长度初始化为0  但是 可以看到 //已经new了  就是在对内存中开辟了新的空间
复制代码

作者: 张歆明    时间: 2013-6-8 16:21
本帖最后由 张歆明 于 2013-6-8 16:30 编辑

第二段代码 接上一段代码
第二段代码 接上一段
//String的第一种构造方式  String s =new String();
public String() {
this.offset = 0;
this.count = 0;
this.value = new char[0];
}
//这个构造方法能解答你的疑问
//大概得看: String(String original) 把你传入的字符串常量对象(你的是“abc”)中的每一个字符赋值到自己的内部封装的字符数组中char[] value中  构造方法结束之后  这个常量池
//的“abc”和你的 String s =new String("abc");的s 以及对应的堆内存都没有任何联系了
public String(String original) {
int size = original.count;
char[] originalValue = original.value;
char[] v;
if (originalValue.length > size) {
// The array representing the String is bigger than the new
// String itself. Perhaps this constructor is being called
// in order to trim the baggage, so make a copy of the array.
int off = original.offset;
v = Arrays.copyOfRange(originalValue, off, off+size);
} else {
// The array representing the String is the same
// size as the String, so no point in making a copy.
v = originalValue;
}
this.offset = 0;
this.count = size;
this.value = v;
}

结论:
所以  String s ="abc";  字符串引用变量在栈内存中   "abc"这个字符串常量在方法区的常量池里面
         String s =new String("abc");如果你的常量池里面实现没有“abc”  现在方法区的常量池里面构建这个"abc"
         然后这个"abc"的引用传给String类的构造方法 public String(String original)   代码你也看到了  就是把"abc"中的字符存到自己封装的数组char[] value中
          这个对象位于堆内存中  构造完之后  和 常量池的"abc"就没有关系了  

作者: 刘晓    时间: 2013-6-8 16:21
String s1=new String("a"); //是先去StringPool去找有没有a,有的话,就直接把s1的引用指向a;没有的话,就创建一个String 对象 a,然后再把s1的引用指向a
作者: 黑夜里的白猫    时间: 2013-6-8 17:18
刘晓 发表于 2013-6-8 16:21
String s1=new String("a"); //是先去StringPool去找有没有a,有的话,就直接把s1的引用指向a;没有的话, ...

按你这样说的话,String s1=new String("a");还是定义了一个对象;视频中说的是new了一个对象,”a”还是一个对象;
作者: 黑夜里的白猫    时间: 2013-6-8 17:20
曹德君 发表于 2013-6-8 16:18
String 类是一个特殊类。字符串被创建的时候会在内存里面建立对象,内存为了节省空间将相同的字符串视为同 ...

老毕说的 String s1=new String(“a");是定义了两个对象,一个是new一个,一个是”a";能不能画出内存图来说明一下;谢谢!
作者: 黑夜里的白猫    时间: 2013-6-8 17:20
张歆明 发表于 2013-6-8 16:21
第二段代码 接上一段代码
第二段代码 接上一段
//String的第一种构造方式  String s =new String();

老毕说的 String s1=new String(“a");是定义了两个对象,一个是new一个,一个是”a";能不能画出内存图来说明一下;谢谢!!!
作者: 刘晓    时间: 2013-6-8 17:22
黑夜里的白猫 发表于 2013-6-8 17:18
按你这样说的话,String s1=new String("a");还是定义了一个对象;视频中说的是new了一个对象,”a”还是 ...

new了一个s1对象,a是创建的一个String对象。这是两个对象吧。。。。。:lol
作者: 张歆明    时间: 2013-6-8 18:00
黑夜里的白猫 发表于 2013-6-8 17:20
老毕说的 String s1=new String(“a");是定义了两个对象,一个是new一个,一个是”a";能不能画出内存图 ...

好的  其实代码已经很清楚了 我给你画一下

String s1 =new String(“abc”);
这是个赋值表达式  我们一步步看:
赋值表达式是从右向左进行  右边是 new String("abc") 这个表达式 先计算形参
毕老师说过:“abc”本身就是常量对象  存储在方法区的常量池里面  特点就是  如果常量池里面 没有这个“abc”  会自动创建一个 假定这个时候常量池没有 "abc"
见图1

之后  就把这个常量"abc"的引用传到String类的构造方法中
调用方法构造方法之前  要对成员进行默认初始化
根据String类的源代码  String类对象的成员有:(仅仅列出有用的哈)
    private final char value[];//存储字符串中每个字符的数组 ---这是个字符串数组  所以  又是一个引用成员变量
    private final int offset;//存储字符数组的指针的偏移量
    private final int count;//表示真正的字符串在数组中占了多少位

这个构造方法就是
public String(String original) {
     int size = original.count;
     char[] originalValue = original.value;
     char[] v;
     if (originalValue.length > size) {
     。。。。
     }
     this.offset = 0;
     this.count = size;
     this.value = v;}
这样  new出来的对象在对内存中的示意图如图2:
最后 建立好这个对象之后  把地址值传给String s
这样  就有 String s = new String("abc");



1.jpg (11.42 KB, 下载次数: 0)

图1

图1

2.jpg (33.46 KB, 下载次数: 0)

图2

图2

作者: 黑夜里的白猫    时间: 2013-6-8 19:52
张歆明 发表于 2013-6-8 18:00
好的  其实代码已经很清楚了 我给你画一下

String s1 =new String(“abc”);

很清楚,非常感谢!
作者: 张歆明    时间: 2013-6-8 20:02
黑夜里的白猫 发表于 2013-6-8 19:52
很清楚,非常感谢!

哈哈 不客气 我也是看到你的问题 很疑惑 没办法 网上没有解释 我就查了一下源代码  哈哈 帮到你很高兴
作者: 小冰块    时间: 2013-6-8 21:48
张歆明 发表于 2013-6-8 18:00
好的  其实代码已经很清楚了 我给你画一下

String s1 =new String(“abc”);

太厉害了,被楼主一说我也有点迷糊,没想到你解释的那么详细,图画的真好!
作者: 张歆明    时间: 2013-6-8 21:54
小冰块 发表于 2013-6-8 21:48
太厉害了,被楼主一说我也有点迷糊,没想到你解释的那么详细,图画的真好! ...

哈哈  我也是 翻了下源代码 结合毕老师讲的 指针 数组 计数器总是一起出现的  我就画了下  呵呵




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2