前言
最近看了几篇文章所以就总结一下自己的理解,个人理解有限,会有一些问题,仅记录一下。。。。
文章1:反射和多态的实现原理详解以及区别
文章2:java方法的虚分派和方法表
文章3:编译时多态、运行时多态
Java虚分派和方法表
虚分派
先看个例子:
class A{
String msg = "A 的属性";
public void test(){
System.out.println("调用了 A 的方法");
}
}
class B extends A{
String msg = "B 的属性";
public void test(){
System.out.println("调用了 B 的方法");
}
}
public class Test {
public static void main(String[] args) {
A test = new B();
//打印test的属性是谁
System.out.println(test.msg);
//调用test的方法,test()已重写
test.test();
/** 结果如下
* A 的属性
* 调用了 B 的方法
*/
}
}
总结:
- msg属性的访问就是引用对象是谁就去找谁
- test()方法的调用就是从new B()这个子类开始找,如果子类中有就调用(大概意思来讲)
- 可以用super调用父类的方法,但涉及的指令,使用了invokespecial指令不一样
java方法调用的指令:
-
指令 invokevirtual ,包含virtual dispatch机制,跟虚分派有关,虚分派意思是从new 的对象本身开始找方法,找到则调用,没找到则找父类,invokevirtual指令用于调用声明为类的方法
-
指令invokerspecial,调用private和构造方法有关
-
指令invokeinterface,跟invokevirtual类似,invokeinterface指令用于调用声明为接口的方法。
-
指令invokestatic,调用静态方法有关
-
invokeinterface比invokevirtual慢,因为invokevirtual是固定地址,查询快;invokeinterface则因为不同类可以实现同一个接口,每次调用都要在方法表中查找,地址不是确定唯一的。
方法表
用方法表的实现虚分派机制,方法表不记录所有方法的表。不会记录用invokestatic调用的静态方法和用invokespecial调用的构造函数和私有方法,每个类对应一个方法表,存于方法区中,Java中每个对象索引到对应的类,在对应的类数据中对应一个方法表。
说明:注意这里只有非私有的实例方法才会出现,并且静态方法也不会出现在这里,原因很容易理解:静态方法跟对象无关,可以将方法地址直接引用,而不像实例方法需要间接引用。方法表的偏移量总是固定的。
多态
多态有编译时多态和运行时多态。
编译时多态包括方法重载和产生方法覆盖时对象实例调用本身类的方法而不是父类或者子类的方法。
除去了编译时多态就是运行时多态了。
多态常见的应用有:
- 类型转换
- 根据类生成Class对象
- 关键字Instanceof,用于判断一个对象是否是某个类的实例
反射
反射:对于任意一个类,都能够在运行时知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
原理:java虚拟机为我们提供了类的class对象,使用class对象的方法(Class.forName()),通过类的全限定名,我们可以去获取这个类的字节码.class文件,有了字节码文件就能产生对象,就能调用该对象的属性和方法
例子:
public class test {
public void showmsg(){
System.out.println("show");
}
public static void main(String[] args) throws ClassNotFoundException {
Class<?> aClass = Class.forName("com.test.example.test");
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method method : declaredMethods){
String name = method.getName();
System.out.println(name);
}
}
}
结果: