黑马程序员___Java基础[04-继承和多态]

时间:2021-12-22 12:00:10

一、继承的概述

1、继承(extends):

A:多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。

B:多个类可以称为子类,单独这个类称为父类或者超类。

C:通过 extends 关键字让类与类之间产生继承关系。

• class SubDemo extends Demo{}

2、继承的体系结构:就是对要描述的事物进行不断的向上抽取,就出现了体系结构。

A:要了解这个体系结构中最共性的内容,就看最顶层的类。

B:要使用这个体系的功能,就用最底层的类创建对象。

3、继承的好处:

A:继承的出现,提高了代码的复用性。

B:继承的出现,让类与类之间产生了关系,extends来表示,这个关系的出现,提供了多态的前提。

4、继承的特点

A:Java只支持单继承,不支持多继承(其实确切的说是java对多继承进行了优化,避免了安全问题)。

• 一个类只能有一个父类,不可以有多个父类。

• class SubDemo extends Demo{} //ok

• class SubDemo extends Demo1,Demo2...//error

B:Java支持多层继承(继承体系)

• class A{}

• class B extends A{}

• class C extends B{}

5、定义继承需要注意:

• 子类可以直接访问父类中的非私有的属性和行为

• 不要仅为了获取其他类中某个功能或部分功能而去继承。

• 类与类之间要有所属( " is a " )关系,xx1是xx2的一种。

如何判断A和B是否有继承关系?

A如果继承B,那么就可以说A是B的一种。

二、super关键字

A:super和this的含义:

this代表本类对象的引用。

super代表本类对象父类的引用。

B:super和this的用法:

this可以用于区*部变量和成员变量同名的情况。

super可以用于区分子类和父类成员变量同名的情况。

注:一般,子类中不会出现和父类同名的成员变量。

C:子类要调用父类构造函数时,可以使用super语句。

三、继承后子父类之间成员的关系

1、成员变量

 1 class Fu {
 2     int num1 = 5;
 3 }
 4 
 5 class Zi extends Fu {
 6     int num1 = 20;
 7     public void show() {
 8         int num1 = 30;
 9         System.out.println("num1:"+num1);
10         //当局部变量和成员变量重名的时候用this来区分
11         System.out.println("this num1:"+this.num1);
12         //当子类和父类出现了同名变量,用super来区分
13         System.out.println("father num1:"+super.num1);
14     }
15 }

总结:

A:在一个类中,如果方法中的局部变量和方法外的成员变量重名:

• 那么如果在方法内输出这变量,就是方法自己的变量里的值;

• 想要区分要用this,加上this就是输出成员变量的值。

B:在子父类中,如果出现成员变量重名的时候:

• 在子类输出会输出自己的变量里的值,

• 想要区分要用super,加上super,就是输出父类里变量的值。

2、成员方法

 1 class Fu {
 2     public void show() {
 3         System.out.println("fu show");
 4     }
 5     public void method() {}
 6 }
 7 
 8 class Zi extends Fu{
 9     /*public void show(){
10         System.out.println("zi show");
11     }*/
12     public void show(){
13         System.out.println("zi show1");
14     }
15 }

总结:

A:子类中存在和父类成员方法相同的这种现象,叫做重写,复写,覆盖。

B:重写(override)和重载(overload)的区别:

• 重载的特点:

在同一类中。

方法名相同,参数列表不同(与返回值类型无关)。

• 重写的特点:

要有继承关系,在子父类中。

方法的声明相同(方法名和参数列表和返回值类型都相同)。

C:覆盖注意事项:

• 父类中的私有方法不可以被覆盖。

• 覆盖时,子类方法权限一定要大于等于父类方法权限;

• 静态只能覆盖静态。

D:覆盖的应用:

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

• 在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取。

3、构造方法

 1 class Fu{
 2     //Fu(){}
 3     Fu(int age){
 4         System.out.println("father age:"+age);
 5     }
 6 }
 7 
 8 class Zi extends Fu{
 9     Zi(){
10         this(40);
11         System.out.println("son");
12     }
13 
14     Zi(int age){
15         super(age);
16         System.out.println("son age:"+age);
17     }
18 }
19 
20 //Zi z = new Zi();  
21 Zi z = new Zi(30);

总结:

