在 Java 中 Object
类是所有类的祖先类, Object
没有定义属性,一共有13个方法。其它所有的子类都会继承这些方法。
private static native void registerNatives(); static { registerNatives(); } 复制代码
在 Java 中,用 native 关键字修饰的函数表明该方法的实现并不是在Java中去完成,而是由C/C++去完成,并被编译成了.dll,由Java去调用
而 registerNatives()
方法的主要作用则是将C/C++中的方法映射到 Java 中的 native
方法,实现方法命名的解耦。
@HotSpotIntrinsicCandidate public Object() {} 复制代码
@HotSpotIntrinsicCandidate注解,该注解是特定于Java虚拟机的注解。通过该注解表示的方法可能( 但不保证 )通过HotSpot VM自己来写汇编或IR编译器来实现该方法以提供性能。 它表示注释的方法可能(但不能保证)由HotSpot虚拟机内在化。如果HotSpot VM用手写汇编和/或手写编译器IR(编译器本身)替换注释的方法以提高性能,则方法是内在的。 也就是说虽然外面看到的在JDK9中weakCompareAndSet和compareAndSet底层依旧是调用了一样的代码,但是不排除HotSpot VM会手动来实现weakCompareAndSet真正含义的功能的可能性
一般创建对象的时候直接使用 new className(Args)
来创建一个新的对象。而在类的定义过程中,对于未定义构造函数的类,那么它就会默认继承 Object
的无参构造函数,如果定了一个或多个构造函数,那么就需要把无参构造函数方法也写上。
@HotSpotIntrinsicCandidate public final native Class<?> getClass(); 复制代码
getClass
返回运行时当前对象的类对象。在 Java 中,类是对具有一组相同特征或行为的实例的抽象进行描述。而 类对象
则是对 类
的特征和行为进行描述(即类的名称,属性,方法...)。也就是说通过获取到类对象,则可以获取到该类的所有属性,方法等。
@HotSpotIntrinsicCandidate public native int hashCode(); 复制代码
hashCode
返回当前对象的哈希码。 hashCode
遵守以下三个约定
hashCode
,那么它们的返回值需要是一致的。(前提:没有对对象进行修改) equals()
方法),那么这两个对象的 hashCode
也是一样 hashCode
方法返回的哈希码相等,这两个对象不一定相等
也即是说,调用 equals
方法返回值相等,那么调用 hashCode
方法返回值也一定相等。所以,在重写 euqlas
方法之后,一定要重写 hashCode
方法。
那么判断对象是否先等可以直接用 equals
来判断,为什么还需要 hashCode
方法呢?
其实 hashCode
方法的一个主要作用是为了增强哈希表的性能。比如:我们知道 Set
集合不能存在相同的两个对象,那么该怎么判断两个对象是否相同呢?如果没有 hashCode
,那么就需要进行遍历来逐一判断。那么有 hashCode
,我们就可以计算出即将要加入集合的对象的 hashCode
,然后查看集合中对应的位置上是否有对象即可。
public boolean equals(Object obj) { return (this == obj); } 复制代码
equals()
用于判断两个对象是否相等。根据 Object
的实现,可以看到判断的依据是看两个对象的引用地址是否相等。
而一般我们会用另外一种方式来判断是否相等。即 ==
, ==
表示的是两个变量值是否相等(基础类型的值在内存地址中存储的是值)
那么我们想要判断是否相等:
==
来判断 equals
方法来判断(在实际业务中,一般会重写 equals
方法)
需要注意的一点是 String
也是引用类型,我们判断 String
的时候是直接使用的 equals
方法,而按照默认的 equals
实现,创建两个具有相同值的 String
对象,那么 equals
返回的应该是 false
,
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String aString = (String)anObject; if (coder() == aString.coder()) { return isLatin1() ? StringLatin1.equals(value, aString.value) : StringUTF16.equals(value, aString.value); } } return false; } 复制代码
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } 复制代码
toString()
返回该对象的字符串表示。在使用 System.out.printLn(obj)
的时候,其内部也是调用的 toString
方法。可以按需重写 toString
方法。
protected native Object clone() throws CloneNotSupportedException; 复制代码
clone()
方法返回的是当前对象的引用,指向的是新 clone
出来的对象,此对象和原对象占用不同的堆空间。
clone
方法的正确调用需要实现 cloneable
接口,如果没有实现该接口,那么子类调用父类的 clone
方法则会抛出 CloneNotSupportedException
异常
Cloneable接口仅仅是一个表示接口,接口本身不包含任何方法,用来指示Object.clone()可以合法的被子类引用所调用。
先看一段代码
public class CloneTest { public static void main(String[] args) { Object o1 = new Object(); try { Object clone = o1.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } } 复制代码
执行这段段代码会抛出 The method clone() from the type Object is not visible
异常。原因是 clone
方法是被 protected
修饰的,也就是说被 protected
修饰的属性和方法, 在同一包下或在不同包下的子类可以访问
。显然, CloneTest
和 Object
不在同一包下,不过按照字面意思, CloneTest
会默认继承 Object
,所以即使在不同的包下,应该也是可以访问的才对。那么问题就出现在「在不同包下的子类可以访问」这句话上:
不同包中的子类可以访问: 是指当两个类不在同一个包中的时候,继承自父类的子类内部且主调(调用者)为子类的引用时才能访问父类用protected修饰的成员(属性/方法)。 在子类内部,主调为父类的引用时并不能访问此protected修饰的成员。(super关键字除外)
也就是说在子类中想要调用父类的 protected
方法,可以
super.methodName()
浅拷贝: 浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是引用类型,拷贝的就是内存地址。 深拷贝: 深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。
对于浅拷贝来说,如果含有引用类型,那么修改其中一个对象的引用值,那么会影响到另外一个对象。按层级来说,浅拷贝只拷贝了第一层。对于默认的 clone
实现是浅拷贝。如果想要实现深拷贝,可以
clone
//序列化实现深拷贝 public class CloneUtils { @SuppressWarnings("unchecked") public static <T extends Serializable> T clone(T obj){ T cloneObj = null; try { //写入字节流 ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream obs = new ObjectOutputStream(out); obs.writeObject(obj); obs.close(); //分配内存,写入原始对象,生成新对象 ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray()); ObjectInputStream ois = new ObjectInputStream(ios); //返回生成的新对象 cloneObj = (T) ois.readObject(); ois.close(); } catch (Exception e) { e.printStackTrace(); } return cloneObj; } } public class Person implements Serializable{ private static final long serialVersionUID = 2631590509760908280L; } public class CloneTest { public static void main(String[] args) { Person person = new Person(); Person person1 = CloneUtils.clone(person); } } 参考:https://blog.csdn.net/chenssy/article/details/12952063 复制代码
protected void finalize() throws Throwable {} 复制代码
finalize()
方法主要与 Java 垃圾回收机制有关,JVM准备对此对形象所占用的内存空间进行垃圾回收前,将被调用。所以此方法并不是由我们主动去调用的。