Java基础(五)——String

一、String

1、介绍

  String是一个final类,不可被继承,代表不可变的字符序列。是一个类类型的变量,Java程序中的所有字符串字面量(如"abc")都作为此类的实例实现,"abc"是一个对象。
  字符串是常量,创建之后不能更改。String 类复写 了Object类中的equals方法。
  String对象的字符内容是存储在一个字符数组value[]中的。

  JDK8部分源码:

  说明:实现了Serializable接口,表示字符串是支持序列化的。实现了Comparable<String>接口,表示字符串可以比较大小。
  内部定义了final char[] value用于储存字符串数据,final体现不可变性。
  通过字面量(区别于new)给一个字符串赋值,此时的字符串声明在字符串常量池中。
  字符串都在字符串常量池中,字符串常量池是不会存储相同内容的字符串。
  总结:最后对String的考查都基于以上最后两点原理。

  不难理解:
  row1:首先常量池什么也没有,就创建了字符串"abc",然后将它的地址赋给了s1。
  row2:常量池已经有了字符串"abc",所以直接将它的地址赋给了s2,此时s1==s2为true。
  row3:常量池没有"hello",就创建了字符串"hello",然后将它的地址赋给了s1。
  深刻理解常量池的概念及字符串的不可变性,不难得出:

1 String s1 = "abc"; // 字面量的定义方式
2 String s2 = "abc";
3 System.out.println(s1 == s2); // true
4 
5 String s3 = s1.replace('a', 'm');
6 System.out.println(s1); // abc
7 System.out.println(s3); // mbc
8 
9 System.out.println(s1 == s3); // false

2、考查点

  代码示例:

 1 String s1 = "javaEE";
 2 String s2 = "javaEE";
 3 String s3 = new String("javaEE");
 4 String s4 = new String("javaEE");
 5 
 6 System.out.println(s1 == s2); // true
 7 System.out.println(s1 == s3); // false
 8 System.out.println(s1 == s4); // false
 9 System.out.println(s3 == s4); // false
10 
11 Person p1 = new Person("Tom", 12);
12 Person p2 = new Person("Tom", 12);
13 System.out.println(p1.name == p2.name); // true
14 System.out.println(p1.name.equals(p2.name)); // true
  说明:

  面试题:String s = new String("abc")的方式,在内存中创建了几个对象?
  答:两个。一个是堆空间中new结构,另一个是char value[]对应的常量池中的数据:"abc"。
  有的答案是一个或两个。原因是如果在字符串常量池已经存在"abc"字符串,就不会再创建了,只会在堆内存创建一个对象。

  代码示例:
 1 String s1 = "java";
 2 String s2 = "mqsql";
 3 String s3 = "javamqsql";
 4 
 5 String s4 = "java" + "mqsql";
 6 
 7 String s5 = s1 + "mqsql";
 8 String s6 = "java" + s2;
 9 String s7 = s1 + s2;
10 
11 // 1.不同拼接的对比
12 System.out.println(s3 == s4); // true
13 System.out.println(s3 == s5); // false
14 System.out.println(s3 == s6); // false
15 System.out.println(s3 == s7); // false
16 
17 System.out.println(s5 == s6); // false
18 System.out.println(s5 == s7); // false
19 
20 System.out.println(s6 == s7); // false
21 
22 // 2.intern().返回常量池里的地址
23 String intern = s5.intern();
24 System.out.println(s3 == intern); // true
25 
26 // 3.final.会使String成为一个常量
27 final String finalStr = "java";
28 String s8 = finalStr + "mqsql";
29 System.out.println(s3 == s8); // true

  内存结构:

  结论:
  ①常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
  ②只要有变量参与的连接,结果就在堆中。s5,存放的是堆空间的地址值。
  ③intern()是返回String对象在常量池中的引用。

  代码示例:

 1 public class Main {
 2     String str = new String("good");
 3     char[] ch = {'t', 'e', 's', 't'};
 4 
 5     public static void main(String[] args) {
 6         Test test = new Test();
 7         test.change(test.str, test.ch);
 8 
 9         System.out.println(test.str); // good
10         System.out.println(test.ch); // best
11     }
12 
13     public void change(String str, char[] ch) {
14         str = "test ok";
15         ch[0] = 'b';
16     }
17 }

3、字符串的内存结构

  JDK 1.6:字符串常量池存储在方法区(永久区)
  JDK 1.7:字符串常量池存储在堆空间
  JDK 1.8:字符串常量池存储在方法区(元空间)
  详细介绍见JVM。JVM中涉及字符串的内存结构。

