14Java基础之抽象类、接口
抽象类
什么是抽象类?
- 在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();
}
}
猫咪,喵喵喵的叫~
狗,汪汪汪的叫~
抽象类的常见应用场景:模板方法设计模式
模板方法设计模式解决了什么问题?
- 解决方法中存在重复代码的问题。
模板方法设计模式的写法
- 定义一个抽象类
- 在里面定义两个方法
- 一个是模板方法:把相同代码放到里面去。
- 一个是抽象方法:具体实现交给子类完成。
- 建议使用final关键字来修饰模板方法,为什么?
- 模板方法是给对象直接使用的,不能被子类重写。
- 一旦子类重写了模板方法,模板方法就失效了。
案例:
目标:完成学生和老师写作文的功能
写作文的统一步骤:
- 第一步,写标题
- 第二步,写正文,但是内容各不相同
- 第三步,写结尾
抽象类:
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.8版本之前,接口中只能定义常量和抽象方法
- 常量:接口中定义常量可以省略public static final,不写,默认会加上
- 抽象方法: 接口中定义抽象方法可以省略public abstract不写,默认会加上
- 接口最需要注意的特点:不能创建对象
- 实现类,相当于子类。实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则这个类必须是抽象类。
接口的好处(重点)
- 弥补了类单继承的不足,一个类同事可以实现多个接口。
- 让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现。(更利于程序的解耦合)
案例:
父类:
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开始,接口中新增了三种形式的方法
- 默认方法
- 默认方法(也就是普通方法):必须用default修饰,它有方法体
- 默认会用public修饰
- 必须用接口的实现类的对象来调用
- 私有方法
- 私有方法(私有的实例方法),JDK9开始有的
- 只能在当前接口内部的默认方法或者私有方法中调用
3.静态方法
- 默认会用public修饰
- 接口的静态方法,必须用接口名本身调用
JDK8开始为什么要新增这三种方法?
- 增强了接口的能力,更便于项目的拓展和维护。
** 接口使用的注意事项**
- 一个接口继承多个接口,如果多个接口中存在方法签名冲突。则此时不支持多继承。
- 一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
- 一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
- 一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
案例:
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基础之抽象类、接口的更多相关文章
- 速战速决 (4) - PHP: 类基础, 抽象类, 接口, trait
[源码下载] 速战速决 (4) - PHP: 类基础, 抽象类, 接口, trait 作者:webabcd 介绍速战速决 之 PHP 类基础 抽象类 接口 trait 示例1.类的相关知识点 1(基础 ...
- 程序猿的日常——Java基础之抽象类与接口、枚举、泛型
再次回顾这些基础内容,发现自己理解的又多了一点.对于一些之前很模糊的概念,渐渐的清晰起来. 抽象类与接口 抽象类通常是描述一些对象的通用方法和属性,并且默认实现一些功能,它不能被实例化.接口仅仅是描述 ...
- Java基础之抽象类与接口
Java基础之抽象类与接口 对于面向对象编程来说,抽象是它的一大特征之一.在Java中,可以通过两种形式来体现OOP的抽象:接口和抽象类.这两者有太多相似的地方,又有太多不同的地方.很多人在初学的时候 ...
- java基础之抽象类与接口的形式参数和返回值
抽象类与接口形式参数和返回值问题 1.形参问题 /* 1.形式参数: 基本类型(太简单,不是我今天要讲解的) 引用类型 (1)类名:(匿名对象的时候其实我们已经讲过了) 需要的是该类的对象 (2)抽象 ...
- 3、java基础:抽象类与接口的区别
抽象类 我们都知道在面向对象的领域一切都是对象,同时所有的对象都是通过类来描述的,但是并不是所有的类都是来描述对象的.如果一个类没有足够的信息来描述一个具体的对象,而需要其他具体的类来支撑它,那么这样 ...
- [.net 面向对象编程基础] (15) 抽象类
[.net 面向对象编程基础] (15) 抽象类 前面我们已经使用到了虚方法(使用 Virtual修饰符)和抽象类及抽象方法(使用abstract修饰符)我们在多态一节中说到要实现类成员的重写必须定义 ...
- “全栈2019”Java第一百零六章:匿名内部类与抽象类接口注意事项
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- 面向对象 继承 抽象类 接口 static 权限修饰符
Day01 面向对象 继承 抽象类 接口 static 1.匿名对象是指创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量. 2.类的继承是指在一个现有类的基础上去构建一个新的类,构建出 ...
- 13. 抽象类 & 接口
一.抽象类 // 抽象类Shape public abstract class Shape { // 1. 成员变量 private String color; // 2. 初始化块 { System ...
- 《Java基础——抽象与接口》
Java基础--抽象与接口 一.抽象: 规则: 关键字 abstract 修饰的类称为抽象类. 子类通过关键字extends实现继承. 关键字 abstract 修饰的方法称为抽象方法,抽 ...
随机推荐
- centos6分区要点
安装centos6系统时,为了以后能够扩展存储,分区时要注意几点: 1.boot引导分区要选固定分区类型存储,大小是500M 2.其余分区全部做成物理卷lvm pyshiic类型存储 3.在这个物理 ...
- AutoCAD 逆向工程中 Shx 字体文件解析
数据格式相关的文章 https://wenku.baidu.com/view/8abbfc33eefdc8d376ee32a1.html 代码实现 https://blog.csdn.net/qq_2 ...
- C# 相等比较
C# 相等比较 有两种类型的相等: 值相等:即两个值是一样的 引用相等:即引用是一样的,也就是同一个对象 默认地,对于值类型来讲,相等指的就是值相等:对于引用类型,相等就是指的引用相等. int a ...
- AI智能体策略FunctionCalling和ReAct有什么区别?
Dify 内置了两种 Agent 策略:Function Calling 和 ReAct,但二者有什么区别呢?在使用时又该如何选择呢?接下来我们一起来看. 1.Function Calling Fun ...
- 用 AI 实现一个 GBK/GB2312 转 UTF-8 工具:轻松解决文本编码转换难题(附完整源码)
用 AI 实现一个 GBK/GB2312 转 UTF-8 工具:轻松解决文本编码转换难题 在处理历史文件或与不同系统交互时,我们经常会遇到 GBK 或 GB2312 编码的文本文件.虽然现在 UTF- ...
- 浅谈Spring、Spring MVC、Spring Boot和Spring Cloud的关系和区别
Spring 框架就像一个家族,有众多衍生产品,例如 boot.security.jpa等等.但它们的基础都是Spring的IOC和AOP等.IOC提供了依赖注入的容器,AOP解决了面向横切面编程 ...
- Web前端入门第 62 问:JavaScript 循环结构注意事项
HELLO,这里是大熊的前端开发笔记. 循环作为 算法与数据结构 中的基石,JS 与其他编程语言一样,都提供了多种循环结构用于处理数据. for 循环 事物的开端往往都是从最常用的开始,循环结构咱们从 ...
- 红色教育软件需求分析 NABCD
N(need) 红色教育指在以红色作为时代精神内涵的象征.务实的落点在于教育.要呼唤有志青年忧国忧民.挑战自我.超越自我.挑战极限.奉献社会的崇高精神.而我们大学生作为实现中华民族伟大复兴的有生力量, ...
- cookie,session以及application的比较
cookie工作原理: cookie代码: 1 @WebServlet(value = "/cookie",name = "CookieServlet") 2 ...
- BAPI_RESERVATION_CHANGE 删除预留
CLEAR: reservationitems[],reservationitems. CLEAR: reservationitemsx[],reservationitemsx,reservation ...