1.介绍一下Object类中的方法?
Class<?> getClass(); 返回该对象的运行时类;
boolean equals(Object obj); 判断指定对象与该对象是否相等;
int hashCode(); 返回该对象的hashCode值,默认情况下,是按照该对象的地址来计算。但很多类都重写了hashCode()方法,不再根据地址来计算返回值;
String toString(); 返回该对象的字符串表示;
clone(); 该方法用于帮助其他对象来实现自我克隆;
wait(); 控制线程的暂停;
notify(); 唤醒某个线程
notifyAll(); 唤醒所有线程
2.说一说hashCode()和equals()的关系?
hashCode用于获取哈希码(散列码),equals用于比较两个对象是否相等
如果两个对象相等,那么他们的哈希码一定相同
如果两个对象的哈希码相同,他们不一定相等
3.为什么要重写hashCode()和equals()?
Object类提供的equals()方法默认是用==来进行比较的,也就是说只有两个对象是同一个对象的时候,他门才相等,而现实业务中,我们需要比较的是两个对象的内容如果相同,则认为两个对象相等,所以Object类中的equals()是没有实际用途的,通常重写。
而hashCode()与equals()是联动的,用equals()方法进行比较时,会首先使用hashCode()方法,判断两个对象的哈希码是否相同,如果相同则再进行比较他们的内容,如果两个对象的哈希码都不同,那么这两个对象必不相等。
4.== 和equals()有什么区别?
==运算符作用于基本数据类型时,比较两个数值是否相等,作用于引用数据类型时,比较两个对象的内存地址是否相同,即判断他们是否为同一个对象;
equals()没有重写时,默认用==来比较两个对象的内存地址是否相同,重写后一般按照两个对象的内容进行比较,如内容相同,那么就认为两个对象相等
5.String类有哪些方法?
- char charAt(int index); 返回指定索引处的字符;
- String subString(int beginIndex,int endIndex); 从一个字符串中截取一部分子字符串
- String[] split(String regex); 以指定规则将字符串分割成数组;
- String trim(); 删除字符串前导和后置的空格;
- int indexOf(String str); 返回子串在此字符串中首次出现的索引;
- int lastIndexOf(String str); 返回子串在此字符串中最后一次出现的索引;
- boolean startWith(String prefix); 判断此字符串是否以指定的前缀开头;
- boolean endWith(String suffix); 判断此字符串是否以指定的后缀结尾;
- String toUpperCase(); 将此字符串所有字符都大写;
- String toLowerCase(); 将此字符串所有字符都小写;
- String replaceFirst(String regex, String replacement):用指定字符串替换第一个匹配的子串;
- String replaceAll(String regex, String replacement):用指定字符串替换所有的匹配的子串。
6.String类可以被继承吗?
不可以,String类是被final修饰的,所以不能被继承
在Java中,String类被设计为不可变类,主要表现在它保存字符串的成员变量是final的。
-
Java 9之前字符串采用char[]数组来保存字符,即 private final char[] value;
-
Java 9做了改进,采用byte[]数组来保存字符,即 private final byte[] value;
所以把String类设计为不可变类,主要是出于安全和性能的考虑,可归纳为如下4点。
-
由于字符串无论在任何 Java 系统中都广泛使用,会用来存储敏感信息,如账号,密码,网络路径,文件处理等场景里,保证字符串 String 类的安全性就尤为重要了,如果字符串是可变的,容易被篡改,那我们就无法保证使用字符串进行操作时,它是安全的,很有可能出现 SQL 注入,访问危险文件等操作。
-
在多线程中,只有不变的对象和值是线程安全的,可以在多个线程中共享数据。由于 String 天然的不可变,当一个线程”修改“了字符串的值,只会产生一个新的字符串对象,不会对其他线程的访问产生副作用,访问的都是同样的字符串数据,不需要任何同步操作。
-
字符串作为基础的数据结构,大量地应用在一些集合容器之中,尤其是一些散列集合,在散列集合中,存放元素都要根据对象的 hashCode() 方法来确定元素的位置。由于字符串 hashcode 属性不会变更,保证了唯一性,使得类似 HashMap,HashSet 等容器才能实现相应的缓存功能。由于 String 的不可变,避免重复计算 hashcode,只要使用缓存的 hashcode 即可,这样一来大大提高了在散列集合中使用 String 对象的性能。
-
当字符串不可变时,字符串常量池才有意义。字符串常量池的出现,可以减少创建相同字面量的字符串,让不同的引用指向池中同一个字符串,为运行时节约很多的堆内存。若字符串可变,字符串常量池失去意义,基于常量池的 String.intern() 方法也失效,每次创建新的字符串将在堆内开辟出新的空间,占据更多的内存。
7.说一说String和StringBuffer有什么区别?
String类是不可变类,一旦一个String对象创建后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁;
StringBuffer对象则代表一个字符序列可变的字符串,通过StringBuffer提供的append(),insert(),reverse(),setCharAt(),setLength()等方法可以改变这个字符串对象的字符序列。一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换成一个String对象。
8.StringBuffer和StringBuilder有什么区别?
StringBuffer和StringBuilder都表示字符序列可变的字符串对象,他们都有共同的父类AbstractStringBuilder,两个类的构造方法大致相同。但是StringBuffer是线程安全的,StringBuilder是非线程安全的,所以StringBuilder性能略高于StringBuffer,一般情况下,要创建一个内容可变的字符串,优先选择StringBuilder。
9.使用字符串时,new和""推荐使用哪一个?
推荐使用"";
举例:String str = "abc"; 和 String str = new String("abc")
当使用第一种,直接使用"abc"字符串直接量时,JVM会使用常量池来管理这个字符串
当使用第二种,JVM会首先使用常量池来管理"abc"这个字符串,然后再调用String类的构造器来创建一个新的String对象,新创建的String对象会被保存到堆内存中。
显然,使用第二种会占用更多的内存,所以推荐使用"";
10.说一说你对字符串拼接的理解?
字符串拼接有很多方式,最常见的有一下四种:
- 使用“+”运算符,如果拼接的都是字符串直接量,则使用运算符+直接拼接
- StringBuilder,如果拼接的字符串中包含变量,且不要求线程安全,则用StringBuilder,调用append()
- StringBuffer,如果拼接的字符串中包含变量,且要求线程安全,则用StringBuffer
- String类中的concat方法:如果只是对两个字符串拼接,并且包含变量,则适用concat方法
11.两个字符串相加的底层时如何实现的?
如果拼接的是两个字符串直接量,则在编译时会将其直接优化成一个完整的字符串
如果拼接的字符串包含变量,则编译时编译器采用StringBuilder对其优化,自动创建StringBuilder实例,并调用其append方法,将这些字符串拼接在一起;
12.String str = "abc"; 说一下这个过程会创建什么?放在哪里?
JVM会使用常量池来管理字符串直接量,在执行这句话时,JVM会首先检查常量池中是否有"abc",如没有,则将"abc"放入常量池,如有,则复用常量池中的"abc",将其引用赋值给str。
13.new String("abc"); 是去了哪里?仅仅是堆里吗?
在执行这句话时,JVM会首先使用常量池来管理字符串直接量,然后再创建一个新的String对象,
这个对象会保存在堆内存中,堆中对象的数据会指向常量池中的字符串直接量!