12、继承

1、介绍:

继承可以解决代码复用,让我们的编程更加靠近人类思维,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。

2、基本语法:

class 子类 extends 父类{

}

(1)子类就会自动拥有父类定义的属性和方法

(2)父类又叫超类、基类

(3)子类又叫派生类

3、继承原理:

4、快速入门

package package20221105;
public class Extends01 {
    public static void main(String[] args) {
        Pupil pupil=new Pupil();
        pupil.name="银角大王~";
        pupil.age=10;
        pupil.testing();
        pupil.setScore(60);
        pupil.showInfo();
        System.out.println("===================");
        Graduate graduate=new Graduate();
        graduate.name="金角大王~";
        graduate.age=22;
        graduate.testing();
        graduate.setScore(100);
        graduate.showInfo();
    }
}
/*输出:小学生银角大王~正在考小学数学
name=银角大王~ age=10 score=60.0
===================
大学生金角大王~正在考大学数学……
name=金角大王~ age=22 score=100.0*/
package package20221105;
public class Student {
    public String name;
    public int age;
    private double score;
    //给属性赋值
    public void setScore(double score){
        this.score=score;
    }
    //打印信息的成员方法
    public void showInfo(){
        System.out.println("name="+name+" age="+age+" score="+score);
    }
}
package package20221105;
//Graduate类继承Student类
public class Graduate extends Student{
    public void testing(){
        System.out.println("大学生"+name+"正在考大学数学……");
    }
}
package package20221105;
//Pupil类继承Student类
public class Pupil extends Student{
    public void testing(){
        System.out.println("小学生"+name+"正在考小学数学");
    }
}

5、继承的优点:

(1)代码的复用性提高了

(2)代码的扩展性和维护性提高了(在父类增加一个方法,下面所有的子类都拥有了)

6、细节问题:

(1)子类继承了所有的属性和方法,但是私有属性不能在子类直接访问,要通过公共的方法去访问 

package package20221105;
public class ExtendsDetail {
    public static void main(String[] args) {
        Sub sub=new Sub();
        sub.sayOk();
    }
}
/*输出:
base()……
sub()...
n1=100n2=200n3=300
test100
test200
test300
n4=400
test400*/
package package20221105;
//父类
public class Base {
    public int n1=100;
    protected int n2=200;
    int n3=300;
    private int n4=400;
    //无参构造器
    public Base(){
        System.out.println("base()……");
    }
    //父类提供一个public的方法,返回了n4
    public int getN4(){
        return n4;
    }
    public void test100(){
        System.out.println("test100");
    }
    protected void test200(){
        System.out.println("test200");
    }
    void test300(){
        System.out.println("test300");
    }
    private void test400(){
        System.out.println("test400");
    }
    public void callTest400(){
        test400();
    }
}
package package20221105;
public class Sub extends Base{
    //无参构造器
    public Sub(){
        System.out.println("sub()...");
    }
    //子类方法
    public void sayOk(){
        //不能访问私有属性n4
        System.out.println("n1="+n1+"n2="+n2+"n3="+n3);
        //不能访问私有方法test400
        test100();
        test200();
        test300();
        //要通过父类提供公共的方法去
        //访问私有属性
        System.out.println("n4="+getN4());
        //访问私有方法
        callTest400();
    }
}

(2)子类必须调用父类的构造器,完成父类的初始化

package package20221105;
public class ExtendsDetail {
    public static void main(String[] args) {
        Sub sub = new Sub();
        //这里没有写明调用子类构造器,
        //但打印的结果证明子类的无参构造器被调用了,
        //而且子类的无参构造器也没有写明调用父类的无参构造器,
        //但子类的无参构造器中有隐藏的super();语句
        // 默认调用了父类的无参构造器
    }
}
/*输出:
父类Base()的无参构造器被调用...
子类sub()的无参构造器被调用...
*/
package package20221105;
public class Base {
    public Base(){
        System.out.println("父类Base()的无参构造器被调用...");
    }
}
package package20221105;
public class Sub extends Base{
    public Sub(){
        super();//默认调用父类的无参构造器,你写不写它都存在
        System.out.println("子类sub()无参构造器被调用...");
    }
}

(3)当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器

