一、继承介绍
引入
编写了两个类,一个是Pupil类(小学生),一个是Graduate(大学毕业生)
问题:两个类的属性和方法有很多是相同的,怎么办?
姓名、年龄、set、get方法是相同的,但是也有不同的。
继承可以解决代码复用,让我们的编程更加靠近人类思维。当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。
继承的示意图

二、继承语法
class 子类 extends 父类 {
}
- 子类会自动拥有父类定义的属性和方法。
- 父类又叫超类,基类。
- 子类又叫派生类。
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)子类继承了所有的属性和方法:通过Debug断电可知,子类继承了父类所有属性和方法,但是是否能使用就需要依靠访问控制修饰符。
(2)子类可以直接访问父类的( public,protected,默认) 属性和方法
(3)子类通过父类提供的 public 方法,访问父类的 private 属性和方法 -
子类必须调用父类的构造器, 完成父类的初始化
-
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过(怎么理解。) [举例说明]
-
如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
-
super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)
-
super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
-
java 所有类都是 Object 类的子类, Object 是所有类的基类
-
父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)
-
子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。
思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】 -
不能滥用继承,子类和父类之间必须满足 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
本文详细介绍了Java中的继承机制,包括其基本概念、语法、好处(提高代码复用和扩展性),以及使用时的细节,如属性和方法的访问控制、构造器的调用和继承层次的理解。

被折叠的 条评论
为什么被折叠?



