面试题总结持续更新(都很重要)

1、String、StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?

一、区别

1、String是字符串常量,而StringBuffer和StringBuilder是字符串变量。由String创建的字符内容是不可改变的,而由StringBuffer和StringBuidler创建的字符内容是可以改变的。

2、StringBuffer是线程安全的,而StringBuilder是非线程安全的。StringBuilder是从JDK 5开始,为StringBuffer类补充的一个单线程的等价类。我们在使用时应优先考虑使用StringBuilder,因为它支持StringBuffer的所有操作,但是因为它不执行同步,不会有线程安全带来额外的系统消耗,所以速度更快。

二、String为什么是不可变的?

String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。

String是被声明为final class,除了hash这个属性其它属性都声明为final。因为它的不可变性,所以例如拼接字符串时候会产生很多无用的中间对象,如果频繁的进行这样的操作对性能有所影响。

StringBuffer就是为了解决大量拼接字符串时产生很多中间对象问题而提供的一个类,提供append和add方法,可以将字符串添加到已有序列的末尾或指定位置,它的本质是一个线程安全的可修改的字符序列,把所有修改数据的方法都加上了synchronized。但是保证了线程安全是需要性能的代价的。

在很多情况下我们的字符串拼接操作不需要线程安全,这时候StringBuilder登场了,StringBuilder是JDK1.5发布的,它和StringBuffer本质上没什么区别,就是去掉了保证线程安全的那部分,减少了开销。

StringBuffer 和 StringBuilder 二者都继承了 AbstractStringBuilder ,底层都是利用可修改的char数组(JDK 9 以后是 byte数组)。

再说说字符串常量池:

Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池。

创建一个字符串时,首先会检查池中是否有值相同的字符串对象,如果有就直接返回引用,不会创建字符串对象;如果没有则新建字符串对象,返回对象引用,并且将新创建的对象放入池中。但是,通过new方法创建的String对象是不检查字符串常量池的,而是直接在堆中创建新对象,也不会把对象放入池中。上述原则只适用于直接给String对象引用赋值的情况。

String str1 = new String(“a”); //不检查字符串常量池的
  String str2 = “bb”; //检查字符串常量池的
  String还提供了intern()方法。调用该方法时,如果字符串常量池中包括了一个等于此String对象的字符串(由equals方法确定),则返回池中的字符串的引用。否则,将此String对象添加到池中,并且返回此池中对象的引用。

在JDK6中,不推荐大量使用intern方法,因为这个版本字符串缓存在永久代中,这个空间是有限了,除了FullGC之外不会被清楚,所以大量的缓存在这容易OutOfMemoryError。

之后的版本把字符串放入了堆中,避免了永久代被挤满。

对于三者使用的总结:

操作少量的数据: 适用String
单线程操作字符串缓冲区下操作大量数据: 适用StringBuilder
多线程操作字符串缓冲区下操作大量数据: 适用StringBuffer

2、== 与 equals的区别?

1)==比较的是值是否相等
如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;

如果作用于引用类型的变量,则比较的是所指向的对象的地址。

2)equals方法不能作用于基本数据类型的变量,只能用于类变量。(对于基本数据类型要用其包装类)

如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;

诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。

【注】Object类中的equals方法和“==”是一样的,没有区别,而String类,Integer类等等一些类,是重写了equals方法,才使得equals和“==不同”,所以,当自己创建类时,自动继承了Object的equals方法,要想实现不同的等于比较,必须重写equals方法。

    String a = new String("ab");  // a为一个引用
    String b = new String("ab");  // b为另一个引用,对象的内容一样
    String aa = "ab";   //放在常量池中
    String bb = "ab";   //从常量池中查找
        
    System.out.println(aa==bb);//true

    System.out.println(a==b); //false,非同一对象
            
    System.out.println((a.equals(b)); //true
            
    System.out.println((42 == 42.0); //true

String 中的 equals 方法是被重写过的,因为 object 的 equals 方法是比较的对象的内存地址,而 String 的 equals 方法比较的是对象的值。
当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。

3、为什么重写equals时必须重写hashCode方法?

当对象的equals()方法被重写时,通常有必要重写 hashCode() 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
(1)两个对象相等,hashcode一定相等
(2)两个对象不等,hashcode不一定不等
(3)hashcode相等,两个对象不一定相等
(4)hashcode不等,两个对象一定不等
hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。这样如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致。

HashMap中的get与put:
(1)put:
1.首先根据put元素的key获取hashcode,然后根据hashcode算出数组的下标位置,如果下标位置没有元素,直接放入元素即可。
2.如果该下标位置有元素,则需要已有元素和put元素的key对象比较equals方法,如果equals不一样,则说明可以放入进map中,会在该数组位置创建一个链表,后put进入的元素到放链表头,原来的元素向后移动。
(2)get:
根据元素的key获取hashcode,然后根据hashcode获取数组下标位置,如果只有一个元素则直接取出。如果该位置是一个链表,则需要调用equals方法遍历链表中的所有元素与当前的元素比较,得到真正想要的对象。(当两个对象的hashcode不同的话,肯定他们不能equals。)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值