首页 > 其他分享 >06-面向对象_继承

06-面向对象_继承

时间:2022-11-27 11:26:28浏览次数:48  
标签:06 构造方法 继承 子类 void class 面向对象 父类 public

typora-root-url: images

一、简介

1.1、问题引入

​ 在现实生活中,继承一般指的是子女继承父辈的财产。在程序中,继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系。

​ 例如公司中的研发部员工和维护部员工都属于员工,程序中便可以描述为研发部员工和维护部员工继承自员工,同理,JavaEE工程师和Android工程师继承自研发部员工,而维网络维护工程师和硬件维护工程师继承自维护部员工。这些员工之间会形成一个继承体系,具体如下图所示:

0076

​ 在Java中,类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类被称作子类,现有类被称作父类,子类会自动拥有父类所有可继承的属性和方法。

1.2、再看一个问题

1.2.1、代码

public class Teacher{
    
    private String name;
    
    private int age;
    
    private double salary;
    
    public void run(){
        System.out.println("跑步");
    }
}

public class Student{
    
    private String name;
    
    private int age;
    
    private String major;
    
    public void study(){
        System.out.println("学习");
    }
}

1.2.2、分析

​ 通过上面的代码发现,这两个类有共同的属性,日后还可能存在大量的共同的方法,那么意味着,需要每次都这么定义,很麻烦,怎么办呢?

​ 我们发现,不管是学生,还是老师,都是人,所以我们可以单独的定义人这样的类,让学生类和老师去继承这个类,这样就简单多了。

1.2.3、结论

  • 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
  • 继承性的好处:
    • ① 减少了代码的冗余,提高了代码的复用性;
    • ② 便于功能的扩展;
    • ③ 为之后多态性的使用,提供了前提。

1.3、总结

0077

​ 其中,多个类可以称为子类,单独那一个类称为父类超类(superclass)或者基类

​ 继承描述的是事物之间的所属关系,这种关系是: is-a 的关系。例如,图中兔子属于食草动物,食草动物属于动物。可见,父类更通用,子类更具体。

​ 我们通过继承,可以使多种事物之间形成一种关系体系。

1.4、继承格式

​ 通过extends关键字,可以声明一个子类继承另外一个父类,定义格式如下:

1.4.1、格式

class 父类 { 
    ... 
}

class 子类 extends 父类 { 
    ... 
}

1.4.2、示例

/*
 * 定义员工类Employee
 */
class Employee {
	String name; // 定义name属性
	// 定义员工的工作方法
	public void work() {
		System.out.println("尽心尽力地工作");
	}
}

/*
 * 定义研发部员工类Developer 继承 员工类Employee
 */
class Developer extends Employee {
	// 定义一个打印name的方法
	public void printName() {
		System.out.println("name=" + name);
	}
}

/*
 * 定义测试类
 */
public class Example01 {
	public static void main(String[] args) {
		Developer d = new Developer(); // 创建一个研发部员工类对象
		d.name = "小明"; // 为该员工类的name属性进行赋值
		d.printName(); // 调用该员工的printName()方法
		d.work(); // 调用Developer类继承来的work()方法
	}
}

​ 在上述代码中,Developer类通过extends关键字继承了Employee类,这样Developer类便是Employee类的子类。从运行结果不难看出,子类虽然没有定义name属性和work()方法,但是却能访问这两个成员。这就说明,子类在继承父类的时候,会自动拥有父类的成员。

二、继承

2.1、继承的好处&注意事项

2.1.1、好处

  • 继承的出现提高了代码的复用性,提高软件开发效率。
  • 继承的出现让类与类之间产生了关系,提供了多态的前提。

2.1.2、需要注意的问题

  • 问题1

    ​ 在Java中,类只支持单继承,不允许多继承,也就是说一个类只能有一个直接父类,例如下面这种情况是不合法的。

    class A{} 
    class B{}
    class C extends A,B{}  // C类不可以同时继承A类和B类
    
  • 问题2

    ​ 多个类可以继承一个父类,例如下面这种情况是允许的。

    class A{}
    class B extends A{}
    class C extends A{}   // 类B和类C都可以继承类A
    
  • 问题3

    ​ 在Java中,多层继承是可以的,即一个类的父类可以再去继承另外的父类,例如C类继承自B类,而B类又可以去继承A类,这时,C类也可称作A类的子类。下面这种情况是允许的。

    class A{}
    class B extends A{}   // 类B继承类A,类B是类A的子类
    class C extends B{}   // 类C继承类B,类C是类B的子类,同时也是类A的子类
    
  • 问题4

    ​ 在Java中,子类和父类是一种相对概念,也就是说一个类是某个类父类的同时,也可以是另一个类的子类。例如上面的这种情况中,B类是A类的子类,同时又是C类的父类。

