首页 > 编程知识 正文

高级java面试题及答案(java面试失败多次怀疑自己)

时间:2023-05-05 19:28:06 阅读:81121 作者:2286

深入学习equals源码

最近,在面试很多工作的3-5年的申请者时,发现很多人没有读过String源代码。 特别是停留在equals和==的差异问题和表层询问的水平上,没有深入理解。

首先,让我们看看Java顶级类Object的源代码。 这个源代码包含12个方法。 那么,开发过程中常用的方法有5个,线程系统中常用的方法有5个。 其余的finalize已在jdk9中标记为deprecated。 registerNatives用于加载本地方法,使用c语言开发。 我们也不经常使用。

equals源码分析

/**

* * @ paramobjthereferenceobjectwithwhichtocompare。

* * @ return { @代码真} ifthisobjectisthesameastheobj

* *组件; { @代码假}其他。

* @see #hashCode () )

* @see java.util.HashMap

*/

公共区域联盟(对象) {

返回(this==OBJ );

}上面简单的三行代码是Object类的equals的源代码。 主要的比较是两个对象的地址。 可以看到,方法主体中的equals比较实际上也是通过“==”实现的。 因此,在这次面试的过程中,在问equals和“==”的区别时,请首先得出结论。==比较两个对象地址,而未改写equals方法的实体类即使使用equals也比较地址。

那么,我们经常在String中使用equals为什么是比较值呢? 接下来,让我们看一下String类的equals的源代码

String中equals方法源码分析

//*

* * @ param an对象

* * theobjecttocomparethis { @代码字符串}代理

*

* * @ return { @代码串} ifthegivenobjectrepresentsa { @代码串}

* *等同于这个字符串,{ @代码假}其他

*

* * @ see #比较到(字符串)

* * @ see #问题忽略情况(字符串) )。

*/

公共物件物件(物件)

在String中新的equals方法在第一步进行地址比较,

//如果两个字符串的地址相等,则他们的值一定相等,所以不需要进行以下值的比较

if (this==安对象) {

返回真;

}

//这个if表示,如果equals的方法主体不是String型的话,就直接返回false

例如:“蜜蜂攻击城堡狮子

if (注意事项序列) {

stringanotherstring=(string ) an对象;

intn=值.长度;

if (n==注释.值.长度) {

charv1=值;

charv2[ ]=anotherstring .值;

int i=0;

威尔(n---- -!=0}{

if (v1 )!=v2)

返回假;

I;

}

返回真;

}

} return false; }

在我们实际编写代码的时候,特别是一些需要重写equals方法的实体类的时候,那么我们需要进行equals的方法重写,重写equals方法需要遵守如下约定。翻译如下

(1)自反性:x.equals(x)必须返回true。

(2)对称性:x.equals(y)与y.equals(x)的返回值必须相等。

(3)传递性:x.equals(y)为true,y.equals(z)也为true,那么x.equals(z)必须为true。

(4)一致性:如果对象x和y在equals()中使用的信息都没有改变,那么x.equals(y)值始终不变。

(5)非null:x不是null,y为null,则x.equals(y)必须为false。

String类中hashCode方法源码

这就涉及到我们上述在Obejct中看到的另一个方法。细心的朋友可能发现了,hashCode方法的修饰符是native。在Object类中有7个方法都是native修饰的。而在String类中hashCode源码如下。可以看到。该hashCode方法返回的是一个整形。它主要计算的是一个字符串的hash值然后将其缓存从而提高其性能,而计算的算法在注释中有提。那就是s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1],算法中31是因为JVM对其进行了优化处理,即:31 * i == (i << 5) - i

/** * Returns a hash code for this string. The hash code for a * {@code String} object is computed as * <blockquote><pre> * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] * </pre></blockquote> * using {@code int} arithmetic, where {@code s[i]} is the * <i>i</i>th character of the string, {@code n} is the length of * the string, and {@code ^} indicates exponentiation. * (The hash value of the empty string is zero.) * * @return a hash code value for this object. */ public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }

重写equals必须重写hashCode的原因

重写hashCode的原则如下,也是我们必须遵守的,就等同于重写equals方法一样也需要遵守原则

(1)如果重写了equals()方法,检查条件“两个对象使用equals()方法判断为相等,则hashCode()方法也应该相等”是否成立,如果不成立,则重写hashCode ()方法。

(2)hashCode()方法不能太过简单,否则哈希冲突过多。

(3)hashCode()方法不能太过复杂,否则计算复杂度过高,影响性能。

其实,简单的来说。在我们重写hashCode方法的时候,我们一般会根据自身的业务来进行hashCode算法编写。正如String方法中的hashCode计算算法一样。我们根据业务涉及出自己所需要的算法。

那为什么我们重写了equals方法就需要重写hashCode方法呢?

其实有些人说的,重写了equals方法就一定要重新hashCode。这句话其实是错的。

因为hashCode和equals没有必然的区别。因为如果你不需要使用你定义的对象进行散列存储,比如使用hashMap,hashSet等集合,你不重写也没关系。因为hashCode本身是用来根据算法计算对象的散列值,然后根据这个值来决定存放位置。

举个简单的例子,如果使用HashMap,那么要保证key唯一,也就是要让其不重复,在java中比较是否相等用equals吧,所以使用equals去进行挨个比较,如果容器中已经存放了多个Key,是不是就需要比较很多次呢?如果有了hashCode值,是不是就简单多了。其实可以看一下hashmap的关于key比较的源码,就一清二楚了。在HashMap源码的625行putVal方法中可以看到634行及以后的内容,hashMap在put值的时候,其实首先用==去比较的是key的hash值。然后再用equals方法。

if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))

最后,也是最重要的一点,如果你重写了equals方法,在set,或hashmap容器中使用的时候,两个相等的对象有可能有不同的hashCode,这样就会导致容器中存放两个相同的对象,导致调用容器的方法出现逻辑错误。导致你的equals方法写了也白写,没有起到根本的作用。

又因为容器在我们日常开发中经常用到,因此才有了重写equals建议重写hashCode。也只是建议!

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。