Java基础之:OOP——继承

Java基础之:OOP——继承

面向对象编程OOP(Object Oriented Programming)的三大特征之二:继承

首先看一个案例,分别创建小学生与研究生类,输出他们的信息:

小学生类:

public class Pupil { //小学生类
​
    String name;
    double score;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getScore() {
        return score;
    }
    public void setScore(double score) {
        this.score = score;
    }
    public void testing() {
        System.out.println("小学生考语文...");
    }
    public void showScore() {
        System.out.println("学生名" + name + " 成绩=" + score);
    }
}
研究生类:
public class Graduate { //研究生
​
    String name;
    double score;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getScore() {
        return score;
    }
    public void setScore(double score) {
        this.score = score;
    }
    public void testing() {
        System.out.println("研究生考的是微积分...");
    }
    public void showScore() {
        System.out.println("学生名 " + name + " 成绩=" + score);
    }
}

可以看到 在小学生类与研究生类中 ,有大量的内容是重复的,只有 testing() 方法不同。所以我们可以将它们两个类中共同的属性或方法抽象出来创建一个Student类,再继承Student。

继承介绍

继承可以解决代码的复用性问题,让编程更解决我们人类的思维逻辑,多个类出现相同的属性/方法时,可以将这些属性/方法抽象出来,放在一个父类中来定义,所有的子类都不需要再定义这些属性/方法,只需要继承(extends)父类即可。

继承语法

class 子类名 extends 父类名 {}

说明:

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

2) 父类又叫 超类,基类。

3) 子类又叫派生类。

简单案例

对上面的小学生类和研究生类进行改进。

Student类:

public class Student { //父类
​
    String name;
    double score;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getScore() {
        return score;
    }
    public void setScore(double score) {
        this.score = score;
    }
    public void showScore() {
        System.out.println("学生名 " + name + " 成绩=" + score);
    }
}

Pupil类:

public class Pupil extends Student { //小学生类, 子类
​
    public void testing() {
        System.out.println("小学生考语文...");
    }
    
}
​ 

Graduate类:

public class Graduate extends Student{ //研究生 子类
​
    public void testing() {
        System.out.println("研究生考的是微积分...");
    }
    
}

继承优点

  1. 代码复用性提高了。

  2. 代码的扩展性和维护性提高了。

继承使用细节与注意事项

  1. 子类继承了所有的属性和方法,只是私有的属性不能直接访问,需要通过公共方法进行访问。(封装的体现)

  2. 子类没有继承父类的构造器,但在子类的构造器中必须调用父类的构造器,完成父类的初始化。(例:肯定是先有爷爷再有爸爸最后有儿子)

  3. 当创建子类时,不管你使用子类的哪个构造方法,默认情况下总会去调用父类的无参构造函数,如果父类没有提供无参构造函数,则必须在子类的构造函数中用 super 去指定使用父类的哪个构造函数完成对父类的初始化工作,否则,编译不会通过。

  4. 如果希望指定调用父类的某个构造方法,需要使用super关键字显式调用。

    1. 无参构造器:super();

    2. 有一个参数:super(参数);

    3. 要注意super在使用时,需要放在方法体的第一句位置。

  5. super() 和 this() 都只能放在构造方法句首,因此这两个方法不能共存在一个方法中

  6. java中所有的类都是Object类的子类

  7. 子类最多只能有一个直接父类,也就是只能继承一个父类。(若需要A类继承B类和C类,则A继承B,B继承C)。

  8. 父类构造器的调用不限于直接父类!将一直往上追溯直到Object类。同样的,若子类调用父类提供的方法,也不限于直接父类。

简单案例

import java.util.ArrayList;
​
public class ExtendsDetail {
​
    public static void main(String[] args) {
        BB bb = new BB();
        bb.m1();
    }
}
​
class DD {
    public DD() {
        System.out.println("DD() 被调用");
    }
}
​
// 子类继承了所有的属性和方法,只是私有的属性不能直接访问,需要通过公共的方法去访问
class AA extends DD {
    // 属性
    public int n1 = 10;
    protected int n2 = 20;
    int n3 = 30;
    private int n4 = 40;
    
    public int getN4() {
        return n4;
    }
    
//  public AA() {
//      System.out.println("AA() 构造器..");
//  }
     public AA(String name) {
     }
     
     public AA() {
         
     }
     
     public AA(int num) {
         //super();
     }
}
​
//类的BB继承 ctrl+t
class BB extends AA { //子类BB 继承 AA
    public void m1() {
        System.out.println(n1 + " " + n2 + " " + n3 + " " /*+ n4 */);
        System.out.println(getN4());
    }
    