2.2、子父类中成员变量的特点

2.2.1、说明

​ 了解了继承给我们带来的好处,提高了代码的复用性。继承让类与类或者说对象与对象之间产生了关系。那么,当继承出现后,类的成员之间产生了那些变化呢?

2.2.2、情况1

​ 成员变量:如果子类父类中出现不同名的成员变量,这时的访问是没有任何问题。

class Fu
{
	//Fu中的成员变量。
	int num = 5;
}
class Zi extends Fu
{
	//Zi中的成员变量
	int num2 = 6;
	//Zi中的成员方法
	public void show()
	{
        //访问父类中的num
		System.out.println("Fu num="+num);
		//访问子类中的num2
		System.out.println("Zi num2="+num2);
	}
}
class Demo 
{
	public static void main(String[] args) 
	{
		Zi z = new Zi(); //创建子类对象
		z.show(); //调用子类中的show方法
	}
}

​ 代码说明:Fu类中的成员变量是非私有的,子类中可以直接访问,若Fu类中的成员变量私有了,子类是不能直接访问的

2.2.3、情况2

​ 当子父类中出现了同名成员变量时,在子类中若要访问父类中的成员变量,必须使用关键字super来完成。super用来表示当前对象中包含的父类对象空间的引用。

class Fu
{
	//Fu中的成员变量。
	int num = 5;
}
class Zi extends Fu
{
	//Zi中的成员变量
	int num = 6;
	void show()
	{
		//子父类中出现了同名的成员变量时
		//在子类中需要访问父类中非私有成员变量时,需要使用super关键字
		//访问父类中的num
		System.out.println("Fu num="+super.num);
		//访问子类中的num2
		System.out.println("Zi num2="+this.num);
	}
}
class Demo5 
{
    public static void main(String[] args) 
	{
		Zi z = new Zi(); //创建子类对象
		z.show(); //调用子类中的show方法
	}
}

2.3、子父类中成员方法特点-重写&应用

2.3.1、子父类中成员方法的特点

​ 当在程序中通过对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。

class Fu{
	public void show(){
		System.out.println("Fu类中的show方法执行");
	}
}
class Zi extends Fu{
	public void show2(){
		System.out.println("Zi类中的show2方法执行");
	}
}
public  class Test{
	public static void main(String[] args) {
		Zi z = new Zi();
		z.show(); //子类中没有show方法,但是可以找到父类方法去执行
		z.show2();
	}
}

2.3.2、成员方法特殊情况:覆盖

​ 子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为override重写、复写或者覆盖。

class Fu
{
	public void show()
	{
		System.out.println("Fu show");
	}
}
class Zi extends Fu
{
	//子类复写了父类的show方法
	public void show()
    {
		System.out.println("Zi show");
	}
}

2.3.3、方法重写的应用

​ 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。

​ 举例:比如手机,当描述一个手机时,它具有发短信,打电话,显示来电号码功能,后期由于手机需要在来电显示功能中增加显示姓名和头像,这时可以重新定义一个类描述智能手机,并继承原有描述手机的类。并在新定义的类中覆盖来电显示功能,在其中增加显示姓名和头像功能。

//手机类
class Phone{
	public void sendMessage(){
		System.out.println("发短信");
	}
	public void call(){
		System.out.println("打电话");
	}
	public void showNum(){
		System.out.println("来电显示号码");
	}
}

//智能手机类
class NewPhone extends Phone{
	
	//覆盖父类的来电显示号码功能,并增加自己的显示姓名和图片功能
	public void showNum(){
		//调用父类已经存在的功能使用super
		super.showNum();
		//增加自己特有显示姓名和图片功能
		System.out.println("显示来电姓名");
		System.out.println("显示头像");
	}
}

public class Test {
	public static void main(String[] args) {
		new NewPhone().showNum();
	}
}

2.3.4、方法重写的注意事项

  • 事项1

    ​ 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。

    class Fu(){	
    	void show(){
            
        }
        public void method(){
            
        }
    }
    
    class Zi() extends Fu{
    	public void show(){
            
        }  //编译运行没问题
        
        void method(){
            
        }      //编译错误
    }
    
  • 事项2

    • 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void;
    • 父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类;
    • 父类被重写的方法的返回值类型如果是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须是:double)。
    • 子类方法抛出的异常不能大于父类被重写的方法抛出的异常
  • 事项3

    ​ 子类与父类中同名同参数的方法必须同时声明为非static的(即为重写)。