4、常用方法

  获取
  int length():获取字符串长度
  char charAt(int index):获取指定位置的字符
  int indexOf(int ch):返回字符ch第一次出现的位置
  int indexOf(String str):!=-1 可判断包含str
  int indexOf(int ch, int fromIndex):从指定位置字符ch第一次出现的位置
  int indexOf(String str, int fromIndex)
  int lastIndexOf(int ch):反向索引,返回字符ch第一次出现的位置
  int lastIndexOf(String str)
  int lastIndexOf(int ch, int fromIndex)
  int lastIndexOf(String str, int fromIndex)

  判断
  boolean startWith(str):是否以指定字符串开头
  boolean endsWith(str):是否以指定字符串结尾
  boolean isEmpty():字符串长度是否为空("" true. null 报空指针异常)
  boolean contains(str):是否包含str
  boolean equals(str):复写了Object类中的equals方法
  boolean equalsIgnoreCase(str)

  转换
  byte[] getBytes():String---->byte[]
  new String(byte[] bytes):byte[]---->String
  char[] toCharArray():String---->char[]
  new String(char[] arr):char[]---->String
  new String(char[] arr, int offset, int count):char[]---->String
  String valueOf():将基本数据类型转换成String
  String toUpperCase()
  String toLowerCase()

  替换
  String replace(char oldChar, char newChar)
  String replace(charSequence tar, charSequence rel)

  切割
  String[] split(regex):按指定规则切割字符串
  String substring(int beginIndex):从指定位置开始获取子串
  String substring(int beginIndex, int endIndex):获取子串[begin,end)

二、StringBuffer

1、介绍

  线程安全可变字符序列,可将字符串缓冲区安全地用于多个线程。
  StringBuffer可以对字符串内容进行增删,是字符串缓冲区,此时不会产生新的对象。是一个容器,返回的还是原缓冲区对象。很多方法与String相同。
  字符串的组成原理就是通过该类实现的。
  StringBuffer是可变长度的。

2、源码分析

 1 String str1 = new String(); // final char[] value = new char[0];
 2 String str2 = new String("abc"); // final char[] value = new char[]{'a', 'b', 'c'};
 3 
 4 // char[] value = new char[16]; 底层创建了一个长度16的数组
 5 StringBuilder sb1 = new StringBuilder();
 6 sb1.append('a'); // char[0] = 'a';
 7 sb1.append('b'); // char[1] = 'b';
 8 
 9 // char[] value = new char["abc".length() + 16];
10 StringBuffer sb2 = new StringBuffer("abc");

  总结:sb.length() == 3
  扩容:从源码中,可以看到,默认空参数的构造器StringBuffer容器大小为16。默认情况下,扩容为原容量2倍 + 2,同时将原有数组中的元素copy到新数组中。
  指导意义:数组copy效率低,在知道需要多少容量时,建议用StringBuffer(int capacity)或StringBuilder(int capacity)

3、常用方法

  存储
  StringBuffer append("abc"):将指定参数追加到末尾
  StringBuffer insert(index, "qq"):将指定参数追加到index处

  删除
  StringBuffer delete(int start, int end):删除缓冲区数据[start,end)
  StringBuffer deleteCharAt(int index):删除缓冲区指定位置字符

  获取
  char charAt(int index)
  int length()
  int indexOf(String str)
  int lastIndexOf(String str)
  String substring(int start, int end)
  void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin):将缓冲区指定数据存储到指定字符数组中

  修改
  StringBuffer replace(int start, int end, string str):把[start,end)替换为str
  void setCharAt(int index, char ch):将缓冲区指定位置换成指定字符

  反转
  StringBuffer reverse():返回的依然是原容器

三、StringBuilder

1、介绍

  线程不安全可变字符序列,将StringBuilder的实例用于多个线程是不安全的。如果需要这样的同步,则建议使用StringBuffer。
  JDK1.5以后,一个可变的字符序列,此类提供一个与StringBuffer兼容的API,但不保证同步。该类被设计用作StringBuffer的一个简易替换。用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer要快。
  每个字符串生成器都有一定的容量。只要字符串生成器所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区。如果内部缓冲区溢出,则此容量自动增大。

2、考查点

 1 String s1 = "abc";
 2 StringBuffer s2 = new StringBuffer("abc");
 3 StringBuffer s3 = s2.reverse();
 4 
 5 System.out.println(s1); // abc
 6 System.out.println(s2); // cba
 7 System.out.println(s3); // cba
 8 
 9 System.out.println(s2 == s3); // true
10 
11 System.out.println(s1.equals(s2)); // false
12 System.out.println(s1.equals(s3)); // false
13 System.out.println(s2.equals(s3)); // true

  记住:StringBuffer是一个容器。返回的还是原缓冲区对象。就不难理解上述答案。

3、String、StringBuffer、StringBuilder异同

  StringBuffer是线程同步。StringBuilder是线程不同步。实际开发中建议使用StringBuilder,效率高。
  String:不可变的字符序列,底层使用char[]存储。
  StringBuffer:可变的字符序列,线程安全的,效率低。底层使用char[]存储。
  StringBuilder:可变的字符序列,线程不安全的,效率高。底层使用char[]存储。

4、String、StringBuffer、StringBuilder效率对比

  从高到低:StringBuilder > StringBuffer > String

posted @ 2020-10-30 11:51  Craftsman-L  阅读(146)  评论(0编辑  收藏  举报