|
| 1 | +# java.lang.String |
| 2 | + |
| 3 | +- 基于 Java9 |
| 4 | +- Tips: 预备知识,建议掌握以后再继续向下看。[Unicode 学习笔记](../common/unicodeStandard.md) |
| 5 | + |
| 6 | +--- |
| 7 | + |
| 8 | +<!-- TOC --> |
| 9 | + |
| 10 | +- [概览](#概览) |
| 11 | +- [继承结构](#继承结构) |
| 12 | + - [Serializable](#serializable) |
| 13 | + - [CharSequence](#charsequence) |
| 14 | + - [Comparable<T>](#comparablet) |
| 15 | +- [字符集简介](#字符集简介) |
| 16 | +- [重要域成员](#重要域成员) |
| 17 | +- [重要方法](#重要方法) |
| 18 | + - [代码点及代码单元](#代码点及代码单元) |
| 19 | + - [比较](#比较) |
| 20 | + - [搜索](#搜索) |
| 21 | + - [提取子串](#提取子串) |
| 22 | + - [创建全大写/全小写副本](#创建全大写全小写副本) |
| 23 | +- [一些体会](#一些体会) |
| 24 | +- [参考](#参考) |
| 25 | + |
| 26 | +<!-- /TOC --> |
| 27 | + |
| 28 | +--- |
| 29 | + |
| 30 | +## 概览 |
| 31 | + |
| 32 | +`String` 类代表了字符串。所有类似于 `"abc"` 的字符串字面量都是该类的实例。 |
| 33 | + |
| 34 | +字符串是常量,从创建后就不可更改。需要修改的字符串可以使用 `StringBuffer`。因为 `String` 实例不可变,所以他们可以安全的共享。一些例子: |
| 35 | + |
| 36 | +```java |
| 37 | +String str = "abc"; |
| 38 | +// 与上面一行代码效果相同 |
| 39 | +char data[] = {'a', 'b', 'c'}; |
| 40 | +String str = new String(data); |
| 41 | + |
| 42 | + |
| 43 | +System.out.println("abc"); |
| 44 | +String cde = "cde"; |
| 45 | +System.out.println("abc" + cde); |
| 46 | +String c = "abc".substring(2,3); |
| 47 | +String d = cde.substring(1, 2); |
| 48 | +``` |
| 49 | + |
| 50 | +`String` 类也包含了一些对单个字符的操作、比较、搜索、提取子串、创建全大写/全小写副本的方法。 |
| 51 | + |
| 52 | +Java 语言为字符串连接操作符(+)添加了特殊支持。向左连接。 |
| 53 | + |
| 54 | +```java |
| 55 | +// example 1 |
| 56 | +"The square root of 2 is " + Math.sqrt(2) |
| 57 | + | |
| 58 | + v |
| 59 | +"The square root of 2 is 1.4142135623730952" |
| 60 | + |
| 61 | +// example 2 |
| 62 | +1 + 2 + " fiddlers" |
| 63 | + | |
| 64 | + v |
| 65 | +"3 fiddlers" |
| 66 | + |
| 67 | +// example 3 |
| 68 | +"fiddlers " + 1 + 2 |
| 69 | + | |
| 70 | + v |
| 71 | +"fiddlers 12" |
| 72 | + |
| 73 | +``` |
| 74 | + |
| 75 | +`String` 使用 UTF-16 来编码(一个字符两个字节或四个字节)。拓展字符用 surrogate pairs 来表示,占用四个字节。(PS:该术语是编码领域的,可以参考之前写的一篇笔记: [Unicode 学习笔记](../common/unicodeStandard.md)) |
| 76 | + |
| 77 | +`String` 也提供了一些处理代码点(Unicode code points)和代码单元(Unicode code units)的方法(PS:这两个也是编码领域术语,可以参考:[Unicode 学习笔记](../common/unicodeStandard.md))。 |
| 78 | + |
| 79 | +String 连接操作符的具体实现留给 Java 编译器来决定,只要编译器能够完全遵循 Java 语言规范即可。例如 `javac` 编译器可能用 `StringBuffer`、`StringBuilder` 或 `java.lang.invoke.StringCOncatFactory` 来实现。 |
| 80 | + |
| 81 | +## 继承结构 |
| 82 | + |
| 83 | + |
| 84 | + |
| 85 | +### Serializable |
| 86 | + |
| 87 | +类通过实现 `java.io.Serializable` 接口来启用序列化能力。未实现该接口的类其状态将不会被序列化(抛出 `NotSerializableException` 异常)。该接口没有任何域或方法,只是表示可序列化的语义。 |
| 88 | + |
| 89 | +```java |
| 90 | + public static void serializableTest() throws IOException, ClassNotFoundException { |
| 91 | + String outputfile = "/Users/chen/Desktop/serializable"; |
| 92 | + ST instance = new ST(); |
| 93 | + |
| 94 | + ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(outputfile)); |
| 95 | + outputStream.writeObject(instance); |
| 96 | + outputStream.close(); |
| 97 | + |
| 98 | + ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(outputfile)); |
| 99 | + ST newInstance = (ST) inputStream.readObject(); |
| 100 | + inputStream.close(); |
| 101 | + |
| 102 | + System.out.println(newInstance); |
| 103 | + System.out.println(instance.equals(newInstance)); // true |
| 104 | + System.out.println(instance == newInstance); //false |
| 105 | + } |
| 106 | + |
| 107 | + static class ST implements Serializable{ |
| 108 | + public int publicField = 1; |
| 109 | + protected int protectedField = 1; |
| 110 | + int defaultField = 1; |
| 111 | + private int privateField = 1; |
| 112 | + |
| 113 | + @Override |
| 114 | + public boolean equals(Object o) { |
| 115 | + if (this == o) { |
| 116 | + return true; |
| 117 | + } |
| 118 | + if (o == null || getClass() != o.getClass()) { |
| 119 | + return false; |
| 120 | + } |
| 121 | + ST st = (ST) o; |
| 122 | + return publicField == st.publicField && |
| 123 | + protectedField == st.protectedField && |
| 124 | + defaultField == st.defaultField && |
| 125 | + privateField == st.privateField; |
| 126 | + } |
| 127 | + |
| 128 | + @Override |
| 129 | + public int hashCode() { |
| 130 | + return Objects.hash(publicField, protectedField, defaultField, privateField); |
| 131 | + } |
| 132 | + |
| 133 | + @Override |
| 134 | + public String toString() { |
| 135 | + return "ST{" + |
| 136 | + "publicField=" + publicField + |
| 137 | + ", protectedField=" + protectedField + |
| 138 | + ", defaultField=" + defaultField + |
| 139 | + ", privateField=" + privateField + |
| 140 | + '}'; |
| 141 | + } |
| 142 | + } |
| 143 | +``` |
| 144 | + |
| 145 | +序列化的对象中引用的所有对象都必须实现了该接口,否则也会抛出 `NotSerializableException` 异常。 |
| 146 | + |
| 147 | +### CharSequence |
| 148 | + |
| 149 | +一个 `CharSequence` 是一个只读的 `char` 序列。该接口为不同的实现提供了统一的只读访问。 |
| 150 | + |
| 151 | +该接口并没有重新定义 `equals() & hashCode()` 方法,直接比较两个实现类的实例结果是未定义的。所以将 `CharSequence` 的实例作为 `set` 的元素或 `map` 的 `key` 是不合适的。 |
| 152 | + |
| 153 | +主要的实现类:`CharBuffer, Segment, String, StringBuffer, StringBuilder` |
| 154 | + |
| 155 | +`String` 中也实现了与 `CharSequence` 实例进行比较、拼接等操作的函数。 |
| 156 | + |
| 157 | +### Comparable<T> |
| 158 | + |
| 159 | +主要用于集合中元素排序,两个元素直接比较。 |
| 160 | + |
| 161 | +## 字符集简介 |
| 162 | + |
| 163 | +`String` 类中用到了两种字符集 `Latin1 & UTF-16`. `Latin1` 拓展了 `ASCII` 编码,但是也是用一个字节来表示,`UTF-16` 使用两个或四个字节表示一个字符。简要介绍请看:[Unicode 学习笔记](../common/unicodeStandard.md) |
| 164 | + |
| 165 | +```java |
| 166 | +/** |
| 167 | + 在构造一个 String 对象时,String 会尝试对传入的参数进行压缩。比如 |
| 168 | +
|
| 169 | + String latin1 = new String("latin1".toCharArray()); |
| 170 | + String utf16 = new String("使用 UTF-16 字符集".toCharArray()); |
| 171 | +
|
| 172 | + 入参是 char[],java 中 char 是两个字节,byte 是一个字节,压缩后 latin1 的 value 字段是 6 个 byte,utf16 无法进行压缩,所以依旧是 26 个 byte。 |
| 173 | +
|
| 174 | + 下面是 java.lang.StringUTF16 中进行压缩的函数。 |
| 175 | + */ |
| 176 | + // compressedCopy char[] -> byte[] |
| 177 | + @HotSpotIntrinsicCandidate |
| 178 | + public static int compress(char[] src, int srcOff, byte[] dst, int dstOff, int len) { |
| 179 | + for (int i = 0; i < len; i++) { |
| 180 | + char c = src[srcOff]; |
| 181 | + if (c > 0xFF) { // 超出了 LATIN1 所能表示的范围,直接返回不再压缩 |
| 182 | + len = 0; |
| 183 | + break; |
| 184 | + } |
| 185 | + dst[dstOff] = (byte)c; |
| 186 | + srcOff++; |
| 187 | + dstOff++; |
| 188 | + } |
| 189 | + return len; |
| 190 | + } |
| 191 | +``` |
| 192 | + |
| 193 | +## 重要域成员 |
| 194 | + |
| 195 | +1. `private final byte[] value;` |
| 196 | + - 用来存储字符串的字节序列。 |
| 197 | +1. `private final byte coder;` |
| 198 | + - 用来暗示 `value` 中的字节数组的编码方式。有 `LATIN1 & UTF16` 可选。 |
| 199 | + - `static final byte LATIN1 = 0;` |
| 200 | + - `static final byte UTF16 = 1;` |
| 201 | +1. `private int hash;` |
| 202 | + - 缓存字符串哈希值。默认是 0. 在 首次调用 `hashCode()` 方法时计算并缓存。 |
| 203 | +1. `static final boolean COMPACT_STRINGS;` |
| 204 | + - 用来决定 `value` 是否进行压缩,默认是 true(压缩)。如果是 false 的话那么总是使用 UTF16 来编码字符串的字节流。在 `String` 类中,该域使用静态初始化块进行初始化。 |
| 205 | + |
| 206 | +## 重要方法 |
| 207 | + |
| 208 | +- TIPS:本来计划中有这部分的内容,但是读过源码理解了字符集的概念和 `String` 的处理方式以后感觉这部分就不需要再写了,有兴趣可以自己看。 |
| 209 | + |
| 210 | +### 代码点及代码单元 |
| 211 | + |
| 212 | +### 比较 |
| 213 | + |
| 214 | +### 搜索 |
| 215 | + |
| 216 | +### 提取子串 |
| 217 | + |
| 218 | +### 创建全大写/全小写副本 |
| 219 | + |
| 220 | +## 一些体会 |
| 221 | + |
| 222 | +理解 `String` 类最重要的不是会用 `String` 的 API,而是对字符集本身的理解,字符集是什么,它解决了什么问题,字符是怎么编码的等等,只有很好的理解了字符集才能很好的理解 `String` 的行为。 |
| 223 | + |
| 224 | +## 参考 |
| 225 | + |
| 226 | +1. [java.lang.String](https://docs.oracle.com/javase/9/docs/api/java/lang/String.html) |
| 227 | +1. [15.18.1. String Concatenation Operator +](https://docs.oracle.com/javase/specs/jls/se9/html/jls-15.html#jls-15.18.1) |
| 228 | +1. [Serializable](https://docs.oracle.com/javase/9/docs/api/java/io/Serializable.html) |
| 229 | +1. [JAVA 对象序列化(一)——Serializable](http://www.cnblogs.com/chenfei0801/archive/2013/04/05/3001149.html) |
| 230 | +1. [CharSequence](https://docs.oracle.com/javase/9/docs/api/java/lang/CharSequence.html) |
| 231 | +1. [ISO/IEC 8859-1](https://en.wikipedia.org/wiki/ISO/IEC_8859-1) |
0 commit comments