2.4、继承后的特点:构造方法

​ 当类之间产生了关系,其中各类中的构造方法,又产生了哪些影响呢?

​ 首先我们要回忆两个事情,构造方法的定义格式和作用。

  • 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。

  • 构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构

    造方法中默认有一个 super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。因为只有创建了父类对象,才有子类对象,先有父亲,再有孩子。代码如下:

    class Fu { 
        
        private int n; 
        
        Fu(){ 
            System.out.println("Fu()"); 
        } 
    }
    
    class Zi extends Fu { 
        Zi(){ 
            // super(),调用父类构造方法 
            super(); 
            System.out.println("Zi()"); 
        } 
    }
    
    public class Demo{ 
        public static void main (String args[]){ 
            Zi zi = new Zi(); 
        } 
    }
    
    输出结果: 
        Fu() 
        Zi()
    

三、super关键字

3.1、父类空间优先于子类对象产生

3.1.1、说明

​ 在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构造方法调用时,一定先调用父类的构造方法。

3.1.2、图示

0078

3.2、super和this的含义

  • super :代表父类的存储空间标识(可以理解为父亲的引用)。
  • this :代表当前对象的引用(谁调用就代表谁)。

3.3、super和this的用法

this.成员变量 ‐‐ 本类的 
super.成员变量 ‐‐ 父类的 
    
this.成员方法名() ‐‐ 本类的 
super.成员方法名() ‐‐ 父类的
class Animal { 
    public void eat() { 
        System.out.println("animal : eat"); 
    } 
}

class Cat extends Animal { 
    public void eat() { 
        System.out.println("cat : eat"); 
    }
    public void eatTest() { 
        this.eat(); // this 调用本类的方法 
        super.eat(); // super 调用父类的方法 
    } 
}

public class Demo { 
    public static void main(String[] args) { 
        Animal a = new Animal(); 
        a.eat(); 
        Cat c = new Cat(); 
        c.eatTest(); 
    } 
}

输出结果为: 
    animal : eat 
    cat : eat 
   	animal : eat

3.4、super访问构造方法

3.4.1、说明

this(...) ‐‐ 本类的构造方法 
super(...) ‐‐ 父类的构造方法

​ 子类的每个构造方法中均有默认的super(),调用父类的空参构造。如果里面带有参数,则指向有参构造

​ 手动调用父类构造会覆盖默认的super()。

​ super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。

3.4.2、思考

​ 为什么子类对象创建都要访问父类中的构造方法?因为子类继承了父类的内容,所以创建对象时,必须要先看父类是如何对其内容进行初始化的,看如下程序:

class Fu{
	int num ;
	Fu(){
		System.out.println("Fu构造方法"+num);
		num = 4;
	}
}
class Zi extends Fu{
	Zi(){
         //super(); 调用父类空参数构造方法
		System.out.println("Zi构造方法"+num);
	}
}

public class Test {
	public static void main(String[] args) {
		new Zi();
	}
}

​ 执行结果:

   Fu构造方法0

   Zi构造方法4

​ 通过结果发现,子类构造方法执行时中,调用了父类构造方法,这说明,子类构造方法中有一句super()。

那么,子类中的构造方法为什么会有一句隐式的super()呢?

​ 原因:子类会继承父类中的内容(数据),可能还会使用父类的数据。所以子类在初始化时,必须先到父类中去执行父类的初始化动作。这样,才可以使用父类中的内容。

​ 当父类中没有空参数构造方法时,子类的构造方法必须有显示的super语句,指定要访问的父类有参数构造方法。

3.5、子类对象创建过程的细节

​ 如果子类的构造方法第一行写了this调用了本类其他构造方法,那么super调用父类的语句还有吗?

​ 这时是没有的,因为this()或者super(),只能定义在构造方法的第一行,因为初始化动作要先执行。

​ 父类构造方法中是否有隐式的super呢?

​ 也是有的。记住:只要是构造方法默认第一行都是super();

​ 父类的父类是谁呢?super调用的到底是谁的构造方法呢?

​ Java体系在设计,定义了一个所有对象的父类Object。

注意:类中的构造方法默认第一行都有隐式的super()语句,从而去访问父类中的空参数构造方法。

如果默认的隐式super()语句在父类中没有对应的构造方法,那么必须在构造方法中通过this或者super的形式明确要调用的构造方法。

四、抽象类

4.1、问题说明

