Java入门part8--String类01

String类


创建字符串的方式

方式1: 直接赋值

String str="abed";//直接赋值

方式2: 构造方法

String str2=new String("abce"); 

这样会产生两个对象 不建议使用

方式3:

 char[] array={'a','b','c','d'};
 String str3=new String(array);

请解释String类中两种对象实例化的区别

  1. 直接赋值:只会开辟一块堆内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用。
  2. 构造方法:会开辟两块堆内存空间,其中一块成为垃圾空间,不会自动保存在对象池中,可以使用intern()方法手工入池。

因此,一般采用直接赋值的方法创建字符串

字符串比较相等

  • == 表示地址比较 不是内容比较 str==str2;//比较str和str2地址是否相同
  • equals内容比较用 str.equals(str2);//比较str和str2的内容是否相同
  • 双引号引起来的内容都放在常量池
    比如str="abc";往常量池中放字符串str时,首先查看常量池中是否有abc,如果已经存在该字符串,那就不入池,直接将当前abc的地址给str
  • 举个例子看各种情况下字符串的内存布局
    public static void main(String[] args) {
        String str = "abcdef";//直接赋值
        String str2 = new String("abcdef");
        char[] array = {'a','b','c','d','e','f'};
        String str3 = new String(array);
        String str4 = "abc"+"def";//编译期已经确定-》"abcdef"
        String str5 = "abc"+new String("def");
        System.out.println(str == str2);//false
        System.out.println(str == str3);//false
        System.out.println(str == str4);//true
        System.out.println(str == str5);//false
        String str6 = "abc";
        String str7 = "def";
        String str8 = str6+str7;
        System.out.println(str == str8);//false

图解剖析内部存储布局:
Java入门part8--String类01
文字详解各种方式的内部存储

  • String str = "abcdef";创建了一个对象
    “abcdef” 对应一个对象,这个对象放在字符串常量池,不管出现多少遍,常量池中都只有一个"abcdef"
  • String str2 = new String("abcdef");创建一个或两个对象
    new String每写一遍,就创建一个新的对象,它使用常量"abcdef"对象的内容来创建出一个新的String对象。注意:此时str2指向的是堆中的String对象,所以 str == str2结果为false
    • 这时如果常量池中已经存在"abcdef",那么就不会再在常量池中创建"abcdef"了,直接从常量池拿,这种情况只创建了一个对象(堆中的String对象);
    • 如果常量池中不存在"abcdef",那么此时就会在常量池中创建一个"abcdef",这种情况它创建两个对象(堆中的String对象和常量池中的"abcdef")。
  • char[] array = {'a','b','c','d','e','f'};此时先在堆中创建一个字符数组,
    String str3 = new String(array);再创建一个String对象指向数组array所在的地址
    这种情况创建了两个对象
    str3指向的是指向数组的这个String的地址所以str == str3结果为false
  • String str4 = "abc"+"def";****
    在编译期时"abc"和"def"已经自动拼接成为"abcdef"了,常量池中已有"abcdef"所以运行时不用入池 ,str4直接指向的是池中"abcdef"的地址,所以str==str4 结果为true
  • String str5 = "abc"+new String("def"); 创建了3个对象(因为"abc"已经在常量池中存在,若不存在则是创建了四个对象)
    如果"abc"不在常量池中的话:
    ""括起来的内容只要""中的内容在常量池中不存在,则要创建一个对象,遇new又要创建一个对象
    new String("def");在常量池中创建"def",在堆中创建一个String对象,共创建两个对象
    "abc"创建一个对象
    "abc"+new String("def")又创建一个对象存储"abc"+new String("def")的结果
    所以一共创建四个对象(如果"abc"在常量池中不存在的话)
    所以str == str5的执行结果也是false
  • String str8 = str6+str7;创建了一个对象,存放str6+str7运行之后的结果
    所有的变量只有在运行的时候才知道结果 所以str==str8 结果为false

注意:
String str="ab"+"cd"+30; 其中产生了两个对象,“ab”+“cd"在编译期间已经确定是"abcd"了,所以"ab”+“cd"创建了一个对象,然后再创建一个对象存放"abcd”+30的结果,将结果的地址给str。所以创建了两个对象

intern()

.intern()手动将String对象加入到字符串常量池中

  1. 常量池当中已经有该字符串 直接返回常量池当中字符串对象的地址
  2. 常量池当中不存在 在常量池当中生成该对象的引用
  • String str2=new String("abcdef").intern(); 这样的话str==str2结果为true

  • String str3=new String(array).intern(); str==str3结果也为true

  • 比较字符串大小

理解字符串的不可变

String类内部部分源码如下:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    private final char value[];
    private int hash; // Default to 0
    ...
    public String() {
        this.value = "".value;
    }
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
    ...

有源码可得String类内部实现也是基于 char[] 来实现的, 但是 String 类中char value[]被final修饰,所以无法修改

String str = "hello" ;
str = "Horld" ;
System.out.println(str);
//执行结果:Hello

Java入门part8--String类01

由上面的内部存储图上看,虽说结果确实改变了,但并不是 String 对象本身发生改变, 而是 str 引用到了其他的对象

如果一定要修改Sring,只有两种方法:

  • 借助原来的字符串创建新字符串
str="Hello";
str="h"+str.substring(1); 

str.substring(1);从1号下标开始提取一直到字符串结束
这样产生了一个新对象"hello" 并没有改变常量池中的Hello

  • 但是可以通过反射 (可以在类外拿到私有的方法 对象)来改变String(了解即可)
 public static void main(String[] args) throws NoSuchFieldException,
        IllegalAccessException {
    String str = "hello";
    
    // 获取 String 类中的 value 字段  这个 value 和 String 源码中的 value 是匹配的
    Class clc = String.class;
    Field field = clc.getDeclaredField("value");

      // 将这个字段的访问属性设为 true
    field.setAccessible(true);
    
    // 把 str 中的 value 属性获取到.
    char[] value = (char[]) field.get(str);
    System.out.println(Arrays.toString(value));//执行结果[h, e, l, l, o]

    
    // 修改 value 的值
    value[0] = 'H';
    System.out.println(Arrays.toString(value));//执行结果[H, e, l, l, o]
}

为什么 String 要不可变?(不可变对象的好处是什么?)

  1. 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑何时深拷贝字符串的问题了.
  2. 不可变对象是线程安全的.
  3. 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中.
Java入门part8--String类01Java入门part8--String类01 穿撒板儿的追风少女 发布了22 篇原创文章 · 获赞 4 · 访问量 2404 私信 关注
上一篇:DJI 大疆无人机视频MP4损坏解决方法


下一篇:CF468C Hack it! 超详细解答