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);
}
}