​ 当编写一个类时,我们往往会为该类定义一些方法,这些方法是用来描述该类的功能具体实现方式,那么这些方法都有具体的方法体。

​ 但是有的时候,某个父类只是知道子类应该包含怎么样的方法,但是无法准确知道子类如何实现这些方法。比如一个图形类应该有一个求周长的方法,但是不同的图形求周长的算法不一样。那该怎么办呢?

​ 分析事物时,发现了共性内容,就出现向上抽取。会有这样一种特殊情况,就是方法功能声明相同,但方法功能主体不同。那么这时也可以抽取,但只抽取方法声明,不抽取方法主体。那么此方法就是一个抽象方法

​ 描述JavaEE工程师:行为:工作。

​ 描述Android工程师:行为:工作。

​ JavaEE工程师和Android工程师之间有共性,可以进行向上抽取。抽取它们的所属共性类型:研发部员工。由于JavaEE工程师和Android工程师都具有工作功能,但是他们具体工作内容却不一样。这时在描述研发部员工时,发现了有些功能(工作)不具体,这些不具体的功能,需要在类中标识出来,通过java中的关键字abstract(抽象)。

​ 当定义了抽象函数的类也必须被abstract关键字修饰,被abstract关键字修饰的类是抽象类。

4.2、抽象类&抽象方法的定义

4.2.1、格式

public abstract class 类名 {
    
	public abstract 返回值类型 方法名(参数);
}

4.2.2、示例

//研发部员工 
abstract class Developer {
    //抽象函数。需要abstract修饰,并分号;结束
	public abstract void work();
}

//JavaEE工程师
class JavaEE extends Developer{
	public void work() {
		System.out.println("正在研发淘宝网站");
	}
}

//Android工程师
class Android extends Developer {
	public void work() {
		System.out.println("正在研发淘宝手机客户端软件");
	}
}

4.3、抽象类的特点

  • 抽象类和抽象方法都需要被abstract修饰。抽象方法一定要定义在抽象类中。
  • 抽象类不可以直接创建对象,原因:调用抽象方法没有意义。
  • 只有覆盖了抽象类中所有的抽象方法后,其子类才可以创建对象。否则该子类还是一个抽象类。
  • 之所以继承抽象类,更多的是在思想,是面对共性类型操作会更简单。

4.4、抽象类的细节问题

4.4.1、问题1

​ 抽象类一定是个父类?

​ 是的,因为不断抽取而来的。

4.4.2、问题2

​ 抽象类中是否可以不定义抽象方法。

​ 是可以的,那这个抽象类的存在到底有什么意义呢?不让该类创建对象,方法可以直接让子类去使用。

4.4.3、问题3

​ 抽象关键字abstract不可以和哪些关键字共存?

  • private:私有的方法子类是无法继承到的,也不存在覆盖,而abstract和private一起使用修饰方法,abstract既要子类去实现这个方法,而private修饰子类根本无法得到父类这个方法。互相矛盾。
  • final,后面讲
  • static,后面讲

4.5、补充说明

  • 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。

    ​ 理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

  • 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

    ​ 理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。

  • 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

    ​ 理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设

    计。

  • 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象

    类。

    ​ 理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有

    意义。

五、综合案例-员工类系列定义

5.1、案例介绍

​ 某IT公司有多名员工,按照员工负责的工作不同,进行了部门的划分(研发部员工、维护部员工)。研发部根据所需研发的内容不同,又分为JavaEE工程师、Android工程师;维护部根据所需维护的内容不同,又分为网络维护工程师、硬件维护工程师。

​ 公司的每名员工都有他们自己的员工编号、姓名,并要做它们所负责的工作。

​ 工作内容:

  • JavaEE工程师:员工号为xxx的 xxx员工,正在研发淘宝网站
  • Android工程师:员工号为xxx的 xxx员工,正在研发淘宝手机客户端软件
  • 网络维护工程师:员工号为xxx的 xxx员工,正在检查网络是否畅通
  • 硬件维护工程师:员工号为xxx的 xxx员工,正在修复打印机

​ 请根据描述,完成员工体系中所有类的定义,并指定类之间的继承关系。进行XX工程师类的对象创建,完成工作方法的调用。

5.2、案例分析

5.2.1、图示

​ 根据上述部门的描述,得出如下的员工体系图:

0079

5.2.2、需求分析

​ 根据员工信息的描述,确定每个员工都有员工编号、姓名、要进行工作。则,把这些共同的属性与功能抽取到父类中(员工类),关于工作的内容由具体的工程师来进行指定。

