抽象类

什么是抽象类?

  • 在Java中有一个关键字叫:abstract,它就是抽象的意思,可以用它修饰类、成员方法。
  • abstract如果修饰方法,那么该方法就是抽象方法,如果修饰类,那么该类就是抽象类。

抽象类的注意事项、特点

  • 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
  • 类该有的成员(成员变量、方法、构造器)抽象类都可以有。
  • 抽象类最主要的特点:抽象类不能使用new关键字来创建对象,它仅作为一种特殊的父类,让子类继承并实现。
  • 子类继承抽象类,那么就必须要重写完抽象类的全部抽象方法,否则该子类也要定义为抽象类。

案例:

父类:
public abstract class A {
// 类该有的成员(成员变量、方法、构造器)抽象类都可以有。
private String name;
private int age;
public A() { }
public A(String name, int age) {
this.name = name;
this.age = age;
}
// 抽象方法:abstract修饰,只能有方法声明,没有方法体。
public abstract void go(); public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
}
} 子类:
// 一个类继承了抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须声明为抽象类。
public class B extends A{ @Override
public void go() {
}
} main方法:
// 目标:认识抽象类,搞清楚它的特点
public class Test {
public static void main(String[] args) {
// 抽象类的核心特点:不能被创建对象
// A a = new A(); // 报错
B b = new B();
b.go();
}
}

抽象类的场景与好处

  • 父类知道每个子类都要做某个行为,但每个子类要做的情况都不一样,父类就定义成抽象类方法,交给子类去重写实现,我们设计这样的抽象类,就是为了更好的支持多态。

案例:

父类:
public abstract class Animal {
private String name; // 抽象类的好处:1. 方法体无意义时可以不写(简化代码)
// 2. 强制子类重写方法(更好的支持了多态)
public abstract void cry(); public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
} 子类:
public class Cat extends Animal { @Override
public void cry() {
System.out.println("猫咪,喵喵喵的叫~");
}
} public class Dog extends Animal{
@Override
public void cry() {
System.out.println("狗,汪汪汪的叫~");
}
} 测试:
//目标:搞清楚抽象类的应用场景
public class Test {
public static void main(String[] args) {
Animal a = new Cat();
a.cry();
Animal b = new Dog();
b.cry();
}
}
猫咪,喵喵喵的叫~
狗,汪汪汪的叫~

抽象类的常见应用场景:模板方法设计模式

模板方法设计模式解决了什么问题?

  • 解决方法中存在重复代码的问题。


模板方法设计模式的写法

  1. 定义一个抽象类
  2. 在里面定义两个方法
  3. 一个是模板方法:把相同代码放到里面去。
  4. 一个是抽象方法:具体实现交给子类完成。
  • 建议使用final关键字来修饰模板方法,为什么?

    1. 模板方法是给对象直接使用的,不能被子类重写。
    2. 一旦子类重写了模板方法,模板方法就失效了。

案例:

目标:完成学生和老师写作文的功能

写作文的统一步骤:

  1. 第一步,写标题
  2. 第二步,写正文,但是内容各不相同
  3. 第三步,写结尾
抽象类:
public abstract class Person {
// 把它设计成模板方法
public final void writeArticle() {
System.out.println("写标题");
System.out.println("第一段内容相同");
// 每个子类都是要写正文的,但是每个子类写的情况是不一样的,我们就可以把正文的书写定义成抽象方法
// 具体的实现交给子类来写
writeBoay();
System.out.println("结尾是相同的");
} public abstract void writeBoay();
}
子类:
public class Student extends Person{
@Override
public void writeBoay() {
System.out.println("我的正文是这样的...");
}
} public class Teacher extends Person{
@Override
public void writeBoay() {
System.out.println("我的正文很优雅是这样的...");
}
} 测试方法:
public class Test {
public static void main(String[] args) {
Student s = new Student();
s.writeArticle(); Teacher t = new Teacher();
t.writeArticle();
}
} 输出结果:
写标题
第一段内容相同
我的正文是这样的...
结尾是相同的
写标题
第一段内容相同
我的正文很优雅是这样的...
结尾是相同的
  • 注意:final关键字和abstract是互斥的。

接口

  • Java提供了一个关键字interface,用这个关键字我们可以定义出一个特殊的结构,接口。


注意

  • 接口不能创建对象。接口是用来被类实现(implements)的,实现接口的类称为实现类
  • 这个实现可以理解为继承,继承接口类。

案例:

