String 可以说是 Java 中使用最多最频繁的类, 也是面试中会经常被问到的知识点.
一. String 的使用
1. 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. For example:
- * ...
- */
- public final class String {
- private final char value[];
- }
String 对象一旦在堆中创建出来, 就无法再修改. 因为 String 对象放在 char 数组中, 该数组由 final 关键字修饰, 不可变.
2. 定义一个字符串
- /**
- * 定义一个字符串
- */
- String str1 = "helloworld";
- // 也可以, 但基本不这样写
- String str2 = new String("helloworld");
上面的这句代码在内存中怎么体现?
3. 再次赋值给已定义的字符串
str1 = "helloChina";
我们开始已经说了 String 是由 final 关键字修饰, 不可变, 那么此时在内存中如何体现呢?
4. 使用变量来赋值变量
String str3 = str1;
这个时候 str3 和 str1 引用了相同的对象 "helloChina"
5. String 对 "+" 的处理
String str4 = "good good" + "study";
通过编译工具后得到
String str4 = "good good study";
因此我们可以发现编译器在编译期间就是进行变量合并, 而不会在内存中创建三个对象 "good good","study","good good study". 但若是:
String str5 = str3 + "good good study";
这种情况无法在编译期间就进行变量合并.
6. String 常用的方法
- //str1 == "hello,world";
- // 获取长度
- str1.length()//12;
- // 截取位置 2 到 5 之间的字符串 (包括位置 2, 不包括位置 5, 从 0 开始)
- str1.substring(2,5);//"llo"
- // 判断是否含有字符串 "ello"
- str1.contains("ello");//true, 通过 indexOf 实现
- // 获取 ello 在 str1 中的开始位置
- str1.indexOf("ello");//1
- // 将字符串转化为字符串数据
- str1.split(",");//["hello","world"]
- // 去掉字符串两侧空格
- str1.trim();//"hello,world"
二. StringBuffer 与 StringBuilder
StringBuilder 与 StringBuffer 作用就是用来处理字符串, 但 String 类本身也具备很多方法可以用来处理字符串, 那么为什么还要引入这两个类呢? 看下面的例子
- public static void main(String[] args) {
- String str0 = "hel,lo,wor,l,d";
- long start = System.currentTimeMillis();
- for (int i = 0; i <100000; i++){
- str0 += i;
- }
- System.out.println(System.currentTimeMillis() - start);
- StringBuilder sb = new StringBuilder("hel,lo,wor,l,d");
- long start1 = System.currentTimeMillis();
- for (int i = 0; i < 100000; i++){
- sb.append(i);
- }
- System.out.println(System.currentTimeMillis() - start1);
- StringBuffer sbf = new StringBuffer("hel,lo,wor,l,d");
- long start2 = System.currentTimeMillis();
- for (int i = 0; i < 100000; i++){
- sbf.append(i);
- }
- System.out.println(System.currentTimeMillis() - start2);
- }
上述代码中 3 处循环完成了同样的功能, 字符串拼接, 执行的结果如下:
36823 3 4
可以看出执行时间差别太大, 为了解决 String 不擅长的大量字符串拼接这种业务场景, 引入了 StringBuffer 和 StringBuilder.
String | StringBuffer | StringBuilder |
---|---|---|
final 修饰,不可继承 | final 修饰,不可继承 | final 修饰,不可继承 |
字符串常量,创建后不可变 | 字符串变量,可动态修改 | 字符串变量,可动态修改 |
不存在线程安全问题 | 线程安全,所有 public 方法由 synchronized 修改 | 线程不安全 |
大量字符串拼接效率最低 | 大量字符串拼接效率非常高 | 大量字符串拼接效率最高 |
StringBuffer 与 StringBuilder 实现非常类似, 下面以 StringBuilder 简单说明一下 append() 方法基本原理
1. 首先创建一个 StringBuilder
- StringBuilder sb1 = new StringBuilder();
- StringBuilder sb2 = new StringBuilder(100);
StringBuilder 对字符串的操作是通过 char[] 来实现的, 通过默认构造器创建的 StringBuilder, 其内部创建的 char[] 的默认长度为 16, 当然可以调用重载的构造器传递初始长度 (推荐这样, 因为这样可以减少数组扩容次数, 提高效率).
- /**
- * Constructs a string builder with no characters in it and an
- * initial capacity of 16 characters.
- */
- public StringBuilder() {
- super(16);
- }
2. StringBuilder 的 append() 方法
每次调用 append(str) 方法时, 会首先判断数组长度是否足以添加传递来的字符串
- /**
- * Appends the specified string to this character sequence.
- * <p>
- * The characters of the {@code String} argument are appended, in
- * order, increasing the length of this sequence by the length of the
- * argument. If {@code str} is {@code null}, then the four
- * characters {@code "null"} are appended.
- *
- * @param str a string.
- * @return a reference to this object.
- */
- public AbstractStringBuilder append(String str) {
- if (str == null)
- return appendNull();
- int len = str.length();
- ensureCapacityInternal(count + len);
- str.getChars(0, len, value, count);
- count += len;
- return this;
- }
- /**
- * For positive values of {@code minimumCapacity}, this method
- * behaves like {@code ensureCapacity}, however it is never
- * synchronized.
- * If {@code minimumCapacity} is non positive due to numeric
- * overflow, this method throws {@code OutOfMemoryError}.
- */
- private void ensureCapacityInternal(int minimumCapacity) {
- // overflow-conscious code
- if (minimumCapacity - value.length> 0) {
- value = Arrays.copyOf(value,
- newCapacity(minimumCapacity));
- }
- }
如果传递的字符串长度 + 数组已存放的字符的长度 > 数组的长度, 这时就需要进行数据扩容了
- /**
- * Returns a capacity at least as large as the given minimum capacity.
- * Returns the current capacity increased by the same amount + 2 if
- * that suffices.
- * Will not return a capacity greater than {@code MAX_ARRAY_SIZE}
- * unless the given minimum capacity is greater than that.
- *
- * @param minCapacity the desired minimum capacity
- * @throws OutOfMemoryError if minCapacity is Less than zero or
- * greater than Integer.MAX_VALUE
- */
- private int newCapacity(int minCapacity) {
- // overflow-conscious code
- int newCapacity = (value.length << 1) + 2;
- if (newCapacity - minCapacity < 0) {
- newCapacity = minCapacity;
- }
- return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
- ? hugeCapacity(minCapacity)
- : newCapacity;
- }
扩容规则如下: 默认将数组长度设置为 "(当前数组长度 * 2) + 2", 但如果按此规则扩容后的数组也不足以添加新的字符串, 就需要将数组长度设置为 "数组内字符长度 + 传递的字符串长度".
因此假如我们知道拼接的字符串大概长度有 100 多字符, 我们就可以设置初始长度 150 或 200, 这样就可以避免或减少数组扩容的次数, 从而提高效率.
三. 总结
本文对 String 的不可变性, 基本原理和内存堆栈情况的分析, 常用的方法, StringBuffer 与 StringBuilder 的创建, append 方法的原理讲解, 对比了 String,StringBuffer 与 StringBuilder 异同, 若有不对之处, 请批评指正, 谢谢!
来源: http://www.bubuko.com/infodetail-3097869.html