在java语言中Object如何成为超类?看完你就懂了

Posted 勇敢牛牛不怕困难@帅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在java语言中Object如何成为超类?看完你就懂了相关的知识,希望对你有一定的参考价值。

Object如何成为所有类的父类?

Object是java.lang包下的一个类,类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。java中的Object是所有类的父类,所有类的对象都是Class类的实例。

java为什么要弄一个超类出来?有什么好处

技术大佬的理解
1.可以使对象之间统一、互转,对于toString、wait、hashCode、clone、equals方法的统一。
2.对父类的逻辑处理统一,不需要根据有没有父类分两种实现,也就是多态。
3.Object 类可以接收任意的引用数据类型,所以在很多的类库设计上都采用 Object 作为方法的参数,这样操作起来会比较方便。

可以自定义继承Object类吗?

其实我们是可以自己 手动去继承(extends Object),继承之后便不能在继承其他的类,java不支持多继承。

Object里重要方法

1.hashCode
返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.Hashtable 提供的哈希表。

  • 在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。

  • 如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。

  • 以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。
    2.equals
    指示某个其他对象是否与此对象“相等”。

  • 自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。

  • 对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。

  • 传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。

  • 一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。

  • 对于任何非空引用值 x,x.equals(null) 都应返回 false。
    Object 类的 equals 方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true(x == y 具有值 true)。
    注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
    3.toString
    返回该对象的字符串表示。通常,toString 方法会返回一个“以文本方式表示”此对象的字符串。结果应是一个简明但易于读懂。建议所有子类都重写此方法。

Object 类的 toString 方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、at 标记符“@”和此对象哈希码的无符号十六进制表示组成。换句话说,该方法返回一个字符串,它的值等于:
getClass().getName() + ‘@’ + Integer.toHexString(hashCode())
4.finalize
finalize 的常规协定是:当 JavaTM 虚拟机已确定尚未终止的任何线程无法再通过任何方法访问此对象时,将调用此方法,除非由于准备终止的其他某个对象或类的终结操作执行了某个操作。finalize 方法可以采取任何操作,其中包括再次使此对象对其他线程可用;不过,finalize 的主要目的是在不可撤消地丢弃对象之前执行清除操作。例如,表示输入/输出连接的对象的 finalize 方法可执行显式 I/O 事务,以便在永久丢弃对象之前中断连接。
5.wait
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。
注意,由于 wait 方法将当前的线程放入了对象的等待集中,所以它只能解除此对象的锁定;可以同步当前线程的任何其他对象在线程等待时仍处于锁定状态。
此方法只应由作为此对象监视器的所有者的线程来调用。请参阅 notify 方法,了解线程能够成为监视器所有者的方法的描述。

6.clone
创建并返回此对象的一个副本。“副本”的准确含义可能依赖于对象的类。一般来说,对于任何对象 x,如果表达式:
x.clone() != x是正确的,则表达式:
x.clone().getClass() == x.getClass()将为 true,但这些不是绝对条件。一般情况下是:
x.clone().equals(x)将为 true,但这不是绝对条件。
按照惯例,返回的对象应该通过调用 super.clone 获得。如果一个类及其所有的超类(Object 除外)都遵守此约定,则 x.clone().getClass() == x.getClass()。
Object 类的 clone 方法执行特定的克隆操作。首先,如果此对象的类不能实现接口 Cloneable,则会抛出 CloneNotSupportedException。注意:所有的数组都被视为实现接口 Cloneable。否则,此方法会创建此对象的类的一个新实例,并像通过分配那样,严格使用此对象相应字段的内容初始化该对象的所有字段;这些字段的内容没有被自我克隆。所以,此方法执行的是该对象的“浅表复制”,而不“深层复制”操作。

如何加载把Object类加载到源文件中

在毫不知情的情况下我们来猜测一下,Object如何加载到各个类里面的?
可能的两种情况:

  1. 在编译源代码时,当遇到没有父类的类时,编译器会将其指定一个默认的父类(一般为Object),而虚拟机在处理到这个类时,由于这个类已经有一个默认的父类了,因此,JVM仍然会按着常规的方法来处理每一个类。对于这种情况,从编译后的二进制角度来看,所有的类都会有一个父类。
  2. 编译器仍然按着实际代码进行编译,并不会做额外的处理。如果一个类没有显式地继承于其他的类,编译后的代码仍然没有父类。然后由虚拟机运行二进制代码时,当遇到没有父类的类时,就会自动将这个类看成是Object类的子类(一般这类语言的默认父类都是Object)。
    三个示例:
    第一个继承Object类