接口类A:
// 接口
// 1.8版本之前,接口中只能定义常量和抽象方法
public interface A {
// 1. 常量:接口中定义常量可以省略public static final,不写,默认会加上
// public static final String SCHOOL_NAME = "霍格沃兹";
String SCHOOL_NAME = "霍格沃兹"; // 2. 抽象方法: 接口中定义抽象方法可以省略public abstract不写,默认会加上
// public abstract void run();
void run();
void go();
}
接口类C:
public interface C {
void eat();
} 实现类:
// 实现类,相当于继承了接口的子类
// 实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则这个类必须是抽象类。
public class BImpl implements A,C{
@Override
public void run() {
System.out.println("run");
} @Override
public void go() {
System.out.println("go");
} @Override
public void eat() {
System.out.println("eat");
}
} 测试:
//目标:认识接口,搞清楚接口的特点。
public class Test {
public static void main(String[] args) {
// 接口最需要注意的特点:不能创建对象
// A a = new A(); // 报错:Cannot instantiate the type A
BImpl b = new BImpl();
b.run();
b.go();
b.eat();
}
}

总结:

  1. 1.8版本之前,接口中只能定义常量和抽象方法
  2. 常量:接口中定义常量可以省略public static final,不写,默认会加上
  3. 抽象方法: 接口中定义抽象方法可以省略public abstract不写,默认会加上
  4. 接口最需要注意的特点:不能创建对象
  5. 实现类,相当于子类。实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则这个类必须是抽象类。

接口的好处(重点)

  • 弥补了类单继承的不足,一个类同事可以实现多个接口。
  • 让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现。(更利于程序的解耦合)

案例:

父类:
public class Person {}
子类:
public class Student extends Person implements Driver, Doctor{}
public class Teacher implements Driver, Doctor{}
接口:
public interface Driver {}
public interface Doctor {}
测试方法:
// 目标:理解接口的好处
public class Test {
public static void main(String[] args) {
// 1.弥补了类单继承的不足,接口让一个对象拥有更多角色,更多的能力。
Person p = new Student();
Driver d = new Student();
Doctor doc = new Student();
// 2.面向接口编程是软件开发中目前很流行的开发模式,能更灵活的实现解耦合。
Driver d1 = new Teacher(); // 多态
Doctor doc1 = new Teacher(); // 多态
}
} 注意:案例仅辅助理解接口的优势。

接口的应用案例:班级学生信息管理模块的开发

学生类:
public class Student {
private String name;
private char sex;
private double score; public Student() {
} public Student(String name, char sex, double score) {
this.name = name;
this.sex = sex;
this.score = score;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public char getSex() {
return sex;
} public void setSex(char sex) {
this.sex = sex;
} public double getScore() {
return score;
} public void setScore(double score) {
this.score = score;
}
}
接口类:
public interface ClassData {
void printAllStudentInfos();
double getAverageScore();
} 实现类1:
// 第一套实现类
public class ClassDataImpl1 implements ClassData{
private ArrayList<Student> students; public ClassDataImpl1(ArrayList<Student> students) {
this.students = students;
}
@Override
public void printAllStudentInfos() {
System.out.println("展示所有学生信息:");
for (int i = 0; i < students.size(); i++) {
System.out.println("姓名:" + students.get(i).getName() +
",性别:" + (students.get(i).getSex() == '男' ? "男" : "女") +
",成绩:" + students.get(i).getScore());
}
} @Override
public double getAverageScore() {
double sum = 0;
System.out.println("展示所有学生的平均成绩");
for (int i = 0; i < students.size(); i++) {
sum += students.get(i).getScore();
}
return sum / students.size();
}
} 实现类2:
public class ClassDataImpl2 implements ClassData {
private ArrayList<Student> students; public ClassDataImpl2(ArrayList<Student> students) {
this.students = students;
}
@Override
public void printAllStudentInfos() {
int count = 0;
System.out.println("展示所有学生信息:");
for (int i = 0; i < students.size(); i++) {
System.out.println("姓名:" + students.get(i).getName() +
",性别:" + (students.get(i).getSex() == '男' ? "男" : "女") +
",成绩:" + students.get(i).getScore()); if(students.get(i).getSex() =='男')count ++;
}
System.out.println("男生人数:" + count);
System.out.println("女生人数:" + (students.size() - count));
} @Override
public double getAverageScore() {
double sum = 0;
double min = students.get(0).getScore();
double max = students.get(0).getScore(); for (int i = 0; i < students.size(); i++) {
sum += students.get(i).getScore(); if(max < students.get(i).getScore())
max = students.get(i).getScore(); if(min > students.get(i).getScore())
min = students.get(i).getScore();
} System.out.println("最高成绩:" + max);
System.out.println("最低成绩:" + min);
System.out.println("展示所有学生的平均成绩");
return (sum - max - min) / (students.size() - 2);
}
} 测试:
// 目标:实现班级学生管理系统
/*
* 1. 每个学生是一个对象,所以先定义学生类,用于创建学生对象,封装学生数据。
* 2. 定义接口:ClassData
* 3. 定义两套实现类,来分别处理,以便解耦合。
* */
public class Test {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<>();
list.add(new Student("张三", '男', 90));
list.add(new Student("李四", '女', 80));
list.add(new Student("王五", '男', 70));
list.add(new Student("赵六", '女', 75));
list.add(new Student("王二", '男', 85));
// 多态:父类引用指向子类对象
//ClassData data = new ClassDataImpl1(list);
ClassData data = new ClassDataImpl2(list);
data.printAllStudentInfos();
System.out.println(data.getAverageScore());
}
}

