第二章:继承和多态
第一节:继承
1.1:继承的概述
面向对象的三大特征:封装性、继承性、多态性
继承是多态的前提,如果没有继承,就没有多态
继承主要解决的问题是:共性抽取
**父类:**也可以叫做基类、超类
**子类:**也可以叫做派生类
继承关系中的特点:
1、子类可以拥有父类的内容
2、子类还可以拥有自己专有的新内容
1.2:继承的格式
在继承的关系中,子类就是一个父类。(子类可以被当做父类看待)
定义父类的格式:
public class 父类名称{
//...
}
定义子类的格式:
public class 子类名称 extends 父类名称{
//...
}
1.3:继承中成员变量的访问特点
在父子类的继承关系中,如果成员名变量重名,则创建子类对象时,访问有两种方式:
1、直接通过子类对象访问成员变量:等号左边是谁,就优先用谁,没有则向上找
2、间接通过成员方法访问成员变量:该方法属于谁,就优先用谁,没有则向上找
1.4:区分子类方法中重名的三种情况
局部变量: 直接写成员变量名
本类的成员变量: this.成员变量名
父类的成员变量: super.成员变量名
1.5:继承中成员方法的访问特点
在父子类的继承关系中,创建子类对象,访问成员方法的规则:
创建的对象是谁,就优先用谁,如果没有就向上找
注意:无论是成员方法还是成员变量,如果没有都是向上找父类,绝不会向下找子类
1.6:方法的覆盖重写
**重写概念:**在继承关系中,方法的名称一样,参数列表也一样
重写(Override):方法的名称一样,参数列表也一样(覆盖、覆写)
重载(Overload):方法的名称一样,参数列表不一样
**方法的覆盖重写特点:**创建的是子类对象,则优先用子类方法
注意:
1、必须保证父子类之间的方法名称相同,参数列表也相同
@Override,写在方法前面,用来检测是否是有效的方法覆盖重写
这个注释就算不写,只要满足要求,也是正确的方法覆盖重写
2、子类方法的返回值必须小于等于父类方法的返回值范围
3、子类方法的权限必须大于等于父类方法的权限修饰符
权限比较:public>protected>(default)>private
**(default)不是关键字,而是什么都不写**
**设计原则:**对于已经投入使用的类,尽量不要进行修改,推荐定义一个新的类,来重复利用其中共性 内容,并且添加改动新内容
1.7:继承中构造方法的访问特点
继承关系中,父子类构造方法的访问特点:
1、子类构造方法中有一个默认隐含的"super()"调用,所以一定是先调用的父类构造,后执行的子类构造
2、子类构造可以通过super关键字来调用父类重载构造
3、super的父类构造调用,必须是子类构造方法的第一个语句,不能一个子类构造调用多次super构造
总结:
子类必须调用父类构造方法,不写则赠送super(),写了则用写的指定的super调用,super只能有1个,还必须是第一个
1.8:关键字super和this的用法
super关键字的用法:
1、在子类的成员方法中,访问父类的成员变量
public void methodzi(){
System.out.println(super.num);//父类中的num
}
2、在子类的成员方法中,访问父类的方法
public void methodzi(){
super.method();//访问父类中的method
}
3、在子类的构造方法中,访问父类的构造方法
public zi(){
super();//可以不写
}
this关键字的用法:
super关键字用来访问父类内容,而this关键字用来访问本类内容
this关键字的用法:
1、在本类的成员方法中,访问本类的成员变量
int num=20;
public void shownum(){
int num=10;
System.out.println(num);//局部变量
System.out.println(this.num);//本类中的成员变量
System.out.println(super.num);//父类中的成员变量
}
2、在本类的成员方法中,访问本类的另一个成员方法
public void methodA(){
System.out.println("AAA");
}
public void methodB(){
this.methodA();
System.out.println("BBB");
}
3、在本类的构造方法中,访问本类的另一个构造方法
注意:A this(…)调用也必须是构造方法的第一个语句,唯一一个
B super和this两种构造调用,不能同时使用
public zi(){
this(123);//本类的无参构造,调用本类的有参构造
}
public zi(int n){
}
this和super关键字图解:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m7oCdNHl-1611640295969)(D:/学习/Java/图片/image-20201122105420796.png)]
1.9:Java继承的三个特点
**1、Java语言是单继承的:**一个类的直接父类只能有唯一一个
2、Java语言可以多级继承
3、一个子类的直接父类是唯一的,但是一个父类可以拥有很多个子类
第二节:抽象类
2.1:抽象的概念
如果父类当中的方法不确定如何进行{}方法体实现,那么这就应该是一个抽象方法
2.2:抽象的方法和抽象的类的格式
**抽象方法:**加上abstract关键字,然后去掉大括号,直接分号结束
**抽象类:**抽象方法所在的类,必须是抽象类才行。在class之前写上abstract即可
public abstract class Animal{
public abstract void eat();
}
2.3:抽象方法和抽象类的使用
如何使用抽象类和抽象方法:
1、不能直接创建new抽象类对象
2、必须用一个子类来继承抽象父类
3、子类必须覆盖重写抽象父类中所有的抽象方法
4、创建子类对象进行使用
**覆盖重写(实现):**去掉抽象方法的abstract关键字,然后补上方法体大括号
2.4:抽象方法和抽象类的注意事项
1、抽象类不能创建对象,只能创建其非抽象子类的对象
2、抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的
3、抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类
4、抽象类的子类,必须重写抽象父类中所有的抽象方法,除非子类是抽象类
第三节:接口
3.1 接口概念
**接口:**一种公共的规范标准
只要符合规范标准,大家就可以通用
接口是多个类的公共规范,是一种引用数据类型,最重要的内容就是其中的抽象方法
3.2 接口的定义基本格式
定义接口的格式:
public interface 接口名称{
//接口内容
}
**备注:**换成了关键字interface之后,编译生成的字节码文件依然是.java–>.class
如果是java 7,那么接口中可以包含的内容有:
1、常量
2、抽象方法
如果是java 8,还可以额外包含有:
3、默认方法
4、静态方法
如果是java 9,还可以额外包含有:
5、私有方法
3.3 接口的抽象方法定义
在任何版本的Java中,接口都能定义抽象方法
格式:
public abstract 返回值类型 方法名称(参数列表);
abstract void 方法名称(参数列表);
public void 方法名称(参数列表);
void 方法名称(参数列表);
注意:
1、接口中的抽象方法,修饰符必须是两个固定的关键字:public abstract
2、这两个关键字修饰符,可以选择性的忽略
3、方法的三要素可以随意定义
3.4 接口的使用步骤
1、接口不能直接使用,必须有一个“实现类”来“实现”该接口
格式:
public class 实现类名称 implements 接口名称{
//...
}
2、接口的实现类必须覆盖重写(实现)接口中所有的抽象方法
实现:去掉abstract关键字,加上方法体大括号
3、创建实现类的对象,进行使用
注意:如果实现类没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类
3.5 接口的默认方法
从Java 8开始,接口里允许定义默认方法
格式:
public default 返回值类型 方法名称(参数列表){
方法体
}
备注:接口中的默认方法可以解决接口升级问题
总结:
1、接口的默认方法,可以通过接口实现类对象直接调用
2、接口的默认方法,也可以被接口实现类进行覆盖重写
3.6 接口的静态方法
从Java 8开始,接口中允许定义静态方法
格式:
public static 返回值类型 方法名称(参数列表){
方法体
}
提示:就是将abstract或者default换成static即可,带上方法体
注意:不能通过接口实现类的对象来调用接口当中的静态方法
正确用法:通过接口名称,直接调用其中的静态方法
格式:
接口名称.静态方法名(参数);
3.7 接口的私有方法
需求:我们需要抽取一个共有方法,用来解决两个默认方法之间重复代码的问题,但是这个公共方法不应该让实现类使用,应该是私有化的
从Java9 开始,接口当中允许定义私有方法
私有方法的种类:
1、普通私有方法:解决多个默认方法之间重复代码问题
格式:
private 返回值类型 方法名称(参数列表){
方法体
}
2、静态私有方法:解决多个静态方法之间重复代码问题
格式:
private static 返回值类型 方法名称(参数列表){
方法体
}
3.8 接口的常量定义和使用
接口当中也可以定义“成员变量”,但是必须使用public static final三个关键字进行修饰
从效果上看,这其实就是接口的**【常量】**
格式:
public static final 数据类型 常量名称=数据值;
都能用 能.出来 不可变
例:public static final int num=10;//这其实就是一个常量,不可以修改
注意:
1、接口当中的常量,可以省略public static final,不写也照样是这样
2、接口当中的常量,必须进行赋值,不能不赋值
3、接口当中常量的名称,使用完全大写的字母,用下划线进行分隔(推荐命名规则)
3.9 内容小结
在Java 9+版本中,接口的内容可以有:
1、成员变量其实是常量,格式:
[public] [static] [final] 数据类型 常量名称=数据值;
注意:常量必须进行赋值,而且一旦赋值不能改变,变量名称完全大写,用下划线进行分隔
2、接口中最重要的就是抽象方法,格式:
[public] [abstract] 返回值类型 方法名称(参数列表);
注意:实现类必须覆盖重写接口中所有的抽象方法,除非实现类是抽象类
3、从Java 8开始,接口里允许定义默认方法,格式:
public default 返回值类型 方法名称(参数列表){
方法体
}
注意:默认方法也可以被覆盖重写
4、从Java 8开始,接口里允许定义静态方法,格式:
[public] static 返回值类型 方法名称(参数列表){
方法体
}
注意:应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法
5、从Java 9开始,接口里允许定义私有方法,格式:
普通私有方法:
private 返回值类型 方法名称(参数列表){
方法体
}
静态私有方法:
private static 返回值类型 方法名称(参数列表){
方法体
}
注意:private的方法只有接口自己才能调用,不能被实现类或别人使用
使用接口的时候,需要注意:
1、接口是没有静态代码块或构建方法的
2、一个类的直接父类是唯一的,但是一个类可以同时实现多个接口
格式:
public class MyInterfaceImp implments MyInterfaceA,MyInterfaceB{
//覆盖重写所有抽象方法
}
3、如果实现类所实现的多个接口中,存在重复的抽象方法,那么只需要覆盖重写一次即可
4、如果实现类没有覆盖重写所有接口中的所有抽象方法,那么实现类就必须是抽象类
5、如果实现类所实现的多个接口中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写
6、一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,会优先用父类中的方法
3.10 接口之间的多继承
1、类与类之间是单继承的,直接父类只有一个
2、类与接口之间是多实现的,一个类可以实现多个接口
3、接口与接口之间是多继承的
注意:
1、多个父接口中的抽象方法如果重复,没关系
2、多个父接口中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,【而且带着default关键字】
第四节:多态
4.1 概述:
面向对象三大特征:封装性、继承性、多态性
extends继承或者implements实现,是多态性的前提
**对象的多态性:**一个对象拥有多种形态
4.2 多态的使用和说明
代码当中体现多态性,其实就是一句话:父类引用指向子类对象
格式:
父类名称 对象名=new 子类名称();
或者:
接口名称 对象名=new 实现类名称();
访问成员变量的两种方式:
1、直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找
2、间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找
第五节:final关键字
5.1 概述
final:最终不可改变的
常见四种用法:
1、可以用来修饰一个类
2、还以用来修饰一个方法
3、还可以用来修饰一个局部变量
4、还可以用来修饰一个成员变量
5.2 final关键字用来修饰一个类
当final关键字用来修饰一个类时,格式:
public final class 类名称{
//...
}
**含义:**当前这个类不能有任何的子类,即不能使用final类作为父类
注意:一个类如果是final类,那么其中所有的成员方法都无法进行覆盖重写
5.3 final关键字用于修饰成员方法
当final关键字用来修饰一个方法的时候,这个方法就是最终方法,也就是不能被覆盖重写
格式:
修饰符 final 返回值类型 方法名称(参数列表){
//方法体
}
注意:对于类、方法来说,abstract关键字和final关键字不能同时使用,因为矛盾
5.4 final关键字用于修饰局部变量
一旦使用final用来修饰局部变量,那么这个变量就不能进行修改,一次赋值,终生不变
final int num=200;//不可再改变num的值了
final int numb;
numb=100;//正确写法,只要保证只有唯一一次赋值即可
不变:对于基本类型来说,不可改变的是变量当中的数据,对于引用类型来说,不可变的是变量中的地址值,但其中的数据可以改变
final Student stu2=new Student("aa");
stu2=new Student("bb");//错误写法,final的引用类型变量,其中的地址不可改变
5.5final关键字用于修饰成员变量
对于成员变量来说,如果使用final关键字修饰,那么这个变量也照样是不可变的
1、由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了
2、对于final的成员变量,要么进行直接赋值,要么进行构造方法赋值(二者选其一)
3、必须保证类中所有重载的构造方法,都最终会对final的成员变量进行赋值
第六节:四种权限修饰符
四种权限修饰符: public>protect>(default)>private
同一个类(我自己) YES YES YES YES
同一个包(我邻居) YES YES YES NO
不同包子类(我儿子) YES YES NO NO
不同包非子类(陌生人) YES NO NO NO
第七节:内部类
7.1 内部类的概念与分类
如果一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类
分类:
1、成员内部类
2、局部内部类(包含匿名内部类)
7.2 成员内部类
定义格式:
修饰符 class 外部类名称{
修饰符 class 内部类名称{
/...
}
//...
}
注意:内用外,随意访问;外用内,需要借助内部类对象
如何使用:
**1、间接方式:**在外部类的方法当中使用内部类,然后main只是调用外部类的方法
Body body=new Body();
body.methodBdy();
**2、直接方式:**公式:外部类名称.内部类名称 对象名=new 外部类名称().new 内部类名称();
Body.Heart heart=new Body().new Heart();
heart.beat();
内部类的同名变量访问:
如果出现了重名现象,那么格式是:
外部类名称.this.外部类成员变量名
public class Outer{
int num=10;
public class Inner{
int num=20;
public void methodInter(){
int num=30;//内部类方法的局部变量
System.out.println(num);//局部变量,就近原则
System.out.println(this.num);//内部类的成员变量
System.out.println(Outer.this.num);//外部类的成员变量
}
}
}
7.4 局部内部类
如果一个类是定义在一个方法内部的,那么这就是一个局部内部类
**“局部”**只有当前所属方法才能使用它,出了这个方法外面就不能用了
定义格式:
修饰符 class 外部类名称{
修饰符 返回值类型 外部类方法名称(参数列表){
class 局部内部类名称{
//...
}
}
}
定义一个类时,权限修饰符规则:
1、外部类:public/(default)
2、成员内部类:public/protected/(default)/private
3、局部内部类:什么都不能写
注意:局部内部类如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】
备注:从Java 8+开始,只要局部变量不变,那么关键字final关键字可以省略
原因:
1、new出来的对象在堆内存当中
2、局部变量是跟着方法走的,在栈内存当中
3、方法运行结束之后立刻出栈,局部变量就会立刻消失
4、new出来的对象会在堆内存中持续存在,知道垃圾回收消失
7.5 匿名内部类
如果接口的实现类(或者父类的子类)只需要使用唯一的一次,那么这种情况下就可以省略该类的定义,而改写为使用**【匿名内部类】**
匿名内部类的定义:
格式:
接口名称 对象名=new 接口名称(){
//覆盖重写所有抽象方法
};
**对格式进行解析:**new 接口名称(){…}
1、new代表创建对象的动作
2、接口名称就是匿名内部类需要实现哪个接口
3、{…}这才是匿名内部类的内容
注意:
1、匿名内部类,在【创建对象】时,只能使用唯一一次。
如果希望多次创建对象,而且类的内容一样,那么必须使用单独定义的实现类
2、匿名对象,在【调用方法】的时候,只能调用唯一一次
如果希望同一个对象调用多次方法,那么就必须给对象起个名字
3、匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略【对象名称】
强调:匿名内部类和匿名对象不是一回事