public class ObjectTest1 extends Object {
	public ObjectTest(){
		super();
		System.out.println("hello");
	}
	public static void main(String[] args) {
		ObjectTest ot = new ObjectTest();
		System.out.println(ot.toString());
		
	}

}

第二个不继承Object类

public class ObjectTest2{
	public ObjectTest(){
		super();
		System.out.println("hello");
	}
	public static void main(String[] args) {
		ObjectTest ot = new ObjectTest();
		System.out.println(ot.toString());
		
	}

}

第三个示例,不设置构造方法:

public class ObjectTest3 {
	public static void main(String[] args) {
		ObjectTest3 ot = new ObjectTest3();
		System.out.println(ot.toString());
		
	}

}

这里我们需要采用反汇编的方式去查看底层运行过程。
首先通过javac把源代码文件编译成字节码文件 java文件----class文件。
在通过javap -c 反汇编将字节码文件编译成汇编代码,此时并没有加载到jvm中去运行。
测试案例1反汇编代码如下:

Compiled from "ObjectTest1.java"
public class ObjectTest1 {
  public ObjectTest1();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       7: ldc           #3                  // String hello
       9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      12: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #5                  // class ObjectTest1
       3: dup
       4: invokespecial #6                  // Method "<init>":()V
       7: astore_1
       8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      11: aload_1
      12: invokevirtual #7                  // Method java/lang/Object.toString:()Ljava/lang/String;
      15: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      18: return
}

测试案例2反汇编代码如下:

在这里插入代码片Compiled from "ObjectTest2.java"
public class ObjectTest2 {
  public ObjectTest2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       7: ldc           #3                  // String hello
       9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      12: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #5                  // class ObjectTest2
       3: dup
       4: invokespecial #6                  // Method "<init>":()V
       7: astore_1
       8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      11: aload_1
      12: invokevirtual #7                  // Method java/lang/Object.toString:()Ljava/lang/String;
      15: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      18: return
}

测试案例3反汇编代码如下:

Compiled from "ObjectTest3.java"
public class ObjectTest3 {
  public ObjectTest3();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class ObjectTest3
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_1
       8: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      11: aload_1
      12: invokevirtual #5                  // Method java/lang/Object.toString:()Ljava/lang/String;
      15: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      18: return
}

总结

从三个示例可以得出以下结论:

  1. 每一个方法末行都会有一个return,代表着该方法的结束标记,自己不写的话编译器会默认返回一个return不带任何参数;

12: return 18: return

  1. 没有写构造方法时,编译器会自动生成一个默认无参的构造方法,构造方法里有调用父类的super方法,也是默认的。

1: invokespecial #1 // Method java/lang/Object.""😦)V
public ObjectTest3();

3.从编译的角度来讲,javap的反汇编的对象是java的字节码文件,也就是二进制文件,此时也并没有java. 调用JVM,所以能够证明一点,Object是由编译器进行增添的,当编译器编译的过程中发现没有继承任何类时,编译器会将其指定一个默认的父类(一般为Object)。便可以得知Object是编译时就被编译器所加载的!
此时你是不是有个疑惑 那java在继承其他类的同时也能调用Object里面的方法,此时java多继承了吗?

其实不然,java语言的原则是不能去打破的,尽管是编译器去加载了Object类,前提也要是该类没有任何父类才回去加载一个Object类,如果你继承了其他父类,编译器此时并不会给你加载Object类的。那既然我不是继承于Object,那我为什么可以调用toString、hashCode等等Object的方法呀,当然可以!因为你继承的父类可能没有继承其他类呀,那编译器就会给你的父类加载一个Object类,所以间接性的你的爷爷就是Object,所以无论有没有直接继承Object类,本质上你还是会间接性的去继承Object!

以上是关于在java语言中Object如何成为超类?看完你就懂了的主要内容,如果未能解决你的问题,请参考以下文章

Go语言的跨平台能力到底有多强?看完你就知道了

惠州制造业MES系统该如何选型?看完你就知道了

java多线程提交,如何按照时间顺序获取线程结果,看完你就懂了 | Java工具类

如何使用Sentinel实现服务熔断和降级,看完你就懂了

《1-5年的JAVA程序员该怎么规划自己的职业》看完你就知道了

用 ArrayList 还是 LinkedList?看完你就懂了!