Java面向对象三大特性:继承

1. 继承思想

需求,使用面向对象的知识定义出老师(Teacher)、学生(Student)、员工(Employee)三个类:

  • 老师:拥有名字、年龄、级别三个状态,有授课和休息两个功能

  • 学生:拥有名字、年龄、学好三个状态,有学习和休息两个功能

  • 员工:拥有名字、年龄、入职时间三个状态,有工作和休息两个功能

代码截图如下:

image.png

此时,发现三个类中的存在着大量的共同代码,而我们要考虑的就是如何解决代码重复的问题。

面向对象的继承思想,可以解决多个类存在共同代码的问题。

继承关系设计图:

image.png

记住几个概念:

  • 被继承的类,称之为父类、基类

  • 继承父类的类,称之为子类,拓展类

  • 父类:存放多个子类共同的字段和方法

  • 子类:存放自己特有的字段和方法

1.1. 继承语法(重点)

在程序中,如果一个类需要继承另一个类,此时使用extends关键字。

public class 子类名 extends 父类名{

}

注意:Java中类只支持单继承,但是支持多重继承。也就是说一个子类只能有一个直接的父类,父类也可以再有父类。

  • 下面是错误的写法! Java中的类只支持单继承。
class SuperClass1{}

class SuperClass2{}

class SubClass extends SuperClass1,SuperClass2{}//错误
  • 下面代码是正确的。一个父类可以有多个子类。
class SuperClass{}

class SubClass1 extends SuperClass{}

class SubClass2 extends SuperClass{}
  • 下面代码是正确的,支持多重继承。
class SupperSuperClass{}

class SupperClass extends SupperClass{}

class SubClass extends SupperClass
  • Object类是Java语言的根类,任何类都是Object的子类,要么是直接子类,要么是间接子类(后讲)
public class  Person{} 等价于 public class Person **extends Object**{}

1.1.1. 继承操作(重点)

父类代码:

public class Person {

    private String name;

    private int age;

    public void rest() {

        System.out.println("休息");

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

}

子类代码:

public class Student extends Person{

    private String sn;// 学号

    public void study() {

        System.out.println("学习");

    }

    public String getSn() {

        return sn;

    }

    public void setSn(String sn) {

        this.sn = sn;

    }

}

测试代码:

public class ExtendsDemo {

    public static void main(String[] args) {

        //创建学生对象

        Student stu = new Student();

        //设置字段信息

        stu.setName("will");  //继承了父类

        stu.setAge(17);  //继承了父类

        stu.setSn("s_123");

        //调用方法

        stu.study();

        stu.rest();  //继承了父类

    }

}

1.1.2. 子类可以继承到父类哪些成员(了解)

子类继承父类之后,可以拥有到父类的某一些成员(字段和方法),根据访问修饰符来判断:

  • 如果父类中的成员使用public和protected修饰,子类都能继承.

  • 如果父类和子类在同一个包中,使用缺省访问修饰的成员,此时子类可以继承到

  • 如果父类中的成员使用private修饰,子类继承不到。private只能在本类中访问

  • 父类的构造器,子类也不能继承,因为构造器必须和当前的类名相同

image.png

1.2. 方法覆盖(掌握)

子类继承了父类,可以拥有父类的部分方法和成员变量。可是当父类的某个方法不适合子类本身的特征时,此时怎么办?比如鸵鸟(Ostrich)是鸟类(Bird)中的一个特殊品种,所以鸵鸟类是鸟类的一个子类,但是鸟类有飞翔的功能,但是对应鸵鸟,飞翔的行为显然不适合于它。

父类:

public class Bird {

    public void fly() {

        System.out.println("飞呀飞...");

    }

}

子类:

public class Ostrich extends Bird{

}

测试类:

public class OverrideDemo {

