【Java】Javase基础常见面试题总结

1.为什么重写equals要重写hashCode

  1. hashCode()与哈希表:
    在Java中,哈希表(如HashMap、HashSet等)是基于哈希函数的数据结构。哈希表用来存储键值对,并允许通过键来快速查找值。在哈希表中,键的哈希码被用于计算存储位置,因此不同的键应该产生不同的哈希码以充分分散数据,减少哈希冲突。如果两个对象在equals()方法中被认为是相等的,那么它们的hashCode()方法应该返回相同的值,以确保它们在哈希表中能够正确存储和检索。

    两个对象equals = true ,那么hashCode = true;
    hashCode = true,不一定equals = true;
    equals = false ,hashCode可能true也可能false;
    hashCode = false , equals一定等于false。

  2. 哈希集合与哈希映射的行为:
    当你向HashSet或HashMap等使用哈希表的集合类中添加元素时,Java会首先计算每个元素的哈希码,并根据该哈希码将元素放置在合适的位置。接着,它会调用元素的equals()方法来检查是否存在相同的元素。如果两个对象在equals()方法中被认为是相等的,但它们的哈希码不同,会导致集合中存在两个相等的元素,这会破坏集合的一致性。

    简单来说就是先判断hashCode是否相同,再判断equals,这样的步骤极大优化了纯靠equals判等的速度,因为hashCode在一个对象创建之初就已经计算完成了,所以说hashCode判等的速度是块于equals的判等速度的

  3. 规范要求:
    Java的Object类中有这样的规定:如果两个对象通过equals()方法比较返回true,则它们的哈希码(由hashCode()方法返回)必须相等。因此,当你重写equals()方法时,为了遵循这个规范,你也应该相应地重写hashCode()方法。

综上所述,重写hashCode()方法是为了保证在集合类中正确地处理对象的插入、查找和删除操作。如果不同时重写hashCode()方法,会导致对象在集合中的行为不稳定,可能导致意外的结果。

String StringBuffer StringBuilder的区别

StringBuilder的拼接速度远远大于String的速度
StringBuffer跟builder逻辑一样,但是加锁了,所以速度稍微慢一些但是更加安全

  • 速度远快于String的原因:
    内存:由一个个电容组成一个个基本单位
    内存在出场时设定:一个字节为一个存储单元
    cpu把想看的地方的地址指令发给内存,内存把数据传给从cpu
    理论上讲,想读一百个字节数据,cpu至少要下达一百次指令,单次通信,cpu到内存之间需要走20到30纳秒-电流在导线中的速度16万千米/s–20万千米/s
    操作系统在分配内存的时候会尽可能挨在一起
    所以每4096B会合成一个内存页
    cpu每次只需要下达每个页的第一个物理单元的指令,只记住第一个bit点的地址,直接返回4096个物理单元的数据
    所以一万字节只需要三个页来存储
    页单元属于物理上重新划分的逻辑单元
    如果一个页里面存两个,第一个大小不确定,不能确定第二个变量的位置,所以也是一种以空间换取时间的操作
    内存为了加快速度,都是以页来分配,所以一个变量都会分配到4kB

    • 但是有例外,利用数组可以把多个变量存到一个页里面

    在页里面的变量是不支持页拓展的,如想要字符串增加内容只能新建一个页,这是正常String逻辑
    StringBuilder逻辑:跟buffer一样,只不过没锁
    StringBuffer的逻辑:申请一个足够大的空间,直接在空间内增加,不用申请新的页
    Buffer:缓冲的意思,带有buffer代表有足够缓冲空间的数组,不用反复申请空间,对内存的消耗大大降低

== 和 equals的区别

==:如果是基本数据类型,比较值,引用类型比较地址
equals:具体看各个类重写equals方法后的比较逻辑,比如String重写了equals后,他比较的是字符串每个位置的字符是否相同
这里要注意128陷阱
在Java中,对于基本数据类型byte, short, int,以及对应的封装类Byte, Short, Integer,在范围-128127内的数值,Java会在启动时自动缓存这些数值的对象。这样做是为了节省内存开销和提高性能。
因此,当使用==操作符比较两个在范围-128127内的Integer对象时,会出现预期外的结果。尽管这两个对象的值相等,但它们实际上不是同一个对象,因为缓存的原因。

Integer a = 1000;
Integer b = 1000;

System.out.println(a == b); // 输出: false
System.out.println(a.equals(b)); // 输出: true
Integer x = 100;
Integer y = 100;
Integer z = new Integer(100);
System.out.println(x == y); // 输出: true
System.out.println(x == z) //输出: false
System.out.println(x.equals(y)); // 输出: true

为了避免出现这个陷阱,通常推荐使用equals()方法来比较Integer对象的值,而不是使用==操作符。这样可以确保始终对值进行比较,而不受缓存机制的影响。

ArrayList 和 LinkedList的区别

首先ArrayList底层是数组实现的,而LinkedList是基于链表实现的
由于底层的差别,所以ArrayList更适合于查找,而LinkedList更适合增删改操作
虽然他俩都实现了LIst接口,但是LinkedList还实现了Deque接口,所以可以当作队列来用

猜你喜欢

转载自blog.csdn.net/qq_67548292/article/details/132097627