Java基础面试题集锦(二)

本文使用 JDK1.8 进行测试

==
equals
的区别

==
在基本类型中比较的是值是否相同;在引用类型中, ==
equals
一样比较的都是对象的地址是否相 同。究其源码,Object 类中的 equals
方法定义如下:

public boolean equals(Object obj){
    return (this == obj);
}

所以当一个类没有重写继承自Object 类的 equals
方法时,默认比较的都是对象地址是否相同,和 ==
的效果完全一致。JDK 中已经重写了 equals
方法的类有 String、Integer、Float 等。如 String 类的 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;
}

equals
hashCode
的区别

我们知道 Java 中的 Set 集合可以保证存储元素的唯一性,即如果试图向 Set 中添加重复元素会添加失败。而 Set 保证元素唯一性的根据是什么,可能有人会说通过重写 equals
方法判断元素是否相同不就好了。但如果随着 Set 中存放的元素越来越多,每次都通过 equals
方法判断效率会大大降低,所以才有了 hashCode
方法的存在。通过重写 Object 类的 hashCode
方法,直接计算出对象将在内存中的存放地址,若该地址没有元素,则将对象存放到该处,如果该地址已经有对象存在,才会调用 equals
方法判断该元素与 Set 中的任意元素是否相同,若存在相同的元素,则添加失败,若不存在,则将该元素散列到其他位置。

比如 String 类重写 hashCode 方法如下:

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;
}

所以 String 类计算 hash 值的算法为:s[0] 31 ^ (n – 1) + s[1]
31 ^ (n – 2) + … + s[n – 1]

String str = "abc";
时,打印 str.hashCode()
值为 96354,即 97 31 ^ 2 + 98
31 ^ 1 + 99 * 31 ^ 0 = 96354

综上,可以得出以下两个结论:

  • 两个对象相同, equals
    返回值一定相同, hashCode
    返回值一定相同
  • 两个对象不同, equals
    返回值一定不同, hashCode
    返回值可能相同

所以当需要重写 equals
时,总会同时重写 hashCode
方法,这样才能够减少 equals
方法的调用,而 hashCode
方法的重写应该尽可能做到对于不同的对象,返回的 hash 值不同

此外当重写完 equals
方法时请检查是否符合以下 5 点要求:

  1. 自反性:对任意引用值X,x.equals(x)的返回值一定为true.
  2. 对称性:对于任何引用值x,y,当且仅当y.equals(x)返回值为true时,x.equals(y)的返回值一定为true;
  3. 传递性:如果x.equals(y)=true, y.equals(z)=true,则x.equals(z)=true
  4. 一致性:如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变
  5. 非空性:任何非空的引用值X,x.equals(null)的返回值一定为false

创建对象的几种方式

  • new
    关键字可以调用有参和无参的构造函数
  • Class
    类的 newInstance
    方法:
Student s = (Student)Class.forName("com.kuranado.Student").newInstance();

或:

Student s = Student.class.newInstance();
  • Constructor
    类的 newInstance
    方法:
Constructor<Student>constructor=Student.class.getConstructor();
Student s = constructor.newInstance();

事实上,java.lang.Class 的 newInstance 方法内部调用的就是 java.lang.reflect.Constructor 类的 newInstance 方法,Spring 框架使用的就是 Constructor 类的 newInstance 方法创建对象

  • 使用 clone
    方法:
Student s = new Student();
Students2= (Student)s.clone();

需要注意要使用 clone 方法需要 Student 类实现 Cloneable
接口,否则 Student 对象调用该方法时会抛出 CloneNotSupportedException
异常。另外 clone
方法为一个 native
方法,其底层使用 C 语言实现,所以按照惯例重写该方法时,需要通过 super.clone()
调用 Object 中的该方法。

native 关键字

相信大家在读 JDK 源码的时候,会经常看到 native
关键字修饰的方法,比如 Object 类中有一半方法都使用了 native
关键字:

private static native void registerNatives();
public final native Class<?> getClass();
public native int hashCode();
protected native Objectclone()throws CloneNotSupportedException;
public final native void notify();
public final native void notifyAll();

native method
译为 本地方法
,本地方法是指一个 Java 调用非 Java 代码的接口,也就是说该方法并不是用 Java 语言实现的,而在 Java 中则是指 C 语言。因为这些方法需要与操作系统底层直接交互,直接使用 Java 实现会很困难,而且性能较低,所以使用其它更适合于操作系统交互的语言来编写这些方法是提升方法性能的一种方式。

Java 中 native 方法的编写需要使用 C,至于如何自己编写 native 方法可参考这里: Java的native关键字

原文 

http://www.kuranado.com/2018/06/17/Java基础面试题集锦(二)/

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Java基础面试题集锦(二)

分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址