文章目录
一、抽象
1. 抽象的概念
- 对于生活中具体的对象来说,其动作是可以比较清晰地描述的,而从若干个具体对象中提炼出抽象的概念后,其动作往往不能清晰定义
- 比如正方形、圆形、三角形都有面积的简便计算公式,而抽象概念 “图形” 没有一个通用的面积简便计算公式;又如猫吃鱼、狗吃骨头,而抽象概念 “动物” 就不好定义它吃什么
- 在面向对象中也有类似的情况,对于比较抽象的父类来说,其每个子类都有大同小异的同名方法,而父类中这个方法往往因太抽象而不好定义
2. java中的抽象方法和抽象类
(1)定义
- java中通过
abstract
关键字定义抽象方法和抽象类,要求- 抽象方法所在的类必须是抽象类
- 抽象类中可以定义普通方法
- 示例
权限修饰符 abstract 返回值类型 抽象类名{ // 定义抽象方法 权限修饰符 abstract 返回值类型 抽象方法名(); // 定义普通方法 ... }
(2)使用
-
使用抽象类和抽象方法的步骤:
- 不能直接
new
创建抽象类对象 - 必须用一个子类继承抽象类
- 子类类中必须重写父类中的抽象方法(去掉
abstract
并加上方法体) - 创建并使用子类对象
- 不能直接
-
示例
// 抽象类 abstract class Animal{ // 抽象方法 public abstract void eat(); } // 继承自抽象类 class Cat extends Animal { // 重写抽象方法 @Override public void eat(){ System.out.println("猫吃鱼"); } } class Dog extends Animal { @Override public void eat(){ System.out.println("狗吃骨头"); } } public class Abstract { public static void main(String[] args) { Cat cat = new Cat(); Dog dog = new Dog(); cat.eat(); // 猫吃鱼 dog.eat(); // 狗吃骨头 } }
-
注意
- 抽象类不能创建对象实例,若创建会报错。只能创建其子类对象实例
- 抽象类中可以有构造方法,是供子类成员创建对象时初始化抽象父类所用(
super()
调用) - 抽象类中不一定包含抽象方法,但有抽象方法的一定是抽象类。未含抽象方法的抽象类,目的就是不想让调用者创建此类对象,一般用于特殊的类结构设计
- 抽象类的子类,必须重写抽象父类中所有的抽象方法,除非此子类也是抽象类
二、接口
1. 接口的概念
- 生活中的接口实例有电源接口、USB接口等,国家制定一套统一的标准,各个厂商按照标准生产,就都可以通用。抽象一点说,接口代表着一种公共的标准和规范
- 把生活中 “接口” 这个概念引用到编程中,我们可以给多个类定义一个公共的规范
2. java中的接口
-
接口是多个类的通用规范,是一种抽象数据类型,最重要的是其中的抽象方法(代表着规范)。接口相当于类的蓝图,指明了一个类必须要做什么和不能做什么
-
关于java接口的理解,可以参考这里:JAVA基础——接口(全网最详细教程)
-
接口所在的
.java
文件,编译后也会生成一个.class
文件,可以把接口看做特殊的类 -
使用接口的步骤
-
使用
interface
关键字定义一个接口public interface 接口名{ // ... }
-
接口不能直接
new
对象使用,必须有一个实现类来 “实现” 此接口public class 实现类名称 implements 接口名称{ // ... }
-
接口的实现类中必须覆写接口中的全部抽象方法,如果有抽象方法没有覆写,这个就只能是抽象类而不能使用了
-
创建实现类的对象,进行使用
-
-
接口中包含的内容:
- java7引入:常量、抽象方法
- java8引入:默认方法、静态方法
- java9引入:私有方法
(1)接口中的抽象方法
-
在接口中:定义抽象方法
- 在任何版本的java中,都可以在接口中定义抽象方法
public abstract 返回值类型 方法名(参数列表);
- 注意:接口中的抽象方法,修饰符必须是
public abstract
,这两个关键字可以省略,但是不能写别的(不省略、省略一部分、都省略,都一样)
- 在任何版本的java中,都可以在接口中定义抽象方法
-
在实现类中:覆盖重写接口中所有的抽象方法。
- 在IDEA中,先写出空的实现类,此时由于没有进行抽象方法覆写,在定义位置会有红色波浪线
- 把光标放在波浪线上,按
ALT+回车
进入自动修复,选implements
可以自动生成所有覆写方法的结构
-
示例
// 1. 定义接口 interface MyInterface { // 这四个都是抽象方法,"public abstract"可以任意省略 public abstract void method1(); public void method2(); abstract void method3(); void method4(); } // 2. 定义实现类 class MyInterfaceImpl implements MyInterface{ @Override public void method1() { System.out.println("这是方法1"); } @Override public void method2() { System.out.println("这是方法2"); } @Override public void method3() { System.out.println("这是方法3"); } @Override public void method4() { System.out.println("这是方法4"); } } // 3. 使用实现类 public class Main { public static void main(String[] args) { MyInterfaceImpl impl = new MyInterfaceImpl(); impl.method1(); // 这是方法1 impl.method2(); // 这是方法2 impl.method3(); // 这是方法3 impl.method4(); // 这是方法4 } }
(2)接口中的默认方法
-
默认方法的作用:解决接口升级问题。
- 比如说如果接口要进行修改升级,多加一个抽象方法,则用户程序中此接口的所有实现类都需要覆写这个新加的方法,这会导致大量的修改工作。
- 为了避免修改,可以不添加抽象方法而是在接口中添加一个默认方法。默认方法其中是有方法体的,因此不需要在实现类覆写。
- 默认方法也可以覆写,通过
实现类.默认方法名()
调用时,先在子类中找,找不到则向上去接口找。
-
在接口中:定义默认方法
- 从java8开始,可以在接口中定义默认方法
public default 返回值类型 方法名(参数列表){ // 方法体... }
- 注意:接口中的默认方法,修饰符必须是
public
,这个关键字可以省略,但是不能写别的
- 从java8开始,可以在接口中定义默认方法
(3)接口中的静态方法
-
类似类中的静态成员方法,接口中的静态成员方法也是和接口一起存储,对于实例对象来说是共享内存的
-
在接口中:定义静态方法
- 从java8开始,可以在接口中定义静态方法
public static 返回值类型 方法名(参数列表){ // 方法体... }
- 注意:接口中的静态方法,修饰符必须是
public
,这个关键字可以省略,但是不能写别的
- 从java8开始,可以在接口中定义静态方法
-
在实现类中:
- 接口中的静态方法不能被继承,因为一个类是可以实现多个接口的,如果接口中的静态方法的方法前面相同,就会发生继承冲突,所以索性就从继承这个层面阻断冲突的发生。
- 不能通过实现类的对象实例调用接口中的静态方法
-
使用静态方法:通过
接口名.静态方法名()
直接调用
(4) 接口中的私有方法
-
当接口中有两个或多个方法存在大量相同代码时,我们应该把它们抽取出来作为一个方法,从而减少冗余代码。这个方法应该仅在接口内使用,而不能让实现类使用,也就是说应该是私有化的
-
在接口中:从java9开始,可以定义私有方法
-
普通私有方法:解决多个默认方法间的重复代码问题
private 返回值类型 方法名称(参数列表){ // 方法体... }
-
静态私有方法:解决多个静态方法间的重复代码问题
private static 返回值类型 方法名称(参数列表){ // 方法体... }
-
-
示例
interface MyInterface { public static void StaticMethod1(){ System.out.println("这是静态方法1"); StaticCommon(); } public static void StaticMethod2(){ System.out.println("这是静态方法1"); StaticCommon(); } // 通过静态私有方法解决静态方法间的重复代码问题 private static void StaticCommon(){ System.out.println("公共内容"); } } public class Main { public static void main(String[] args) { // 使用接口名直接调用接口的静态防方法 MyInterface.StaticMethod1(); MyInterface.StaticMethod2(); } } /* 这是静态方法1 公共内容 这是静态方法1 公共内容 */
(5)接口中的 “成员变量”
- 接口中也可定义成员变量,但是必须用
public static final
修饰。类似接口抽象方法,程序中可以任意省略这三个修饰符,但是不改变其特性,从效果上看,这就是接口的常量 - 在接口中定义 “成员变量” 的格式:
public static final 数据类型 常量名称 = 常量;
- 注意:
- 由于
public
,接口的 “成员变量” 可以被实现类继承 - 由于
static
,可以通过接口名直接访问“成员变量” - 由于
final
,这是一个不可变变量,在定义时必须赋值;若不手动赋值,自动赋值为0
- 由于
- 对于常量,推荐使用全大写+下划线进行命名
(6)接口成员小结(java 9)
-
成员变量其实是常量
- 格式
[public][static][final] 数据类型 常量名称 = 常量值;
- 注意:
- 常量必须赋值,且一经赋值不可改变
- 常量名称推荐全大写+下划线分割
- 格式
-
接口中最重要的是抽象方法
- 格式
[public][abstract] 返回值类型 方法名称(参数列表);
- 注意:
- 抽象方法不能直接使用,必须通过 “实现类” 间接使用
- 实现类中必须覆写所有接口所有抽象方法,除非实现类也是抽象类
- 格式
-
从java 8开始,接口中允许定义默认方法
- 格式
[public] default 返回值类型 方法名称(参数列表){ // 方法体... }
- 注意:默认方法也可以在实现类中覆写
- 格式
-
从java 8开始,接口中允许定义静态方法
- 格式
[public] static 返回值类型 方法名称(参数列表){ // 方法体... }
- 注意:应该通过接口名称调用,不能通过实现类对象调用接口的静态方法
- 格式
-
从java 9开始,接口中允许定义私有方法
- 格式
// 普通私有方法 private 返回值类型 方法名称(参数列表){ // 方法体... } // 静态私有方法 private static 返回值类型 方法名称(参数列表){ // 方法体... }
- 注意:私有方法只能在接口中使用,不能通过实现类使用
- 格式
(7)用接口实现多继承
-
java中的类是没有多继承的,但是可以通过接口实现类似的效果
-
使用接口时,需要注意
-
接口是没有静态代码块,没有构造方法的
-
一个类的直接父类有且只有一个,但一个类可以同时实现多个接口,格式
public class MyInterfaceImpl implents InterfaceA,InterfaceB{ // 覆盖重写实现的所有接口的抽象方法 }
-
如果类实现的多个接口中有重名的
抽象方法
,只要覆盖重写一个即可 -
如果类实现的多个接口中有重名的
默认方法
,实现类必须对冲突的默认方法覆盖重写 -
如果实现类的直接父类中的方法,和接口中的默认方法同名,优先用父类中的方法
-
如果实现类中没有覆写所有接口中的所有抽象方法,这个类只能是抽象类
-
-
类和接口的关系
- 类和类之间是单继承的,直接父类仅有一个
- 类和接口直接是多实现的,一个类可以实现多个接口
- 接口和接口之间是多继承的,注意
- 多个父接口中的抽象方法若重复,没关系(因为没有方法体)
- 多个父接口中的默认方法若重复,子接口必须覆写默认方法,而且要带着
default
关键字
-
示例
interface MyInterfaceA{ public abstract void methodA(); public abstract void methodCommon(); public default void methodDefault(){ System.out.println("接口A默认方法"); } } interface MyInterfaceB{ public abstract void methodB(); public abstract void methodCommon(); public default void methodDefault(){ System.out.println("接口B默认方法"); } } /* 这个子接口中有四个抽象方法 1. methodA 来自 MyInterfaceA 2. methodB 来自 MyInterfaceB 3. methodCommon 来自 MyInterfaceA和MyInterfaceB 4. method 来自 自己 */ interface MyInterface extends MyInterfaceA,MyInterfaceB{ public abstract void method(); @Override default void methodDefault() { System.out.println("子接口中需要覆写冲突的默认方法"); } } public class Main { public static void main(String[] args) { // ... } }