继承

继承

继承关键字extends英文含义是延伸、扩展的意思。继承的本质就是在复用父类代码的基础上进行功能的扩展、延伸。需要指出的是,子类不会继承父类的私有属性、方法以及构造函数。

创建子类对象的时候会创建父类对象吗?

不会,这一点可以通过在父类和子类的构造方法中输出 this 的哈希地址证明。

public Person(){
    System.out.println(this);
}
public student(){//继承自Person类
    System.out.println(this);
}
//输出结果都是子类对象的哈希地址

子类对象是怎么创建的?

public class Person
{
    private  String name;
    private int age;
    String sex;
    public Person(){ 
        this.name="123";
        this.age=20;
        this.sex="man";
        //this.department="教务部";
    }
    public String showName(){
        return this.name;
    }
    //三个属性的 get/set 方法,节约篇幅,省略
}
public class Teacher extends Person
{
    //扩充父类属性
    private String department;
    //扩充父类方法
    //新增属性的 get/set 方法
    public Teacher()
    {     //this.name="123";
          //this.age=20;
          this.sex="woman";
      this.department="教务部";
        }
    //方法覆盖
    public String showName()
    {       //return this.name;
      return getName()+"Teacher 老师";
    }
    //测试
    public static void main(String[] args)
    {
        Person t = new Teacher();
         t.setName("Alex");
        System.out.println(t.showName());
       // System.out.println(tje());
        System.out.println(t.sex);
    }   
}

通过上述代码为例讲解具体过程:

  1. 在堆里面为父类中的私有属性开辟空间,并在父类中构造一个动态绑定表[子类对象地址,父类私有属性空间地址,父类字节码文件],将这块私有属性空间和父类做动态绑定。
  2. 为子类新增属性和从父类继承而来的非私有属性开辟空间
  3. 为父类的方法开辟空间
  4. 为子类重写了父类的方法开辟空间
  5. 为子类新增的方法开辟空间
  6. 子类对象内存空间开辟完成,将此 this 地址给动态绑定表。通过查询父类维护的动态绑定表调用父类的构造函数,并将子类对象的地址 this 作为参数传递过去。这就是动态绑定机制,没有通过对象使用父类的属性和方法,而是通过类维护的动态绑定表找到其所在地址。
  7. 在调用父类构造函数给 name 和 age 赋值时,首先会检查子类是否有相同属性,如果有就给子类赋值。如果没有,则通过动态绑定表找到这些父类的私有属性进行赋值的。
  8. 给子类继承过来的和新增加的属性赋值,返回 this 地址给该对象的引用。至此,子类对象构造完成。

理解了调用父类构造函数时给父类私有属性赋值的动态绑定机制,就完全能够理解调用继承而来的父类方法中是如何使用父类私有属性的了,二者完全一样。t.setName("Alex");

_子类对象内存模型.png

上转型和下转型

向上向下转型都是对对象内容的强制转换,并没有改变对象的数据类型,仍然是子类对象啊。

显示上转型

Person t = new Teacher()中把 Teacher 类型对象的地址给了一个 Person 类型的变量 t ,这种将父类引用指向子类对象的写法就是向上转型,转型后的引用变量不能看见子类所特有的属性和方法。但是,对于子类重写的父类的方法,父类引用所看到的是重写后的方法。所以,方法重写也叫作覆盖!

向上转型不仅是多态的一个基础,还能够给程序增加灵活性,面向接口编程就是一个典型应用。

隐式上转型

被老师称之为隐式上转型的想法是出自于,一个父类引用指向子类对象的时候,拿到的是子类的 this 指针,以为这样在调用父类构造函数时可以直接找到子类的属性,好像也符合逻辑哦?但是跳出这个逻辑,这个属性至少在编译阶段就不可能通过啊,因为这个属性都不存在。

所以,感觉是老师故意塞进来的一个概念,目的是为了引出重写。通过重写就可以让父类的引用通过重写后的方法使用到子类的属性。

下转型

只有经历过上转型的对象,才可以向下转型回到最初的对象。在实际编码中最好使用 instance of 进行判断,以免出现类型转换异常。