    public static void main(String[] args) {

        //创建鸵鸟对象

        Ostrich os = new Ostrich();

        //调用飞翔功能

        os.fly();

    }

}

运行结果:

飞呀飞...

上述代码从语法是正确的,但从逻辑上是不合理的,因为鸵鸟不能飞翔,此时怎么办?——方法覆盖操作。

1.2.1. 方法覆盖操作(重点掌握)

当子类存在一个和父类一模一样的方法时,我们就称之为子类覆盖了父类的方法,也称之为重写。那么我们就可以在子类方法体中,重写编写逻辑代码。

public class Ostrich extends Bird{

    public void fly() {

        System.out.println("扑扑翅膀,快速奔跑...");

    }

}

运行测试代码:

扑扑翅膀,快速奔跑...

方法的调用顺序:

通过对象调用方法时,先在子类中查找有没有对应的方法,若存在就执行子类的,若子类不存在就执行父类的,如果父类也没有,报错。

方法覆盖的细节:

private修饰的方法不能被子类所继承,也就不存在覆盖的概念。

① 实例方法签名必须相同 (方法签名= 方法名 + 方法的参数列表)

② 子类方法的返回值类型是和父类方法的返回类型相同或者是其子类

③ 子类方法中声明抛出的异常小于或等于父类方法声明抛出异常类型

④ 子类方法的访问权限比父类方法访问权限更大或相等

上述的方法覆盖细节真多,记不住,那么记住下面这句话就万事OK了。

精华:直接拷贝父类中方法的定义粘贴到子类中,再重新编写子类方法体,打完收工!

1.2.2. super关键字(掌握)

问题,在子类中的某一个方法中需要去调用父类中被覆盖的方法,此时得使用super关键字。

public class Ostrich extends Bird{

    public void fly() {

        System.out.println("扑扑翅膀,快速奔跑...");

    }

    public void say() {

        super.fly();//调用父类被覆盖的方法

        fly();//调用本类中的方法

    }

}

如果调用被覆盖的方法不使用super关键字,此时调用的是本类中的方法。

super关键字表示父类对象的意思,更多的操作,后面再讲。

super.fly()可以翻译成调用父类对象的fly方法。

1.3. 抽象方法和抽象类(掌握)

需求:求圆(Circle)和矩形(Rectangle)两种图形的面积。

分析:无论是圆形还是矩形,还是其他形状的图形,只要是图形,都有面积,也就说图形都有求面积的功能,那么我们就可以把定义一个图形(Graph)的父类,该类拥有求面积的方法,但是作为图形的概念,而并不是某种具体的图形,那么怎么求面积是不清楚的,姑且先让求面积的getArea方法返回0。

父类代码:

public class Graph {

    public double getArea() {

        return 0.0;

    }

}

子类代码(圆形):

public class Circle extends Graph {

    private int r;  //半径

    public void setR(int r) {

        this.r = r;

    }

    public double getArea() {

        return 3.14 * r * r;

    }

}

子类代码(矩形):

public class Rectangle extends Graph {

    private int width; // 宽度

    private int height; // 高度

    public void setWidth(int width) {

        this.width = width;

    }

    public void setHeight(int height) {

        this.height = height;

    }

    public double getArea() {

        return width * height;

    }

}

测试代码:

public class GraphDemo {

    public static void main(String[] args) {

        // 圆

        Circle c = new Circle();

        c.setR(10);

        double ret1 = c.getArea();

        System.out.println("圆的面积:" + ret1);

        // 矩形

        Rectangle r = new Rectangle();

        r.setWidth(5);

        r.setHeight(4);

        double ret2= r.getArea();

        System.out.println("矩形的面积:" + ret2);

    }

}

运行结果如下:

圆的面积:314.0

矩形的面积:20.0

1.3.1. 引出抽象方法(了解)

问题1:既然不同的图形求面积的算法是不同的,所以必须要求每一个图形子类去覆盖getArea方法,如果没有覆盖,应该以语法报错的形式做提示。

问题2:在Graph类中的getArea方法的方法体没有任何存在意义,因为不同图形求面积算法不一样,子类必须要覆盖getArea方法。

要满足上述对方法的要求,就得使用abstract来修饰方法,被abstract修饰的方法具备两个特征:

  • 该方法没有方法体

