继承(面向对象编程三大特征之一)

本文详细介绍了Java中的继承机制,包括其基本概念、语法、好处(提高代码复用和扩展性),以及使用时的细节,如属性和方法的访问控制、构造器的调用和继承层次的理解。

一、继承介绍

引入
编写了两个类,一个是Pupil类(小学生),一个是Graduate(大学毕业生)
问题:两个类的属性和方法有很多是相同的,怎么办?
姓名、年龄、set、get方法是相同的,但是也有不同的。

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

继承的示意图
在这里插入图片描述

二、继承语法

	class 子类 extends 父类 {
	}
  1. 子类会自动拥有父类定义的属性和方法。
  2. 父类又叫超类,基类。
  3. 子类又叫派生类。

Pupil类和Graduate类 继承Student类。
Student.java

	package com.edu.extend_.improve_;
	public class Student {
	    //共有的属性
	    public String name;
	    public int age;
	    private double score;
	
	    //共有的方法
	    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 double getScore() {return score;}
	    public void setScore(double score) {this.score = score;}
	
	    public void showInfo() {
	        System.out.println("学生名字="+this.name
	                +" 年龄="+this.age+" 分数="+this.age);
	    }
	}

Pupil.java

	package com.edu.extend_.improve_;
	public class Pupil extends Student{
	    public void testing() {
	        System.out.println("小学生"+this.name+"在考试");
	    }
	}

Gradute.java

	package com.edu.extend_.improve_;
	public class Graduate extends Student{
	    public void testing() {
	        System.out.println("大学生"+this.name+"在考试");
	    }
	}

Extends01.java

	package com.edu.extend_.improve_;
	
	import com.edu.extend_.Graduate;
	import com.edu.extend_.Pupil;
	
	public class Extends01 {
	    public static void main(String[] args) {
	        com.lxhedu.extend_.Pupil pupil = new Pupil();
	        pupil.name = "银角大王~2";
	        pupil.age = 11;
	        pupil.testing();
	        pupil.setScore(80);
	        pupil.showInfo();
	
	        System.out.println("======");
	        com.lxhedu.extend_.Graduate graduate = new Graduate();
	        graduate.name = "金角大王~2";
	        graduate.age = 23;
	        graduate.testing();
	        graduate.setScore(99);
	        graduate.showInfo();
	
	    }
	}

三、继承好处

  1. 代码的复用性提高了
  2. 代码的扩展性和维护性提高了

四、继承的使用细节

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问(见文末代码)
    (1)子类继承了所有的属性和方法:通过Debug断电可知,子类继承了父类所有属性和方法,但是是否能使用就需要依靠访问控制修饰符。
    (2)子类可以直接访问父类的( public,protected,默认) 属性和方法
    (3)子类通过父类提供的 public 方法,访问父类的 private 属性和方法

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

  3. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过(怎么理解。) [举例说明]

  4. 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)

  5. super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)

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

  7. java 所有类都是 Object 类的子类, Object 是所有类的基类

  8. 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)

  9. 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。
    思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】

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

五、继承的使用细节-代码

细节1

父类Base和子类Sub和测试代码类ExtendsDetail
Base.java

	public class Base  { //父类
	    //4 个属性
	    public int n1 = 100;
	    protected int n2 = 200;
	    int n3 = 300;
	    private int n4 = 400;
	
	
	    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 int getN4() {return n4;}
	   
	    public void callTest4() {test400();}
	}

Sub.java

	public class Sub extends Base{
	    public Sub() {System.out.println("子类 sub()构造器被调用...");}
	    
	    public void sayOk() {//子类方法

	//        1.子类可以访问父类的( public protected 默认 )【属性】
	        System.out.println("public n\n1="+n1
	                            +" protected n2="+n2
	                            +" 默认 n3="+n3);
	                            
	//        2.子类可以访问父类的( public protected 默认 )【成员方法】
	        test100();
	        test200();
	        test300();
	        
	//        3.子类可以通过父类提供的公共public方法访问private【属性】
	        System.out.println("public get n4="+getN4());
	
	//        4.子类可以通过父类提供的公共public方法访问private【方法】
	        callTest4();
	    }
	}

ExtendsDetail.java

	public class ExtendsDetail {
	    public static void main(String[] args) {
	        Sub sub = new Sub();
	        sub.sayOk();
	    }
	}

