一、为什么需要继承
我们编写了两个类,一个是Pupil类,一个是Graduate类
问题:两个类的属性和方法有很多是相同的,怎么办?
Pupil类:
package com.javase.extend_;public class Pupil {public String name;public int age;private double score;public void setScore(double score) {this.score = score;}//考试public void testing() {System.out.println("小学生" + name + "正在考小学数学");}//输出学生信息public void showInfo() {System.out.println("学生姓名" + name + " 年龄 " + age + " 成绩 " + score);}
}
Graduate类:
package com.javase.extend_;public class Graduate {public String name;public int age;private double score;public void setScore(double score) {this.score = score;}//考试public void testing() {System.out.println("大学生" + name + "正在考大学数学");}//输出学生信息public void showInfo() {System.out.println("学生姓名" + name + " 年龄 " + age + " 成绩 " + score);}
}
两个类的代码中属性以及功能都是重复的,代码复用性低。
二、什么是继承
基本介绍:
继承可以解决代码复用,当多个类存在相同的属性和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。
基本语法:
class 子类 extends 父类 {
}
1、子类会自动拥有父类定义的属性和方法
2、父类又称超类或基类
3、子类又称派生类
示意图:
使用继承解决代码复用问题:
Student父类
package com.javase.extend_.improve;
//父类 Pupil和Graduate的分类
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 + " 年龄 " + age + " 成绩 " + score);}}
Pupil类
package com.javase.extend_.improve;public class Pupil extends Student{//考试public void testing() {System.out.println("学生" + name + "正在考小学数学");}
}
Graduate类
package com.javase.extend_.improve;public class Graduate extends Student{//考试public void testing() {System.out.println("学生" + name + "正在考小学数学");}
}
输出:
package com.javase.extend_.improve;public class Extends01 {public static void main(String[] args) {Pupil p = new Pupil();p.name = "张三";p.age = 10;p.testing();p.setScore(60);p.showInfo();System.out.println("========");Graduate g = new Graduate();g.name = "李四";g.age= 20;g.testing();g.setScore(100);g.showInfo();}
}
继承的好处:
1、代码复用性提高了
2、代码的扩展性和维护性提高了
三、继承的使用细节和注意事项
1、子类继承了所有的属性和方法,私有属性不能在子类直接访问,但是非私有属性能在子类直接访问,通过父类提供公共的方法去访问私有属性和方法。
父类Base
package com.javase.extend_.improve;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 setN4() {test400();}
}
子类Sub
package com.javase.extend_.improve;public class Sub extends Base {public Sub() {System.out.println("Sob()...");}public void sayOk() {//子类方法//非私有的属性和方法可以在子类直接访问//但是私有属性和方法不能直接在子类访问System.out.println(n1 + " " + n2 + " " + n3 + " " + getN4());test100();test200();test300();//test400();报错setN4();}
}
输出:
package com.javase.extend_.improve;public class ExtendsDetail {public static void main(String[] args) {Sub sub = new Sub();sub.sayOk();}
}
2、子类必须调用父类的构造器,完成父类的初始化
3、当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果当父类没有提供无参构造器,则必须在子类的构造器中使用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不通过。
情况一:不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器
调用两个构造器:
运行结果:
情况二 : 如果当父类没有提供无参构造器,则必须在子类的构造器中使用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不通过
子类编译会报错
需要用super去指定父类构造器
运行:
调用了父类有参构造器
4、如果要指定去调用父类的某个构造器,需要用super显示调用
5、super在使用时,必须放在构造器第一行。注意:super只能在构造器中使用
不在第一行会报错
6、super()和this()都只能放在构造器第一行,所以这两个方法不能共存在一个构造器。
7、Java所有类都是Object类的子类,Object是所有类的父类或基类。
在idea中ctrl + H可以查看类的关系
8、父类构造器的调用不限于直接父类,将一直往上追溯直到Object类。
9、子类最多只能继承一个父类(直接继承),在Java中是单继承机制。
10、不能滥用继承,子类和父类之间必须满足is-a的逻辑关系。
四、继承的本质
案例演示:
package com.javase.extend_;public class ExtendsThory {public static void main(String[] args) {}
}class GradPa {String name = "大头爷爷";String hobby = "旅游";
}
class Father extends GradPa {String name = "大头爸爸";int age = 30;
}
class Son extends Father {String name = "大头儿子";
}
当子类对象创建好后,建立查找关系。
1、首先看子类中是否具有需要的属性。
2、当子类中有需要的属性,并且可以访问,就返回信息
3、当子类中没有我们需要的属性,就需要到父类中查找是否有我们需要的属性,并且可以访问,就返回信息。
4、当父类中没有我们需要的属性,就继续往上一级查找,直到Object类。
内存图:
五、入门练习
练习一 :
输出结果:
a
b name
b
练习二:
输出结果:
我是A类
hahah我是B类有参构造
我是C类有参构造
我是C类无参构造
练习三:
Computer类:
package com.javase.extend_.exercise;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 getDetails() {return "CPU=" + cpu + " 内存=" + memory + " 硬盘=" + 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;}
}
Pc类:
package com.javase.extend_.exercise;//编写Pc子类,继承Computer类,添加特有属性
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;}}
NotePad类:
package com.javase.extend_.exercise;import javax.sound.midi.Soundbank;public class NotePad extends Pc{private String color;public NotePad(String cpu, int memory, int disk, String brand, String color) {super(cpu, memory, disk, brand);this.color = color;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public void printInfo() {System.out.println("电脑信息如下:");System.out.println(getDetails());System.out.println("品牌=" + getBrand());System.out.println("颜色=" + color);}
}
输出:
package com.javase.extend_.exercise;public class ExtendsExercise03 {public static void main(String[] args) {NotePad notePad = new NotePad("intle:i5-8700", 16, 256, "ROG", "黑色");notePad.printInfo();}
}