java.lang.Object
Class Object is the root of the class hierarchy. Every class has Object as a superclass. All objects, including arrays, implement the methods of this class.
— Java SE 17 API Documentation: java.lang.Object
Object 类是类层次结构的根。每个类都以 Object 作为超类。所有对象(包括数组)都实现了该类的方法。
方法
作用
equals(Object obj)
判断两个对象在逻辑上是否“相等”
hashCode()
返回对象的哈希码,用于哈希表(如 HashMap)
toString()
返回对象的字符串表示,便于调试和日志
getClass()
获取运行时类信息,是反射的基础
clone()
创建对象副本(需实现 Cloneable 接口)
wait() / notify() / notifyAll()
支持线程间协作与同步
finalize()
已废弃 ,曾用于对象销毁前的资源清理
Modifier and Type
Method
Description
protected Object
clone()
Creates and returns a copy of this object.
boolean
equals(Object obj)
Indicates whether some other object is “equal to” this one.
protected void
finalize()
Deprecated. The finalization mechanism is inherently problematic.
final Class<?>
getClass()
Returns the runtime class of this Object.
int
hashCode()
Returns a hash code value for the object.
final void
notify()
Wakes up a single thread that is waiting on this object’s monitor.
final void
notifyAll()
Wakes up all threads that are waiting on this object’s monitor.
String
toString()
Returns a string representation of the object.
final void
wait()
Causes the current thread to wait until it is awakened, typically by being notified or interrupted.
final void
wait(long timeoutMillis)
Causes the current thread to wait until it is awakened, typically by being notified or interrupted, or until a certain amount of real time has elapsed.
final void
wait(long timeoutMillis, int nanos)
Causes the current thread to wait until it is awakened, typically by being notified or interrupted, or until a certain amount of real time has elapsed.
这些方法构成了 Java 对象模型的最小公共接口 。无论你定义的是 Person 或者 Animal ,他们都支持这些操作。在面向对象语言中,若所有对象都具备某些共性行为,那么就应通过一个统一的根类来表达这种共性,避免重复定义。
多态 是面向对象的三大特性之一(封装、继承、多态),其核心含义是:
同一个方法调用,在不同对象上表现出不同的行为
在 Java 中,这通常通过 父类引用指向子类对象 ,并 重写(override)方法 来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class Father { public static int testStaticMethod () { return 1 ; } public void testMethod () { System.out.println("father" ); } public static void main (String[] args) { Father father = new Father (); Father fatherSon = new Son (); Son son = new Son (); father.testMethod(); fatherSon.testMethod(); son.testMethod(); int i = testStaticMethod(); } }class Son extends Father { public static int testStaticMethod () { return 1 ; } @Override public void testMethod () { System.out.println("son" ); } }
以该代码为例,使用以下命令反编译
1 javap -c -verbose Father
我们将得到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 Classfile /D:/Father.class Last modified 2025 年10 月20 日; size 838 bytes SHA-256 checksum ffa2d66d70489db7e8cd156b8d8465bf2168344a28e81acfbac72942d4a55f76 Compiled from "Father.java" public class Father minor version: 0 major version: 61 flags: (0x0021 ) ACC_PUBLIC, ACC_SUPER this_class: #21 super_class: #2 interfaces: 0 , fields: 0 , methods: 4 , attributes: 1 Constant pool: #1 = Methodref #2.#3 #2 = Class #4 #3 = NameAndType #5:#6 #4 = Utf8 java/lang/Object #5 = Utf8 <init> #6 = Utf8 ()V #7 = Fieldref #8.#9 #8 = Class #10 #9 = NameAndType #11:#12 #10 = Utf8 java/lang/System #11 = Utf8 out #12 = Utf8 Ljava/io/PrintStream; #13 = String #14 #14 = Utf8 father #15 = Methodref #16.#17 #16 = Class #18 #17 = NameAndType #19:#20 #18 = Utf8 java/io/PrintStream #19 = Utf8 println #20 = Utf8 (Ljava/lang/String ;)V #21 = Class #22 #22 = Utf8 Father #23 = Methodref #21.#3 #24 = Class #25 #25 = Utf8 Son #26 = Methodref #24.#3 #27 = Methodref #21.#28 #28 = NameAndType #29:#6 #29 = Utf8 testMethod #30 = Methodref #24.#28 #31 = Methodref #21.#32 #32 = NameAndType #33:#34 #33 = Utf8 testStaticMethod #34 = Utf8 ()I #35 = Utf8 Code #36 = Utf8 LineNumberTable #37 = Utf8 LocalVariableTable #38 = Utf8 this #39 = Utf8 LFather; #40 = Utf8 main #41 = Utf8 ([ Ljava/lang/String ;)V #42 = Utf8 args #43 = Utf8 [ Ljava/lang/String ; #44 = Utf8 fatherSon #45 = Utf8 son #46 = Utf8 LSon; #47 = Utf8 i #48 = Utf8 I #49 = Utf8 SourceFile #50 = Utf8 Father.java { public Father(); descriptor: ()V flags: (0x0001 ) ACC_PUBLIC Code: stack =1 , locals=1 , args_size=1 0 : aload_0 1 : invokespecial #1 4 : return LineNumberTable: line 1 : 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LFather; public static int testStaticMethod(); descriptor: ()I flags: (0x0009 ) ACC_PUBLIC, ACC_STATIC Code: stack =1 , locals=0 , args_size=0 0 : iconst_1 1 : ireturn LineNumberTable: line 5 : 0 public void testMethod(); descriptor: ()V flags: (0x0001 ) ACC_PUBLIC Code: stack =2 , locals=1 , args_size=1 0 : getstatic #7 3 : ldc #13 5 : invokevirtual #15 8 : return LineNumberTable: line 10 : 0 line 11 : 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this LFather; public static void main(java.lang.String [ ] ); descriptor: ([ Ljava/lang/String ;)V flags: (0x0009 ) ACC_PUBLIC, ACC_STATIC Code: stack =2 , locals=5 , args_size=1 0 : new #21 3 : dup 4 : invokespecial #23 7 : astore_1 8 : new #24 11 : dup 12 : invokespecial #26 15 : astore_2 16 : new #24 19 : dup 20 : invokespecial #26 23 : astore_3 24 : aload_1 25 : invokevirtual #27 28 : aload_2 29 : invokevirtual #27 32 : aload_3 33 : invokevirtual #30 36 : invokestatic #31 39 : istore 4 41 : return LineNumberTable: line 14 : 0 line 15 : 8 line 16 : 16 line 18 : 24 line 19 : 28 line 20 : 32 line 22 : 36 line 23 : 41 LocalVariableTable: Start Length Slot Name Signature 0 42 0 args [ Ljava/lang/String ; 8 34 1 father LFather; 16 26 2 fatherSon LFather; 24 18 3 son LSon; 41 1 4 i I } SourceFile: "Father.java"
我们观察 main 方法中这三行调用
1 2 3 father.testMethod(); // 字节码: invokevirtual fatherSon.testMethod(); // 字节码: invokevirtual son.testMethod(); // 字节码: invokevirtual
尽管 fatherSon 的声明类型是 Father,但字节码中调用的是 invokevirtual #27 而不是 son.testMethod
但是在实际运行的时候,JVM 会根据对象的实际类型去查找并执行 Son 中重写的 testMethod 。
这就是多态的底层实现机制:动态分配 (Dynamic Dispatch)
而invokevirtual是 JVM 用于调用 实例方法 的指令。那么我们再从 src/hotspot/cpu/x86/templateTable_x86_64.cpp入手。
1 2 3 4 5 6 7 void TemplateTable::invokevirtual (int byte_no) { prepare_invoke (byte_no, rax, rdx, CHECK); invokevirtual_helper (rax, rdx); }
其中 prepare_invoke 会从常量池加载方法符号(如 Father.testMethod:()V),但不绑定具体实现 。
而invokevirtual_helper是关键,该函数会调用 方法解析与分派逻辑 (src/hotspot/share/interpreter/linkResolver.cpp)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void LinkResolver::resolve_virtual_call ( CallInfo& result, Handle receiver, const LinkInfo& link_info, Bytecodes::Code bytecode, TRAPS ) { Klass* recvrKlass = receiver->klass (); methodHandle method = recvrKlass->method_at_vtable ( link_info.index () ); result.set_virtual (method, recvrKlass, ); }
然后会在vtable(虚方法表)中查找 (src/hotspot/share/oops/instanceKlass.hpp)
1 2 3 4 5 6 class InstanceKlass : public Klass { Array<Method*>* _methods; int * _vtable_indices; }
vtable 的结构保证:子类重写的方法会覆盖父类同签名方法的位置 。
然后根据继承链查找,进行实际调用。
总结来说就是以下步骤
步骤
Java 描述
HotSpot C++ 实现
1. 取 this
从操作数栈弹出引用
解释器 pop oop
2. 获取实际类型
运行时类型
receiver->klass() → Klass*
3. 查找方法
虚方法表
recvrKlass->method_at_vtable(index)
4. 继承链查找
仅在解析期 确定 vtable 索引
LinkResolver::lookup_method_in_klasses
5. 调用
执行方法
跳转到 Method* 的解释器/JIT 入口