工作内容

  • JavaEE工程师:员工号为xxx的 xxx员工,正在研发淘宝网站
  • Android工程师:员工号为xxx的 xxx员工,正在研发淘宝手机客户端软件
  • 网络维护工程师:员工号为xxx的 xxx员工,正在检查网络是否畅通
  • 硬件维护工程师:员工号为xxx的 xxx员工,正在修复打印机创建JavaEE工程师对象,完成工作方法的调用

5.2.3、开发

​ 创建JavaEE工程师对象,完成工作方法的调用

5.3、代码实现

5.3.1、定义员工类(抽象类)

public abstract class Employee {
	private String id;// 员工编号
	private String name; // 员工姓名

	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	//工作方法(抽象方法)
	public abstract void work(); 
}

5.3.2、研发部员工类

public abstract class Developer extends Employee {
    
}

5.3.3、维护部员工类

public abstract class Maintainer extends Employee {
    
}

5.3.4、定义JavaEE工程师

public class JavaEE extends Developer {
	@Override
	public void work() {
		System.out.println("员工号为" + getId() + "的" + getName() + "员工,正在研发淘宝网站");
    }
}

5.3.5、Android工程师

public class Android extends Developer {
	@Override
	public void work() {
		System.out.println("员工号为"+ getId() +"的"+ getName()+"员工,正在研发淘宝手机客户端");
	}
}

5.3.5、Network网络维护工程师

public class Network extends Maintainer {
	@Override
	public void work() {
		System.out.println("员工号为"+ getId() +"的"+ getName() + "员工,正在检查网络是否畅通");
	}
}

5.3.6、Hardware硬件维护工程师

public class Hardware extends Maintainer {
	@Override
	public void work() {
		System.out.println("员工号为" + getId() + "的" + getName() + "员工,正在修复打印机");
	}
}

5.3.7、测试类

​ 创建JavaEE工程师对象,完成工作方法的调用。

public class Test {
	public static void main(String[] args) {
		//创建JavaEE工程师员工对象
		JavaEE ee = new JavaEE();
		//设置该员工的编号
		ee.setId("000015");
		//设置该员工的姓名
		ee.setName("小明");
		//调用该员工的工作方法
		ee.work();
	}
}

标签:06,构造方法,继承,子类,void,class,面向对象,父类,public
From: https://www.cnblogs.com/rito-blog/p/16929205.html

相关文章

  • 05-面向对象_封装
    typora-root-url:images一、编程思想1.1、什么是思想​ 何谓编程思想?首先解释一下“思想”。​ 先问你个问题:你想做个怎样的人?​ 可能你会回答:我想做个好人,孝敬......
  • TypeScript继承
    继承  * -使用继承后,子类将会拥有父类所有的方法和属性  * -通过继承可以将多个类中共有的代码写在一个父类中,  *   这样只需要写一次即可......
  • 实验5 继承和多态
    task4Pets.h#pragmaonce#include<iostream>#include<string>usingnamespacestd;classMachinePets{public:MachinePets(conststrings);virtual......
  • 面向对象的JavaScript框架 MooTools
    MooTools是一个简洁,模块化,面向对象的JavaScript框架。它能够帮助你更快,更简单地编写可扩展和兼容性强的JavaScript代码。Mootools从Prototype.js中汲取了许多有益的设计理......
  • jquery006-点赞
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title><style>.click{color:darkred;positio......
  • <一>继承的基本意义
    1:继承的本质和原理2:派生类的构造过程3:重载,覆盖,隐藏4:静态绑定和动态绑定5:多态,vfptr,vftable6:抽象类的设计原理7:多重继承以及问题8:虚基类vbptr和vbtable......
  • 06-1-2大屏实时计算技术的深度剖析【双元】(1) flink写入es和kafka ---(2)flink案例代
                                                        ......
  • C++:类继承知识回顾
    概述  在实际代码开发中,我们通常不会去开发最底层,而是成为“调库侠”。面对众多类库,我们需要掌握基本库的用法,比如string、valarray、iostream、any等,本白在开发capl测......
  • Java基础——继承(Extends)
    使用extends(继承)有什么好处?使用继承可以实现代码的重用,通过继承,子类可以继承父类的成员变量及成员方法,同时子类也可以定义自己的成员变量和成员方法。届时,子类将具有父类......
  • 实验5 继承和多态
    task4:pets.hpp1#pragmaonce2#include<iostream>3#include<string>4usingnamespacestd;56//定义基类,机器宠物类MachinePets7classMachinePe......