JAVA研发面试题(基础)Object中的方法-clone/hashCode/equals/tostring/wait-notify-notifyall/finalize

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33173608/article/details/88535199

JAVA研发面试题(基础)

JAVA基础(目录):
Object类的方法,逐个解释一下(clone,hashCode,equals,wait,finalize,notify)
Java的Exception类型 
Integer和int有啥区别,integer中有哪些特殊的函数?
说一下String实现 intern 
final 关键字 
序列化,远程方法调用

Object类的方法

public class Object {

    private static native void registerNatives();
    
    static {
        registerNatives();
    }
    
    public final native Class<?> getClass();
    
    public native int hashCode();
	
	public boolean equals(Object obj) {
        return (this == obj);
    }
    
	protected native Object clone() throws CloneNotSupportedException;

	public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    public final native void notify();
    public final native void notifyAll();
    
	public final native void wait(long timeout) throws InterruptedException;

	public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }
    public final void wait() throws InterruptedException {
        wait(0);
    }
    
	protected void finalize() throws Throwable { }
}

只有继承了Object类的对象

clone–native方法/可以重写

protected native Object clone() throws CloneNotSupportedException;

基本数据类型(八种:byte-1个字节、short-2个字节、int-4个字节、long-8个字节、float-4个字节、double-8个字节、boolean-4个字节/数组1个字节、char-2个字节)