方法重载

方法重载是指在一个类中存在多个方法名相同,但是参数签名不同的方法。他们在实际功能上含义相同,只是因为不同情况要达到相同功能时,需要采用不同处理方式。比如Arrays.sort()out.println()方法都重载了很多次,还有类的构造方法。

方法重载对返回值的类型和访问权限没有要求,调用哪个重载方法在编译阶段就会被识别出来,这叫做静态联编。抽象方法、main 方法和静态方法都是方法啊,方法有什么不可以被重载的呢?

重写

实例方法重写

当子类继承父类时,重新实现了父类中的非私有属性的方法,就叫做重写。实例方法重写还可以叫做覆盖,因为父类引用指向子类对象时,看到的是子类重写过后的方法,在子类对象的内存空间根本不存在父类继承下来的方法。

方法重写要求是方法名、参数签名、返回值类型必须完全相同,可以使用 @override注解来保证一定是重写了父类的方法。父类的私有方法不存在重写的概念,因为根本就不会被子类继承。

方法访问权限必须大于或等于父类,子类在重写父类中的抽象方法时处理的异常一定要比父类中处理的异常多,也就是说子类 throws 的异常一定只能比父类 throws 的异常少。说的更直白点,就是儿子一定不能比老子懒,再不济也要一样勤快。只要这样世界才会不断进步呢!。

静态方法和属性重写

当子类中有和父类相同的属性以及静态方法时,这种称之为隐藏。在子类的内存空间中,会同时存在父类和子类的这种属性和方法的内存空间。父类引用看到的是父类的属性和方法,子类引用看到的是子类的属性和方法。以下可以证明,但在实际开发中用的不多,规范化命名不就好了,吃多了没事做。

class Person{
    public int age;
    public Person(){
        this.age = 20;
    }
    public static void print(){
        System.out.println("我是父类方法");
    }
}
class Student extends Person{
    public int age;
    public Student(){
        this.age = 30;
    }
    public static void print(){
        System.out.println("我是子类方法");
    }
}
public class Main{
    public static void main(String[] args){
        Person p = new Student();
        System.out.println("p: "+p.age);//输出20
        System.out.println(p);//输出Student@15db9742
        p.print();//输出"我是父类方法"

        Student s = (Student)p;
        System.out.println("s: "+s.age);//输出30
        System.out.println(s);//输出Student@15db9742
        s.print();//输出"我是子类方法"
    }
}

super关键字

在子类的构造函数中通过 super 关键字显式的调用父类的构造方法 。如果子类的构造方法中没有显式指定调用哪个父类构造函数,那么就会有一个 默认的无参的super()

在分析构造函数调用时可以使用构造函数方法栈来辅助思考,不就是方法调用栈吗?不是什么新概念。

如果子类重写了父类的方法,但又想调用父类原有的方法,怎么办呢?可以使用 super 关键字显示调用父类的方法。本质是去找和父类动态绑定的实例方法并将 this 作为参数传递过去。还可以调用父类的已经被子类隐藏了的属性!

但是这样做,完全破坏了封装性,实际写代码中应该完美避开这些坑。所以,这里只要知道有这些东西就可以了。

this关键字

代表对象的地址,在类的实例方法中用来指明对象调用的实例方法。在 IDE 工具中还可以用这个关键字来作为代码提示的工具。

在构造函数中使用 this 显式调用本类中某个重载的构造函数,同 super 关键字一样都只能位于构造函数的第一条语句,所以 super 和 this 不可能同时在一个构造函数中显式出现。但是,一个只有 this 出现的子类的构造函数中,会有一个默认的无参的super()出现。

多态

多态是面向对象的一个非常重要的特性,本以为老师会大讲特讲,竟然几句话就给说完了,我还以为自己听错了呢。

这里只讲方法多态。首先要向上转型,父类引用指向子类对象,调用的是父类的被子类重写的方法,这样在编译阶段无法确定具体调用哪个对象的方法,只能在程序运行的时候最终确定。这叫做动态绑定,也叫动态多态。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容