    //子类没有继承父类的构造器,但必须调用父类的构造器, 完成父类的初始化.
    //至于调用父类的哪个构造器,无所谓,但是一定要调用一个
    public BB() {
        //默认有一句话 super(), 父类的无参构造器
        
        //如果希望指定去调用父类的某个构造方法,则显示的调用一下
        //super在使用时,需要放在方法体的第一句位置
        //super() 和 this() 都只能放在构造方法句首,因此这两个方法不能共存在一个方法中
        super(10);
        System.out.println("BB() 构造器..");
    }   
}
 

  

内存分析案例

public class ClassTest {
    public static void main(String[] args) {
        son son = new son();
    }
}
​
class Guandpa {
    private String name;
    
    public Guandpa(String name) {
        super();    //指向 Object();
        this.name = name;
    }
​
    public Guandpa() {
        //默认存在super();  即使不显式的写出
        this.name = "爷爷";
    }
    
    public  void show() {
        System.out.println("Guandpa:" + name);
    }
}
​
class father extends Guandpa{
    private String name;
​
    public father(String name) {
        super();
        this.name = name;
    }
​
    public father() {
        super();// 指向 Guandpa();
        this.name = "父亲";
    }
    
    public void show(){
        System.out.println("father"+name);
    }
    
}
​
class son extends father{
    private String name;
    
    public son(String name) {
        super();    
        //默认存在super();即使不显式的写出。
        //当我们在father类中 无参构造方法被覆盖时, son子类的构造方法就会报错。
        //因为son子类在构造时 ,会首先调用super(); 
        this.name = name;
    }
​
    public son() {
        super();    //指向 father();
        this.name = "儿子";
    }
    
    public void f1() {
        show();
    }
}

  

super关键字

super代表父类的引用,用于访问父类的属性、方法、构造器

基本语法

  1. 访问父类的属性 , 不能访问父类的private属性 [案例] super.属性名;

  2. 访问父类的方法,不能访问父类的private方法

    super.方法名(参数列表);

  3. 访问父类的构造器(只能访问非私有的父类构造器):

    super(参数列表); 构造器的调用只能放在构造器中,且一定在第一行。

细节说明

  1. 调用父类的构造器 (分工明确, 父类属性由父类初始化,子类的属性由子类初始化)

  2. 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果!

  3. super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类中都有同名的成员,使用super访问遵循就近原则。A->B->C

简单案例

public class SuperTest {

	public static void main(String[] args) {
		BB bb = new BB();
		bb.m1();
	}

}

class AA {
	public int n1 = 10;
	protected int n2 = 20;
	int n3 = 30;
	private int n4 = 40;
	
	public void run() {
		
	}
	protected void eat() {
		
	}
	void sleep() {
		
	}
	private void cry() {
		
	}
	
	public  AA() {
		
	}
	public AA(String name) {
		
	}
}

class BB extends AA{
	public void m1() {
		//访问父类的属性 , 不能访问父类的private属性 [案例]    super.属性名
		//如果子类,和父类不在同一个包 ,默认的属性是否可以访问? 答不能
		System.out.println(super.n1 + " " + super.n2 + " " + super.n3 /*+ super.n4*/);
	}
	
	//访问父类的方法,不能访问父类的private方法    super.方法名(参数列表);
	//如果子类,和父类不在同一个包 ,默认的方法是否可以访问? 答不能
	
	public void m2() {
		
		super.eat();
		super.run();
		super.sleep();
		//super.cry();
		
	}
	
	//访问父类的构造器(这点前面用过):    super(参数列表);只能放在构造器的第一句,而且只能出现一句
	//也只能访问 非私有的构造器,如果子类和父类不在同一个包,默认的构造器,也不能使用
	
	public BB() {
//		super();
		super("hello");
	}
}

  特别注意:如果子类,和父类不在同一个包 ,默认的属性不可以访问。在上面的案例中可以可以通过super访问AA类中的属性与方法,是因为AA与BB在同一个包下!

super与this

 

对于继承的总结:

继承的本质是建立一种查找关系,就像this关键字中的访问属性和调用方法时一样。

 

 

继承应用案例1

为了代码方便阅读这里将各个类写在了一起,但在实际开发中应该保证一个类一个文件,所有类在同一个包下。

/**
 * 
 * 编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息
 * 编写PC子类,继承Computer类,添加特有属性【品牌brand】 编写NotePad子类,继承Computer类,添加特有属性【演示color】
 * 编写Test类,在main方法中创建PC和NotePad对象,分别给对象中特有的属性赋值,
 * 以及从Computer类继承的属性赋值,并使用方法并打印输出信息
 *
 */
public class Test {
    public static void main(String[] args) {
        PC pc = new PC("intel_i9", "16GB", "1TB", "PCbrand");
        NotePad notePad = new NotePad("cpu", "8GB", "128GB", "NotePadcolor");
        
        System.out.println(pc.getDetails());
        System.out.println(notePad.getDetails());
    }
}
​
//编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息
class Computer {
    //private + set & get 方法,体现封装
    private String CPU;
    private String memory;
    private String disk;
    
