JAVA研发面试题(基础)
JAVA基础(目录): Object类的方法,逐个解释一下(clone,hashCode,equals,wait,finalize,notify) Java的Exception类型 Integer和int有啥区别,integer中有哪些特殊的函数? 说一下String实现 intern final 关键字 序列化,远程方法调用
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类的对象
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
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;
}//(自动生成的)
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 )
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) +
'}';
}
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