java-字符串

Java字符串表示方式有三种:

  1. 字符串常量,以双引号括起来的内容

    1
    String literialStr = “字符串常量”;
  2. 字符数组,以单引号括起来的内容表示一个字符,多个连起来表示字符串,String内部就是用字符数组来表示的

    1
    char[] chars = {'字', '符', '串', '常', '量'};
  3. String实例

    1
    String strs = new String("字符串常量");

字符串特性:

  1. 比较,使用==比较的是两个字符串是否引用同一个对象;使用equals比较两个字符串内容是否相等

  2. StringBuilder(多线程不安全)和StringBuffer(多线程安全)的toString方法返回的是new String("…")

  3. 字符串常量池,javap -verbose ClassName,反编译类后可以看到一个java文件被编译成class文件后的结构和内容。其中有一项结构是Constant pool(常量池),它里面会保存此class文件用到的所有常量包括类名、方法名、字符串常量。当class文件被载入jvm运行时,字符串常量池也会相应的载入内存成为运行时常量池。个人理解类似程序和进程的概念,一种是静态的描述,一种是载入内存后动态的表示。

  4. intern方法,查看JDK文档,关注重点:

    1. 调用intern方法时,会从字符串常量池去找,如果找到(通过equals方法判断),直接返回字符串常量池的引用
    2. 没有找到,则会将此String对象加入字符串常量池(字符串常量池此时将建立一个执向堆内存String对象的引用),然后还是返回字符串常量池的引用(注意:此时字符串常量池的引用指向堆内存的String对象)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    /**
    * Returns a canonical representation for the string object.
    * <p>
    * A pool of strings, initially empty, is maintained privately by the
    * class {@code String}.
    * <p>
    * When the intern method is invoked, if the pool already contains a
    * string equal to this {@code String} object as determined by
    * the {@link #equals(Object)} method, then the string from the pool is
    * returned. Otherwise, this {@code String} object is added to the
    * pool and a reference to this {@code String} object is returned.
    * <p>
    * It follows that for any two strings {@code s} and {@code t},
    * {@code s.intern() == t.intern()} is {@code true}
    * if and only if {@code s.equals(t)} is {@code true}.
    * <p>
    * All literal strings and string-valued constant expressions are
    * interned. String literals are defined in section 3.10.5 of the
    * <cite>The Java&trade; Language Specification</cite>.
    *
    * @return a string that has the same contents as this string, but is
    * guaranteed to be from a pool of unique strings.
    */
    public native String intern();

下面,基于intern的处理逻辑,看看几个例子

参考: https://developer.aliyun.com/article/48686

第一个:

1
2
3
4
5
6
7
//首先,将"string_test_1"存入字符串常量池,然后在堆上创建一个String对象,并返回堆上String对象的引用
String s = new String("string_test_1");
//调用intern方法,发现字符串常量池里已有“string_test_1”,返回字符串常量池对象的引用
s.intern();
//字符串常量池已有“string_test_1”,返回字符串常量池的引用
String s2 = "string_test_1";
System.out.println(s == s2); //堆上String对象引用 不等于 字符串常量池对象引用,结果为false

第二个:

1
2
3
4
5
6
7
//直接在堆上创建一个String对象,并返回堆上String对象的引用
String s3 = new StringBuilder().append ( "string_" ).append ( "test_2" ).toString ();
//调用intern方法,发现字符串常量池里还没有“string_test_2”,将在字符串常量池创建一个引用指向堆上String对象
s3.intern();
//字符串常量池已有指向堆上String对象的引用,equals判等,返回字符串常量池的引用(指向堆上String对象)
String s4 = "string_test_2";
System.out.println(s3 == s4); //堆上String对象引用 等于 字符串常量池的引用 结果为true

第三个:

1
2
3
4
5
//首先,将"same1"存入字符串常量池,并返回字符串常量池里String对象的引用
String s1 = "same1";
//字符串常量池已有“same1”,返回字符串常量池里的String对象引用
String s2 = "same1";
System.out.println(s1 == s2); //s1、s2同为字符串常量池里的String对象引用,结果为true

第四个:

1
2
3
4
5
//首先,将"same2"存入字符串常量池,然后在堆上创建一个String对象,并返回堆上String对象的引用
String s1 = new String("same2");
//字符串常量池已有“same2”,返回字符串常量池里的String对象引用
String s2 = "same2";
System.out.println(s1 == s2); //堆上String对象引用 不等于 字符串常量池对象引用,结果为false

第五个:

1
2
3
4
5
6
7
8
9
10
11
12
//将"first"存入字符串常量池,并返回字符串常量池里String对象的引用
String s1 = "first";
//将"second"存入字符串常量池,并返回字符串常量池里String对象的引用
String s2 = "second";
//将"firstsecond"存入字符串常量池,并返回字符串常量池里String对象的引用
String s3 = "firstsecond";
//s1+s2是调用StringBuilder返回一个new String对象引用,在堆上
String s4 = s1 + s2;
//字符串常量相加,在编译期替换成“firstsecond”,从字符串常量池查找并返回字符串常量池里String对象的引用
String s5 = "first" + "second";
System.out.println(s3 == s4); // false
System.out.println(s3 == s5); // true

第六个:

1
2
3
4
5
6
7
8
final String s1 = "first";
final String s2 = "second";
String s3 = "firstsecond";
//s1、s2被final修饰后,在编译期被替换成字符串常量相加,得到“firstsecond”,从字符串常量池查找并返回字符串常量池里String对象的引用
String s4 = s1 + s2;
String s5 = "first" + "second";
System.out.println(s3 == s4); // true
System.out.println(s3 == s5); // true
-------------本文结束感谢您的阅读-------------
Good for you!