# Java 面向对象编程

面向对象编程(Object-Oriented Programming,OOP)是Java的核心编程范式。本章将详细介绍面向对象的基本概念和实践应用。

# 类和对象

# 什么是类?

类是对象的模板,定义了对象的属性和行为。类是面向对象编程的基本单元。

// 定义一个简单的类
public class Student {
    // 属性(成员变量)
    private String name;
    private int age;
    
    // 构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // 方法
    public void study() {
        System.out.println(name + "正在学习");
    }
}

# 对象的创建和使用

对象是类的实例,我们可以通过new关键字来创建对象。

// 创建对象
Student student1 = new Student("张三", 18);
student1.study(); // 输出:张三正在学习

# 封装

封装是面向对象编程的三大特性之一,它通过访问修饰符来控制类的属性和方法的访问权限。

# 访问修饰符

  • private:只能在类内部访问
  • default(不写):同包内可访问
  • protected:同包和子类可访问
  • public:所有类都可访问
public class Person {
    private String name; // 私有属性
    
    // getter和setter方法
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}

# 继承

继承允许我们创建一个类作为另一个类的基础,这样可以重用代码并建立类之间的关系。

// 父类
public class Animal {
    protected String name;
    
    public void eat() {
        System.out.println(name + "正在吃东西");
    }
}

// 子类
public class Dog extends Animal {
    public Dog(String name) {
        this.name = name;
    }
    
    public void bark() {
        System.out.println(name + "在汪汪叫");
    }
}

# 方法重写

子类可以重写父类的方法,使用@Override注解。

public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println(name + "优雅地吃东西");
    }
}

# 多态

多态允许我们以不同的方式执行相同的操作,这增加了代码的灵活性。

# 多态的实现

// 接口
public interface Shape {
    double getArea();
}

// 实现类
public class Circle implements Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

public class Rectangle implements Shape {
    private double width;
    private double height;
    
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    @Override
    public double getArea() {
        return width * height;
    }
}

# 多态的使用

Shape circle = new Circle(5);
Shape rectangle = new Rectangle(4, 6);

System.out.println("圆的面积:" + circle.getArea());
System.out.println("矩形的面积:" + rectangle.getArea());

# 抽象类和接口

# 抽象类

抽象类用abstract关键字声明,可以包含抽象方法和具体方法。

public abstract class Vehicle {
    // 抽象方法
    public abstract void move();
    
    // 具体方法
    public void stop() {
        System.out.println("停止移动");
    }
}

# 接口

接口定义了一组抽象方法,所有方法默认都是public abstract的。

public interface Flyable {
    void fly();
    void land();
}

// 实现多个接口
public class Bird implements Flyable, Comparable<Bird> {
    @Override
    public void fly() {
        System.out.println("鸟儿在飞");
    }
    
    @Override
    public void land() {
        System.out.println("鸟儿降落");
    }
    
    @Override
    public int compareTo(Bird other) {
        // 比较逻辑
        return 0;
    }
}

# 最佳实践

  1. 使用封装保护数据,提供必要的访问方法
  2. 合理使用继承,避免过深的继承层次
  3. 优先使用组合而不是继承
  4. 多用接口定义行为
  5. 正确使用访问修饰符

# 练习

  1. 设计一个简单的银行账户系统,包含以下功能:

    • 账户的创建和管理
    • 存款和取款操作
    • 不同类型账户(储蓄账户、信用账户)的实现
  2. 实现一个简单的图形编辑器,能够:

    • 创建不同的图形(圆形、矩形、三角形)
    • 计算图形的面积和周长
    • 移动和缩放图形

# 下一步

# 泛型编程

泛型是Java 5引入的重要特性,它提供了编译时类型安全检查机制。

// 泛型类
public class Box<T> {
    private T content;
    
    public void set(T content) {
        this.content = content;
    }
    
    public T get() {
        return content;
    }
}

// 泛型方法
public class Util {
    public static <T> void swap(T[] array, int i, int j) {
        T temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

// 泛型通配符
public void processElements(List<? extends Number> numbers) {
    for (Number n : numbers) {
        System.out.println(n);
    }
}

# 类型擦除

Java的泛型是通过类型擦除实现的,在运行时泛型类型会被替换为原始类型。

List<String> strList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();

// 在运行时,两者的类型都是List
System.out.println(strList.getClass() == intList.getClass()); // true

# 注解

注解是代码的元数据,可以用来修饰包、类、方法、变量等程序元素。

// 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
    String value() default "";
    int priority() default 0;
}

// 注解的使用
public class MyTest {
    @Test(value = "测试方法", priority = 1)
    public void testMethod() {
        // 测试代码
    }
}

// 注解处理
public class TestProcessor {
    public static void process(Class<?> clazz) {
        for (Method method : clazz.getDeclaredMethods()) {
            Test test = method.getAnnotation(Test.class);
            if (test != null) {
                System.out.println("测试方法:" + test.value());
                System.out.println("优先级:" + test.priority());
            }
        }
    }
}

# 反射机制

反射允许在运行时检查和操作类、接口、字段和方法。

// 获取类信息
Class<?> clazz = Class.forName("com.example.MyClass");

// 创建实例
Object obj = clazz.newInstance();

// 获取方法
Method method = clazz.getMethod("myMethod", String.class);

// 调用方法
method.invoke(obj, "参数");

// 获取和设置字段值
Field field = clazz.getDeclaredField("myField");
field.setAccessible(true); // 访问私有字段
field.set(obj, "新值");

# 反射的应用场景

  1. 依赖注入框架(如Spring)
  2. ORM框架(如Hibernate)
  3. 单元测试框架(如JUnit)
  4. 动态代理

# 枚举类型

枚举提供了一种定义固定常量集的方式。

// 基本枚举
public enum DayOfWeek {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

// 带属性的枚举
public enum Status {
    SUCCESS(200, "成功"),
    NOT_FOUND(404, "未找到"),
    ERROR(500, "服务器错误");
    
    private final int code;
    private final String message;
    
    Status(int code, String message) {
        this.code = code;
        this.message = message;
    }
    
    public int getCode() {
        return code;
    }
    
    public String getMessage() {
        return message;
    }
}

// 实现接口的枚举
public enum Operation implements Calculator {
    PLUS {
        public double calculate(double x, double y) { return x + y; }
    },
    MINUS {
        public double calculate(double x, double y) { return x - y; }
    };
    
    public abstract double calculate(double x, double y);
}

# 最佳实践

  1. 合理使用泛型提高代码的类型安全性和重用性
  2. 注解要有明确的目的和规范的命名
  3. 谨慎使用反射,注意性能开销
  4. 优先使用枚举替代常量定义
  5. 注意泛型类型擦除带来的限制

# 性能优化建议

  1. 缓存反射获取的Class、Method、Field对象
  2. 合理使用注解,避免过度使用
  3. 注意泛型装箱拆箱的性能开销
  4. 枚举类的单例特性可用于优化内存使用