    public String getCPU() {
        return CPU;
    }
​
    public void setCPU(String cPU) {
        CPU = cPU;
    }
​
    public String getMemory() {
        return memory;
    }
​
    public void setMemory(String memory) {
        this.memory = memory;
    }
​
    public String getDisk() {
        return disk;
    }
​
    public void setDisk(String disk) {
        this.disk = disk;
    }
​
    
    public String getDetails() {
        return "Computer [CPU=" + CPU + ", memory=" + memory + ", disk=" + disk + "]";
    }
​
    public Computer(String cPU, String memory, String disk) {
        super();
        CPU = cPU;
        this.memory = memory;
        this.disk = disk;
    }
​
    public Computer() {
        super();
    }
    
    
}
​
//编写PC子类,继承Computer类,添加特有属性【品牌brand】
class PC extends Computer {
    private String brand;   //private + set & get 方法,体现封装
    
    public String getBrand() {
        return brand;
    }
​
    public void setBrand(String brand) {
        this.brand = brand;
    }
​
    @Override
    public String getDetails() {
        return super.getDetails() + "
PC [brand=" + brand + "]";
    }
​
    public PC(String cPU, String memory, String disk, String brand) {
        super(cPU, memory, disk);
        this.brand = brand;
    }
    
    
}
​
//编写NotePad子类,继承Computer类,添加特有属性【演示color】
class NotePad extends Computer {
    private String color;   //private + set & get 方法,体现封装
    
    public String getColor() {
        return color;
    }
​
    public void setColor(String color) {
        this.color = color;
    }
​
    @Override
    public String getDetails() {
        return super.getDetails() + "
NotePad [color=" + color + "]";
    }
​
    public NotePad(String cPU, String memory, String disk, String color) {
        super(cPU, memory, disk);
        this.color = color;
    }
    
}

继承应用案例2

为了代码方便阅读这里将各个类写在了一起,但在实际开发中应该保证一个类一个文件,所有类在同一个包下。

/**
 * 定义一个ManKind类: 成员变量int sex和int salary; 要求加上两个参数有参构造 
 * 方法void manOrWomen():根据sex的值显示“man”(sex==1)或者“women”(sex==0); 
 * 方法void employeed():根据salary的值显示“no job”(salary==0)或者“ job”(salary!=0)。 
 * 定义类Kids继承ManKind,并包括成员变量int yearsOld; 方法printAge()打印yearsOld的值。 
 * 在Kids类的main方法中实例化Kids的对象someKid,用该对象访问其父类的成员变量及方法。
 */
public class HomeWork {
​
    public static void main(String[] args) {
        Kids kids = new Kids(2,-100,18);
        
        kids.manOrWomen();
        kids.employeed();
        kids.printAge();
​
    }
​
}
​
class ManKind {
    private int sex;
    private int salary;
 
    public int getSex() {
        return sex;
    }
​
    public void setSex(int sex) {
        this.sex = sex;
    }
​
    public int getSalary() {
        return salary;
    }
​
    public void setSalary(int salary) {
        this.salary = salary;
    }
​
    // 方法void manOrWomen():根据sex的值显示“man”(sex==1)或者“women”(sex==0);
    public void manOrWomen() {
        if (sex == 1) {
            System.out.println("man");
        } else{
            System.out.println("women");
        }
    }
​
    // 方法void employeed():根据salary的值显示“no job”(salary==0)或者“ job”(salary!=0)。
    public void employeed() {
        if (salary == 0) {
            System.out.println("no job!");
        } else {
            System.out.println("job!");
        }
    }
​
    public ManKind(int sex, int salary) {
        super();
        if(sex != 0 && sex !=1) {
            System.out.println("性别输入内容错误,0表示女,1表示男!");
            this.sex = 0;
        }else {
            this.sex = sex;
        }
        if (salary < 0) {
            System.out.println("输入工资错误,默认值1000");
            this.salary = 1000;
        } else {
            this.salary = salary;
        }
    }
​
    public ManKind() {
        super();
    }
​
}
​
//定义类Kids继承ManKind,并包括成员变量int yearsOld; 方法printAge()打印yearsOld的值。 
class Kids extends ManKind{
    private int yearsOld;
    
    public int getYearsOld() {
        return yearsOld;
    }
​
    public void setYearsOld(int yearsOld) {
        this.yearsOld = yearsOld;
    }
​
    public void printAge() {
        System.out.println("年龄:"+yearsOld);
    }
​
    public Kids(int sex, int salary, int yearsOld) {
        super(sex, salary);
        this.yearsOld = yearsOld;
    }
    
    public static void main(String[] args) {    
        //在另外一个非public类中 也有一个main时,点击Run As 会询问执行哪一个main
        Kids kids = new Kids(1,2000,18);
        kids.setSex(0);
        kids.manOrWomen();
        kids.employeed();
        kids.printAge();
    }
}

  

 

原文地址:https://www.cnblogs.com/SongHai/p/14075033.html