在这里插入图片描述

细节2

父类Base和子类Sub
在Sub类的构造函数里面,默认会调用super();函数

public class Base  { //父类}
public class Sub extends Base{ 
	public Sub() {
		super(); 
		System.out.println("子类 sub()构造器被调用...");
	}
}

super(); 程序员写不写都会默认调用。 函数的意思是:默认调用父类的无参构造器。

细节3

Base.java

public class Base  { //父类
 	public Base() { //无参构造器
        System.out.println("父类 Base()默认构造器被调用....");
    }

    public Base(int n1, int n2, int n3, int n4) {
        System.out.println("父类 Base(int n1, int n2, int n3, int n4)构造器被调用....");
    }
}

Sub.java

	public class Sub extends Base{
	    public Sub() {
	        System.out.println("子类 sub()默认构造器被调用...");
	    }
	    public Sub(String name) {
	        System.out.println("子类 sub(String name)构造器被调用...");
	    }
	}

ExtendsDetail.java

	public class ExtendsDetail {
	    public static void main(String[] args) {
	        Sub sub = new Sub();
	        System.out.println("===========");
	        Sub sub1 = new Sub("jack");
	    }
	}

在这里插入图片描述

细节4

Base.java

	public class Base  { //父类
	    public Base() { //无参构造器
	        System.out.println("父类 Base()默认构造器被调用....");
	    }
	
	    public Base(String name) {
	        System.out.println("父类 Base(String name)构造器被调用....");
	    }
	
	    public Base(String name,int age) {
	        System.out.println("父类 Base(String name,int age) 构造器被调用....");
	    }
	}

Sub.java

	public class Sub extends Base{
	    public Sub() {
	        //1、调用父类的无参构造器
	        super(); //不写也可以
	        System.out.println("子类 sub()默认构造器被调用...");
	    }
	    public Sub(String name) {
	        //2、调用父类的有参构造器
	        super(name);
	        System.out.println("子类 sub(String name)构造器被调用...");
	    }
	    public Sub(String name,int age) {
	        //2、调用父类的有参构造器
	        super(name,age);
	        System.out.println("子类 sub(String name)构造器被调用...");
	    }
	}

ExtendsDetail.java

	public class ExtendsDetail {
	    public static void main(String[] args) {
	    
	        System.out.println("=====调用父类的无参构造器==============");
	        Sub sub = new Sub();
	        
	        System.out.println("=====调用父类的有参(一个参数)构造器======");
	        Sub sub1 = new Sub("jack");
	        
	        System.out.println("=====调用父类的有参(二个参数)构造器======");
	        Sub sub2 = new Sub("haha",2);
	    }
	}

在这里插入图片描述

细节7

CTRL + H 看继承关系

六、继承本质

	public class ExtendsTheory {
	    public static void main(String[] args) {
	        Son son = new Son();//内存的布局
	    }
	}
	
	class GrandPa { //爷类
	    String name = "大头爷爷";
	    String hobby = "旅游";
	}
	class Father extends GrandPa {//父类
	    String name = "大头爸爸";
	    private int age = 39;
	    public int getAge() {
	        return age;
	    }
	}
	class Son extends Father { //子类
	    String name = "大头儿子";
	}

Son ->要加载Son类,先寻找Son的父类
所以加载顺序为:Object->Grandpa->Father->Son

new->在堆中分配内存空间(分配空间同时分配了地址)
先分配Grandpa类 的属性name和hobby
再分配Father类 的属性name和age
最后分配Son类 的属性name

Son son = new Son()->在栈中开辟空间存储son变量,把堆中的地址赋给son
在这里插入图片描述

查找关系

System.out.println(son.name);

    public static void main(String[] args) {
        Son son = new Son();//内存的布局
        System.out.println(son.name); // 返回的是“大头儿子”
        System.out.println(son.age); // 返回的是39
        System.out.println(son.hobby); // 返回的是"旅游"
    }

会输出Son的属性
(1) 首先看Son类是否有该属性
(2) 如果Son类有这个属性,并且可以访问,则返回信息
(3) 如果Son类没有这个属性,就看Son的父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息…)
(4) 如果父类没有就按照(3)的规则,继续找上级父类,直到 Object

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值