第一章 Java语言的三个特点
第二章 抽象方法和抽象类
2.1 概述:
当一个类应该有一个方法来实现某个功能,但是却不知道如何实现的时候,可以定义一个抽象方法,以此来表示,该类具有此功能,只是不知道该如何实现吗,而这个类叫做抽象类。而它的子类一定也会继承该抽象方法,代表它的子类也具有该功能。
生活联想: 想一下动物作为抽象类,它一定要吃东西,但是不知道吃什么,吃就是抽象方法
此时,它的子类要么重写父类所有的抽象方法,使其具体化(写清楚,这个子类该如何实现该功能),变为普通类。要么,它的子类不重写父类所有的抽象方法,则该子类会继承抽象父类中那些没有被重写了的抽象方法,但是该类也不知道该如何实现,而抽象方法所在的类必须是抽象类所以,所以这个子类也是抽象类。
生活联想: 想一下动物的子类,有脊椎动物和无脊椎动物、这两个子类也都要吃东西,但是同样无法具体到具体吃什么,所以这两个子类虽然继承了“吃”这个抽象方法,但是无法重写,使其具体,因此,这两个也是抽象类。
想一下动物的子类,猫。猫这个子类很显然,我们知道它具体吃什么,老鼠或者鱼干等等,因此我们可以把它继承来的“吃”这个抽象方法给具体化,从而使其变成一个普通类。
2.2 抽象方法的定义格式
抽象方法定义格式: 修饰符 abstract 返回值类型 方法名称(参数列表);
注意: 后面所学接口当中的抽象方法在定义时候,可以省略修饰符、abstract,但是抽象类当中定义抽象方法时候则不能省略
抽象类定义格式: 抽象方法所在的类,必须是抽象类才行,在class之前写上abstract即可
注意: 抽象方法所在的类一定是抽象类,但是抽象类不一定包括抽象方法
比如:
定义一个父类动物,动物一定要吃东西,但是具体吃什么要根据每种动物来确定,因此,动物这个类当中一定有一个方法叫“吃”,但是没法写方法体。此时,动物类的“吃”这个方法就是抽象方法。
public abstract class Animal{
// 这是一个抽象方法
public abstract void eat();
// 这是一个普通方法
public void normalMethod(){
}
}
2.2 抽象方法和抽象类的使用
如何使用抽象类和抽象方法:
1.不能直接创建new抽象类对象。
哪里有一个对象是动物的?即使创建了动物的实例对象,这也是没有意义的。不难理解,方法都是抽象的,理应抽象类应该没有具体对象
2.必须用一个子类来继承抽象父类。(注意前面所讲,子类就是一个父类)
3.子类必须覆盖重写抽象父类当中所有的抽象方法
覆盖重写(实现): 子类去掉抽象方法的abstract关键字,然后补上方法体大括号
4.创建子类对象进行使用
// 定义抽象类Animal作为父类
public abstract class Animal{
// 这是一个抽象方法,代表吃东西,但是具体吃什么(大括号内的内容,也就是方法体)不确定。
public abstract void eat();
// 这是普通的成员方法。
public void normalMethod(){
}
// 定义子类Cat继承抽象父类Animal
public class Cat extends Animal{
@Override
public void eat(){
System.out.println("猫吃鱼");
}
}
// 定义测试类
public class DemoMain {
public static void main(String[] args) {
Cat cat = new Cat();
cat.eat();
}
}
2.3 抽象方法和抽象类的注意事项
- 1.抽象类不能创建对象,如果创建,编译无法通过而报错,只能创建其非抽象子类的对象
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义 - 2.抽象类中,可以有构造方法,是供子类创建对象时候,初始化父类成员使用的。
理解:抽象父类是无法通过new来实现其构造方法的,然而子类的构造方法当中有默认的super(),会访问父类的构造方法。
代码如下:
// 定义抽象父类
public abstract Fu{
// 定义构造方法
public Fu(){
System.out.println("抽象父类构造方法执行!");
}
// 定义抽象方法
public abstract void eat();
}
// 定义普通子类
public class Zi{
// 定义构造方法
public Zi{
// 调用父类构造方法(构造方法一定是不能被继承的,只能被子类调用)
super();
System.out.println("子类构造方法执行!");
}
// 重写抽象方法
@Override
public void eat(){
System.out.println("吃饭饭");
}
}
-
3.抽象类当中,不一定包含抽象方法,但是抽象方法所在的类必须是抽象类
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计 -
4.抽象类的子类,必须要覆盖重写抽象父类当中所有的抽象方法,否则,编译无法通过而报错,除非该类也是抽象类。
理解:假设不重写所有的抽象法方法,则类当中可能包含抽象方法,那么创建对象以后,调用抽象的方法,则没有意义。
第三章 接口
3.1 接口概述:
简单理解,接口就是一种公共的规范标准,只要符合规范标准,大家就都可以使用。
生活联想: U盘、充电器、夜读灯等各种设备只要有USB类型的接口,就一定可以在任何有USB接口的电脑上使用,这种USB接口就相当于一个接口。
Java理解:接口就是多个类的公共规范。
接口是一种引用数据类型,其中最重要的内容就是其中的抽象方法。
3.2 接口的定义和基本使用
3.2.1 接口定义的基本格式
格式: public interface 接口名称{ }
**备注:**换成了关键字interface之后,编译生成的字节码文件仍然是:.java—>.class
如果是Java7,那么接口当中的内容可以包含:
1.常量
2.抽象方法
如果是Java8,还可以额外包含有:
3.默认方法
4.静态方法
如果是Java9,还可以额外包含有:
5.私有方法
3.2.2 接口的抽象方法定义
在任何版本的Java当中,接口都可以定义抽象方法:
格式:
public abstract 返回值类型 方法名称(参数列表);
注意:
1.接口当中的抽象方法,修饰符必须是固定的两个关键字public abstract
2.public和abstract这两个关键字修饰符,可以省略其中任何一个单词,也可以两个单词都省略,但是编译器依然会默认是public abstract
3.方法的三要素可以随意定义
// 定义一个接口
public Interface MyInterface{
// 这是一个抽象方法
public abstract void methodAbs1();
// 这也是一个抽象方法
abstract void methodAbs2();
// 这也是一个抽象方法
public void methodAbs3();
// 这也是一个抽象方法
void methodAbs4();
}
3.2.3 接口的抽象方法使用
接口使用步骤:
1.接口不能直接使用,必须有一个 “实现类”来“实现接口”
格式:
public class 实现类名称 implements 接口名称{
// ...
}
2.接口的实现类必须覆盖重写(实现)接口中所有的抽象方法
实现:去掉abstract关键字,再加上方法体大括号。
3.创建实现类的对象,进行使用
注意事项:
1.对比接口中的抽象方法,接口中的抽象方法格式中的public也可以省略,但是和接口中的抽象方法一样,即使省略了,这里编译器依然默认修饰符是public,而若写上也一定要写public,不能是default,protected,private等
2.如果实现类没有重写接口当中所有的抽象方法,那么实现类就好比继承了一些接口当中的抽象方法,此时这个所谓的接口实现类必须是一个抽象类。
// 定义一个接口
public Interface MyInterface{
// 这是一个抽象方法
public abstract void methodAbs1();
// 这也是一个抽象方法
abstract void methodAbs2();
// 这也是一个抽象方法
public void methodAbs3();
// 这也是一个抽象方法
void methodAbs4();
}
// 创建接口的实现类
public class MyInterfaceImpl implements MyInterface{
@Override
public void methodAbs1(){
System.out.println("这是第一个方法");
}
@Override
public void methodAbs2(){
System.out.println("这是第二个方法");
}
@Override
public void methodAbs3(){
System.out.println("这是第三个方法");
}
@Override
public void methodAbs4(){
System.out.println("这是第四个方法");
}
}
// 定义测试类
public class Demo01Interface{
public void main(Stingr[] args){
// 错误写法,不能直接new接口对象使用
// MyInterface impl= new MyInterface();
// 创建实现类的对象使用
MyInterfaceImpl impl= new MyInterfaceImpl();
impl.methodAbs1();
impl.methodAbs2();
}
}
3.2.4 接口的默认方法定义
从Java8开始,接口里允许定义默认方法
格式:
public default 返回值类型 (参数列表){
// 方法体
}
接口中默认方法的意义:解决接口升级的问题。
备注: 接口当中的默认方法会被实现类所继承
举例说明默认方法的意义如下:
// 定义接口
public interface MyInterface{
// 抽象方法
public abstract void methodAbs();
// 新添加了一个抽象方法
public abstract void methodAbs2();
}
// 定义实现类MyInterfaceDefaultA
public class MyInterfaceDefaultA implements MyInterface{
@Override
public void methodAbs(){
System.out.println("实现了抽象方法AA");
}
}
// 定义实现类MyInterfaceDefaultB
public class MyInterfaceDefaultB implements MyInterface{
@Override
public void methodAbs{
System.out.println("实现了抽象方法BBB");
}
}
在上面代码块中,编译器一定会报错,因为接口当中添加了新的抽象方法methodAbs2,但是已经投入使用的两个实现类MyInterfaceDefaultA 和MyInterfaceDefaultB当中都没有对这个新的抽象方法methodAbs2进行重写,而此时这两个实现类又不是抽象类,故会报错。而如果再在MyInterfaceDefaultA 和MyInterfaceDefaultB两个类当中重写methodAbs2,既浪费时间,也有可能会使代码产生bug。默认方法的出现,就是为了解决如上情况下的接口升级问题
3.2.5 接口的默认方法使用
用默认方法对3.2.3中所示问题代码块进行修复
// 定义接口
public interface MyInterface{
// 抽象方法
public abstract void methodAbs();
// 新添加了一个抽象方法
// public abstract void methodAbs2();
// 新添加的方法,改成默认方法
public default void methodDefault(){
System.out.println("这是新添加的默认方法");
}
}
// 定义实现类MyInterfaceDefaultA
public class MyInterfaceDefaultA implements MyInterface{
@Override
public void methodAbs(){
System.out.println("实现了抽象方法AA");
}
}
// 定义实现类MyInterfaceDefaultB
public class MyInterfaceDefaultB implements MyInterface{
@Override
public void methodAbs{
System.out.println("实现了抽象方法BBB");
}
}
// 定义测试类
public class Demo02Interface{
public static void main(String[] args){
// 创建了实例对象
MyInterfaceDefaultA a = new MyInterfaceDefaultA() ;
a.methodAbs(); // 调用抽象方法,实际运行的是右侧的实现类
// 调用默认方法,如果实现类当中没有,会向上查找接口的
a.methodDefault();
System.out.println("=================");
MyInterfaceDefaultB b = new MyInterfaceDefaultB() ;
b.methodAbs();
b.methodDefault();
}
}
注意:
1.接口的默认方法,可以通过接口的实现类对象,直接调用。
2.接口的默认方法,也可以被接口的实现类进行覆盖重写。
如:
// 定义接口
public interface MyInterface{
// 抽象方法
public abstract void methodAbs();
// 新添加了一个抽象方法
// public abstract void methodAbs2();
// 新添加的方法,改成默认方法
public default void methodDefault(){
System.out.println("这是新添加的默认方法");
}
}
// 定义实现类MyInterfaceDefaultA
public class MyInterfaceDefaultA implements MyInterface{
@Override
public void methodAbs(){
System.out.println("实现了抽象方法AA");
}
}
// 定义实现类MyInterfaceDefaultB
public class MyInterfaceDefaultB implements MyInterface{
@Override
public void methodAbs{
System.out.println("实现了抽象方法BBB");
}
// 重写接口当中的默认方法
public void methodDefault(){
System.out.println("实现类B覆盖重写了接口当中的默认方法")
}
}
// 定义测试类
public class Demo02Interface{
public static void main(String[] args){
// 创建了实例对象
MyInterfaceDefaultA a = new MyInterfaceDefaultA() ;
a.methodAbs(); // 调用抽象方法,实际运行的是右侧的实现类
// 调用默认方法,如果实现类当中没有,会向上查找接口的
a.methodDefault();
System.out.println("=================");
MyInterfaceDefaultB b = new MyInterfaceDefaultB() ;
b.methodAbs();
b.methodDefault(); // 实现类B覆盖重写了接口的默认方法
}
3.2.6 接口的静态方法定义
从Java 8开始,接口当中允许定义静态方法。
格式:
public static 返回值类型 方法名称(参数列表){
方法体
}
提示:就是将abstract或者default换成static即可,带上方法体
3.2.7 接口的静态方法使用
// 定义一个接口
public interface MyInterfaceStatic{
public static void methodStatic(){
System.out.println("这是接口的静态方法!");
}
}
// 定义一个实现类
public class MyInterfaceImpl implements MyInterfaceStatic{
}
public class Demo03Interface{
public static void main(Srtring[] args){
// 创建一个实现类对象
MyInterfaceImpl impl = new MyInterfaceImpl();
// 错误写法
// impl.methodSraruc
// 直接通过接口名称调用静态方法
MyInterfaceStatic.methodStatic();
}
}
注意事项:
1.不能通过接口实现类对象来调用接口当中的静态方法
2.正确用法是:通过接口名称.静态方法名(参数)
3.2.8 接口的私有方法(用来解决默认方法、静态方法之间代码重复的问题)
问题描述:
我们需要抽取一个公共方法,用来解决两个默认方法之间重复代码的问题,但是这个公共方法不应该让实现类使用,应该是私有化的。
解决方案:
从Java 9开始,接口中允许定义私有方法
1.普通私有方法,解决多个默认方法之间重复代码问题
格式:
private 返回值类型 方法名称(参数列表){
// 方法体
}
2.静态私有方法,解决多个静态方法之间重复代码问题
格式:
private static 返回值类型 方法名称(参数列表){
// 方法体
}
如:
普通私有方法
public interface MyInterfacePrivateA{
public deafult void methodDefault1(){
System.out.println("默认方法1");
methodCommon();
}
public deafult void methodDefault2(){
System.out.println("默认方法2");
methodCommon();
}
private deafult void methodCommon(){
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}
静态私有方法
public interface MyInterfacePrivateA{
public staticvoid methodStatic1(){
System.out.println("静态方法1");
methodStaticCommon();
}
public staticvoid methodStatic2(){
System.out.println("静态方法2");
methodStaticCommon();
}
private staticvoid methodStaticCommon(){
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}
3.2.9 接口的常量定义和使用
接口当中也可以定义“成员变量”,但是必须使用public static final三个关键字进行修饰。
从效果上看,这其实就是接口的【常量】
定义格式;
public static final 数据类型 常量名称 = 数据值;
使用格式:
接口名称.常量名称
注意:
1.一旦使用final关键字进行修饰,说明不可改变。
2.接口当中的常量,可以省略public static final,注意,即使不写,编译器也默认是这样的
3.接口当中的常量,必须进行赋值,不能不赋值。这不像成员变量,是不会自动赋上默认值的。
4.接口当中的常量名称,应该使用完全大写的字母,用下划线进行分割。
代码如下:
// 定义接口
public interface MyInterfaceConst{
// 这其实就是一个常量,一旦赋值,无法进行修改
public static final int NUM_OF_MY_CLASS= 10;
}
// 定义实现类
public class MyInterfaceConstImpl{
}
// 定义测试类
public class Demo05Interface{
public static void main(Stringp[] args){
System.out.println("MyInterface.num");
}
}
3.3 接口的内容小结
在Java 9+的版本中,接口的内容可以有:
下面笔记中其中带[]部分意思是可以省略不写,编译器默认为[]中的值
1.成员变量其实是常量,格式:
[public] [static] [final] 数据类型 常量名称 = 数据值;
注意:
常量必须进行赋值,而且一旦赋值将无法再进行改变
常量命名应该全用大写字母表示,单词之间用下划线进行分割
2.接口中最重要的就是抽象方法,格式:
[public] [abstract] 返回值类型 方法名称(参数列表);
注意:实现类必须覆盖重写接口当中所有的抽象方法,否则实现类必须是抽象类
3.从Java8开始,接口允许定义默认方法,格式:
[public] default 返回值类型 方法名称(参数列表){
方法体
}
注意:默认方法也也会被实现类继承,同时也可以被实现类覆盖重写,
4.从Java 8开始,接口里允许定义静态方法,格式:
[public] static 返回值类型 方法名称(参数列表){
方法体
}
5.从Java 9开始,接口里允许定义私有方法,格式:
普通私有方法:private 返回值类型 方法名称(参数列表){
方法体
}
静态私有方法:private static 返回值类型 方法名称(参数列表){
方法体
}
注意:private的方法只有接口自己才能调用,不能被实现类或者其他人调用
3.4 接口的注意事项
使用接口的时候,需要注意:
1.接口是没有静态代码块或者构造方法的,但是抽象类当中可以有构造方法,且抽象类的构造方法是为了其子类在构造方法中用super()进行调用来初始化变量用的
2.一个类的直接父类是唯一的,但是一个类可以同时实现多个接口
格式:
public class NyInterfaceImpl implements MyInterfaceA,MyInterfaceB{
// 覆盖重写所有抽象方法
}
3.如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可。
4.如果实现类没有覆盖重写所有接口当中的所有的抽象方法,那么实现类就必须是一个抽象类。
【5.】如果实现类所实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写。
【6.】一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,【优先用父类当中的方法。】
3.5 类与接口的关系
关系如下:
1.类与类之间是单继承的。一个类的直接父类只有一个。
2.类与接口之间是多实现的。一个类可以实现多个接口。
3.接口与接口之间是多继承的。
注意事项:
1.多个父类接口之间的抽象方法如果冲突,因为没有具体的方法体,所以没关系
2.多个父类接口之间的默认方法如果冲突,那么子接口必须进行默认方法的覆盖重写。【而且必须带着default关键字,原因在于接口当中的deafult关键字是不能省略的】
// 定义接口MyInterfaceA
public interface MyInterfaceA{
public abstract void methodA();
public abstract void methodCommon();
public deafult void methodDefault(){
System.out.println("AAA");
}
}
// 定义接口MyInterfaceB
public interface MyInterfaceB{
public abstract void methodB();
public abstract void methodCommon();
public deafult void methodDefault(){
System.out.println("BBB");
}
}
// 定义子接口MyInterface,这个子接口当中共有4个抽相方法
// methodA来源于接口A
// methodB来源于接口B
// methodCommon来源于接口A和接口B
// method来源于我自己
public Interface MyInterface extends MyInterfaceA,MyInterfaceB{
public abstract void method();
}