java快速学习速查(5)

这个部分包含了Java面向对象部分的全部数据类型,继承,重载,多态,抽象类,封装,接口,枚举,包,反射

接下来是详解部分:

Java 继承全面解析

继承是面向对象编程的三大特性之一(封装、继承、多态),下面我将系统地讲解 Java 继承的各种功能和使用场景。

一、继承基础

1. 基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Animal {
private String name;

public Animal(String name) {
this.name = name;
}

public void eat() {
System.out.println(name + "正在吃东西");
}
}

class Dog extends Animal {
private String breed;

public Dog(String name, String breed) {
super(name); // 调用父类构造方法
this.breed = breed;
}

public void bark() {
System.out.println("汪汪叫");
}

// 方法重写
@Override
public void eat() {
System.out.println(getName() + "(" + breed + ")正在啃骨头");
}

public String getBreed() {
return breed;
}
}

2. 继承的特点

  1. 子类拥有父类非 private 的属性和方法
  2. 子类可以添加自己的属性和方法
  3. 子类可以重写父类的方法
  4. Java 是单继承,一个类只能直接继承一个父类
  5. 构造方法不能被继承,但可以通过 super 调用

二、方法重写(Override)

1. 重写规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Shape {
public void draw() {
System.out.println("绘制形状");
}

public double getArea() {
return 0.0;
}
}

class Circle extends Shape {
private double radius;

public Circle(double radius) {
this.radius = radius;
}

@Override
public void draw() {
System.out.println("绘制圆形,半径: " + radius);
}

@Override
public double getArea() {
return Math.PI * radius * radius;
}
}

重写规则

  1. 方法名和参数列表必须相同
  2. 返回类型可以相同或是父类返回类型的子类
  3. 访问修饰符不能比父类更严格
  4. 不能抛出比父类更宽泛的异常

2. @Override 注解

  • 不是必须的,但建议使用
  • 帮助编译器检查是否满足重写条件
  • 提高代码可读性

三、super 关键字

1. 使用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Vehicle {
protected String brand;

public Vehicle(String brand) {
this.brand = brand;
}

public void start() {
System.out.println("车辆启动");
}
}

class Car extends Vehicle {
private int year;

public Car(String brand, int year) {
super(brand); // 调用父类构造方法
this.year = year;
}

@Override
public void start() {
super.start(); // 调用父类方法
System.out.println(year + "年款" + brand + "汽车启动");
}

public void showInfo() {
System.out.println("品牌: " + super.brand + ", 年份: " + year);
}
}

四、继承中的构造方法

1. 构造方法调用顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class GrandParent {
public GrandParent() {
System.out.println("GrandParent构造方法");
}
}

class Parent extends GrandParent {
public Parent() {
System.out.println("Parent构造方法");
}
}

class Child extends Parent {
public Child() {
System.out.println("Child构造方法");
}
}

// 测试
public class ConstructorTest {
public static void main(String[] args) {
new Child();
}
}
/*
输出:
GrandParent构造方法
Parent构造方法
Child构造方法
*/

2. super() 使用规则

  1. 必须出现在子类构造方法的第一行
  2. 如果没有显式调用 super(),编译器会自动添加无参 super()
  3. 如果父类没有无参构造方法,子类必须显式调用 super(参数)

五、final 关键字

1. final 用法

1
2
3
4
5
6
7
8
9
final class CannotInherit {  // 不能被继承
final int MAX_VALUE = 100; // 常量

final void cannotOverride() { // 不能被子类重写
System.out.println("这是最终方法");
}
}

// class TryExtend extends CannotInherit {} // 编译错误

六、Object 类

1. 常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Person {
private String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && name.equals(person.name);
}

@Override
public int hashCode() {
return Objects.hash(name, age);
}

@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}

七、抽象类与继承

1. 抽象类示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
abstract class Animal {
protected String name;

public Animal(String name) {
this.name = name;
}

public abstract void makeSound();

public void sleep() {
System.out.println(name + "正在睡觉");
}
}

class Cat extends Animal {
public Cat(String name) {
super(name);
}

@Override
public void makeSound() {
System.out.println(name + "说: 喵喵~");
}
}

八、继承与多态

1. 多态示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Employee {
protected String name;
protected double salary;

public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}

public double calculateBonus() {
return salary * 0.1;
}
}

class Manager extends Employee {
private double bonus;

public Manager(String name, double salary, double bonus) {
super(name, salary);
this.bonus = bonus;
}

@Override
public double calculateBonus() {
return salary * 0.15 + bonus;
}

public void manageTeam() {
System.out.println(name + "正在管理团队");
}
}