接口的多继承

  • 一个接口可以同时继承多个接口

接口多继承的作用

  • 便于实现类去实现。

案例:

// 目标:接口的多继承
public class Test {
public static void main(String[] args) {
// 类与类是单继承的,一个类只能直接继承一个父类
// 类与接口是多实现的,一个类可以同时实现多个接口 }
}
// 接口的多继承可以让实现类只实现一个接口,相当于实现了很多个接口
class D implements A{
@Override
public void a() {
System.out.println("a方法");
} @Override
public void b() {
System.out.println("b方法");
} @Override
public void c() {
System.out.println("c方法");
}
} // 接口与接口是多继承的,一个接口可以同时继承多个接口
interface A extends B,C{
void a();
} interface B{
void b();
} interface C{
void c();
}

总结:

  • 类与类是单继承的,一个类只能直接继承一个父类
  • 类与接口是多实现的,一个类可以同时实现多个接口
  • 接口与接口是多继承的,一个接口可以同时继承多个接口

JDK8开始,接口中新增了三种形式的方法

  1. 默认方法
  • 默认方法(也就是普通方法):必须用default修饰,它有方法体
  • 默认会用public修饰
  • 必须用接口的实现类的对象来调用
  1. 私有方法
  • 私有方法(私有的实例方法),JDK9开始有的
  • 只能在当前接口内部的默认方法或者私有方法中调用

3.静态方法

  • 默认会用public修饰
  • 接口的静态方法,必须用接口名本身调用

JDK8开始为什么要新增这三种方法?

  • 增强了接口的能力,更便于项目的拓展和维护。

** 接口使用的注意事项**

  1. 一个接口继承多个接口,如果多个接口中存在方法签名冲突。则此时不支持多继承。
  2. 一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
  3. 一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
  4. 一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。

    案例:
public class Test {
public static void main(String[] args) {
// Cat c = new Cat();
// c.run();
// c.test();
}
} // 1. 一个接口继承多个接口,如果多个接口中存在方法签名冲突。则此时不支持多继承。
/*
interface A1{
String run();
}
interface B1{
void run();
}
interface C1 extends A1,B1{}
*/
// 2. 一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
/*
interface A2{
String run();
}
interface B2{
void run();
}
class C2 implements A2,B2{}
*/
// 3. 一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
/*
class Animal{
public void run(){
System.out.println("动物跑的贼快~");
}
}
interface Go{
default void run(){
System.out.println("跑的贼快~");
}
}
class Cat extends Animal implements Go{
public void test(){
run();
Go.super.run();// 这种方式会调用接口的默认方法
}
}
*/
// 4. 一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
interface A3{
default void run(){
System.out.println("A3 run");
}
}
interface B3{
default void run(){
System.out.println("B3 run");
}
}
class C3 implements A3,B3{ @Override
public void run() {
//A3.super.run();
//B3.super.run();
System.out.println("C3 run");
}
}

14Java基础之抽象类、接口的更多相关文章

  1. 速战速决 (4) - PHP: 类基础, 抽象类, 接口, trait

