final
final修饰变量
基本用法
被final修饰的变量不可以被改变,又被称为常量
java
final double PI = 3.14159;
PI = 3.14; // 编译错误:不能再次赋值
空白 final(Blank final)
声明时不赋初值,但必须在构造方法或初始化块中完成赋值,且只能赋值一次
java
class Circle {
final double PI; // 空白 final
Circle() {
PI = 3.14159; // 构造方法中赋值
}
}
final 修饰引用类型变量
引用变量本身不能再指向其他对象
但对象的内容(成员变量)仍可改变(除非对象的类做了不可变设计)
java
final StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // ✅ 允许,对象内容改变
sb = new StringBuilder(); // ❌ 错误:引用不能改变
final 修饰成员变量
实例变量:必须显式赋值或在构造方法中赋值
静态变量:必须显式赋值或在静态代码块中赋值
java
class Example {
final int a = 10; // 直接赋值
final int b; // 空白 final,需在构造器中赋值
static final int C = 100; // 静态常量
static final int D; // 空白 static final,需在静态块中赋值
Example() { b = 20; }
static { D = 200; }
}
局部变量
局部变量被 final 修饰后,值不能改变
java
public void method() {
final int x = 5;
x = 6; // 错误
}
方法参数
方法参数被 final 修饰后,在方法体内不能改变参数引用
java
public void print(final int value) {
// value = 10; // 错误
System.out.println(value);
}
final修饰方法
被final修饰的方法不可以被重写
主要目的:防止子类修改该方法的行为
注意:final 方法仍可以被继承,只是子类不能重写
java
class Parent {
public final void show() {
System.out.println("Parent show");
}
}
class Child extends Parent {
// public void show() { } // 编译错误:不能重写 final 方法
}
private 方法隐式是 final 的(因为子类不可见),但通常不写 final构造方法不能是 final(构造方法不能被继承)。
final修饰类
被final修饰的类不可以被继承
典型例子:String、Integer、System 等
java
final class FinalClass {
// 类体
}
// class SubClass extends FinalClass { } // 编译错误:不能继承 final 类
final 类的所有方法隐式也是 final 的(因为无法被重写),但成员变量可以是普通的
final 与 static 的区别
| 特性 | final |
static |
|---|---|---|
| 作用 | 不可改变(值、方法、类) | 属于类,不依赖对象 |
| 修饰变量 | 只能赋值一次(常量) | 类变量,所有对象共享一份 |
| 修饰方法 | 不能被子类重写 | 类方法,不能被子类重写(隐藏) |
| 修饰类 | 不能被继承 | 不能修饰外部类(可修饰内部类) |
| 常见组合 | static final 表示全局常量 |
- |
全局变量通常写成 public static final,例如 Math.PI
java
public static final double PI = 3.141592653589793;
总结就是不可变
final 与不可变性(Immutability)
final 只是保证引用不可变,并不保证对象内容不可变
真正的不可变类要求:
1.类声明为 final(防止子类修改行为)
2.所有成员变量为 private final
3.不提供任何修改成员变量的方法(setter)
4.如果有可变成员(如 Date、集合),应返回其副本或不可变视图
java
public final class ImmutablePerson {
private final String name;
private final int age;
public ImmutablePerson(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
}
抽象
抽象:现实中不存在的东西
在Java中的抽象表示只声明,不实现
抽象方法
抽象方法:使用abstract来修饰,不可以有方法体,直接用分号结束
抽象类:如果一个类中有抽象方法,那么这个类必须是一个抽象类
特点:
1.抽象类不可以创建对象
2.抽象类的子类,必须重写父类中的抽象方法,否则子类必须也是抽象类
3.抽象方法不能被 private、final、static 修饰
抽象类
通过抽象类可以强制要求子类中必须有哪些方法
抽象类不能被实例化 (不能 new)
抽象类的构造方法用于被子类调用(super)
抽象类可以没有抽象方法(但通常至少有一个抽象方法)
抽象类可以包含成员变量、常量、构造方法、具体方法、静态方法等
抽象类中可以有正常的方法
java
public abstract class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
// 抽象方法
public abstract void sound();
// 具体方法
public void sleep() {
System.out.println(name + "在睡觉");
}
}
继承抽象类
子类继承抽象类后,必须:
实现所有抽象方法(除非子类也是抽象类)
可以使用 super 调用父类构造方法
java
class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void sound() {
System.out.println(name + "汪汪叫");
}
}
class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void sound() {
System.out.println(name + "喵喵喵");
}
}
常见误区
1.误区:抽象类必须包含抽象方法。
正解:抽象类可以没有抽象方法(例如作为工具类禁止实例化)。
2.误区:抽象类不能有构造方法。
正解:有构造方法,用于子类初始化。
3.误区:abstract 和 final 可以同时修饰方法。
正解:不能,final 方法不能被重写,而抽象方法必须被重写,矛盾。
4.误区:抽象类可以实例化匿名内部类。
正解:这是创建了匿名子类的对象,不是抽象类本身的实例。
接口
接口实际上是一种特殊的抽象类
接口中所有的方法都是抽象方法
接口使用interface来声明,接口中的方法默认是 public abstract(可省略),成员变量默认是 public static final(常量)
java
public interface Animal {
// 常量(默认 public static final)
String TYPE = "动物";
// 抽象方法(默认 public abstract)
void eat();
void sleep();
}
类只能单继承,接口支持多实现
使用 implements 关键字,一个类可以实现多个接口(多实现)。必须实现所有抽象方法(除非该类是抽象类)
java
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
@Override
public void sleep() {
System.out.println("狗睡觉");
}
}
接口同样具有多态性
接口可以把很多不相关的内容进行整合
特点:
1.接口中所有的方法都是抽象方法,都是公开的
2.接口中所有的变量都是全局静态变量
| 特性 | 说明 |
|---|---|
| 接口不能实例化 | 不能 new 接口名(),但可以用实现类对象赋值给接口引用(多态) |
| 接口没有构造方法 | 不能被 new,也不需要有构造方法 |
| 接口中的方法默认修饰符 | public abstract(可省略) |
| 接口中的成员变量默认修饰符 | public static final(可省略),必须显式赋值 |
| 一个类可以实现多个接口 | 解决 Java 单继承的局限 |
| 接口可以继承多个接口 | 使用 extends,接口之间可以多继承 |
| 接口可以包含默认方法 | Java 8 引入,使用 default 关键字,提供默认实现,不强制实现类重写 |
| 接口可以包含静态方法 | Java 8 引入,使用 static 关键字,通过接口名调用 |
| 接口可以包含私有方法 | Java 9 引入,用于默认方法或静态方法内部复用代码 |
接口的多实现
1.一个类可以实现多个接口。
2.如果多个接口有相同签名的默认方法,实现类必须重写该方法(或指定调用哪个父接口的默认方法)。
3.如果多个接口有相同签名的抽象方法,实现类只需实现一次
java
interface Flyable {
void fly();
}
interface Swimmable {
void swim();
}
class Duck implements Flyable, Swimmable {
@Override
public void fly() { System.out.println("鸭子飞"); }
@Override
public void swim() { System.out.println("鸭子游泳"); }
}
接口与抽象类的区别
| 特性 | 接口(interface) | 抽象类(abstract class) |
|---|---|---|
| 关键字 | interface |
abstract class |
| 实例化 | 不能 | 不能 |
| 成员变量 | 默认 public static final(常量) |
可以是普通变量、常量 |
| 构造方法 | 无 | 有,供子类调用 |
| 抽象方法 | 默认 public abstract |
可以是 protected、public 等 |
| 具体方法 | Java 8+ 可以有 default/static 方法 |
可以有任意具体方法 |
| 私有方法 | Java 9+ 允许 | 允许 |
| 继承/实现 | 一个类可以实现多个接口 | 一个类只能继承一个抽象类(单继承) |
| 设计意图 | 定义"能做什么"(can-do)能力契约 | 定义"是什么"(is-a),复用代码 |
常见误区
| 误区 | 正确理解 |
|---|---|
| 接口中不能有方法体 | Java 8+ 可以有 default 和 static 方法体 |
接口中的方法必须是 public |
默认就是 public,不能是 protected / private(除非 Java 9+ 的私有方法) |
| 接口可以实例化 | 不能,但可以用匿名内部类创建接口的匿名实现类 |
| 接口中的变量可以修改 | 接口中的变量默认是 final,不能修改 |
| 实现接口必须重写所有方法 | 如果实现类是抽象类,可以不重写 |