Java String
小小String,内有大文章。
String是Java中最常见的。正是因为它常见,所以大家经常忽略。但是在各种“高级别讨论”中又经常出现,因此自己尝试着总结一下。
- String的定义
- String的创建
- String的内存模型
- immutable
- String,StringBuilder,StringBuffer
- == vs equals
- intern()
- 一些测试题目
1 String的定义
学习任何东西最好从第一手资料开始。下面的文字摘自JDK中java.lang.String的头部。
/** * The {@code String} class represents character strings. All * string literals in Java programs, such as {@code "abc"}, are * implemented as instances of this class. * <p> * Strings are constant; their values cannot be changed after they * are created. String buffers support mutable strings. * Because String objects are immutable they can be shared.
从这里看出字符串就是“一串字符”(废话!!)。所有的字面量都是字符串。
字符串是常量,一旦创建不可改变。这里有个专门术语叫做“immutable”。因为比较重要,后面又单独的一节来讲immutable。
这个定义和C++里面的定义完全不同。C++里面的字符串更像是StringBuilder。
且C/C++的字符串以'\0'结尾。Java的字符串不包括'\0',因为String里面有字符的个数。
下面是JDK自带的源代码定义
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { private final char value[]; private int hash; // Default to 0 private static final long serialVersionUID = -6849794470754667710L; private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; //无参构造函数 public String() { this.value = new char[0]; } //最常用的构造函数,注意这里是浅拷贝。因为是immutable,无需实现深拷贝。 public String(String original) { this.value = original.value; this.hash = original.hash; } //入参为char数组的构造参数 public String(char value[]) { this.value = Arrays.copyOf(value, value.length); } public String(char value[], int offset, int count) { if (offset < 0) { throw new StringIndexOutOfBoundsException(offset); } if (count < 0) { throw new StringIndexOutOfBoundsException(count); } // Note: offset or count might be near -1>>>1. if (offset > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); } this.value = Arrays.copyOfRange(value, offset, offset+count); } ........ }
2 String的创建
(1) String str = "hello"; //在running constant pool创建"hello"
(2) char data[] = {'h', 'e', 'l','l','o'};//在栈上创建data String str = new String(data);//先去running consant pool里面查找有没有"hello",没有的话创建一个;且在堆上创建String对象,存放"hello"
(3) String str = new String("hello");//先去running consant pool里面查找有没有"hello",没有的话创建一个;且在堆上创建String对象,存放"hello"
(4)String str = "Hel" +"lo";//编译优化,在编译的时候就能确定str为"hello",优化为一个变量String str = "Hello"
(5)String str = "hel" + new String("lo"); //被编译器转化为str = (new StringBuilder()).append("hel").append("lo").toString();
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 load StringBuilder类 2 生成临时变量StringBuilder,保存"hel"字符串 3 执行String(String)的构造函数,得到字符串"lo" 4 执行StringBuilder.append() 把"lo” append到“hel”上得到字符串“hello” 5 执行StringBuilder.toString() 返回"hello”给变量S3.
(6)String stemp = "lo";
String str = "hel" + stemp;//同5,stemp是变量,编译时候不能确定,所以只能运行时候确定
(7)final String stemp = new String("lo");//new string运行时候才能确定,所以stemp无法编译优化
String str = "hel" + stemp;//同5
(8)final String stemp = "lo";//final常量,编译的时候能够确定
String str = "hel" + stemp;//同4,编译优化
(9)final String stemp = getFinalString();//虽然stemp是常量,但是函数调用只有在运行时才能确定
String str = "hel" + stemp;//同5,运行时确定
(10)你可以列出属于自己的方法
可以看到String的创建方法比较多,且背后的执行也不同。这个是“高级谈话”的开始。所以这里一定要弄清楚,每个创建方式的异同。
JAVA里面没有运算符重载,“hel” + new String("lo") 会被替代为StringBuilder执行程序。
3 String的内存模型
根据刚才String的创建过程,我们看到String主要是涉及到了running constant pool和heap。
4 immutable
5 String,StringBuilder,StringBuffer
String是不可变的;StringBuilder和StringBuffer的内容是可变的。
StringBuffer是线程安全的,从JDK1.0就引入了;
StringBuilder是线程不安全的,从JDK1.5引入。
String 是不可变的,如
String str = "hello";
String str = "world";//看上去是str指向的内容变了,其实是str指向"world"了,str不再直线“hello”。
6 == vs equals
==永远比较的地址
equals是祖宗Object的虚方法,String重载了此方法,用来比较内容是否相同
7 intern()
关于intern(),美团有这个牛的文章,自己实在不敢下笔了,直接转发过来。
http://tech.meituan.com/in_depth_understanding_string_intern.html
8 一些测试题目
8.1 写一个immutable类
8.2 下面代码的输出是什么
public class JavaTest { public static void main(String args[]) { String s1 = "123"; String s2 = "123"; String s3 = new String("123"); System.out.println((s1 == s2) + " " + (s2 == s3)); System.out.println((s1.equals(s2)) + " " + (s2.equals(s3))); } }