• 子类中所有的构造方法默认都会访问父类中空参数的构造方法,因为每一个构造方法的第一行都有一条默认的语句super();

• 当父类中没有空参数的构造方法时,子类的构造函数必须通过this或者super语句指定要访问的构造方法,或者手动提供无参构造方法。

this(...):调用本类中的构造方法

super(...):调用父类中的构造方法

• 构造方法用于创建对象,并进行初始化。建议如果你写了有参的构造函数,也要把空参的构造函数再手动加上。

否则你定义了有参的构造函数,空参的构造函数系统就不会再给了。这时候如果出现继承,往往会出错。

这样创建子类对象的时候就会报错Zi zi = new Zi(); //这句话是会去找空参的构造函数   

四、子类的实例化过程

A:子类中所有的构造函数默认都会访问父类中空参数的构造函数。

因为每一个构造函数的第一行都有一条默认的语句super();

子类会具备父类中的数据,所以要先明确父类是如何对这些数据初始化的。

B:当父类中没有空参数的构造函数时,子类的构造函数必须通过this或者super语句指定要访问的构造函数。

五、final关键字 

1、final可以用来修饰什么呢?

A:final可以用来修饰类:被fainl修饰的类不能被继承。

B:final可以用来修饰成员方法:被final修饰的成员方法不能被重写。

C:final可以用来修饰变量:被final修饰的变量为常量,值不能被修改(即只能被赋值一次)。

常量的命名规范:要求大写。

final int PI = 3.14;

注意: 内部类只能访问被final修饰的局部变量(内部类讲)。

2、final必须声明的时候就赋值吗?

一般来说,是这样的。但是特殊情况:在构造方法可以给final修饰的变量赋值。

六、抽象类

1、抽象类概述

A:抽象定义:

• 抽象就是从多个事物中将共性的,本质的内容抽取出来。

• 例如:狼和狗共性都是犬科,犬科就是抽象出来的概念。

B:抽象类和抽象方法:

• Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。

• 没有方法体的方法称为抽象方法:

抽象方法只有方法声明,没有方法体,定义在抽象类中。

格式:修饰符 abstract 返回值类型   函数名(参数列表);

• 抽象类和抽象方法必须用 abstract 关键字来修饰。

C:抽象方法的由来:

• 多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,

   那么只有功能声明,没有功能主体的方法称为抽象方法。

• 例如:狼和狗都有吼叫的方法,可是吼叫内容是不一样的。所以抽象出来的犬科虽然有吼叫功能,但是并不明确吼叫的细节。

2、抽象类的特点

1)抽象类的特点

A:一个类如果有了抽象方法,那么这个类必须是抽象类;但抽象类里边可以没有抽象方法。

B:抽象类是不能够被实例化的,不可以用new创建对象的。原因如下:

• 抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。例如:犬科是一个抽象的概念,真正存在的是狼和狗。

• 而且抽象类即使创建了对象,调用抽象方法也没有意义。

C:抽象类通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,否则该子类也是抽象类。

2)抽象类的成员特点:

A:成员变量:子类可以直接继承抽象类中的成员变量。(抽象类中的成员变量可以和以前是一样的)

B:成员方法:抽象类中分为两种方法,

一种是抽象方法,这种方法在子类中需要被实现;

一种是普通的方法,可以被子类直接继承使用。

C:构造方法:抽象类不能被实例化,

那么它有构造方法吗?抽象类是class,那么它就有构造方法。

它的构造方法有用吗?有,为了让子类实例化的时候使用。

3、抽象类举例代码讲解

雇员示例:

• 需求:公司中程序员有姓名,工号,薪水,工作内容。

• 项目经理除了有姓名,工号,薪水,还有奖金,工作内容。

• 对给出需求进行数据建模。

4、抽象类相关问题

A:抽象类中是否有构造函数?抽象类是class,那么它就有构造方法。它的构造方法有用吗?有,为了让子类实例化的时候使用。

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

• private

私有的,外部直接无法访问。

• static

那么这个时候抽象方法就可以可以通过类名调用,但是这样是没有意义的。

• final

final修饰的方法不能被重写。所以它和abstract冲突。

C:抽象类中可不可以没有抽象方法?

可以。如果这么做只有一个目的不让你创建这个类的对象

七、接口

1、接口

1)格式:

interface {}

2)解决了java中只能单继承的问题(对多继承进行了优化)。