boolean 4个字节理由来源是《Java虚拟机规范》一书中的描述:“虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位”。这样我们可以得出boolean类型占了单独使用是4个字节,在数组中又是1个字节。显然第三条是更准确的说法,那虚拟机为什么要用int来代替boolean呢?为什么不用byte或short,这样不是更节省内存空间吗。大多数人都会很自然的这样去想,我同样也有这个疑问,经过查阅资料发现,使用int的原因是,对于当下32位的处理器(CPU)来说,一次处理数据是32位(这里不是指的是32/64位系统,而是指CPU硬件层面),具有高效存取的特点。 (本段话来自https://www.jianshu.com/p/2f663dc820d0)

/*
需要实现Cloneable接口,否则抛出异常:
Exception in thread "main" java.lang.NullPointerException
 */
public class ObjectCloneTest implements Cloneable{
    int a = 0;
    int[] b = {0,1,2,3};
    public static void main(String [] args){
        ObjectCloneTest obj = new ObjectCloneTest();
        ObjectCloneTest oo=null;
        try {
            oo = (ObjectCloneTest)obj.clone();
        }catch(CloneNotSupportedException e){
            System.out.println("clone出错" + e.getMessage());
        }
        obj.a = 1;
        obj.b = new int[]{01,11,21,31};
        System.out.println(" 基本数据类型 " + oo.a +" clone的是值  "+ obj.a);
        System.out.println(" 引用数据类型 " + oo.b +" clone的是地址--对象的引用 "+ obj.b);

        int a=0;
        //a.clone();基本数据类型没有object的方法

        int[] aa = new int[]{0,0,0};
        int[] bb = aa.clone();
        System.out.println("数组  " + aa.toString() + " clone的是值而不是引用 " + bb.toString());
        aa[1] = 2;
        System.out.println("原数组变化  " + aa[1] + " clone数组不变 " + bb[1]);
    }
}

输出:
基本数据类型 0 clone的是值  1
引用数据类型 [I@1540e19d clone的是地址--对象的引用 [I@677327b6
数组  [I@14ae5a5 clone的是值而不是引用 [I@7f31245a
原数组变化  2 clone数组不变 0

浅复制:对于基本数据类型来说,浅复制和深复制效果相同,都是复制的值,对于引用数据类型,object默认方法就是浅复制,即复制的是引用对象的地址。
深复制:对于引用数据类型,深复制要实现将每个引用类型里面的各个基本类型都复制一遍,即深入到每个属性里面的基本类型都拷贝一遍。我们可以重写对象的clone方法,以实现引用对象的深复制。

	@Override
    protected Object clone() throws CloneNotSupportedException {
        ObjectCloneTest ooo = new ObjectCloneTest();
        ooo.a = this.a;
        int [] b = new int [this.b.length];
        for(int i=0;i<this.b.length;i++){
            b[i] = this.b[i];
        }
        ooo.b = b;
        return ooo;
    }

hashCode–native方法/可以重写

public native int hashCode();

底层调用了c++实现的本地方法库,hashCode()是一个native方法,返回值类型是整数;
看了很多博客,没有看到有说c++是怎么实现的,之后了解了再补上
我们就先这样理解吧:这个native方法将对象在内存中的地址作为哈希码返回,保证了不同对象的返回值不同。

扩展:
基本上两个相同的对象,equals方法返回相同,则hashCode的返回值也最好相同;
但是当两个对象的hashCode的返回值相同,还要再去equals比较一下确认是否相同。
这个思想可以参考hashmap判断两个对象相同的源码(如果你重写了equals()方法,那么一定要记得重写hashCode()方法)

通常我们如果要用到hashcode都会重写这个方法

@Override
    public int hashCode() {
        int result = 17;
        result = result * 31 + b.hashCode();
        result = result * 31 + a;
        return result;
    }//(自动生成的)

equals–可重写

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

可以从源码中看出:equals方法直接使用的是“ == ” 来实现的 ( “==”和clone方法类似,对于基本数据类型比较的是值是否相等,对于引用数据类型比较的是引用类型的地址是否相等)
我们可以重写这个方法来实现比较一个引用对象的各个属性/单个属性 相等来代替对象相等。

	@Override
    public boolean equals(Object obj) {
        return this.a==obj.a;
    }

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方法
建议使用自动生成的
在这里插入图片描述

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    ObjectCloneTest that = (ObjectCloneTest) o;
    return a == that.a &&
            Arrays.equals(b, that.b);
}
@Override
public int hashCode() {
    int result = 17;
    result = result * 31 + b.hashCode();
    result = result * 31 + a;
    return result;
}

为什么重写equals一定要重写hashcode?

现在有两个Student对象:

Student s1=new Student("小明",18);

Student s2=new Student("小明",18);

此时s1.equals(s2)一定返回true

假如只重写equals而不重写hashcode,那么Student类的hashcode方法就是Object默认的hashcode方法,由于默认的hashcode方法是根据对象的内存地址经哈希算法得来的,显然此时s1!=s2,故两者的hashcode不一定相等。

然而重写了equals,且s1.equals(s2)返回true,根据hashcode的规则,两个对象相等其哈希值一定相等,所以矛盾就产生了,因此重写equals一定要重写hashcode,而且从Student类重写后的hashcode方法中可以看出,重写后返回的新的哈希值与Student的两个属性有关。

以下是关于hashcode的一些规定:

两个对象相等,hashcode一定相等

两个对象不等,hashcode不一定不等

hashcode相等,两个对象不一定相等

(例子及下面解释来自https://blog.csdn.net/xl_1803/article/details/80445481 )

toString-可重写

public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

getClass()是native方法,toString()方法调用了hashCode方法,返回内存地址,可以重写返回具体的属性值
建议自动生成:

    @Override
    public String toString() {
        return "ObjectCloneTest{" +
                "a=" + a +
                ", b=" + Arrays.toString(b) +
                '}';
    }

wait/notify/notifyAll–本地方法/不可重写(final)

finalize-可重写,子类可访问

protected void finalize() throws Throwable { }

垃圾对象/不可达对象:没有引用的对象
可达对象:有引用引着的对象
此方法主要用于GC在回收垃圾对象时给对象一次“等一下”的机会,看看有没有可能复活
GC在回收这个类的实例对象时,先会调用这个方法,如果重写了这个方法,并为其添加了新的引用,则对象就不再是不可达对象了,GC就不会将其加入回收队列,但是下一次失去引用时就没有“等一下”机会了,此方法每个对象仅能调用一次,下一次会直接加到回收队列中。

博主小白,如有哪块写的不妥,欢迎评论~~

参考资料:
https://blog.csdn.net/xl_1803/article/details/80445481
https://baijiahao.baidu.com/s?id=1567910969491375&wfr=spider&for=pc

猜你喜欢

转载自blog.csdn.net/qq_33173608/article/details/88535199