Java 面向对象核心知识总结:static、继承与final
一、static关键字
1. 核心作用
static 是类级别的修饰符,用于标记成员(变量、方法、代码块)属于类本身,而非类的实例对象,实现“所有对象共享同一资源”的效果。
2. static修饰变量(静态变量/类变量)
特点
- 存储位置:存于 JVM 元空间(方法区),而非堆内存的对象中。
- 生命周期:随类加载而创建,随类卸载而销毁(生命周期长于对象)。
- 共享性:该类所有实例共享同一静态变量,一个对象修改后,所有对象可见。
- 访问方式:推荐
类名.变量名(如Student.total),也可通过对象访问(不推荐,易混淆)。
适用场景
- 全局计数器(如统计对象创建数量)。
- 定义常量(需结合
final,如public static final double PI = 3.14159)。
代码示例
class Student {
String name; // 实例变量(每个对象独有)
static int totalStudents; // 静态变量(所有对象共享)
public Student(String name) {
this.name = name;
totalStudents++; // 每创建一个学生,总数+1
}
}
public class TestStaticVar {
public static void main(String[] args) {
System.out.println("初始学生总数:" + Student.totalStudents); // 0(类加载时初始化)
Student s1 = new Student("张三");
System.out.println("创建s1后总数:" + Student.totalStudents); // 1
Student s2 = new Student("李四");
System.out.println("创建s2后总数:" + Student.totalStudents); // 2
// 不推荐:通过对象访问静态变量
System.out.println("通过s1访问总数:" + s1.totalStudents); // 2
}
}3. static修饰方法(静态方法/类方法)
特点
- 调用方式:无需创建对象,直接
类名.方法名()(如Math.abs(-5))。 访问限制:
- 不能直接访问非静态成员(变量/方法),因静态方法加载时实例可能未创建。
- 不能使用
this或super关键字(无“当前对象”绑定)。
适用场景
- 工具类方法(如
Math类的sqrt()、Arrays类的sort())。 - 与类相关但不依赖实例状态的操作(如对象创建校验)。
代码示例
class Calculator {
// 静态方法(工具方法,不依赖实例状态)
public static int add(int a, int b) {
return a + b;
}
// 非静态方法(依赖实例,需创建对象调用)
public int subtract(int a, int b) {
return a - b;
}
}
public class TestStaticMethod {
public static void main(String[] args) {
// 直接调用静态方法
int sum = Calculator.add(5, 10);
System.out.println("和:" + sum); // 15
// 调用非静态方法:需先创建对象
Calculator calc = new Calculator();
int diff = calc.subtract(10, 5);
System.out.println("差:" + diff); // 5
}
}4. static注意事项
- 内存问题:静态变量生命周期长,若持有大量引用对象未清理,可能导致内存泄漏。
- 滥用风险:过度使用静态成员会增加代码耦合度,降低可测试性(静态方法无法被Mock)。
- 静态导入:通过
import static可直接使用静态成员,无需类名(如import static java.lang.Math.PI)。
二、继承
1. 核心概念
继承是面向对象四大特性之一,允许子类(派生类)通过 extends 关键字继承父类(基类)的非私有成员(属性/方法),实现代码复用和类层次结构设计。
2. 继承的核心特点
(1)单一继承
Java 不支持多继承(子类不能同时继承多个父类),但可通过接口实现“类似多继承”的效果(如 class A implements B, C)。
错误示例:class A extends B, C {}(编译报错)。
(2)多层继承(层次继承)
子类可作为父类被再次继承,形成类链。例如:A extends B,B extends C,则 A 间接继承 C 的成员。
(3)传递性
若 B 继承 A,C 继承 B,则 C 不仅拥有 B 的成员,还拥有 A 的非私有成员。
代码示例(多层继承与传递性)
// 顶层父类 A
class A {
public int a = 1;
public String name = "张三";
}
// 子类 B 继承 A
class B extends A {
public int b = 2;
// 重写父类 name(就近原则)
public String name = "李四";
}
// 子类 C 继承 B(多层继承)
class C extends B {
public int c = 3;
// 重写父类 name
public String name = "王五";
// 访问不同层级的 name
public void show(String name) {
System.out.println("局部name:" + name); // 方法参数(局部)
System.out.println("本类name:" + this.name); // C类的name
System.out.println("父类B的name:" + super.name); // B类的name
System.out.println("祖父类A的name:" + super.super.name); // 错误!super不能跨层,需通过B类间接访问
}
}
public class TestInheritance {
public static void main(String[] args) {
C c = new C();
System.out.println("a=" + c.a + ", b=" + c.b + ", c=" + c.c); // 1,2,3(传递性)
c.show("局部变量"); // 调用show方法,验证就近原则
}
}3. 继承中成员的访问规则
(1)成员变量:就近原则
访问顺序:局部变量 → 本类成员变量 → 父类成员变量(未找到则编译报错)。
- 若父子类变量重名,子类默认访问本类变量;
- 需访问父类变量时,用
super.父类变量名。
(2)成员方法:动态绑定(运行看右边)
访问顺序:子类重写方法 → 父类方法(未找到则报错)。
- 方法调用时,实际执行的是“对象真实类型”的方法(多态基础);
- 需访问父类方法时,用
super.父类方法名()。
(3)访问控制修饰符
子类仅能访问父类的 public、protected 成员,private 成员完全不可见(需通过父类的 getter/setter 访问)。
| 修饰符 | 本类 | 同包 | 子类 | 任意类 |
|---|---|---|---|---|
| private | ✔ | ❌ | ❌ | ❌ |
| 缺省 | ✔ | ✔ | ❌ | ❌ |
| protected | ✔ | ✔ | ✔ | ❌ |
| public | ✔ | ✔ | ✔ | ✔ |
4. 方法重写(Override)
核心概念
子类定义与父类方法名、参数列表完全一致的方法,覆盖父类方法的实现(需满足访问权限和返回值规则)。
重写规则
- 访问权限:子类方法权限 ≥ 父类方法(如父类
protected,子类可protected或public)。 返回值类型:
- 基本类型/void:必须与父类完全一致;
- 引用类型:子类返回值类型 ≤ 父类(如父类返回
Object,子类可返回String)。
- 不可重写的方法:
private方法(子类不可见)、static方法(属于类,不参与动态绑定)。 - 注解:推荐添加
@Override,让编译器校验重写语法正确性。
代码示例(方法重写)
// 父类 Animal
class Animal {
// 父类方法(protected权限)
protected void sound() {
System.out.println("动物发出声音");
}
}
// 子类 Dog 重写 sound 方法
class Dog extends Animal {
// 访问权限:public ≥ protected(符合规则)
@Override
public void sound() {
System.out.println("狗叫:汪汪汪");
}
}
// 子类 Cat 重写 sound 方法
class Cat extends Animal {
@Override
public void sound() {
System.out.println("猫叫:喵喵喵");
}
}
public class TestOverride {
public static void main(String[] args) {
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.sound(); // 运行Dog的sound:汪汪汪(动态绑定)
a2.sound(); // 运行Cat的sound:喵喵喵(动态绑定)
}
}5. 构造器的调用顺序
核心规则
创建子类对象时,父类构造器先执行,子类构造器后执行(确保父类成员先初始化)。
细节说明
- 默认调用:子类构造器第一行隐含
super()(调用父类无参构造器); - 显式调用:若父类无无参构造器,子类必须在构造器第一行显式调用
super(参数)(指定父类有参构造器); - this与super冲突:
this(...)(调用本类其他构造器)和super(...)不能同时存在于构造器第一行。
代码示例(构造器调用顺序)
// 父类 Car
class Car {
private String brand;
private double price;
// 父类无参构造器
public Car() {
System.out.println("Car无参构造器执行");
}
// 父类有参构造器
public Car(String brand, double price) {
this.brand = brand;
this.price = price;
System.out.println("Car有参构造器执行(品牌:" + brand + ",价格:" + price + ")");
}
}
// 子类 Truck 继承 Car
class Truck extends Car {
private double load;
// 子类无参构造器(隐含 super())
public Truck() {
// super(); // 隐含,调用父类无参构造器
System.out.println("Truck无参构造器执行");
}
// 子类有参构造器(显式调用父类有参构造器)
public Truck(String brand, double price, double load) {
super(brand, price); // 显式调用父类有参构造器
this.load = load;
System.out.println("Truck有参构造器执行(载重:" + load + "吨)");
}
}
public class TestConstructorOrder {
public static void main(String[] args) {
System.out.println("--- 创建Truck无参对象 ---");
new Truck(); // 先执行Car无参,再执行Truck无参
System.out.println("\n--- 创建Truck有参对象 ---");
new Truck("东风", 280000, 10.5); // 先执行Car有参,再执行Truck有参
}
}运行结果
--- 创建Truck无参对象 ---
Car无参构造器执行
Truck无参构造器执行
--- 创建Truck有参对象 ---
Car有参构造器执行(品牌:东风,价格:280000.0)
Truck有参构造器执行(载重:10.5吨)6. this与super的区别
| 关键字 | 访问成员变量 | 访问成员方法 | 访问构造器 |
|---|---|---|---|
| this | this.本类变量 | this.本类方法() | this() / this(参数)(本类) |
| super | super.父类变量 | super.父类方法() | super() / super(参数)(父类) |
三、final关键字
1. 核心作用
final 表示“最终的、不可变的”,可修饰类、方法、变量,核心是禁止修改。
2. final修饰类
- 效果:类不可被继承(最终类),避免子类篡改核心逻辑。
- 典型示例:Java 标准库的
String类(确保字符串不可变性)。 代码示例:
final class FinalClass { // 最终类,不可被继承 public void show() { System.out.println("这是final类的方法"); } } // class SubClass extends FinalClass {} // 编译报错:Cannot inherit from final 'FinalClass'
3. final修饰方法
- 效果:方法不可被子类重写,确保核心逻辑稳定性。
- 注意:
final方法可被继承和重载,但不能被重写。 代码示例:
class Parent { // final方法,不可重写 public final void finalMethod() { System.out.println("父类final方法"); } // 普通方法,可重写 public void normalMethod() { System.out.println("父类普通方法"); } } class Child extends Parent { // @Override // public void finalMethod() {} // 编译报错:Cannot override the final method from Parent // 重写普通方法 @Override public void normalMethod() { System.out.println("子类重写的普通方法"); } }
4. final修饰变量
核心规则
变量初始化后不可再次赋值,具体分两种类型:
- 基本类型:值不可变(如
final int num = 10; num = 20;编译报错); - 引用类型:引用地址不可变,但对象内部内容可修改(如
final List<String> list = new ArrayList<>(); list.add("a");合法,list = new ArrayList<>();编译报错)。
初始化时机
- 局部变量:必须在使用前显式初始化(可先声明后赋值,仅一次);
- 成员变量:必须在声明时、非静态初始化块、构造器中初始化(三者选其一);
- 静态变量:必须在声明时、静态初始化块中初始化。
代码示例
import java.util.ArrayList;
import java.util.List;
public class TestFinalVar {
// 1. 成员变量:声明时初始化
final String memberVar1 = "声明时赋值";
// 2. 成员变量:初始化块中赋值
final String memberVar2;
{
memberVar2 = "初始化块赋值";
}
// 3. 成员变量:构造器中赋值
final String memberVar3;
public TestFinalVar(String var3) {
this.memberVar3 = var3; // 构造器赋值
}
// 4. 静态变量:静态初始化块赋值
static final int staticVar;
static {
staticVar = 100;
}
public static void main(String[] args) {
// 局部变量:先声明后赋值(仅一次)
final int localVar;
localVar = 5;
// localVar = 6; // 编译报错:不可二次赋值
// 引用类型变量:地址不可变,内容可改
final List<String> list = new ArrayList<>();
list.add("Java"); // 合法:修改内容
// list = new ArrayList<>(); // 编译报错:不可修改地址
// 测试成员变量
TestFinalVar obj = new TestFinalVar("构造器赋值");
System.out.println(obj.memberVar1); // 声明时赋值
System.out.println(obj.memberVar2); // 初始化块赋值
System.out.println(obj.memberVar3); // 构造器赋值
System.out.println(staticVar); // 100
}
}5. final的使用场景
| 场景 | 示例 | 目的 |
|---|---|---|
| 定义常量 | public static final double PI = 3.14 | 避免魔法值,提高可维护性 |
| 保护核心方法 | public final void validate() | 防止子类修改核心校验逻辑 |
| 设计不可变类 | final class String | 确保对象状态安全(如线程安全) |
| 固定对象引用 | final User user = new User() | 防止意外修改引用指向 |
四、常见面试题与作业
1. 面试题(判断题)
- Java支持多继承?(×,仅支持单一继承)
- 子类可直接访问父类的private成员?(×,需通过getter/setter)
- final修饰的引用类型变量,其指向的对象内容不可修改?(×,仅地址不可改)
- 子类构造器默认调用父类的无参构造器?(√,隐含super())
- 方法重写时,子类方法的访问权限必须大于父类?(×,≥即可)
2. 作业题:黑马管理系统角色设计
需求
- 设计
Employee(员工)、Teacher(讲师)、Consultant(咨询师)类,Teacher和Consultant继承Employee; Employee有name、age、salary字段和work()方法;Teacher新增subject字段和teach()方法;Consultant新增special字段和advice()方法;- 创建测试类,实例化对象并调用方法。
解答代码
// 父类:Employee
class Employee {
public String name;
public int age;
public double salary;
// 构造器
public Employee(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
// 父类方法
public void work() {
System.out.println(name + "(" + age + "岁,薪资:" + salary + ")正在工作");
}
}
// 子类:Teacher(讲师)
class Teacher extends Employee {
public String subject; // 新增字段:教授学科
// 构造器:调用父类构造器
public Teacher(String name, int age, double salary, String subject) {
super(name, age, salary);
this.subject = subject;
}
// 新增方法:授课
public void teach() {
System.out.println(name + "教授" + subject + "课程");
}
// 重写父类work方法
@Override
public void work() {
System.out.println(name + "(讲师)负责" + subject + "教学,薪资:" + salary);
}
}
// 子类:Consultant(咨询师)
class Consultant extends Employee {
public String special; // 新增字段:咨询领域
// 构造器:调用父类构造器
public Consultant(String name, int age, double salary, String special) {
super(name, age, salary);
this.special = special;
}
// 新增方法:提供咨询
public void advice() {
System.out.println(name + "提供" + special + "领域咨询");
}
// 重写父类work方法
@Override
public void work() {
System.out.println(name + "(咨询师)专注" + special + "咨询,薪资:" + salary);
}
}
// 测试类
public class TestEmployeeSystem {
public static void main(String[] args) {
// 实例化讲师
Teacher t = new Teacher("张三", 30, 8000.0, "Java");
t.work(); // 调用重写的work方法
t.teach(); // 调用子类特有方法
// 实例化咨询师
Consultant c = new Consultant("李四", 35, 12000.0, "企业管理");
c.work(); // 调用重写的work方法
c.advice(); // 调用子类特有方法
// 多态:父类引用指向子类对象
Employee emp1 = new Teacher("王五", 28, 7500.0, "Python");
Employee emp2 = new Consultant("赵六", 32, 11000.0, "人力资源");
emp1.work(); // 执行Teacher的work(动态绑定)
emp2.work(); // 执行Consultant的work(动态绑定)
}
}运行结果
张三(讲师)负责Java教学,薪资:8000.0
张三教授Java课程
李四(咨询师)专注企业管理咨询,薪资:12000.0
李四提供企业管理领域咨询
王五(讲师)负责Python教学,薪资:7500.0
赵六(咨询师)专注人力资源咨询,薪资:11000.0