    [源码下载] 速战速决 (4) - PHP: 类基础, 抽象类, 接口, trait 作者:webabcd 介绍速战速决 之 PHP 类基础 抽象类 接口 trait 示例1.类的相关知识点 1(基础 ...

  2. 程序猿的日常——Java基础之抽象类与接口、枚举、泛型

    再次回顾这些基础内容,发现自己理解的又多了一点.对于一些之前很模糊的概念,渐渐的清晰起来. 抽象类与接口 抽象类通常是描述一些对象的通用方法和属性,并且默认实现一些功能,它不能被实例化.接口仅仅是描述 ...

  3. Java基础之抽象类与接口

    Java基础之抽象类与接口 对于面向对象编程来说,抽象是它的一大特征之一.在Java中,可以通过两种形式来体现OOP的抽象:接口和抽象类.这两者有太多相似的地方,又有太多不同的地方.很多人在初学的时候 ...

  4. java基础之抽象类与接口的形式参数和返回值

    抽象类与接口形式参数和返回值问题 1.形参问题 /* 1.形式参数: 基本类型(太简单,不是我今天要讲解的) 引用类型 (1)类名:(匿名对象的时候其实我们已经讲过了) 需要的是该类的对象 (2)抽象 ...

  5. 3、java基础:抽象类与接口的区别

    抽象类 我们都知道在面向对象的领域一切都是对象,同时所有的对象都是通过类来描述的,但是并不是所有的类都是来描述对象的.如果一个类没有足够的信息来描述一个具体的对象,而需要其他具体的类来支撑它,那么这样 ...

  6. [.net 面向对象编程基础] (15) 抽象类

    [.net 面向对象编程基础] (15) 抽象类 前面我们已经使用到了虚方法(使用 Virtual修饰符)和抽象类及抽象方法(使用abstract修饰符)我们在多态一节中说到要实现类成员的重写必须定义 ...

  7. “全栈2019”Java第一百零六章:匿名内部类与抽象类接口注意事项

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  8. 面向对象 继承 抽象类 接口 static 权限修饰符

    Day01 面向对象 继承 抽象类 接口 static 1.匿名对象是指创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量. 2.类的继承是指在一个现有类的基础上去构建一个新的类,构建出 ...

  9. 13. 抽象类 & 接口

    一.抽象类 // 抽象类Shape public abstract class Shape { // 1. 成员变量 private String color; // 2. 初始化块 { System ...

  10. 《Java基础——抽象与接口》

    Java基础--抽象与接口       一.抽象: 规则: 关键字 abstract 修饰的类称为抽象类. 子类通过关键字extends实现继承. 关键字 abstract 修饰的方法称为抽象方法,抽 ...

随机推荐

  1. 仿EXCEL插件,智表ZCELL产品V2.1 版本发布,增加列标、行标自定义设置及单元格属性自定义相关功能,优化公式随动功能

    详细请移步 智表(ZCELL)官网www.zcell.net 更新说明  这次更新主要应用户要求,增加列标.行标自定义设置及单元格属性自定义相关功能,优化公式随动功能 ,欢迎大家体验使用. 本次版本更 ...

  2. 网络编程:UDP网路编程

    参考:盛延敏:网络编程实战 一.UDP和TCP的不同 UDP 是一种"数据报"协议,而 TCP 是一种面向连接的"数据流"协议. TCP 是一个面向连接的协议, ...

  3. codeup之查找

    Description 输入数组长度 n 输入数组 a[1-n] 输入查找个数m 输入查找数字b[1-m] 输出 YES or NO 查找有则YES 否则NO . Input 输入有多组数据. 每组输 ...

  4. WindowsPE文件格式入门01.PE头

    https://www.bpsend.net/thread-288-1-2.html​ portable excute 可移植,可执行的文件(exe dll) 能够解析的文件,其内部都是有格式的,不是 ...

  5. 【拥抱鸿蒙】Flutter+Cursor轻松打造HarmonyOS应用(一)

    前言 在移动应用开发领域,Flutter以其出色的跨平台能力和高效的开发体验赢得了众多开发者的青睐,是许多移动开发者混合开发的首选. 随着HarmonyOS的崛起,许多开发者开始探索如何将Flutte ...

  6. RabbitMQ的连接方式

    一.帐号密码连接 直接设置各个属性值,其中许多属性有其默认值,例如 connection = pika.BlockingConnection(pika.ConnectionParameters(vir ...

  7. ODOO14里面qweb使用案例

    在ODOO里面中,通过QWeb来对模板进行渲染后加载到浏览器中,故作笔记以便于查询简介:Qweb被用作OpenERP的Web客户端模板引擎.它是一种基于XML的模板语言,同Genshi, Thymel ...

  8. Feign Client 超时时间配置

      在Spring Boot微服务架构中,大部分公司都是利用Open Feign进行服务间的调用,而在业务场景比较简单的时候,使用默认配置是不会遇到多大问题的.但是如果业务比较复杂,服务要进行比较繁杂 ...

  9. QMediaPlayer+QVideoWidget+QAudioOutput实现一个简单视频播放器-Qt6.8

    此篇是我在观看使用nginx搭建音视频点播服务器 - seedoubleu - 博客园后,想着使用qt widget写的一个简单播放器 完成nginx搭建音视频点播服务器的话,我推荐使用ffplay进 ...

  10. 几种简单的springboot启动后启动一条死循环线程方式

    前言 之前有测试 # 启动类加 @EnableAsync # 方法上加注解 @Async @PostConstruct 但是依旧会卡主主线程,所有另辟蹊径 第一种 在启动类上加注解 @EnableAs ...