目录
一、final 关键字
final在 Java 中意为 “最终的、不可改变的”,可以修饰变量、方法、类,核心作用是限制修改,也是定义 “常量” 的核心关键字。
1. 修饰变量:变成 “常量”
- 被
final修饰的变量,赋值后就不能修改。
- 局部变量:声明后可以先不赋值,但只能赋值一次;
- 成员变量:必须在声明时 / 构造方法中赋值,且赋值后不可变;
- 常量命名规范:全大写,多个单词用下划线分隔(如
MAX_SCORE)。
代码示例:
public class FinalDemo {
// 1. 成员常量:声明时直接赋值
public static final int MAX_SCORE = 100; // 全局常量(常用static+final)
private final String USER_NAME; // 成员常量,构造方法中赋值
// 构造方法中给final成员变量赋值
public FinalDemo(String userName) {
this.USER_NAME = userName; // 只能赋值一次
}
public void testFinalVar() {
// 2. 局部常量:声明后赋值,且只能赋值一次
final int age;
age = 18;
// age = 20; // 报错:final变量赋值后不可修改
System.out.println(MAX_SCORE); // 输出100
System.out.println(USER_NAME); // 输出构造方法传入的值
}
public static void main(String[] args) {
FinalDemo demo = new FinalDemo("张三");
demo.testFinalVar();
// FinalDemo.MAX_SCORE = 90; // 报错:常量不可修改
}
}
2. 修饰方法:禁止子类重写
- 被
final修饰的方法,子类不能重写(防止核心逻辑被篡改)。
代码示例:
class Parent {
public final void sayHello() {
System.out.println("你好");
}
}
class Child extends Parent {
// @Override // 报错:无法重写final方法
// public void sayHello() {}
}
3. 修饰类:禁止被继承
- 被
final修饰的类,不能有子类(比如 Java 自带的String类就是 final 的,保证字符串不可变)。
代码示例:
final class FinalClass {}
// class SubClass extends FinalClass {} // 报错:无法继承final类
核心总结
final的核心是 “不可变”:
- 变量→常量(赋值后不能改);
- 方法→不能被重写;
- 类→不能被继承。
二、单例设计模式
单例模式是 Java 最常用的设计模式之一,核心目标:让一个类在程序运行期间,只能创建一个对象。
常见的单例模式有两种实现方式:饿汉式、懒汉式。
1. 饿汉式单例:“饿了就先吃”(立即加载)
- 类加载时就创建好实例,不管后续是否使用(优点:线程安全;缺点:可能浪费内存)。
代码示例:
// 饿汉式单例
public class HungrySingleton {
// 1. 私有构造方法:禁止外部new对象
private HungrySingleton() {}
// 2. 类加载时就创建唯一实例(静态变量)
private static final HungrySingleton INSTANCE = new HungrySingleton();
// 3. 提供公共方法获取实例
public static HungrySingleton getInstance() {
return INSTANCE;
}
// 测试方法
public void sayHi() {
System.out.println("饿汉式单例:我是唯一的实例");
}
public static void main(String[] args) {
// 获取两个实例,判断是否是同一个
HungrySingleton s1 = HungrySingleton.getInstance();
HungrySingleton s2 = HungrySingleton.getInstance();
System.out.println(s1 == s2); // 输出true:同一个对象
}
}
2. 懒汉式单例:“需要时再吃”(延迟加载)
- 类加载时不创建实例,只有第一次调用
getInstance()时才创建(优点:节省内存;缺点:基础版非线程安全)。
代码示例:
// 懒汉式单例(基础版,非线程安全)
public class LazySingleton {
// 1. 私有构造方法
private LazySingleton() {}
// 2. 声明实例变量,初始为null
private static LazySingleton INSTANCE = null;
// 3. 调用时才创建实例
public static LazySingleton getInstance() {
if (INSTANCE == null) { // 为空才创建
INSTANCE = new LazySingleton();
}
return INSTANCE;
}
public void sayHi() {
System.out.println("懒汉式单例:我是唯一的实例");
}
public static void main(String[] args) {
LazySingleton s1 = LazySingleton.getInstance();
LazySingleton s2 = LazySingleton.getInstance();
System.out.println(s1 == s2); // 输出true
}
}
饿汉式 vs 懒汉式
| 特性 | 饿汉式 | 懒汉式 |
|---|---|---|
| 加载时机 | 类加载时创建 | 第一次调用时创建 |
| 线程安全 | 安全 | 不安全 |
| 内存占用 | 启动时占用,可能浪费 | 按需加载,更节省 |
三、枚举
枚举(
enum)是 Java 5 新增的特性,本质是 “特殊的类”,用于定义一组固定的、有限的常量,相比普通常量更安全、更易读。
1. 认识枚举类
- 用
enum关键字声明,里面的每个值都是枚举类的实例(默认public static final)。
代码示例:
// 定义枚举类:季节
public enum Season {
// 枚举常量(每个都是Season的实例)
SPRING("春天", "春暖花开"),
SUMMER("夏天", "烈日炎炎"),
AUTUMN("秋天", "秋高气爽"),
WINTER("冬天", "寒风刺骨");
// 枚举类的成员变量(自定义属性)
private final String name;
private final String desc;
// 枚举的构造方法:必须私有(默认也是私有)
private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
// 提供get方法(枚举常量不可修改,无需set)
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
// 测试枚举
public static void main(String[] args) {
// 1. 直接使用枚举常量
Season spring = Season.SPRING;
System.out.println(spring.getName()); // 输出:春天
System.out.println(spring.getDesc()); // 输出:春暖花开
// 2. 遍历所有枚举常量
for (Season season : Season.values()) {
System.out.println(season + ":" + season.getDesc());
}
// 3. 枚举的switch用法(更简洁)
switch (spring) {
case SPRING:
System.out.println("当前是春天");
break;
case SUMMER:
System.out.println("当前是夏天");
break;
// 其他case...
}
}
}
2. 枚举的应用场景
- 固定状态:订单状态(待支付、已支付、已取消)、用户状态(正常、禁用、锁定);
- 有限选项:星期(周一到周日)、季节、性别;
- 替代常量:相比
public static final int SPRING = 1,枚举更安全,不会传错值; - 实现单例:枚举是实现单例的最佳方式。
四、抽象类
抽象类用
abstract关键字声明,是 “不完整的类”—— 里面可以包含抽象方法(只有声明,没有实现) 和普通方法,核心作用是 “定义模板,让子类实现细节”。
1. 抽象类的基本规则
- 抽象类不能被实例化(不能
new),只能被继承; - 包含抽象方法的类,必须是抽象类;
- 子类继承抽象类后,必须实现所有抽象方法(除非子类也是抽象类)。
代码示例:
// 抽象类:动物
public abstract class Animal {
// 普通方法(有实现)
public void breathe() {
System.out.println("所有动物都要呼吸");
}
// 抽象方法(只有声明,没有实现)
public abstract void makeSound();
}
// 子类:狗(实现抽象方法)
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("小狗汪汪叫");
}
}
// 子类:猫(实现抽象方法)
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("小猫喵喵叫");
}
}
// 测试
class TestAnimal {
public static void main(String[] args) {
// Animal animal = new Animal(); // 报错:抽象类不能实例化
Animal dog = new Dog();
dog.breathe(); // 输出:所有动物都要呼吸
dog.makeSound(); // 输出:小狗汪汪叫
Animal cat = new Cat();
cat.makeSound(); // 输出:小猫喵喵叫
}
}
2. 抽象类的好处
- 代码复用:抽象类中的普通方法可以被所有子类复用(比如上面的
breathe()); - 约束子类:抽象方法强制子类实现核心逻辑(比如所有动物都要实现
makeSound(),保证子类有统一的行为); - 定义骨架:结合 “模板方法模式”,固定核心流程,子类只改细节。
3. 模板方法设计模式
- 抽象类定义一个 “模板方法”,流程中的可变步骤交给子类实现。
代码示例:
// 抽象类:饮品制作模板
public abstract class DrinkTemplate {
// 模板方法:固定制作流程(final防止子类修改)
public final void makeDrink() {
boilWater(); // 步骤1:烧水(固定)
brew(); // 步骤2:冲泡(可变,子类实现)
pourInCup(); // 步骤3:倒入杯中(固定)
addCondiments(); // 步骤4:加调料(可变,子类实现)
}
// 固定方法:烧水
private void boilWater() {
System.out.println("烧开水(100℃)");
}
// 固定方法:倒入杯中
private void pourInCup() {
System.out.println("将饮品倒入杯中");
}
// 抽象方法:冲泡(子类实现)
public abstract void brew();
// 抽象方法:加调料(子类实现)
public abstract void addCondiments();
}
// 子类:制作咖啡
class Coffee extends DrinkTemplate {
@Override
public void brew() {
System.out.println("冲泡咖啡粉");
}
@Override
public void addCondiments() {
System.out.println("加牛奶和糖");
}
}
// 子类:制作茶
class Tea extends DrinkTemplate {
@Override
public void brew() {
System.out.println("冲泡茶叶");
}
@Override
public void addCondiments() {
System.out.println("加柠檬");
}
}
// 测试
class TestDrink {
public static void main(String[] args) {
DrinkTemplate coffee = new Coffee();
System.out.println("制作咖啡:");
coffee.makeDrink(); // 按模板流程制作咖啡
System.out.println("========");
DrinkTemplate tea = new Tea();
System.out.println("制作茶:");
tea.makeDrink(); // 按模板流程制作茶
}
}
输出结果:
制作咖啡:
烧开水(100℃)
冲泡咖啡粉
将饮品倒入杯中
加牛奶和糖
========
制作茶:
烧开水(100℃)
冲泡茶叶
将饮品倒入杯中
加柠檬
抽象类的应用场景
- 多个类有共同的核心流程,但细节不同(比如上面的饮品制作);
- 需要定义统一的父类,约束子类的行为,但又不想让父类被实例化;
- 代码复用 + 行为约束的场景。
五、接口
接口(
interface)是 Java 中 “纯抽象” 的规范,核心作用是定义契约(规则),让实现类遵守规则,是实现 “多态” 和 “解耦” 的核心。
1. 认识接口
- 用
interface关键字声明; - JDK8 前:只能包含
public static final常量 +public abstract方法; - JDK8 后:新增默认方法、静态方法、私有方法;
- 类用
implements实现接口,必须实现所有抽象方法(除非是抽象类)。
代码示例:
// 定义接口:跑
public interface Runable {
// 接口中的常量(默认public static final)
int MAX_SPEED = 100;
// 抽象方法(默认public abstract)
void run();
}
// 实现类:人
class Person implements Runable {
@Override
public void run() {
System.out.println("人用双腿跑,最高速度:" + MAX_SPEED);
}
}
// 实现类:汽车
class Car implements Runable {
@Override
public void run() {
System.out.println("汽车用轮子跑,最高速度:" + MAX_SPEED);
}
}
// 测试
class TestRunable {
public static void main(String[] args) {
Runable person = new Person();
person.run(); // 输出:人用双腿跑,最高速度:100
Runable car = new Car();
car.run(); // 输出:汽车用轮子跑,最高速度:100
}
}
2. 接口的好处
- 解耦:接口定义规则,实现类负责实现,修改实现类不影响调用方;
- 多实现:Java 类只能单继承,但可以实现多个接口(弥补单继承的不足);
- 标准化:统一接口,不同实现类遵循同一规则;
- 多态:接口引用指向实现类对象,提高代码灵活性。
3. JDK8 新增的三个方法
- JDK8 为接口新增了 3 种方法,让接口更灵活(不用强制所有实现类修改):
| 方法类型 | 关键字 | 特点 |
|---|---|---|
| 默认方法 | default | 有方法体,实现类可重写,也可直接使用 |
| 静态方法 | static | 有方法体,只能通过接口名调用(不能被实现类重写) |
| 私有方法 | private | 有方法体,只能在接口内部调用(供默认方法 / 静态方法复用) |
代码示例:
public interface JDK8Interface {
// 1. 抽象方法
void abstractMethod();
// 2. 默认方法
default void defaultMethod() {
System.out.println("接口默认方法");
privateMethod(); // 调用私有方法
}
// 3. 静态方法
static void staticMethod() {
System.out.println("接口静态方法");
privateStaticMethod(); // 调用私有静态方法
}
// 4. 私有方法(非静态)
private void privateMethod() {
System.out.println("接口私有方法:供默认方法复用");
}
// 5. 私有静态方法
private static void privateStaticMethod() {
System.out.println("接口私有静态方法:供静态方法复用");
}
}
// 实现类
class JDK8Impl implements JDK8Interface {
@Override
public void abstractMethod() {
System.out.println("实现抽象方法");
}
// 可选:重写默认方法
@Override
public void defaultMethod() {
System.out.println("重写接口的默认方法");
}
}
// 测试
class TestJDK8Interface {
public static void main(String[] args) {
JDK8Interface impl = new JDK8Impl();
impl.abstractMethod(); // 输出:实现抽象方法
impl.defaultMethod(); // 输出:重写接口的默认方法
// 静态方法只能通过接口名调用
JDK8Interface.staticMethod(); // 输出:接口静态方法 + 接口私有静态方法
}
}
4. 接口的注意事项
- 接口不能被实例化(不能
new); - 接口的成员:
- 变量:默认
public static final(必须初始化); - 方法(JDK8 前):默认
public abstract;
- 变量:默认
- 一个类可以实现多个接口(用逗号分隔);
- 接口可以继承多个接口(用
extends); - 默认方法冲突:如果实现类同时实现两个接口,且接口有同名默认方法,必须重写该方法。
接口 vs 抽象类
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 关键字 | abstract class | interface |
| 继承 / 实现 | 类用 extends 单继承 | 类用 implements 多实现 |
| 方法 | 可包含抽象方法 + 普通方法 | JDK8 前:只有抽象方法;JDK8 后:可包含默认 / 静态 / 私有方法 |
| 成员变量 | 可定义任意变量 | 只能是 public static final 常量 |
| 构造方法 | 有构造方法(不能实例化) | 无构造方法 |
| 核心作用 | 代码复用 + 定义骨架 | 定义契约 + 解耦 |
| 使用场景 | 子类有共同的父类,共享代码 | 不同类需要遵守同一规则,解耦 |
总结
final:修饰变量→常量、修饰方法→不可重写、修饰类→不可继承;- 单例模式:饿汉式(立即加载,线程安全)、懒汉式(延迟加载,基础版非线程安全),核心是保证类只有一个实例;
- 枚举:定义固定常量集,类型安全,适合状态 / 选项类场景;
- 抽象类:包含抽象方法,不能实例化,模板方法模式是经典应用,核心是 “骨架 + 可变细节”;
- 接口:定义契约,支持多实现,JDK8 新增默认 / 静态 / 私有方法,和抽象类的核心区别是 “契约 vs 复用”。

1万+

被折叠的 条评论
为什么被折叠?