public class PolymorphismDemo {
public static void main(String[] args) {
Employee emp1 = new Employee("张三", 5000);
Employee emp2 = new Manager("李四", 8000, 2000);

System.out.println("张三的奖金: " + emp1.calculateBonus());
System.out.println("李四的奖金: " + emp2.calculateBonus());

// emp2.manageTeam(); // 编译错误,Employee类型没有manageTeam方法

if (emp2 instanceof Manager) {
Manager manager = (Manager) emp2;
manager.manageTeam();
}
}
}

九、继承最佳实践

1. 设计原则

  1. 里氏替换原则:子类应该能够替换父类而不影响程序正确性
  2. 优先使用组合而非继承:除非确实是”is-a”关系,否则考虑使用组合
  3. 避免过深的继承层次:通常不超过3层
  4. 将通用方法放在高层类:提高代码复用性
  5. 使用抽象类定义接口:为子类提供通用实现

2. 示例:图形类继承体系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
abstract class Shape {
protected String color;

public Shape(String color) {
this.color = color;
}

public abstract double getArea();
public abstract double getPerimeter();

@Override
public String toString() {
return "Shape[color=" + color + "]";
}
}

class Circle extends Shape {
private double radius;

public Circle(String color, double radius) {
super(color);
this.radius = radius;
}

@Override
public double getArea() {
return Math.PI * radius * radius;
}

@Override
public double getPerimeter() {
return 2 * Math.PI * radius;
}

@Override
public String toString() {
return "Circle[" + super.toString() + ",radius=" + radius + "]";
}
}

class Rectangle extends Shape {
private double length;
private double width;

public Rectangle(String color, double length, double width) {
super(color);
this.length = length;
this.width = width;
}

@Override
public double getArea() {
return length * width;
}

@Override
public double getPerimeter() {
return 2 * (length + width);
}

@Override
public String toString() {
return "Rectangle[" + super.toString() +
",length=" + length + ",width=" + width + "]";
}
}

public class ShapeDemo {
public static void main(String[] args) {
Shape[] shapes = {
new Circle("Red", 5.0),
new Rectangle("Blue", 4.0, 6.0)
};

for (Shape shape : shapes) {
System.out.println(shape);
System.out.println("Area: " + shape.getArea());
System.out.println("Perimeter: " + shape.getPerimeter());
System.out.println();
}
}
}

十、常见面试问题

  1. 继承和接口的区别

    • 继承:is-a关系,单继承,可以包含实现
    • 接口:can-do关系,多实现,只有抽象方法(Java 8前)
  2. 什么时候用继承

    • 当两个类之间有明显的is-a关系时
    • 需要复用父类代码时
    • 需要实现多态时
  3. 为什么Java不支持多继承

    • 避免”钻石问题”(菱形继承问题)
    • 简化语言设计,减少复杂性
  4. 构造方法能否被重写

    • 不能,构造方法不是成员方法
    • 子类构造方法必须调用父类构造方法
  5. 如何防止类被继承

    • 使用final修饰类
    • 将构造方法设为private,并提供静态工厂方法

Java 重写(Override)与重载(Overload)全面解析

下面我将系统地讲解 Java 中方法重写和方法重载的核心概念、使用场景和实际应用。

一、方法重写(Override)深度解析

1. 重写基础示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}

protected String getInfo() {
return "动物基本信息";
}
}

class Cat extends Animal {
// 正确重写 - 相同方法签名
@Override
public void makeSound() {
System.out.println("喵喵叫");
}

// 正确重写 - 返回类型是父类返回类型的子类
@Override
public String getInfo() {
return "猫的信息: " + super.getInfo();
}

// 编译错误 - 不能缩小访问权限
// @Override
// void makeSound() { ... }
}

public class OverrideDemo {
public static void main(String[] args) {
Animal myCat = new Cat();
myCat.makeSound(); // 输出"喵喵叫"
System.out.println(myCat.getInfo());
}
}

2. 重写规则详解

  1. 方法签名必须相同:方法名、参数列表完全一致
  2. 返回类型协变:Java 5+ 允许子类方法返回父类方法返回类型的子类
  3. 访问修饰符不能更严格:可以更宽松但不能更严格
  4. 异常限制
    • 不能抛出新的检查异常
    • 不能抛出比父类更宽泛的检查异常
    • 可以抛出更具体的检查异常或不抛出异常
    • 可以抛出任何非检查异常
  5. 不能重写 final/static/private 方法

3. @Override 注解的重要性

1
2
3
4
5
6
7
8
9
10
11
12
13
class Parent {
public void show(String msg) {
System.out.println("Parent: " + msg);
}
}

class Child extends Parent {
// 本意是重写,但拼写错误导致成为新方法
@Override // 加上这个注解会立即发现错误
public void sho(String msg) {
System.out.println("Child: " + msg);
}
}

二、方法重载(Overload)深度解析

