我们都知道java语言有4个特性:继承、封装、多态、抽象。
但是java继承在底层是如何实现的我们知道的甚少,我就在此解析下java语言继承在底层是如何实现的。
先看一段代码:
public class Person {
int id = 12; String name = "SuperMan"; static { System.out.println("Person class"); }
public Person() { System.out.println("Person's constructor method"); }
public void whoru() { System.out.println(this.name); }
public void method(){ System.out.println("hi"); } }
public class Cheater extends Person { String name = "LittleMan"; static { System.out.println("Cheater class"); }
public Cheater() { System.out.println("Cheater's constructor method"); }
public void whoru() { System.out.println(this.name); } }
public class Test { public static void main(String[] args) { Cheater c = new Cheater(); // Person class // Cheater class // Person's constructor method // Cheater's constructor method c.whoru();// LittleMan c.method();// hi System.out.println("c'id:" + c.id + ";c.name:" + c.name);// c'id:12;c.name:LittleMan } }
首先我们知道java中的继承指的是父类与子类之间的关系,即:由于子类继承了父类,所以子类可以使用父类所共享的属性和方法。
在上面的代码中,我们发现父类的属性和方法都没有用private来修饰,表明其属性和方法都是共享的。
内存分析图如下:
先解析下: c.whoru();// LittleMan 为何会打印出:LittleMan 呢? 当我们用Cheater来申明的变量c,再有变量c调用方法whoru()时,系统会去查找又Cheater.class在jvm解析过后存放在方法区的代码域里的Cheater这块内存中,发现能够在这里找到whoru()这个方法,故不会再去其父类Person这块内存中查找whoru()这方法了,所以会打印出 LittleMan 的。
在解析下: c.method();// hi
其原因和上面的一样,先会去方法区的代码域找Cheater这块内存中是否存在method()这个方法,但是没有找到。于是会继续由其父类Person这块内存中是否有method()这个方法,找到了,所以就执行method()方法打印出 hi。
这个就是子类继承父类之后可以调用到父类共享方法的原因。
在说说子类是如何调用到了父类的属性的吧!
先说 c.id 打印出了 12 :
变量c调用属性id,会从其在内存的堆中自身的cheater实例中开始查找,可是没有找到,于是会找其父类中的属性是否有个名称为 id 的,于是找到了父类的 id 的属性值,所以打印出了 12 。
而 c.name 打印出 LittleMan:
是因为在查找的过程中,在找自身的cheater实例中找到了属性名为 name 的属性值,所以就不会继续查找其父的属性信息了。
这就是子类为什么可以调用父类的共享属性的原因了。
综上所述:我们发现了为什么在堆内存中生成子类的实例,为什么会先从其“祖宗类”来开始生成的原因了,即为了实现继承的关系,为了让子类可以调用到父类的共享属性和方法,所以才会在生成子类的之前要先把其“祖宗类”先生成;说得更明确些:子类之所以能使用到父类的共享的方法和属性,不是由于子类自身会带着“祖宗类”的共享的方法和属性,而是在查找子类的方法属性的时候,没有找到其自身的方法和属性,于是就要去其“祖宗类”中查找了,就是这个原因,使我们感觉到子类拥有了其“祖宗类”的共享的所有的方法和属性。