一、简介
Object类是Java类继承的顶层。所有类继承自Object,包括Arrays的很多
类、都需要实现Object的方法。
二、方法分析
总览:
1、构造方法
类没有给出构造方法,所以构造方法是默认的无参构造方法。
2、private static native void registerNatives();
static {
registerNatives();
}
registerNatives(),本地静态方法,静态代码块中被调用,随着类的加载被加载进内存,那该方法作用是什么,首先要搞清楚什么是本地方法。
本地方法:在jdk中,jdk源码主要是由c++,java,c,汇编语言组成的,java中存在java方法和本地方法,java方法是由java编写,编译成字节码文件,存储在class文件中。本地方法定义用native修饰,只有定义,没有实现,但和抽象方法不同,也无法和abstract连用,本地方法是由其他语言编写实现,编译成机器代码,保存在动态链接库中,所有虚拟机装载包含本地方法的动态库。
除此之外,本地方法其实和一般方法并无太太区别,可以被调用,被static,final等关键字修饰,可以被继承,被重写。那为什么需要存在本地方法?
事实上,本地方法作用很大,他有效第拓展了jvm,使得java程序能够超越java运行的界限。列如,在和java外部环境交互时,当需要和操作系统底层硬件交换信息时,利用本地方法提供的接口,使得我们不必了解java之外细节。
有关本地方法参考 https://blog.csdn.net/wike163/article/details/6635321
再说到registerNatives方法,该方法本身是一个本地方法,但又区别于一般本地方法,它主要是对类中所有本地方法注册。一个java方法想要调用一个本地方法,需要将包含本地方法实现的动态文件加载进内存,虚拟机在加载的动态文件中定位链接该本地方法,再执行。registerNatives方法就是执行第二步,方便高效。
3、public final native Class<?> getClass();
返回类的一个对象引用,,在同一个类型多个对象调用这个方法,返回同一个类对象引用。被final修饰,不可被继承,重写。
Class<? extends MyObject> aClass = new MyObject().getClass();
Class<? extends MyObject> aClass1= new MyObject().getClass();
System.out.println(aClass); //class YuanMa.MyObject
System.out.println(aClass.equals(aClass1)); //true
4、public native int hashCode();
public boolean equals(Object obj) { return (this == obj); }
返回对象的哈希码值,具体的实现是对象在内存的地址通过特定方法转换为数字。同一个对象的哈希码值一定相等,不同对象的哈希码值可能相等。
在object里,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)一致性,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致地返回相同结果;
(5)x.equals(null) = false;
在对象重写equals方法,也应该重写hashcode方法,对象比较时,通过先比较hashcode值,如果相同在调用equals方法,如果不同,两个对象必定不同,性能更叫高效。
5、protected native Object clone() throws CloneNotSupportedException;
克隆方法,对象的克隆,就是说从一个对象中复制出一个新对象出来,其状态和之前的完全一致,当然我们也可以通过构造方法或者set注入,但是这个效率不高,首先一个对象要重写clone方法,必须写实现Cloneable接口,重写clone方法,调用父类clone方法。
深克隆和浅克隆:
克隆有深克隆和浅克隆之分,主要区别在于对引用对象的处理。
浅拷贝:仅仅克隆基本类型变量,而不克隆引用类型的变量
深克隆:既克隆基本类型变量,也克隆引用类型变量
浅克隆
class My implements Cloneable {
int my1;
public My(int a) {
this.my1 = a;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class MyObject implements Cloneable {
public int a;
public My my;
public MyObject(int a, My my) {
this.a = a;
this.my = my;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
MyObject myObject1 = new MyObject(1,new My(1));
MyObject myObject2=(MyObject)myObject1.clone();
System.out.println(myObject1==(myObject2)); //false
System.out.println(myObject1.a==myObject2.a); //true
System.out.println(myObject1.my==(myObject2.my)); //true
System.out.println("-----------------------");
System.out.println(myObject1.a+"---"+myObject2.a); // 1---1
System.out.println(myObject1.my.my1+"----"+myObject2.my.my1); //1----1
myObject1.my.my1=12;
myObject1.a=12;
System.out.println(myObject1.a+"---"+myObject2.a); //12---1
System.out.println(myObject1.my.my1+"----"+myObject2.my.my1); //12----12
}
}
上述,myObject2仅仅克隆的是myObject1 中 My 的地址引用,myObject1与myObject2的my属性指向的其实是同一个对象。所以就会引发出一个问题,myObject1.my内部的属性,myObject2.my也被修改了。要是myObject1.my=new My()..,修改其中一个则不会影响到副本,myObject1.my引用地址改变,指向对象不同了。
深克隆:
class My implements Cloneable {
int my1;
public My(int a) {
this.my1 = a;
}
@Override
protected Object clone() throws CloneNotSupportedException {
My my = (My) super.clone();
my.my1=this.my1;
//my.Str=new String(this.Str);
return my;
}
}
public class MyObject implements Cloneable {
public int a;
public My my;
public MyObject(int a, My my) {
this.a = a;
this.my = my;
}
@Override
protected Object clone() throws CloneNotSupportedException {
MyObject myObject=(MyObject) super.clone();
myObject.a=this.a;
myObject.my=(My)this.my.clone();
return myObject;
}
public static void main(String[] args) throws CloneNotSupportedException {
MyObject myObject1 = new MyObject(1,new My(1));
MyObject myObject2=(MyObject)myObject1.clone();
System.out.println(myObject1==(myObject2)); //false
System.out.println(myObject1.a==myObject2.a); //true
System.out.println(myObject1.my==(myObject2.my)); //false
System.out.println("-----------------------");
System.out.println(myObject1.a+"---"+myObject2.a); // 1---1
System.out.println(myObject1.my.my1+"----"+myObject2.my.my1); //1----1
myObject1.my.my1=12;
myObject1.a=12;
System.out.println(myObject1.a+"---"+myObject2.a); //12---1
System.out.println(myObject1.my.my1+"----"+myObject2.my.my1); //12----1
}
}
引用不同,值一样,克隆成功。
要是修改成文中那样的深克隆,需要修改大量的类,对一个企业级项目来说,影响是巨大的。因此,我们也只能通过序列化的方式来实现对象的克隆,占用空间来节省时间!而且是非侵入的,不需要修改目标代码就可以实现!
6、public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
返回类名+“@”+对象的16进制表示的哈希值,建议所有子类重写该方法。
7、protected void finalize() throws Throwable { }
简单说,finalize方法是对象回收器回收时将会触发的一个方法。
在java中存在垃圾回收器回收无用对象所占据的内存资源。存在有一些特殊情况,某些对象获得了一块特殊的内存空间(非使用new),垃圾回收器只会释放由new创建的内存,所以无法处理该内存,所以允许在类中定义一个finalize方法处理该内存。
其原理是一旦垃圾回收器准备去释放该区域,先调用该方法,方法处理一些重要工作,在下一次垃圾回收才会对象所占的内存。
例如在分配内存是采用了c中的malloc()函数,如果不在finalize中调用free()函数,内存将无法回收。
但是通常我们不应该将finalize方法当做通用的清理方法,因为垃圾回收可能不会发生,finalize方法得不到调用,因为垃圾回收本身也是需要消耗资源,如果不主动调用,在内存未满并不会发生。
finalize方法还可以用作某些终结条件的验证,例如当某个对象可以被回收时,假设是一个文件,对象被回收前应该关闭这个文件。只要对象存在未被清理的部分程序就存在缺陷,当某次finalize调用可以发现该问题所在。
8、wait,notify,notifyAll
wait方法是指是的当前线程阻塞,但是前提线程必须获取所对象,所有一般通synchronize关键字使用,在synchronize内可以保证线程必定获取到锁。当线程执行wait方法,便会释放所对象,当前线程重新进入等待状态,让出cpu,只有当被notify,notifyAll唤醒时,该线程重新进入就绪状态,一旦获取到cpu执行权,继续执行。
注意的是,notify,notifyAll方法并不是立即释放锁,所得释放需要看代码块执行情况,
notify,notifyAll区别:
notify随机唤醒一个等待的线程,notifyAll唤醒所有等待的线程,哪一个线程获取到cpu便会有限执行。
wait(long timeout, int nanos),wait() ,wait(long timeout)
wait(long timeout, int nanos)和,wait(long timeout)如果线程在等待时间未被唤醒,将会自动醒来,int nanos指timeout的额外的时间。