编程语言Java——核心技术篇(二)类的高级特性
续前篇:编程语言Java——核心技术篇(一)封装、继承和多态-CSDN博客
目录
2. 类的高级特性
2.1 包
2.1.1 包的基本概念
2.1.2 包的声明和使用
2.1.3 包与文件系统的映射关系
2.1.4 高级包特性
2.2 final 关键字
2.2.1 final 修饰类(Final Class)
2.2.2 final 修饰方法(Final Method)
2.2.3 final 修饰变量(Final Variable)
2.2.4 final 与并发编程
2.2.5 final 的进阶用法
2.2.6 final 的注意事项
2.3 内部类
2.3.1 内部类本质
2.3.2 成员内部类深度剖析
2.3.3 静态内部类进阶应用
2.3.4 局部内部类与闭包
2.3.5 匿名内部类高级话题
2.3.6 内部类和内存泄露
2.4 抽象类
2.4.1 抽象类基础定义
2.4.2 抽象类核心特性
2.4.3 抽象类和接口的区别总览
2.4.4 抽象类的继承体系
2.5 小结
2. 类的高级特性
Java类的高级特性通过继承与多态实现代码复用和扩展,利用抽象类与接口定义规范与契约,借助内部类实现逻辑封装与组织,通过枚举类保证类型安全,运用泛型增强代码复用性与类型安全,结合注解添加元数据信息,采用Lambda表达式简化函数式编程,利用记录类(Record)快速创建不可变数据对象,并通过密封类(Sealed Class)精确控制继承层次,这些特性共同构成了Java强大的面向对象编程能力,显著提升了代码的可维护性、安全性和表达力。
2.1 包
包(Package)是Java语言中非常重要的组织机制,它不仅是简单的代码容器,更是Java程序架构设计的基石。下面我将从多个维度全面深入地讲解Java包的相关知识。
2.1.1 包的基本概念
1. 包的核心作用:
-
命名空间管理:解决大型项目中类名冲突问题
-
访问控制:通过包级别的可见性控制实现封装
-
模块化设计:促进高内聚、低耦合的代码结构
-
逻辑分组:将功能相关的类和接口组织在一起
2. Java包的设计原则:
-
唯一性:包名通常采用逆域名确保全球唯一
-
层次性:通过点分结构实现逻辑分层
-
可见性:与访问修饰符配合实现精细的访问控制
-
可扩展性:支持子包划分,适应项目增长
2.1.2 包的声明和使用
1. 包声明语法
// 必须位于文件首行(注释除外)
package 顶级域名.二级域名.项目名.模块名.子模块;// 示例:电商系统的用户模块工具类
package com.jd.ecommerce.user.util;
2. 导入语句的完整语法
// 1. 单类型导入(推荐)
import java.util.ArrayList;// 2. 按需导入(谨慎使用)
import java.util.*;// 3. 静态导入(适用于工具类常量)
import static java.lang.Math.PI;
import static java.lang.Math.pow;// 4. 导入嵌套类
import java.util.Map.Entry;// 5. 冲突解决:同名类需要全限定名
import com.example.Date;
java.util.Date utilDate = new java.util.Date();
3. 包访问规则示例
// File: com/example/pkg1/ClassA.java
package com.example.pkg1;public class ClassA {void defaultMethod() {} // 包私有protected void protMethod() {} // 子类可见
}// File: com/example/pkg1/ClassB.java
package com.example.pkg1;public class ClassB {void test() {ClassA a = new ClassA();a.defaultMethod(); // 可访问a.protMethod(); // 可访问}
}// File: com/example/pkg2/ClassC.java
package com.example.pkg2;import com.example.pkg1.ClassA;public class ClassC extends ClassA {void test() {// a.defaultMethod(); // 编译错误protMethod(); // 可访问(继承)}
}
2.1.3 包与文件系统的映射关系
1. 标准目录结构
项目根目录/
│
├── src/
│ ├── main/
│ │ ├── java/ # Java源代码
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── util/
│ │ │ │ └── StringUtil.java
│ │ │ └── App.java
│ │ └── resources/ # 资源文件
│ └── test/ # 测试代码
│
├── target/ # 输出目录
│ ├── classes/
│ │ └── com/
│ │ └── example/
│ └── project.jar
│
└── pom.xml # Maven配置
2. 编译与执行命令
# 编译(自动创建包目录)
javac -d target/classes src/main/java/com/example/App.java# 运行(需指定类路径)
java -cp target/classes com.example.App# 打包为JAR
jar cvfe target/project.jar com.example.App -C target/classes .# 运行JAR
java -jar target/project.jar
2.1.4 高级包特性
1. 包密封(Package Sealing)
// MANIFEST.MF文件
Sealed: true// 或通过代码
JarFile jarFile = new JarFile("example.jar");
Attributes attrs = jarFile.getManifest().getMainAttributes();
attrs.putValue("Sealed", "true");
2. 包注解(Package-info.java)
/**
* 用户管理模块包
* 包含用户实体、服务等核心类
* @since 1.0
* @version 2.1
*/
@Deprecated
package com.example.user;import javax.annotation.ParametersAreNonnullByDefault;// 包级注解
@ParametersAreNonnullByDefault
3. 常用Java内置包
包名 | 描述 |
---|---|
java.lang | 核心类(自动导入) |
java.util | 实用工具类(集合、日期等) |
java.io | 输入输出相关 |
java.net | 网络编程相关 |
java.sql | 数据库操作 |
java.awt/javax.swing | GUI编程 |
java.nio | 非阻塞I/O |
java.time | 日期时间API(Java 8) |
2.2 final 关键字
final
是 Java 中最常用的关键字之一,它可以修饰类、方法和变量,具有不同的语义和作用。下面我将全面详细地讲解 final
的用法和原理。
2.2.1 final 修饰类(Final Class)
当一个类被声明为 final
,它 不能被继承(即不能有子类)。
1. 核心特性
-
不可继承:任何尝试继承
final
类的代码都会导致编译错误。 -
隐式
final
方法:类中的所有方法都隐式成为final
方法(不可重写)。 -
常用于工具类、不可变类:如
String
、Integer
、Math
等。
2. 示例
final class ImmutablePoint {private final int x;private final int y;public ImmutablePoint(int x, int y) {this.x = x;this.y = y;}public int getX() { return x; }public int getY() { return y; }
}// 编译错误!不能继承 final 类
// class ExtendedPoint extends ImmutablePoint { ... }
3. 适用场景
✅ 安全性:防止恶意子类篡改行为(如 String
类)。
✅ 性能优化:JVM 可能对 final
类进行内联优化。
✅ 不可变对象:如 Integer
、LocalDate
等。
2.2.2 final
修饰方法(Final Method)
当一个方法被声明为 final
,它 不能被子类重写(Override)。
1. 核心特性
-
禁止重写:子类不能修改
final
方法的行为。 -
静态绑定:方法调用在编译时确定(非虚方法),可能被 JVM 内联优化。
-
不影响重载(Overload):仍然可以在子类中定义同名但参数不同的方法。
2. 示例
class Parent {public final void criticalOperation() {System.out.println("This cannot be overridden!");}public void normalMethod() {System.out.println("Can be overridden");}
}class Child extends Parent {// 编译错误!不能重写 final 方法// @Override public void criticalOperation() { ... }@Overridepublic void normalMethod() {System.out.println("Overridden normal method");}
}
3. 适用场景
✅ 关键业务逻辑:如支付计算、权限校验等。
✅ 模板方法模式:固定算法骨架,允许子类扩展部分逻辑。
✅ 性能敏感方法:final
方法可能被 JIT 内联优化。
2.2.3 final
修饰变量(Final Variable)
final
变量只能被 赋值一次,之后不能再修改。
1. 核心特性
变量类型 | 初始化时机 | 示例 |
---|---|---|
final 实例变量 | 必须在声明时、构造方法或初始化块中赋值 |
|
final 静态变量 | 必须在声明时或静态初始化块中赋值 |
|
final 局部变量 | 使用前必须赋值(可延迟初始化) |
|
2. 示例
class Example {final int instanceVar; // 实例变量static final double PI = 3.14159; // 静态常量public Example(int value) {this.instanceVar = value; // 构造方法赋值}void method() {final int localVar; // 局部变量localVar = 100; // 第一次赋值// localVar = 200; // 编译错误,不能二次赋值}
}
3 final
引用类型
final
修饰引用变量时,引用不能变,但对象内容可以变:
final List<String> names = new ArrayList<>();
names.add("Alice"); // 允许修改内容
// names = new ArrayList<>(); // 编译错误,不能重新赋值
4. 适用场景
✅ 常量定义:如 Math.PI
。
✅ 线程安全:final
变量在构造后对其他线程立即可见(JMM 保证)。
✅ Lambda/匿名内部类:访问的外部变量必须是 final
或 effectively final
。
2.2.4 final
与并发编程
1. 不可变对象(Immutable Object)
final 是实现不可变对象的关键:
public final class ImmutablePerson {private final String name;private final int age;public ImmutablePerson(String name, int age) {this.name = name;this.age = age;}// 只有 getter,没有 setterpublic String getName() { return name; }public int getAge() { return age; }
}
特点:
✔ 线程安全:无需同步,可安全共享。
✔ 缓存友好:适合作为 HashMap
的 key
。
2. final
的内存语义
-
禁止指令重排序:JVM 保证
final
字段的初始化不会重排序到构造方法外。 -
安全发布:正确构造的对象中
final
字段对其他线程立即可见。
2.2.5 final
的进阶用法
1. final
参数
void process(final int param) {// param = 10; // 编译错误,不能修改System.out.println(param);
}
✅ 防止意外修改,提高代码可读性。
2. 空白 final
(Blank Final)
class BlankFinalExample {final int blankFinal; // 未初始化public BlankFinalExample(int value) {this.blankFinal = value; // 构造方法赋值}
}
✅ 灵活初始化,但必须在构造方法中赋值。
3. final
与 static
结合
class Constants {public static final int MAX_SIZE = 1024; // 全局常量
}
✅ 编译时常量,会被 JVM 内联优化。
2.2.6 final
的注意事项
1. final
不等于 immutable
(不可变)
-
final
只保证 引用不变,但对象内部状态可能改变:final List<String> list = new ArrayList<>(); list.add("Hello"); // 允许
-
真正的不可变需要:
final List<String> immutableList = Collections.unmodifiableList(new ArrayList<>());
2. 反射可以修改 final
字段(但不推荐)
Field field = MyClass.class.getDeclaredField("finalField");
field.setAccessible(true);
field.set(myObj, newValue); // 危险操作!
这里展开说一下不建议用反射修改final字段的原因:(反射虽然前面还没讲不过就当做个扩展,后面章节专门讲反射):
(1) 破坏语言设计原则:
违反 final
的语义:final
关键字明确表示"不可修改",反射修改违背了语言设计初衷
破坏封装性:绕过正常的访问控制机制,破坏面向对象的封装原则
(2) 导致不可预测的行为:
class Constants {public static final int MAX = 100;
}// 编译时可能被优化为直接使用100
int size = Constants.MAX * 2; // 编译为 int size = 200;
JVM 优化问题:JVM 可能对 final
字段进行优化(如内联常量),修改后可能导致不一致
内存可见性问题:final
字段有特殊的内存语义,反射修改可能破坏这些保证
(3) 线程安全问题:
破坏不可变对象:不可变对象依赖 final
字段保证线程安全
可见性问题:其他线程可能看不到反射修改后的值,导致数据不一致
(4) 维护性问题:
代码难以理解:其他开发者无法从代码中看出字段会被修改
调试困难:违反预期的修改可能导致难以追踪的bug
(5) 平台依赖性:
不同JVM实现行为可能不同:某些JVM可能完全不允许修改 final
字段
未来版本可能限制:Java未来版本可能加强限制
3. final
与 effectively final
(Java 8+)
Lambda/匿名类可以访问 final
或 effectively final
(未修改的局部变量):
public class EffectivelyFinalExample {public static void main(String[] args) {int count = 0; // 没有 final 修饰// 合法,因为 count 是 effectively finalRunnable r = () -> System.out.println("Count: " + count);// 如果取消下面这行注释,会导致编译错误:// count++; // 修改 count 会破坏 effectively final 状态new Thread(r).start();}
}
关键概念解释:
(1)Effectively Final(事实上的 final):
指一个变量虽然没有显式声明为 final
,但它在初始化后从未被修改过
Java 8 开始,Lambda 表达式和匿名内部类可以访问这样的变量
(2)为什么需要这个特性?
在 Lambda 中使用外部变量时,该变量必须是 final 或 effectively final
这是为了保证线程安全,防止并发修改问题
2.3 内部类
2.3.1 内部类本质
1. 内部类的核心价值:
内部类(Inner Class)是Java最强大的封装机制之一,它打破了传统类定义的边界,允许在一个类的内部定义另一个完整的类结构。这种设计并非语法糖,而是体现了以下核心思想:
-
逻辑绑定:将紧密关联的类物理上放在一起,增强代码的内聚性
-
访问特权:内部类拥有访问外部类所有成员(包括private)的特殊权限
-
多重继承模拟:通过内部类继承不同类,突破Java单继承的限制
-
闭包实现:在Lambda出现前,内部类是Java实现闭包的主要方式
2. JVM层面的实现原理
(1)合成访问方法(Synthetic Accessor):
对于private成员的访问,编译器会生成包可见的access$XXX方法
// 源码
class Outer {private int x;class Inner { void useX() { x++; } }
}// 编译后等价于
class Outer {private int x;static int access$000(Outer o) { return o.x; }static void access$100(Outer o, int val) { o.x = val; }
}class Outer$Inner {final Outer this$0;void useX() {Outer.access$100(this$0, Outer.access$000(this$0) + 1);}
}
(2) 隐式引用:
非静态内部类自动持有指向外部类实例的final引用(this$0)
这是内存泄漏的潜在根源,也是序列化问题的原因
2.3.2 成员内部类深度剖析
1. 字节码层面分析
观察编译后的成员内部类字节码,可以看到三个关键特征:
(1)构造器注入:
// 源码
Outer.Inner inner = outer.new Inner();// 字节码等价逻辑
Outer$Inner inner = new Outer$Inner(outer);
(2)字段表结构:
// 内部类的字段表
Field #1: this$0 LOuter;
(3)访问外部private字段的指令:
aload_0
getfield #1 // 获取this$0引用
invokevirtual #2 // 调用合成访问方法
2. 典型应用场景
场景1:迭代器模式实现
public class LinkedList<E> implements Iterable<E> {private Node<E> head;private int size;// 节点静态嵌套类private static class Node<T> {T data;Node<T> next;Node(T data) {this.data = data;}}// 成员内部类实现迭代器private class ListIterator implements Iterator<E> {private Node<E> current;private int expectedModCount = size;ListIterator() {this.current = head;checkForComodification();}@Overridepublic boolean hasNext() {return current != null;}@Overridepublic E next() {checkForComodification();if (!hasNext()) throw new NoSuchElementException();E data = current.data;current = current.next;return data;}// 并发修改检查final void checkForComodification() {if (size != expectedModCount)throw new ConcurrentModificationException();}}// 外部类方法public void add(E element) {Node<E> newNode = new Node<>(element);newNode.next = head;head = newNode;size++;}@Overridepublic Iterator<E> iterator() {return new ListIterator();}
}
这个看不懂也当扩展,因为集合类就会遇到迭代器了。
场景2:事件回调机制
public class Button {private String id;private List<ActionListener> listeners = new ArrayList<>();// 成员内部类实现标准回调接口private class ButtonActionListener implements ActionListener {private final long createdTime;ButtonActionListener() {this.createdTime = System.currentTimeMillis();}@Overridepublic void actionPerformed(ActionEvent e) {System.out.printf("Button %s clicked at %d (listener created at %d)%n",Button.this.id, e.getWhen(), this.createdTime);// 可以访问外部类的私有字段System.out.println("Total listeners: " + listeners.size());}}public Button(String id) {this.id = id;}public void addDefaultListener() {listeners.add(new ButtonActionListener());}public void addListener(ActionListener listener) {listeners.add(listener);}public void click(long timestamp) {ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "click", timestamp);for (ActionListener listener : listeners) {listener.actionPerformed(event);}}
}
设计优势分析:
(1)自然访问上下文:回调可以直接访问按钮的所有状态(如id
)
(2)自动绑定:内部类实例隐式持有外部类引用(Button.this
)
(3)封装实现细节:回调的具体实现对外不可见
(4)状态保持:每个监听器可以维护自己的状态(如createdTime
)
使用示例:
Button btn = new Button("loginBtn");// 添加默认内部类监听器
btn.addDefaultListener();// 添加外部监听器
btn.addListener(e -> System.out.println("External handler"));// 模拟点击事件
btn.click(System.currentTimeMillis());
2.3.3 静态内部类进阶应用
1 与普通内部类的本质区别
特性 | 非静态内部类 | 静态内部类 |
---|---|---|
外部类引用 | 隐式持有this$0 | 无 |
实例化方式 | 需要外部类实例 | 直接实例化 |
访问外部类成员 | 可访问所有成员 | 仅能访问静态成员 |
内存占用 | 每个实例持有外部引用 | 无额外开销 |
序列化安全性 | 可能造成序列化问题 | 可安全序列化 |
2. 典型设计模式应用
(1)构建器模式(Builder Pattern)
public class Computer {private final String CPU;private final String RAM;private Computer(Builder builder) {this.CPU = builder.CPU;this.RAM = builder.RAM;}// 静态内部类作为构建器public static class Builder {private String CPU;private String RAM;public Builder setCPU(String cpu) {this.CPU = cpu;return this;}public Builder setRAM(String ram) {this.RAM = ram;return this;}public Computer build() {return new Computer(this);}}
}// 使用方式
Computer computer = new Computer.Builder().setCPU("i7").setRAM("16GB").build();
(2)线程局部存储模式
public class ThreadLocal<T> {// 静态内部类实现线程安全private static class ThreadLocalMap {private Map<Thread, Object> map = new WeakHashMap<>();synchronized void set(Thread thread, Object value) {map.put(thread, value);}synchronized Object get(Thread thread) {return map.get(thread);}}private ThreadLocalMap map = new ThreadLocalMap();public void set(T value) {map.set(Thread.currentThread(), value);}@SuppressWarnings("unchecked")public T get() {return (T) map.get(Thread.currentThread());}
}
2.3.4 局部内部类与闭包
1. 变量捕获机制
局部内部类访问的局部变量必须是final或effectively final,这是由Java语言规范强制要求的,根本原因在于:
(1)生命周期不一致:
局部变量存储在栈帧中,方法执行完毕即销毁
内部类对象可能长期存在(如被返回或传递给其他线程)
(2)JVM实现限制:
局部变量无法直接跨方法共享
编译器通过值拷贝实现捕获,为保证一致性必须禁止修改
public class ClosureExample {public Runnable createCounter() {int count = 0; // effectively final// 局部内部类class Counter implements Runnable {public void run() {// 捕获局部变量System.out.println("Count: " + count);// count++; // 编译错误}}return new Counter();// 如果在此处修改count,同样会导致编译错误// count = 1;}
}
2. 与Lambda表达式的对比
特性 | 局部内部类 | Lambda表达式 |
---|---|---|
类文件生成 | 每个类生成独立.class | 不生成额外类文件 |
this引用 | 指向自身实例 | 指向外围实例 |
变量捕获 | 显式要求final | 隐式要求effectively final |
重载支持 | 可以重载 | 不能重载 |
适用场景 | 复杂逻辑 | 单一抽象方法 |
2.3.5 匿名内部类高级话题
1. 初始化块技巧
匿名内部类可以有实例初始化块(IIB),这是实现复杂初始化的有效方式:
interface Processor {void process(String input);
}public class AnonymousClassDemo {public Processor createProcessor(final String prefix) {return new Processor() {private int callCount;// 实例初始化块{callCount = 0;System.out.println("Processor initialized");}@Overridepublic void process(String input) {System.out.println(prefix + ": " + input);callCount++;}// 可以添加额外方法(但外部不可调用)private void logCall() {System.out.println("Total calls: " + callCount);}};}
}
2. 双括号初始化(Double Brace Initialization)
利用匿名内部类+实例初始化块实现简洁的集合初始化:
List<String> list = new ArrayList<String>() {{add("Java");add("Python");add("C++");
}};
注意事项:
-
每个
{{}}
都会生成一个新类 -
可能导致内存泄漏(持有外部类引用)
-
序列化可能有问题
2.3.6 内部类和内存泄露
1. 典型泄漏场景
public class Outer {private byte[] largeData = new byte[1024 * 1024 * 10]; // 10MBclass Inner {void doSomething() {System.out.println("Working with " + largeData.length);}}Inner getInner() {return new Inner();}
}// 使用方式导致内存泄漏
public class LeakDemo {public static void main(String[] args) {Outer.Inner inner = new Outer().getInner();// 即使Outer实例不再被引用,inner仍持有this$0导致无法回收}
}
2. 解决方案
(1)使用静态内部类,因为静态内部类只访问类内的成员:
static class Inner { /* 不持有外部引用 */ }
(2)弱引用(WeakReference):
class Inner {private WeakReference<Outer> outerRef;Inner(Outer outer) {this.outerRef = new WeakReference<>(outer);}
}
(3)显式清理:
class Outer {private Inner inner;void clear() { this.inner = null; }
}
2.4 抽象类
抽象类是 Java 面向对象编程的核心概念之一,它介于普通类和接口之间,提供了一种部分实现但不可直接实例化的类结构。下面从 7 个维度全面解析抽象类。
2.4.1 抽象类基础定义
1. 语法法则
// 使用 abstract 关键字声明
public abstract class Animal {// 普通字段private String name;// 普通方法(已实现)public void eat() {System.out.println("Eating...");}// 抽象方法(未实现)public abstract void makeSound();
}
2.4.2 抽象类核心特性
1. 不能被实例化
-
本质特性:抽象类不能直接创建实例对象,只能作为其他类的基类被继承
-
设计目的:抽象类存在的意义是为派生类提供公共接口和部分实现,而不是被直接使用
-
示例:
Shape shape = new Shape();
是非法的(假设Shape是抽象类)
2. 包含抽象方法
-
抽象方法声明:使用
abstract
关键字声明,没有方法体(不提供实现) -
实现要求:任何非抽象的子类必须实现(重写)父抽象类的所有抽象方法
-
语法示例:
public abstract void draw(); // 没有大括号和方法体
3. 可以包含具体实现
-
混合特性:抽象类可以同时包含抽象方法和具体实现的方法
-
优势:既能定义规范(抽象方法),又能提供公共实现(具体方法)
-
示例:
public abstract class Animal {// 抽象方法public abstract void makeSound();// 具体方法public void breathe() {System.out.println("Breathing...");} }
4. 可以包含成员变量
-
状态保持:抽象类可以定义各种访问修饰符的成员变量
-
与接口区别:接口通常只能有静态常量(public static final)
-
示例:
public abstract class Vehicle {protected int speed; // 子类可继承的成员变量private String model; // 私有变量 }
5. 构造方法
-
存在性:抽象类可以有构造方法,虽然不能直接实例化
-
用途:供子类构造时调用,初始化抽象类中定义的成员变量
-
示例:
public abstract class Person {private String name;public Person(String name) {this.name = name;} }
6. 继承关系
-
单继承限制:在Java等语言中,一个类只能继承一个抽象类
-
多层级抽象:抽象类可以继承另一个抽象类,无需实现父类的抽象方法
-
最终实现:只有非抽象的子类才需要实现继承链中的所有抽象方法
7. 设计模式中的应用
-
模板方法模式:抽象类定义算法骨架,具体步骤由子类实现
-
代码复用:抽象类可以封装多个子类的公共代码,减少重复
-
部分实现:抽象类可以提供部分实现,子类只需关注特定功能的实现
2.4.3 抽象类和接口的区别总览
对比维度 | 抽象类 | 接口 |
---|---|---|
关键字 | abstract class | interface |
实例化 | 不能直接实例化 | 不能直接实例化 |
方法实现 | 可包含具体和抽象方法 | Java 8 前只能有抽象方法 |
变量类型 | 任意类型变量 | 只能是 public static final 常量 |
构造方法 | 有 | 无 |
继承/实现 | 单继承 | 多实现 |
设计目的 | 代码复用 + 规范定义 | 纯行为规范定义 |
访问修饰符 | 任意访问控制 | 默认 public |
多态支持 | 通过继承实现 | 通过实现实现 |
版本演进 | Java 1.0 存在 | Java 8/9 增强功能 |
设计模式应用 | 模板方法模式 | 策略模式、工厂模式等 |
性能影响 | 类加载开销 | 接口方法表(vtable)查找 |
1. 抽象类
public abstract class Animal {// 普通字段private String name;// 构造方法public Animal(String name) {this.name = name;}// 具体方法public void eat() {System.out.println(name + " is eating");}// 抽象方法public abstract void makeSound();
}
2. 接口
public interface Flyable {// 常量(默认 public static final)double MAX_ALTITUDE = 10000;// 抽象方法(默认 public abstract)void fly();// Java 8+ 默认方法default void emergencyLand() {System.out.println("Emergency landing");}// Java 8+ 静态方法static boolean canFly(Object obj) {return obj instanceof Flyable;}
}
2.4.4 抽象类的继承体系
1. 多级抽象
2. 与接口的组合使用
interface Flyable {void fly();
}abstract class Bird implements Flyable {public abstract void makeSound();@Overridepublic void fly() {System.out.println("Flapping wings");}
}class Eagle extends Bird {@Overridepublic void makeSound() {System.out.println("Screech!");}
}
2.5 小结
从基础篇过渡到核心技术篇可以明显感受到知识的难度变高了,架构也变深了。有些时候仅仅通过理解概念是远远不够的;但是举的例子有很可能还没学到,且核心技术篇各个区块知识存在一定关联,所以示例代码看不明白很正常。大部分的示例后面也还会遇到,第一次遇到留个印象就好了这个不用着急。