为了面试系列(一)001-String详解
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不可变
意义何在?
- 字符串常量池的需要
- 允许String对象缓存HashCode(hashMap)
- 安全性(网络连接地址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
的区别
==可以理解为是比较栈内存中的值,如果变量是基本数据类型,则栈内存中存放的就是具体数值,如果是引用类型,则栈内存中存放的是引用的内存地址
equals
是Object
类提供的一个方法,其本质就是在用==
进行判断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 被缓存下来,不需要再次计算,所以相⽐于其他对象更快。