接口的出现将“多继承”通过另一种形式体现出来,即“多实现”。

A:类与类之间是继承关系:只能是单继承。 extends

B:接口与接口之间是继承关系:可以是单继承,也可以是多继承。 extends

C:类与接口之间是实现关系:可以是单实现,也可以是多实现。 implements

2、接口的特点

1)成员特点:接口中的成员修饰符是固定的。

A:只有成员变量和成员方法。

B:成员变量默认修饰符 public static final

int X = 20;

其实是这样的 public static final int X = 20;

C:成员方法 默认修饰符 public abstract

void show();

其实是这样的 public abstract void show();

注意:接口中的成员都是public的。

建议:为了便于阅读,自己手动加上修饰符。

2)接口特点:

A:接口是对外暴露的规则

B:接口是功能的扩展

C:接口降低了程序的耦合性。

内聚(自己实现功能的能力)

高内聚,低耦合。

举例:主板和CPU,USB接口,电源插座。

D:扩展说了下接口的理解

狭义的理解就是java中的接口。

广义的理解就是:任何定义的规范都是接口。

3、接口与抽象类

A:抽象类体现继承关系,一个类只能单继承;接口体现实现关系,一个类可以多实现。

B:抽象类中可以定义非抽象方法,供子类直接使用;接口的方法都是抽象,接口中的成员都有固定修饰符。

抽象类中的成员:

成员变量:可以是常量,也可以是变量。

成员方法:可以是抽象的,也可以是非抽象的。

构造方法:虽然不可以创建对象,但是可以给子类实例化用。

接口中的成员:

成员变量:只能是常量。默认修饰符 public static final

成员方法:只能是抽象的。默认修饰符 public abstract

C:抽象类中定义的是体系结构中的共性的内容;接口中定义的是对象的扩展功能。

D:抽象类被继承表示的是:"is a"的关系。xx是yy中的一种。

接口被实现表示的是: "like a"的关系。xx像yy中的一种。

4、接口的应用举例

原理:类可以继承一个类的同时实现多个接口。

学生:Student

A:属性:学号,姓名,年龄

B:方法:学习(study),吃饭(抽象eat),抽烟(是不是所有的学员都抽烟呢?),篮球(是不是所有的人都会打篮球呢?)

分析:学员都具备学习的行为和吃饭的行为,但是并不是所有的学员都抽烟,也不是所有的学员都打篮球。

interface Smoking {

public abstract void smoking();

}

interface Sport{

public abstract void playBasketBall();

}

描述即会抽烟又会打篮球的学生:SmokeStudent extends Student implements Smoking,Sport

一个类只能继承一个类,但是可以实现多个接口,每实现一个接口,功能就扩展了一部分

SmokeStudent ss = new SmokeStudent();

ss.eat();

ss.study();

ss.smoking();

ss.playBasketBall();

八、多态

1、多态的定义

1)定义:某一类事物的多种存在形态。

例:动物中猫,狗。

猫这个对象对应的类型是猫类型

• 猫 x = new 猫();

同时猫也是动物中的一种,也可以把猫称为动物。

• 动物  y = new 猫();

• 动物是猫和狗具体事物中抽取出来的父类型。

• 父类型引用指向了子类对象。

2)程序中体现:父类或者接口的引用指向或者接收自己的子类对象。

3)好处和作用:多态的存在提高了程序的扩展性和后期可维护性

多态思想:可以指挥同一类型的一批对象做事情。多态的出现让我们复杂的问题简单化了。

A:Animal Cat、Dog

method(Animal a){a.eat();}//Animal a = new Cat();

2、多态的前提

A:类与类(或接口)要有继承(或实现)关系。

B:一定要有方法的重写(即要有覆盖操作)。

C:一定要有父类(或者接口)的引用指向子类的对象。

Person p = new SuperMan();

SuperMan sm = (SuperMan)p;

3、多态中成员的特点

1)对于名字相同的:

Fu f = new Zi();

A:成员变量:编译和运行都看Fu。

B:非静态方法:编译看Fu,运行看Zi。

C:静态方法:编译和运行都看Fu。

2)要调用子类特有的内容,需要向下转型。

总结:无论是向上转型还是向下转型,变化的都是子类对象,绝对不能把父类对象强转为子类类型。