001-String详解

1. String三大特性

1.1 三大特性
  • 不变性

    是⼀个 immutable 模式的对象,不变模式的主要作⽤是当⼀个对象需要被多线程共享并频繁访问时,可以保证数据的⼀致性。

  • 常量池优化

    String 对象创建之后,会在字符串常量池中进⾏缓存,下次创建同样的对象时,会直接返回缓存的引⽤。

  • final

    String 类不可被继承,提⾼了系统的安全性。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    //......
}

jdk1.9之后使用的是字节数组byte[](为了节省空间)

查看Class结构快捷键alt+7

1.2 常见考点
  • String不是基本数据类型

  • String实例化(两种方式)

    • 直接赋值

      String str1 = "hello";
    • 通过构造函数,直接传字符串,或者传入一个char数组

      String str2 = new String("hello");
      
      char[] chars = {'h','e','l','l','o'};
      String str3 = new String(chars);

    区别:

    存储区域不同,直接赋值存储在字符串常量池中,通过构造函数创建,存储在堆内存中

  • equals方法

    public boolean equals(Object anObject) {
            if (this == anObject) {
                return true;
            }
            if (anObject instanceof String) {
                String anotherString = (String)anObject;
                int n = value.length;
                if (n == anotherString.value.length) {
                    char v1[] = value;
                    char v2[] = anotherString.value;
                    int i = 0;
                    while (n-- != 0) {
                        if (v1[i] != v2[i])
                            return false;
                        i++;
                    }
                    return true;
                }
            }
            return false;
        }
  • String不可变

    意义何在?

    1. 字符串常量池的需要
    2. 允许String对象缓存HashCode(hashMap)
    3. 安全性(网络连接地址URL,文件路径path,还有反射机制所需要的String参数等)
  • intern()方法

    当调⽤某个字符串对象的 intern() ⽅式,会去字符串常量池中寻找,如果已经存在⼀个值相等的字符串

    对象的话,则直接返回该对象的引⽤,如果不存在,则在字符串常量池中创建该对象,并返回。

1.3 常用方法
方法 描述
public String() 创建一个值为空的对象
public String(String original) 创建一个值为original的对象
public String(char value[]) 将一个char型数组转为字符串对象
public String(char value[],int offset,int count) 将一个指定范围的char型数组转为字符串对象
public String(byte bytes) 将一个byte型数组转为字符串对象
public String(byte bytes,int offset,int length) 将一个指定范围的byte数组转为字符串对象
public int length() 返回字符串长度
public boolean isEmpty() 判断字符串是否为空
public char charAt(int index) 返回字符串中指定位置的字符
public byte[] getBytes() 将字符串转成byte数组
public boolean equals(Object anObject) 判断两个字符串是否相等
public boolean equalsIgnoreCase(String anString) 判断两个字符串是否相等并忽略大小写
public int compareTo(String anotherString) 对两个字符串进行排序
public boolean startsWith(String prefix) 判断是否以指定值开头
public boolean endWith(String suffix) 判断是否以指定值结尾
public int hashCode() 获取字符串的散列值
public int indexOf(String str) 从头开始查找指定字符的位置
public int indexOf(String str, int fromIndex) 从指定的位置开始查找指定字符的位置
public String substring(int beginIndex) 截取字符串从指定位置开始到结尾
public String substring(int beginIndex, int endIndex) 截取字符串从指定位置开始到指定位置结束
public String concat(String str) 追加字符串
public String replaceAll(String regex, String replacement) 替换字符串
public String[] split(String regex) 指定字符串对⽬标字符串进⾏分割,返回数组
public String toLowerCase() 将字符串转为⼩写
public String toUpperCase() 将字符串转为⼤写
public char[] toCharArray() 将字符串转为char型数组

2. 经典面试题

2.1 ==equals的区别
  • ==可以理解为是比较栈内存中的值,如果变量是基本数据类型,则栈内存中存放的就是具体数值,如果是引用类型,则栈内存中存放的是引用的内存地址

  • equalsObject类提供的一个方法,其本质就是在用==进行判断

    public boolean equals(Object obj) {
        return (this == obj);
    }
  • java中的任意一个类都可以对其进行重写,根据具体的需求重新定义其判断逻辑。

    public class Student {
        private Integer id;
        private String name;
    
        public Student(Integer id, String name) {
            this.id = id;
            this.name = name;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
    
            Student student = (Student) o;
            if(id==student.id&&name.equals(student.name)){
                return true;
            }else {
                return false;
            }
    
        }
    
    }
2.2 字符串字⾯值 + 字符串字⾯值
String str1 = "Hello World";
String str2 = "Hello"+" World";
System.out.println(str1 == str2);
  • "Hello" 和 " World" 都是字符串字⾯值,字符串字⾯值 + 字符串字⾯值的结果仍然保存在字符串

    常量池中,所以 str1 和 str2 相同。

2.3 字符串字⾯值 + 字符串变量
String str1 = "Hello World";
String str2 = "Hello";
str2 += " World";
System.out.println(str1 == str2);
  • false。这道题看似与第二题一样,为什么结果完全不同呢?因为str1 = "Hello World"是直接创建,str2 = "Hello";str2 += " World"是先创建再修改的,同时修改完成之后的字符串是放在堆内存中的。为什么呢?因为str2是一个字符串变量," World"是字符串字面值,当这两者拼接时,得到的新字符串不再保存到常量池中,而是在堆中开辟一块新的空间存储。
2.4 下述代码的运⾏结果是?
String str1 = "Hello World";
String str2 = " World";
String str3 = "Hello"+str2;
System.out.println(str1 == str3);
  • false,str2 是变量,"Hello" 是字符串字⾯值,字符串字⾯值 + 变量会在堆内存中开辟新的空间来存储,所以 str1 和 str3 不同。
2.5 下述代码的运行结果是?
String str1 = "Hello World";
final String str2 = " World";
String str3 = "Hello"+str2;
System.out.println(str1 == str3);
  • true,"Hello" 是字符串字⾯值,str2 是常量,字符串字⾯值+常量的结果仍然保存在字符串常量池中,

    所以 str1 和 str3 相同。

2.6 下述代码的运行结果是?
String str1 = "Hello World";
final String str2 = new String(" World");
String str3 = "Hello"+str2;
System.out.println(str1 == str3);
  • false,str2 是常量,但是 new String(" World") 保存在堆内存中,所以即使使⽤ final 进⾏了修饰,

    str2 仍然保存在堆中,则 str3 也就保存在堆中,所以 str1 和 str3 不同。

2.7 下述代码的运行结果是?
String str1 = "Hello World";
String str2 = "Hello";
String str3 = " World";
String str4 = str2 + str3;
System.out.println(str4 == str1);
System.out.println(str4.intern() == str1);
2.8 什么是字符串常量池
  • 字符串常量池位于堆内存中,专门用来存储字符串常量,可以提高内存使用率,避免开辟空间存储相同的字符串,在创建字符串时,JVM 会首先检查字符串常量池,如果池中已存在该字符串,则返回它的引用,如果不存在则实例化一个字符串放到池中,并返回引用。
2.9 String是线程安全的吗?
  • 是。String为不可变类,一旦创建String对象,就无法改变。因此是线程安全的,同一个字符串可以被多个线程共享,保证多线程的安全性。
2.10 在使用hashMap的时候,用string做key有什么好处
  • HashMap 内部实现是通过 key 的 hashcode 来确定 value 的存储位置,⽽因为字符串是不可变的,当

    创建字符串时,它的 hashcode 被缓存下来,不需要再次计算,所以相⽐于其他对象更快。