  • 要求子类必须覆盖该方法

这种方法,我们就称之为抽象方法。

1.3.2. 抽象方法和抽象类(重点掌握)

使用abstract修饰的方法,称为抽象方法。

public abstract 返回类型 方法名(参数);

特点:

  • 使用abstract修饰,没有方法体,留给子类去覆盖

  • 抽象方法必须定义在抽象类或接口中

使用abstract修饰的类,成为抽象类。

public abstract class 类名{

}

一般的,抽象类以Abstract作为类名前缀,如AbstractGraph,一看就能看出是抽象类。

特点:

  • 抽象类不能创建对象,调用没有方法体的抽象方法没有任何意义

  • 抽象类中可以同时拥有抽象方法和普通方法

  • 抽象类要有子类才有意义,子类必须覆盖父类的抽象方法,否则子类也得作为抽象类

image.png

父类代码:

public abstract class AbstractGraph {

    public abstract double getArea();  //没有方法体

}

子类代码:

public class Circle extends AbstractGraph {

    private int r;// 半径

    public void setR(int r) {

        this.r = r;

    }

    public double getArea() {//覆盖父类抽象方法

        return 3.14 * r * r;//编写方法体

    }

}

测试类没有改变。

11.4.Object类和常用方法(掌握)

Object本身表示对象的意思,是Java中的根类,要么是一个类的直接父类,要么就是一个类的间接父类。

class  A{}   其实等价于  class  A extends Object{}

因为所有类都是Object类的子类, 所有类的对象都可以调用Object类中的方法,常见的方法:

  • boolean equals(Object obj):拿当前调用该方法的对象和参数obj做比较

在Object类中的equals方法和“ == ”符号相同都是比较对象是否是同一个的存储地址。

public class ObjectDemo {
    public static void main(String[] args) {
        //创建Person对象p1
        Person p1 = new Person();
        //创建Person对象p2
        Person p2 = new Person();
        
        //比较p1和p2的内存地址是否相同
        boolean ret1 = p1 == p2;
        boolean ret2 = p1.equals(p2);
        System.out.println(ret1);   //false
        System.out.println(ret2);   //false
    }
}

官方建议:每个类都应该覆盖equals方法去比较我们关心的数据,而不是内存地址。

  • String toString():表示把对象中的字段信息转换为字符串格式
    打印对象时其实打印的就是对象的toString方法
Person p = new Person();
p.setName("will");
p.setAge(17);
System.out.println(p);
System.out.println(p.toString());

其中:

System.out.println(p);//等价于 System.out.println(p.toString());

打印格式如:

cn.wolfcode._04_object.Person@15db9742  

默认情况下打印的是对象的hashCode值,但是我们更关心对象中字段存储的数据。
官方建议:应该每个类都应该覆盖toString返回我们关心的数据,如:

public class Person {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}

此时打印对象,看到的是该对象的字段信息。

Person [name=will, age=17]

可以通过Eclipse生成toString方法,刚开始一定要手写。
== 符号到底比较的是什么:
比较基本数据类型:比较两个值是否相等
比较对象数据类型:比较两个对象是否是同一块内存空间
每一次使用new关键字,都表示在堆中创建一块新的内存空间。

若要获得最好的学习效果,需要配合对应教学视频一起学习。需要完整教学视频,请参看https://ke.qq.com/course/272077

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

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,531评论 18 399
  • 1.import static是Java 5增加的功能,就是将Import类中的静态方法,可以作为本类的静态方法来...
    XLsn0w阅读 1,202评论 0 2
  • 前后端分离的Restful架构大行其道,传统的模板技术已经不多见了。实际上只是渲染的地方由后端转移到了前端,模板的...
    人世间阅读 54,691评论 5 44
  • 莫言輕易得,只緣性相隨。 若去凡塵戀,無處不驚雷。
    三坡梦阅读 209评论 0 0
  • 又又又又失眠,于是在知乎上怒答法学问题。不知道第二天的国私是否还能头脑清醒T_T
    小王大柱儿阅读 148评论 0 0