1. 重载基础示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Calculator {
// 方法1
public int add(int a, int b) {
return a + b;
}

// 重载方法2 - 参数个数不同
public int add(int a, int b, int c) {
return a + b + c;
}

// 重载方法3 - 参数类型不同
public double add(double a, double b) {
return a + b;
}

// 重载方法4 - 参数顺序不同
public String add(String s, int n) {
return s + n;
}

public String add(int n, String s) {
return n + s;
}

// 不是重载 - 仅返回类型不同会导致编译错误
// public double add(int a, int b) { return a + b; }
}

public class OverloadDemo {
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println(calc.add(5, 3)); // 调用方法1
System.out.println(calc.add(5, 3, 2)); // 调用方法2
System.out.println(calc.add(2.5, 3.7)); // 调用方法3
System.out.println(calc.add("ID", 100)); // 调用方法4
System.out.println(calc.add(100, "ID")); // 调用方法5
}
}

2. 重载规则详解

  1. 必须改变参数列表
    • 参数数量不同
    • 参数类型不同
    • 参数顺序不同
  2. 可以改变的内容
    • 返回类型
    • 访问修饰符
    • 抛出异常
  3. 不能仅靠返回类型区分重载
  4. 自动类型转换影响重载解析

3. 重载解析过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class OverloadResolution {
public void process(int i) {
System.out.println("处理整数: " + i);
}

public void process(double d) {
System.out.println("处理浮点数: " + d);
}

public void process(String s) {
System.out.println("处理字符串: " + s);
}

public static void main(String[] args) {
OverloadResolution resolver = new OverloadResolution();
resolver.process(10); // 调用process(int)
resolver.process(10.0); // 调用process(double)
resolver.process("10"); // 调用process(String)
resolver.process('A'); // 调用process(int) - char自动转为int
resolver.process(10L); // 调用process(double) - long转为double
}
}

三、重写与重载对比

1. 核心区别对照表

特性 方法重写(Override) 方法重载(Overload)
发生位置 子类与父类之间 同一个类或父子类之间
方法签名 必须相同 必须不同
返回类型 相同或子类(协变返回) 可以不同
访问修饰符 不能比父类更严格 可以不同
异常抛出 不能更宽泛 可以不同
调用机制 运行时根据对象类型决定 编译时根据参数类型决定
多态性体现 子类替换父类行为 同一方法名处理不同类型参数

2. 典型场景示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class OverrideVsOverload {
static class Base {
// 可重载方法
public void execute(int num) {
System.out.println("Base execute with int: " + num);
}

// 可重写方法
public void show() {
System.out.println("Base show");
}
}

static class Derived extends Base {
// 重载父类方法
public void execute(String str) {
System.out.println("Derived execute with String: " + str);
}

// 重写父类方法
@Override
public void show() {
System.out.println("Derived show");
}
}

public static void main(String[] args) {
Base obj = new Derived();

// 重载方法调用 - 编译时决定
// obj.execute("test"); // 编译错误,Base没有execute(String)
((Derived)obj).execute("test"); // 需要向下转型

// 重写方法调用 - 运行时决定
obj.show(); // 输出"Derived show"
}
}

四、高级话题与应用场景

1. 构造方法的重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Person {
private String name;
private int age;

// 构造方法重载
public Person() {
this("无名氏", 18); // 调用另一个构造方法
}

public Person(String name) {
this(name, 18);
}

public Person(String name, int age) {
this.name = name;
this.age = age;
}

// 方法重载
public void introduce() {
System.out.println("我是" + name + ",今年" + age + "岁");
}

public void introduce(String greeting) {
System.out.println(greeting + ",我是" + name);
}
}

public class ConstructorOverload {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person("张三");
Person p3 = new Person("李四", 25);

p1.introduce();
p2.introduce("你好");
}
}

2. 重写 equals 和 hashCode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Student {
private String id;
private String name;

public Student(String id, String name) {
this.id = id;
this.name = name;
}

@Override
public boolean equals(Object o) {
// 1. 检查是否同一对象
if (this == o) return true;
// 2. 检查是否为null或类不同
if (o == null || getClass() != o.getClass()) return false;
// 3. 类型转换
Student student = (Student) o;
// 4. 比较关键字段
return id.equals(student.id) && name.equals(student.name);
}

@Override
public int hashCode() {
return Objects.hash(id, name);
}

@Override
public String toString() {
return "Student{id='" + id + "', name='" + name + "'}";
}
}

public class ObjectMethodOverride {
public static void main(String[] args) {
Student s1 = new Student("1001", "张三");
Student s2 = new Student("1001", "张三");
Student s3 = new Student("1002", "李四");

System.out.println("s1.equals(s2): " + s1.equals(s2)); // true
System.out.println("s1.equals(s3): " + s1.equals(s3)); // false
System.out.println("s1 hashCode: " + s1.hashCode());
System.out.println("s2 hashCode: " + s2.hashCode());
System.out.println("s1 toString: " + s1);
}
}

3. 桥接方法与重写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
interface Processor<T> {
void process(T t);
}