package package20221105;
public class ExtendsDetail {
    public static void main(String[] args) {
        System.out.println("=========第一对象==========");
        Sub sub1=new Sub();
        System.out.println("=========第二对象==========");
        Sub sub2 = new Sub("jack");
    }
}
/*输出:
=========第一对象==========
父类Base()的无参构造器被调用...
子类sub()无参构造器被调用...
=========第二对象==========
父类Base()的无参构造器被调用...
子类Sub(String name)构造器被调用...*/
package package20221105;
public class Base {
    String name;
    public Base(){
        System.out.println("父类Base()的无参构造器被调用...");
    }
}
package package20221105;
public class Sub extends Base{
    public Sub(){
        System.out.println("子类sub()无参构造器被调用...");
    }
    public Sub(String name){
        System.out.println("子类Sub(String name)构造器被调用...");
    }
}

如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则编译不会通过

package package20221105;
public class ExtendsDetail {
    public static void main(String[] args) {
        System.out.println("=========第一对象==========");
        Sub sub1=new Sub();
        System.out.println("=========第二对象==========");
        Sub sub2 = new Sub("jack");
    }
}
/*输出:
=========第一对象==========
父类Base(String name)构造器被调用...
子类sub()无参构造器被调用...
=========第二对象==========
父类Base(String name)构造器被调用...
子类Sub(String name)构造器被调用...
*/
package package20221105;
public class Base {
    String name;
    //父类只有一个有参构造器
    public Base(String name){
        System.out.println("父类Base(String name)构造器被调用...");
    }
}
package package20221105;
public class Sub extends Base{
    public Sub(){
        //必须指定父类中的构造器,
        // 否则这里找不到父类中的无参构造器会报错
        super("jack");
        System.out.println("子类sub()无参构造器被调用...");
    }
    public Sub(String name){
        //必须指定父类中的构造器,
        // 否则这里找不到父类中的无参构造器会报错
        super("tom");
        System.out.println("子类Sub(String name)构造器被调用...");
    }
}

(4)如果希望指定去调用父类的某个构造器,则显式地调用一下(具体可以见上面一段代码):

super(参数列表);
//1、调用父类的无参构造器
super();
//2、调用父类的Base(String name)构造器
super("jack");
//3、调用父类的Base(String name,int age)构造器
super("tom",18);
//...

(5)super在使用时,需要放在构造器的第一行(super只能在构造器中使用)

(6)super()和this()都只能放在构造器的第一行,所以这两个方法不能共存在一个构造器

(7)java所有类都是Object类的子类

快捷键Ctrl+H,即可在页面右侧看到该类的层次结构:

 

(8)父类的构造器的调用不限于直接父类,可一直往上追溯到Object类(最终父类,顶级父类)

package package20221105;
public class ExtendsDetail {
    public static void main(String[] args) {
        System.out.println("=========第三对象==========");
        Sub sub3 = new Sub("jack");
        //调用最底层的构造器,注意输出的父类构造器的顺序
    }
}
/*输出:
=========第三对象==========
构造器TopBse()被调用...
构造器Base(String name)被调用...
子类Sub(String name)构造器被调用...
*/
package package20221105;
public class TopBase {//父类是Object
    public TopBase() {
        System.out.println("构造器TopBse()被调用...");
    }
}
package package20221105;
public class Base extends TopBase{
    String name;
    public Base(String name){
        System.out.println("构造器Base(String name)被调用...");
    }
}
package package20221105;
public class Sub extends Base{
    public Sub(String name){
        super("tom");
        System.out.println("子类Sub(String name)构造器被调用...");
    }
}

(9)子类最多只能继承一个父类(指直接继承),即在java中是单继承机制

思考:如何让A类继承B类和C类?——>[A继承B,B继承C]

(10)不能滥用继承,子类和父类之间必须满足is-a的逻辑关系

//Person is a Music?
Music extends Person//音乐不是人,继承Person类不合理
//Cat is an animal?
Cat extends Animal//猫是动物,继承Animal类合理

7、继承的本质:

package package20221105;
public class ExtendsTheory {
    public static void main(String[] args) {
        Son son=new Son();//内存的布局
        System.out.println(son.name);//输出:大头儿子
        //问上面的语句打印出什么?
        //按照查找关系来舞台信息:
        //(1)先看子类是否有该属性
        //(2)如果子类有且可以访问,则返回该信息
        //(3)如果没有,则向上看父类有没有,直到找到为止
        //(4)如果找到Object类都没找到,则报错
        //(5)如果在向上查找的过程发现要找的属性是private,则直接报错,不会再继续向上查找了
        System.out.println(son.age);//输出:39
        System.out.println(son.hobby);//输出:旅游
    }
}
class GrandPa{
    String name="大头爷爷";
    String hobby="旅游";
}
class Father extends GrandPa{
    String name="大头爸爸";
    int age=39;
}
class Son extends Father{
    String name="大头儿子";
}

 8、练习题:

(1)写出下列代码的输出结果

package package20221105;

public class ExtendsExercise01 {
    public static void main(String[] args) {
        B b=new B();
    }
}
class A {
    A() {
        System.out.println("a");//打印的第一步
    }
    A(String name){
        System.out.println("a name");
    }
}
class B extends A{
    B(){
        this("abc");//没有super();语句
        System.out.println("b");//打印的第三步
    }
    B(String name){
        //super();默认调用父类的无参构造器
        System.out.println("b.name");//从父类回来,打印的第二步
    }
}
//注意执行的顺序
/*输出:
a
b.name
b*/

(2)

/*题目:编写Computer类,包含CPU、内存、硬盘等属性,
getDetails方法用于返回Computer的详细信息。
编写PC子类,继承Computer类,添加特有属性[品牌brand],
编写NotePad子类,继承Computer类,添加特有属性[颜色color],
编写Test类,在main方法中创建PC和NotePad对象,分别给对象中特有的属性赋值,
以及从Computer类继承的属性赋值,并使用方法并打印输出信息。*/
//我的答案:
package package20221105;
public class ExtendsExercise03 {
    public static void main(String[] args) {
        PC t1=new PC("华为");
        t1.getDetails();
        NotePad t2=new NotePad("red");
        t2.getDetails();
    }
}
class Computer{
    String CPU;
    String memory;
    String hard;
    public Computer(String CPU,String memory, String hard){
        this.CPU=CPU;
        this.memory=memory;
        this.hard=hard;
    }
    public void getDetails(){
        System.out.println( "CPU="+CPU+" memory="+memory+" hard="+memory);
    }
}
class PC extends Computer{
    String brand;
    public PC(String brand){
        super("CPU1","memory1","hard1");
        this.brand=brand;
    }
}
class NotePad extends Computer{
    String color;
    public NotePad(String color){
        super("CPU2","memory2","hard2");
        this.color=color;
    }
}
/*输出:
CPU=CPU1 memory=memory1 hard=memory1
CPU=CPU2 memory=memory2 hard=memory2*/
//老师的答案:
package package20221105;
public class ExtendsExercise03 {
    public static void main(String[] args) {
        PC pc=new PC("intel",16,500,"IBM");
        pc.printInfo();
    }
}
/*输出:cpu=intel memory=16 disk=500 brand=IBM*/
package package20221105;

public class Computer {
    private String cpu;
    private  int memory;
    private int disk;

    public Computer(String cpu, int memory, int disk) {
        this.cpu = cpu;
        this.memory = memory;
        this.disk = disk;
    }

    public String getCpu() {
        return cpu;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public int getMemory() {
        return memory;
    }

    public void setMemory(int memory) {
        this.memory = memory;
    }

    public int getDisk() {
        return disk;
    }

    public void setDisk(int disk) {
        this.disk = disk;
    }
    public String getDetails(){
        return "cpu="+cpu+" memory="+memory+" disk="+disk;
    }
}
package package20221105;
public class PC extends Computer{
    private String brand;
    //这里IDEA根据继承的规则,自动把构造器的调用写好了
    //体现了继承设计的基本思想:父类的构造器完成父类属性初始化,
    //子类的构造器完成子类属性初始化
    public PC(String cpu, int memory, int disk, String brand) {
        super(cpu, memory, disk);
        this.brand = brand;
    }
    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }
    public void printInfo(){
        //调用父类的getDetails()方法输出属性信息
        System.out.println(getDetails()+" brand="+brand);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

戏拈秃笔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值