# 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;
}
}
# 最佳实践
- 使用封装保护数据,提供必要的访问方法
- 合理使用继承,避免过深的继承层次
- 优先使用组合而不是继承
- 多用接口定义行为
- 正确使用访问修饰符
# 练习
设计一个简单的银行账户系统,包含以下功能:
- 账户的创建和管理
- 存款和取款操作
- 不同类型账户(储蓄账户、信用账户)的实现
实现一个简单的图形编辑器,能够:
- 创建不同的图形(圆形、矩形、三角形)
- 计算图形的面积和周长
- 移动和缩放图形
# 下一步
# 泛型编程
泛型是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, "新值");
# 反射的应用场景
- 依赖注入框架(如Spring)
- ORM框架(如Hibernate)
- 单元测试框架(如JUnit)
- 动态代理
# 枚举类型
枚举提供了一种定义固定常量集的方式。
// 基本枚举
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);
}
# 最佳实践
- 合理使用泛型提高代码的类型安全性和重用性
- 注解要有明确的目的和规范的命名
- 谨慎使用反射,注意性能开销
- 优先使用枚举替代常量定义
- 注意泛型类型擦除带来的限制
# 性能优化建议
- 缓存反射获取的Class、Method、Field对象
- 合理使用注解,避免过度使用
- 注意泛型装箱拆箱的性能开销
- 枚举类的单例特性可用于优化内存使用