class StringProcessor implements Processor<String> {
// 编译器会生成桥接方法 void process(Object)
@Override
public void process(String s) {
System.out.println("处理字符串: " + s);
}
}

public class BridgeMethod {
public static void main(String[] args) {
Processor<String> processor = new StringProcessor();
processor.process("测试"); // 实际调用process(String)

// 通过反射查看方法
for (Method method : StringProcessor.class.getMethods()) {
if (method.getName().equals("process")) {
System.out.println(method + " is bridge: " + method.isBridge());
}
}
}
}

五、常见问题与最佳实践

1. 常见陷阱

  1. 意外重载而非重写

    1
    2
    3
    4
    5
    6
    7
    8
    class Parent {
    void doSomething(List<String> list) {}
    }

    class Child extends Parent {
    // 实际是重载而不是重写
    void doSomething(ArrayList<String> list) {}
    }
  2. **静态方法”重写”**:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Parent {
    static void staticMethod() {
    System.out.println("Parent static");
    }
    }

    class Child extends Parent {
    // 这是隐藏(hiding)而不是重写
    static void staticMethod() {
    System.out.println("Child static");
    }
    }

2. 最佳实践

  1. 总是使用 @Override 注解:避免意外重载而非重写
  2. 保持重写方法行为一致:遵守里氏替换原则
  3. 谨慎重载可变参数方法:容易导致混淆
  4. 避免过度重载:考虑使用不同方法名提高可读性
  5. 文档化重写方法:说明与父类方法的差异

Java 面向对象核心特性全面解析(多态、抽象类、封装和接口这四大面向对象特性)

下面我将依次详细讲解Java面向对象编程的四大核心特性:多态、抽象类、封装和接口。

一、多态(Polymorphism)

1. 多态基础

多态是指同一操作作用于不同对象时,可以有不同的解释和执行结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}

class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("汪汪叫");
}

public void fetch() {
System.out.println("叼回飞盘");
}
}

class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("喵喵叫");
}

public void scratch() {
System.out.println("挠沙发");
}
}

public class PolymorphismDemo {
public static void main(String[] args) {
// 编译时类型为Animal,运行时类型为具体子类
Animal myPet1 = new Dog();
Animal myPet2 = new Cat();

myPet1.makeSound(); // 输出"汪汪叫"
myPet2.makeSound(); // 输出"喵喵叫"

// myPet1.fetch(); // 编译错误,Animal类没有fetch方法

if (myPet1 instanceof Dog) {
((Dog)myPet1).fetch(); // 向下转型后调用子类特有方法
}
}

// 多态方法参数
public static void animalSound(Animal animal) {
animal.makeSound(); // 根据实际对象类型调用相应方法
}
}

2. 多态实现形式

  1. **方法重写(Override)**:子类重写父类方法
  2. **方法重载(Overload)**:同名不同参
  3. 接口实现:不同类实现同一接口
  4. 抽象类和抽象方法:提供统一接口,具体实现由子类完成

3. 多态的优势

  1. 可替换性:子类对象可以替换父类对象
  2. 可扩展性:新增子类不影响已有代码
  3. 灵活性:同一方法不同表现
  4. 简化性:统一接口处理不同对象

二、抽象类(Abstract Class)

1. 抽象类基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// 抽象类
abstract class Shape {
protected String color;

public Shape(String color) {
this.color = color;
}

// 抽象方法 - 无实现体
public abstract double getArea();

// 具体方法
public String getColor() {
return color;
}

// 可以包含静态方法
public static void printShapeInfo(Shape shape) {
System.out.println("颜色: " + shape.color);
System.out.println("面积: " + shape.getArea());
}
}

class Circle extends Shape {
private double radius;

public Circle(String color, double radius) {
super(color);
this.radius = radius;
}

@Override
public double getArea() {
return Math.PI * radius * radius;
}
}

class Rectangle extends Shape {
private double length;
private double width;

public Rectangle(String color, double length, double width) {
super(color);
this.length = length;
this.width = width;
}

@Override
public double getArea() {
return length * width;
}
}

public class AbstractClassDemo {
public static void main(String[] args) {
Shape circle = new Circle("红色", 5.0);
Shape rectangle = new Rectangle("蓝色", 4.0, 6.0);

Shape.printShapeInfo(circle);
Shape.printShapeInfo(rectangle);

// Shape shape = new Shape("绿色"); // 编译错误,抽象类不能实例化
}
}

2. 抽象类特点

  1. 不能被实例化:只能被继承
  2. 可以包含抽象方法:没有实现的方法,必须被子类实现
  3. 可以包含具体方法:有实现的方法,子类可以直接使用或重写
  4. 可以包含成员变量:可以是各种访问修饰符
  5. 构造方法:虽然不能实例化,但可以有构造方法供子类调用

