前言
回顾上上个章节当中讲到的遗产(inheritance)的概念, 我们可以通过创建一个类的子类来继承其 datafield 和 methods. 并且这个类也可以单独创建实例. 而抽象类就是一种特殊的父类.
抽象类
就跟刚刚讲到的一样, 抽象类其实就是一个特殊的父类, 作为一个父类它无法实例化. 而是要通过创建其子类然后才能实例化.
为什么我们要使用抽象类, 而不是把所有类都写成可实例化的呢
就如之前章节提到的方法重写签名一样,其实抽象类本身改为可实例化的类并不会影响子类的继承和代码的运行。🔧 它的存在主要是为了代码规范性的问题。
📝 抽象类就像是一个模板或者标准,指引着子类的定义。比如课程就是一个抽象类。📚 课程本身并没有意义,但是不同种类的课程都会有一些共同的属性和方法,比如:
- ⏰ 课程时间
- 📍 课程地点
- 👨🎓 上课学生
- 👩🏫 指导老师
并且它们都可以进行以下操作:
- 🏫 上课
- 🛑 下课
- ➕ 添加学生
- ➖ 踢出学生
- 🔄 更换指导老师
💡 以上例子展示了抽象类的子类都有共同的 datafield 和 methods。使用抽象类作为模板可以:
- ✅ 大大减少代码冗余
- ⚡ 加速开发效率
代码示例
其实抽象类的定义就只需要在可实例化的类签名添加abstract关键词就好了
abstract class Course<T> {
protected String courseTime;
protected String courseLocation;
protected List<T> students;
//这里是一个泛型后面的章节会提到
protected String instructor;
public Course(String courseTime, String courseLocation, String instructor) {
this.courseTime = courseTime;
this.courseLocation = courseLocation;
this.students = new ArrayList<>();
this.instructor = instructor;
}
// 即使是抽象类也需要构造器
public abstract void startClass();
public abstract void endClass();
//这里是抽象类的一个特点, 可以只声明一种方法而不写其具体的实现.
public void addStudent(T student) {
students.add(student);
}
public void removeStudent(T student) {
students.remove(student);
}
public void changeInstructor(String newInstructor) {
this.instructor = newInstructor;
}
}
抽象类的子类
class ProgrammingCourse extends Course<String> {
// 可以看到这里将String传入了泛型, 那父类中的对应位置就会编译为String
public ProgrammingCourse(String courseTime, String courseLocation, String instructor) {
super(courseTime, courseLocation, instructor);
}
@Override
public void startClass() {
System.out.println("Programming class started at " + courseTime);
}
@Override
public void endClass() {
System.out.println("Programming class ended at " + courseTime);
}
}
这里有一些课本中零碎的细节
- 抽象类的子类必须要完成父类的抽象方法, 除非它自己也是抽象的. 📚
- 抽象类无法实例化(这是基本概念). 🛑
- 只有抽象类才能使用抽象方法. ✒️
- 抽象子类可以将父类的
非抽象方法重写为抽象方法. 🔧抽象子类的父类可以是非抽象的. 🔄- 虽然抽象类并不能实例化, 但是依然可以最为一种数据类型. 📊
generics | 范型的首次引入(大纲外)
范型是 java 中的一种机制, 它允许你可以对某些类的类型不做定义, 等到需要实例化类的时候再定义其类型.
优势
- 泛型是在开发时才存在于源码中的,当我们编译时,范型会自动转换为我们使用的类型。这样,泛型在运行之前就能检测代码错误,并且提高代码效率. 💻🔍
- 提高代码的复用性,这里显而易见,不做过多解释. 🔄🔁
- 提高可读性和可维护性,我们不再需要针对不同的数据类型做调整. 📖🛠️
限制
- 不能使用基本数据类型 🔥
- 范型没有数组, 所以如果要传入数组, 那就创建一个包含多个类的类. 如 class 包含 stus 类. 📦
- 范型无法实例化. 🚫
具体用法
已经包含在了上文的代码块中
interface | 接口
如果前面的抽象类概念都能理解的话,那么接口(interface)就更容易掌握了!👌
💡 简单来说,接口就是一种特殊的抽象类,它有以下特点:
- 只能包含静态常量(public static final)
- 方法默认都是抽象且公开的(public abstract)
- 不能包含普通成员变量
- 不能有构造器
🌰 举个栗子:接口就像是一个纯抽象的模板,只定义规范不实现细节。
📌 重要提示
- 🔄 静态方法特性:所有类中的静态方法都是类方法,无法被子类重写。
- ⚠️ 尽量避免在子类中使用同名方法
- ✅ 如果必须使用,建议直接通过类名调用(如
ClassName.method())而非实例名
- 🏗️ 接口类构造器:由于接口类只包含常量(public static final)和抽象方法,所以不需要定义构造器。
- 💡 接口本质上是一个纯抽象模板
- 🚫 与抽象类不同,接口完全不能包含任何实现细节(除非使用 default 方法)
如果我们要在接口中定义默认方法的实现怎么办
这个时候我们就可以使用default关键字定义这个方法的默认实现.
public interface A {
default void doSomething(){
//doSomething
}
static void do(){
// implement
}
//静态方法一定要定义
}
接口的使用
public interface Animal {
void makeSound();
void eat();
}
class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
@Override
public void eat() {
System.out.println("Dog is eating.");
}
}
native 关键字
这个关键字首次在第 13 章中出现 ✨。native是一个方法修饰符,主要用于:
🔌 跨语言调用:允许 Java 代码调用其他语言(如 C/C++)编写的函数 ⚡ 性能优化:某些底层操作使用原生语言实现效率更高
💡 使用场景举例:
- 需要直接操作硬件或系统 API 时
- 需要极致性能优化的关键代码段
- 复用已有的原生语言库
📚 深入理解:由于涉及 JNI(Java Native Interface)技术,建议参考这篇详细资料