3. 抽象类应用场景

  1. 定义通用接口:为相关类提供统一的操作规范
  2. 部分实现:提供部分通用实现,子类完成剩余部分
  3. 模板方法模式:定义算法骨架,具体步骤由子类实现

三、封装(Encapsulation)

1. 封装基础实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
class BankAccount {
// 私有字段 - 数据隐藏
private String accountNumber;
private double balance;
private String owner;

// 构造方法
public BankAccount(String accountNumber, String owner, double initialBalance) {
this.accountNumber = accountNumber;
this.owner = owner;
this.balance = initialBalance;
}

// 公共方法 - 控制访问
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("存款成功,当前余额: " + balance);
} else {
System.out.println("存款金额必须大于0");
}
}

public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("取款成功,当前余额: " + balance);
} else {
System.out.println("取款失败,金额无效或余额不足");
}
}

// Getter方法 - 受控访问私有字段
public double getBalance() {
return balance;
}

public String getAccountNumber() {
return accountNumber;
}

public String getOwner() {
return owner;
}

// Setter方法 - 受控修改私有字段
public void setOwner(String owner) {
if (owner != null && !owner.trim().isEmpty()) {
this.owner = owner;
}
}

// 不提供setBalance方法,防止随意修改余额
}

public class EncapsulationDemo {
public static void main(String[] args) {
BankAccount account = new BankAccount("123456789", "张三", 1000);

account.deposit(500); // 存款成功,当前余额: 1500.0
account.withdraw(200); // 取款成功,当前余额: 1300.0
account.withdraw(2000); // 取款失败,金额无效或余额不足

System.out.println("账户余额: " + account.getBalance());
// account.balance = 10000; // 编译错误,balance是私有的
}
}

2. 封装原则

  1. 最小访问原则:使用最严格的访问修饰符
  2. 数据隐藏:字段通常设为private
  3. 受控访问:通过public方法暴露必要操作
  4. 不变性保护:对不应修改的字段不提供setter

3. 封装优势

  1. 安全性:防止外部直接访问内部数据
  2. 灵活性:可以修改内部实现而不影响外部代码
  3. 可维护性:易于修改和扩展
  4. 数据验证:可以在方法中添加业务规则验证

四、接口(Interface)

1. 接口基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// 基本接口
interface Switchable {
// 常量 (默认 public static final)
int MAX_BRIGHTNESS = 100;

// 抽象方法 (默认 public abstract)
void turnOn();
void turnOff();

// Java 8+ 默认方法
default void adjustBrightness(int level) {
System.out.println("调整亮度至: " + Math.min(level, MAX_BRIGHTNESS));
}

// Java 8+ 静态方法
static void printMaxBrightness() {
System.out.println("最大亮度: " + MAX_BRIGHTNESS);
}
}

// 接口继承
interface SmartDevice extends Switchable {
void connectToWifi(String ssid);
void runApp(String appName);
}

// 类实现接口
class LightBulb implements Switchable {
@Override
public void turnOn() {
System.out.println("灯泡亮起");
}

@Override
public void turnOff() {
System.out.println("灯泡熄灭");
}
}

class SmartTV implements SmartDevice {
@Override
public void turnOn() {
System.out.println("智能电视开机");
}

@Override
public void turnOff() {
System.out.println("智能电视关机");
}

@Override
public void connectToWifi(String ssid) {
System.out.println("连接到WiFi: " + ssid);
}

@Override
public void runApp(String appName) {
System.out.println("运行应用: " + appName);
}

// 覆盖默认方法
@Override
public void adjustBrightness(int level) {
System.out.println("智能电视亮度调节至: " + level);
}
}

public class InterfaceDemo {
public static void main(String[] args) {
Switchable bulb = new LightBulb();
bulb.turnOn();
bulb.adjustBrightness(80); // 使用默认方法
bulb.turnOff();

SmartTV tv = new SmartTV();
tv.turnOn();
tv.connectToWifi("HomeWiFi");
tv.runApp("Netflix");
tv.adjustBrightness(60); // 使用重写的默认方法
tv.turnOff();

Switchable.printMaxBrightness(); // 调用接口静态方法
}
}

2. 接口特性

  1. 多继承:一个类可以实现多个接口
  2. 默认方法:Java 8+ 允许接口包含具体实现的方法
  3. 静态方法:Java 8+ 允许接口包含静态方法
  4. 私有方法:Java 9+ 允许接口包含私有方法
  5. 常量定义:接口中定义的变量默认是public static final

3. 接口与抽象类对比

特性 接口(Interface) 抽象类(Abstract Class)
实例化 不能 不能
方法实现 Java 8+ 可以有默认方法 可以有具体方法
字段 只能是常量(public static final) 可以是普通成员变量
构造方法 没有
多继承 一个类可实现多个接口 一个类只能继承一个抽象类
访问修饰符 方法默认public 方法可以有各种访问修饰符
设计目的 定义行为规范 提供通用实现和规范

五、四大特性综合应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// 封装 - 隐藏实现细节
abstract class Vehicle {
private String model; // 私有字段

protected Vehicle(String model) {
this.model = model;
}

// Getter方法控制访问
public String getModel() {
return model;
}

// 抽象方法 - 多态基础
public abstract void start();
public abstract void stop();

// 具体方法
public void displayInfo() {
System.out.println("车型: " + model);
}
}

// 接口定义额外能力
interface Electric {
void charge();
int getBatteryLevel();
}

// 具体类实现多态
class ElectricCar extends Vehicle implements Electric {
private int batteryLevel;

public ElectricCar(String model) {
super(model);
this.batteryLevel = 100;
}

@Override
public void start() {
System.out.println(getModel() + "电动车静音启动");
}

@Override
public void stop() {
System.out.println(getModel() + "电动车再生制动停止");
}

@Override
public void charge() {
batteryLevel = 100;
System.out.println(getModel() + "已充满电");
}

@Override
public int getBatteryLevel() {
return batteryLevel;
}

// 特有方法
public void autoPilot() {
System.out.println(getModel() + "自动驾驶模式激活");
}
}

public class OOPIntegration {
public static void main(String[] args) {
Vehicle[] vehicles = {
new ElectricCar("Tesla Model S"),
// 可以添加其他Vehicle子类
};

for (Vehicle vehicle : vehicles) {
vehicle.displayInfo();
vehicle.start();

if (vehicle instanceof Electric) {
Electric electric = (Electric) vehicle;
System.out.println("电量: " + electric.getBatteryLevel() + "%");
electric.charge();
}

if (vehicle instanceof ElectricCar) {
((ElectricCar)vehicle).autoPilot();
}

vehicle.stop();
System.out.println();
}
}
}

六、设计原则与最佳实践

1. SOLID原则

  1. **单一职责原则(SRP)**:一个类只负责一个功能领域
  2. **开闭原则(OCP)**:对扩展开放,对修改关闭
  3. **里氏替换原则(LSP)**:子类必须能替换父类
  4. **接口隔离原则(ISP)**:客户端不应依赖它不需要的接口
  5. **依赖倒置原则(DIP)**:依赖抽象而非具体实现

2. 面向对象设计技巧

  1. 优先使用组合而非继承:除非明确is-a关系
  2. 面向接口编程:提高灵活性和可扩展性
  3. 合理使用访问控制:遵循最小权限原则
  4. 避免过度设计:根据实际需求设计类结构
  5. 保持类和方法小巧:单一职责,高内聚低耦合

Java 枚举、包与反射全面解析

下面我将系统地讲解 Java 中枚举、包和反射的核心概念与使用场景。

一、枚举(Enum)

1. 枚举基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// 基本枚举定义
public enum Day {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}

// 带属性的枚举
public enum Planet {
MERCURY(3.303e+23, 2.4397e6),
VENUS(4.869e+24, 6.0518e6),
EARTH(5.976e+24, 6.37814e6);

private final double mass; // in kilograms
private final double radius; // in meters

Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}

public double surfaceGravity() {
return 6.67300E-11 * mass / (radius * radius);
}

public double surfaceWeight(double otherMass) {
return otherMass * surfaceGravity();
}
}

public class EnumDemo {
public static void main(String[] args) {
// 基本使用
Day today = Day.WEDNESDAY;
System.out.println("Today is: " + today);

// 遍历枚举
System.out.println("All days:");
for (Day day : Day.values()) {
System.out.println(day);
}

// 带属性的枚举使用
double earthWeight = 70; // kg
double mass = earthWeight / Planet.EARTH.surfaceGravity();
for (Planet p : Planet.values()) {
System.out.printf("Your weight on %s is %f%n",
p, p.surfaceWeight(mass));
}

// switch语句中使用枚举
switch (today) {
case MONDAY:
System.out.println("星期一综合症");
break;
case FRIDAY:
System.out.println("感谢上帝,今天是星期五");
break;
default:
System.out.println("普通工作日");
}
}
}

2. 枚举高级特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public enum Operation {
PLUS("+") {
public double apply(double x, double y) { return x + y; }
},
MINUS("-") {
public double apply(double x, double y) { return x - y; }
},
TIMES("*") {
public double apply(double x, double y) { return x * y; }
},
DIVIDE("/") {
public double apply(double x, double y) { return x / y; }
};

private final String symbol;

Operation(String symbol) {
this.symbol = symbol;
}

@Override
public String toString() {
return symbol;
}

public abstract double apply(double x, double y);

// 根据符号查找枚举
public static Operation fromSymbol(String symbol) {
for (Operation op : Operation.values()) {
if (op.symbol.equals(symbol)) {
return op;
}
}
throw new IllegalArgumentException("未知运算符: " + symbol);
}
}

public class AdvancedEnum {
public static void main(String[] args) {
double x = 10.5;
double y = 2.5;

for (Operation op : Operation.values()) {
System.out.printf("%f %s %f = %f%n",
x, op, y, op.apply(x, y));
}

Operation op = Operation.fromSymbol("*");
System.out.println("10 * 5 = " + op.apply(10, 5));
}
}

3. 枚举最佳实践

  1. 单例模式实现:枚举是实现单例的最佳方式
  2. 策略模式:利用枚举的抽象方法实现策略模式
  3. 状态机:适合用枚举实现有限状态机
  4. 替代常量:比常量类更类型安全

二、包(Package)

1. 包基础使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 文件: com/example/utils/MathUtils.java
package com.example.utils;

public class MathUtils {
public static int add(int a, int b) {
return a + b;
}

public static int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
}

// 文件: com/example/Main.java
package com.example;

import com.example.utils.MathUtils;
import static com.example.utils.MathUtils.add; // 静态导入

public class Main {
public static void main(String[] args) {
// 使用完全限定名
System.out.println(com.example.utils.MathUtils.factorial(5));

// 使用import后的类名
System.out.println(MathUtils.add(3, 4));

// 使用静态导入的方法
System.out.println(add(5, 6));
}
}

2. 包的组织原则

  1. 功能相关性:相同功能的类放在同一包中
  2. 层次结构:按功能模块分层,如com.公司名.项目名.模块名
  3. 访问控制:利用包级私有(package-private)保护实现细节
  4. 避免循环依赖:包之间不应有循环依赖关系

3. JDK常用包

包名 描述
java.lang 核心类(自动导入)
java.util 工具类和集合框架
java.io 输入输出相关
java.net 网络编程
java.sql 数据库操作
java.time 日期时间API

三、反射(Reflection)

1. 反射基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.lang.reflect.*;

public class ReflectionBasics {
public static void main(String[] args) throws Exception {
// 获取Class对象的三种方式
Class<?> stringClass1 = String.class;
Class<?> stringClass2 = "Hello".getClass();
Class<?> stringClass3 = Class.forName("java.lang.String");

System.out.println(stringClass1 == stringClass2); // true
System.out.println(stringClass2 == stringClass3); // true

// 获取类信息
System.out.println("类名: " + stringClass1.getName());
System.out.println("简单类名: " + stringClass1.getSimpleName());
System.out.println("是否是接口: " + stringClass1.isInterface());

// 获取修饰符
int modifiers = stringClass1.getModifiers();
System.out.println("修饰符: " + Modifier.toString(modifiers));

// 获取父类
Class<?> superClass = stringClass1.getSuperclass();
System.out.println("父类: " + superClass.getName());

// 获取实现的接口
Class<?>[] interfaces = stringClass1.getInterfaces();
System.out.println("实现的接口:");
for (Class<?> iface : interfaces) {
System.out.println(" " + iface.getName());
}
}
}

2. 反射操作类成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import java.lang.reflect.*;
import java.util.*;

class Person {
private String name;
private int age;

public Person() {}

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}

private void privateMethod() {
System.out.println("私有方法被调用");
}
}

public class ReflectionMembers {
public static void main(String[] args) throws Exception {
Class<?> personClass = Person.class;

// 获取构造方法
System.out.println("构造方法:");
Constructor<?>[] constructors = personClass.getConstructors();
for (Constructor<?> c : constructors) {
System.out.println(" " + c);
}

// 创建实例
Constructor<?> constructor = personClass.getConstructor(String.class, int.class);
Object person = constructor.newInstance("张三", 25);
System.out.println(((Person)person).getName());

// 获取字段
System.out.println("\n字段:");
Field[] fields = personClass.getDeclaredFields();
for (Field field : fields) {
System.out.println(" " + field);
}

// 访问私有字段
Field nameField = personClass.getDeclaredField("name");
nameField.setAccessible(true); // 设置可访问
nameField.set(person, "李四");
System.out.println("修改后name: " + nameField.get(person));

// 获取方法
System.out.println("\n方法:");
Method[] methods = personClass.getDeclaredMethods();
for (Method method : methods) {
System.out.println(" " + method);
}

// 调用方法
Method getNameMethod = personClass.getMethod("getName");
System.out.println("调用getName: " + getNameMethod.invoke(person));

// 调用私有方法
Method privateMethod = personClass.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true);
privateMethod.invoke(person);
}
}

3. 反射应用场景

  1. 动态代理:AOP实现的基础
  2. 注解处理:框架中处理自定义注解
  3. 类浏览器/IDE:获取类结构信息
  4. 序列化/反序列化:JSON/XML库的实现
  5. 插件架构:动态加载类
  6. 测试工具:Mock框架的实现

4. 反射性能与安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class ReflectionPerformance {
private static final int ITERATIONS = 1000000;

public static void main(String[] args) throws Exception {
// 直接调用
long start = System.nanoTime();
Person person = new Person();
for (int i = 0; i < ITERATIONS; i++) {
person.getName();
}
long directTime = System.nanoTime() - start;

// 反射调用
Method getNameMethod = Person.class.getMethod("getName");
start = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
getNameMethod.invoke(person);
}
long reflectionTime = System.nanoTime() - start;

// 反射调用(设置accessible)
getNameMethod.setAccessible(true);
start = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
getNameMethod.invoke(person);
}
long reflectionAccessibleTime = System.nanoTime() - start;

System.out.printf("直接调用耗时: %,d ns%n", directTime);
System.out.printf("反射调用耗时: %,d ns%n", reflectionTime);
System.out.printf("反射(setAccessible)调用耗时: %,d ns%n", reflectionAccessibleTime);

// 安全考虑
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
}
}
}

性能提示

  1. 反射操作比直接调用慢
  2. 通过setAccessible(true)可以提升性能
  3. 缓存Method/Field/Constructor对象避免重复查找

安全考虑

  1. 反射可以绕过访问控制检查
  2. 安全管理器可以限制反射操作
  3. 生产环境应谨慎使用反射

四、综合应用示例

1. 注解处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import java.lang.annotation.*;
import java.lang.reflect.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Test {
int priority() default 5;
}

class TestRunner {
public static void runTests(Class<?> testClass) throws Exception {
Object testInstance = testClass.getDeclaredConstructor().newInstance();

// 获取所有方法并按优先级排序
Method[] methods = testClass.getDeclaredMethods();
Arrays.sort(methods, (m1, m2) -> {
Test t1 = m1.getAnnotation(Test.class);
Test t2 = m2.getAnnotation(Test.class);
int p1 = t1 != null ? t1.priority() : 0;
int p2 = t2 != null ? t2.priority() : 0;
return Integer.compare(p2, p1); // 降序
});

// 执行测试方法
for (Method method : methods) {
if (method.getAnnotation(Test.class) != null) {
System.out.println("Running test: " + method.getName());
method.invoke(testInstance);
}
}
}
}

class MyTests {
@Test(priority = 1)
public void testFeatureA() {
System.out.println("Testing important feature A");
}

@Test(priority = 3)
public void testFeatureB() {
System.out.println("Testing feature B");
}

@Test // 默认优先级5
public void testFeatureC() {
System.out.println("Testing feature C");
}

public void helperMethod() {
// 不会被测试运行器执行
}
}

public class AnnotationProcessor {
public static void main(String[] args) throws Exception {
TestRunner.runTests(MyTests.class);
}
}

2. 简单DI容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

@Retention(RetentionPolicy.RUNTIME)
@interface Inject {}

class SimpleDIContainer {
private Map<Class<?>, Object> instances = new HashMap<>();

public void register(Class<?> clazz) throws Exception {
Constructor<?>[] constructors = clazz.getConstructors();
if (constructors.length != 1) {
throw new RuntimeException("类必须有且只有一个公共构造方法");
}

Constructor<?> constructor = constructors[0];
Object[] params = Arrays.stream(constructor.getParameterTypes())
.map(paramType -> {
if (!instances.containsKey(paramType)) {
throw new RuntimeException("未注册的依赖类型: " + paramType);
}
return instances.get(paramType);
})
.toArray();

Object instance = constructor.newInstance(params);
instances.put(clazz, instance);
}

public <T> T getInstance(Class<T> clazz) {
return clazz.cast(instances.get(clazz));
}

public void injectFields(Object obj) throws Exception {
for (Field field : obj.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(Inject.class)) {
field.setAccessible(true);
Class<?> fieldType = field.getType();
if (!instances.containsKey(fieldType)) {
throw new RuntimeException("未注册的依赖类型: " + fieldType);
}
field.set(obj, instances.get(fieldType));
}
}
}
}

// 测试类
class ServiceA {
public void execute() {
System.out.println("ServiceA executed");
}
}

class ServiceB {
@Inject
private ServiceA serviceA;

public void doWork() {
System.out.println("ServiceB starting work");
serviceA.execute();
System.out.println("ServiceB finished work");
}
}

public class DIContainerDemo {
public static void main(String[] args) throws Exception {
SimpleDIContainer container = new SimpleDIContainer();

// 注册组件
container.register(ServiceA.class);
container.register(ServiceB.class);

// 获取实例并使用
ServiceB serviceB = container.getInstance(ServiceB.class);
serviceB.doWork();
}
}

五、关键知识点总结

  1. 枚举

    • 类型安全的常量集合
    • 可以包含字段、方法和构造方法
    • 适合实现单例、策略模式等
    • 组织类和接口的命名空间
    • 控制访问权限(包级私有)
    • 避免命名冲突
  2. 反射

    • 运行时检查和操作类、方法、字段
    • 强大的但应谨慎使用
    • 性能开销较大,适合框架开发