面向对象编程(Object-Oriented Programming,OOP)是Java的核心特性。本章将深入介绍类和对象、封装、继承、多态等面向对象的基本概念和实现方法。

4.1 类和对象

4.1.1 类的定义

/**
 * 学生类 - 演示类的基本结构
 */
public class Student {
    // 实例变量(属性/字段)
    private String name;        // 姓名
    private int age;           // 年龄
    private String studentId;  // 学号
    private double gpa;        // 平均绩点
    
    // 类变量(静态变量)
    private static int totalStudents = 0;
    public static final String SCHOOL_NAME = "Java大学";
    
    // 构造方法
    public Student() {
        this("未知", 0, "000000", 0.0);
    }
    
    public Student(String name, int age, String studentId) {
        this(name, age, studentId, 0.0);
    }
    
    public Student(String name, int age, String studentId, double gpa) {
        this.name = name;
        this.age = age;
        this.studentId = studentId;
        this.gpa = gpa;
        totalStudents++;
    }
    
    // 实例方法
    public void study(String subject) {
        System.out.println(name + "正在学习" + subject);
    }
    
    public void takeExam(String subject, double score) {
        System.out.println(name + "参加了" + subject + "考试,得分:" + score);
        updateGPA(score);
    }
    
    private void updateGPA(double score) {
        // 简化的GPA计算
        this.gpa = (this.gpa + score / 25.0) / 2.0;
    }
    
    // Getter和Setter方法
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        if (name != null && !name.trim().isEmpty()) {
            this.name = name;
        }
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        if (age >= 0 && age <= 150) {
            this.age = age;
        }
    }
    
    public String getStudentId() {
        return studentId;
    }
    
    public void setStudentId(String studentId) {
        this.studentId = studentId;
    }
    
    public double getGpa() {
        return gpa;
    }
    
    // 静态方法
    public static int getTotalStudents() {
        return totalStudents;
    }
    
    public static void printSchoolInfo() {
        System.out.println("学校:" + SCHOOL_NAME);
        System.out.println("总学生数:" + totalStudents);
    }
    
    // toString方法
    @Override
    public String toString() {
        return String.format("Student{name='%s', age=%d, studentId='%s', gpa=%.2f}", 
                           name, age, studentId, gpa);
    }
    
    // equals方法
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        
        Student student = (Student) obj;
        return studentId.equals(student.studentId);
    }
    
    // hashCode方法
    @Override
    public int hashCode() {
        return studentId.hashCode();
    }
}

4.1.2 对象的创建和使用

public class StudentDemo {
    public static void main(String[] args) {
        System.out.println("=== 对象创建和使用 ===");
        
        // 创建对象
        Student student1 = new Student();
        Student student2 = new Student("张三", 20, "2023001");
        Student student3 = new Student("李四", 19, "2023002", 3.5);
        
        // 使用对象
        System.out.println("初始状态:");
        System.out.println(student1);
        System.out.println(student2);
        System.out.println(student3);
        
        // 调用方法
        student2.study("Java编程");
        student2.takeExam("Java编程", 95);
        student3.study("数据结构");
        student3.takeExam("数据结构", 88);
        
        // 使用getter和setter
        student1.setName("王五");
        student1.setAge(21);
        student1.setStudentId("2023003");
        
        System.out.println("\n更新后状态:");
        System.out.println(student1);
        System.out.println(student2);
        System.out.println(student3);
        
        // 调用静态方法
        Student.printSchoolInfo();
        
        // 对象比较
        Student student4 = new Student("赵六", 20, "2023002", 3.2);
        System.out.println("\n对象比较:");
        System.out.println("student3.equals(student4): " + student3.equals(student4)); // true,学号相同
        System.out.println("student2.equals(student3): " + student2.equals(student3)); // false,学号不同
        
        // 对象引用
        Student student5 = student2;  // 引用赋值
        System.out.println("\n引用赋值:");
        System.out.println("student2 == student5: " + (student2 == student5)); // true,同一对象
        System.out.println("student2.equals(student5): " + student2.equals(student5)); // true
        
        student5.setName("张三(修改)");
        System.out.println("修改student5后,student2的名字: " + student2.getName()); // 也被修改
    }
}

4.2 封装

4.2.1 访问修饰符

/**
 * 银行账户类 - 演示封装和访问修饰符
 */
public class BankAccount {
    // private:只能在本类内部访问
    private String accountNumber;
    private double balance;
    private String password;
    
    // protected:可以在本类、子类和同包类中访问
    protected String accountType;
    
    // 默认(包私有):可以在同包类中访问
    String bankName;
    
    // public:可以在任何地方访问
    public String ownerName;
    
    // 构造方法
    public BankAccount(String accountNumber, String ownerName, String password) {
        this.accountNumber = accountNumber;
        this.ownerName = ownerName;
        this.password = password;
        this.balance = 0.0;
        this.accountType = "储蓄账户";
        this.bankName = "Java银行";
    }
    
    // 公共方法提供对私有数据的访问
    public double getBalance() {
        return balance;
    }
    
    public String getAccountNumber() {
        return accountNumber;
    }
    
    // 存款方法
    public boolean deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("存款成功,金额:" + amount + ",余额:" + balance);
            return true;
        } else {
            System.out.println("存款金额必须大于0");
            return false;
        }
    }
    
    // 取款方法
    public boolean withdraw(double amount, String inputPassword) {
        if (!verifyPassword(inputPassword)) {
            System.out.println("密码错误");
            return false;
        }
        
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            System.out.println("取款成功,金额:" + amount + ",余额:" + balance);
            return true;
        } else if (amount > balance) {
            System.out.println("余额不足");
            return false;
        } else {
            System.out.println("取款金额必须大于0");
            return false;
        }
    }
    
    // 转账方法
    public boolean transfer(BankAccount targetAccount, double amount, String inputPassword) {
        if (!verifyPassword(inputPassword)) {
            System.out.println("密码错误");
            return false;
        }
        
        if (this.withdraw(amount, inputPassword)) {
            targetAccount.deposit(amount);
            System.out.println("转账成功,从" + this.ownerName + "转给" + targetAccount.ownerName);
            return true;
        }
        return false;
    }
    
    // 私有方法:验证密码
    private boolean verifyPassword(String inputPassword) {
        return this.password.equals(inputPassword);
    }
    
    // 修改密码
    public boolean changePassword(String oldPassword, String newPassword) {
        if (verifyPassword(oldPassword)) {
            if (newPassword != null && newPassword.length() >= 6) {
                this.password = newPassword;
                System.out.println("密码修改成功");
                return true;
            } else {
                System.out.println("新密码长度至少6位");
                return false;
            }
        } else {
            System.out.println("原密码错误");
            return false;
        }
    }
    
    // 账户信息(隐藏敏感信息)
    public void printAccountInfo() {
        System.out.println("账户信息:");
        System.out.println("账号:" + accountNumber);
        System.out.println("户主:" + ownerName);
        System.out.println("账户类型:" + accountType);
        System.out.println("银行:" + bankName);
        System.out.println("余额:" + balance);
        // 注意:不显示密码
    }
}

4.2.2 封装的实际应用

public class EncapsulationDemo {
    public static void main(String[] args) {
        System.out.println("=== 封装演示 ===");
        
        // 创建银行账户
        BankAccount account1 = new BankAccount("123456789", "张三", "password123");
        BankAccount account2 = new BankAccount("987654321", "李四", "mypassword");
        
        // 正常操作
        account1.deposit(1000);
        account1.withdraw(200, "password123");
        
        // 错误操作演示
        account1.withdraw(100, "wrongpassword");  // 密码错误
        account1.withdraw(2000, "password123");   // 余额不足
        
        // 转账操作
        account2.deposit(500);
        account1.transfer(account2, 300, "password123");
        
        // 显示账户信息
        System.out.println("\n账户信息:");
        account1.printAccountInfo();
        account2.printAccountInfo();
        
        // 修改密码
        account1.changePassword("password123", "newpass123");
        
        // 尝试直接访问私有变量(编译错误)
        // System.out.println(account1.password);  // 编译错误
        // account1.balance = 10000;  // 编译错误
        
        // 通过公共方法访问
        System.out.println("\n通过公共方法访问:");
        System.out.println("账号:" + account1.getAccountNumber());
        System.out.println("余额:" + account1.getBalance());
        
        // 可以直接访问public字段(不推荐)
        System.out.println("户主:" + account1.ownerName);
        account1.ownerName = "张三(已修改)";
        System.out.println("修改后户主:" + account1.ownerName);
    }
}

4.3 继承

4.3.1 基本继承

/**
 * 动物基类
 */
public class Animal {
    protected String name;
    protected int age;
    protected String species;
    
    // 构造方法
    public Animal() {
        this("未知动物", 0, "未知物种");
    }
    
    public Animal(String name, int age, String species) {
        this.name = name;
        this.age = age;
        this.species = species;
    }
    
    // 基本行为方法
    public void eat() {
        System.out.println(name + "正在吃东西");
    }
    
    public void sleep() {
        System.out.println(name + "正在睡觉");
    }
    
    public void makeSound() {
        System.out.println(name + "发出了声音");
    }
    
    // 移动方法(将被子类重写)
    public void move() {
        System.out.println(name + "正在移动");
    }
    
    // Getter和Setter方法
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        if (age >= 0) {
            this.age = age;
        }
    }
    
    public String getSpecies() {
        return species;
    }
    
    // 获取动物信息
    public String getInfo() {
        return String.format("%s,年龄:%d岁,物种:%s", name, age, species);
    }
    
    @Override
    public String toString() {
        return getInfo();
    }
}

4.3.2 子类实现

/**
 * 狗类 - 继承自Animal
 */
public class Dog extends Animal {
    private String breed;  // 品种
    private boolean isTrained;  // 是否训练过
    
    // 构造方法
    public Dog() {
        super();  // 调用父类构造方法
        this.breed = "混种";
        this.isTrained = false;
    }
    
    public Dog(String name, int age, String breed) {
        super(name, age, "犬科");  // 调用父类构造方法
        this.breed = breed;
        this.isTrained = false;
    }
    
    public Dog(String name, int age, String breed, boolean isTrained) {
        super(name, age, "犬科");
        this.breed = breed;
        this.isTrained = isTrained;
    }
    
    // 重写父类方法
    @Override
    public void makeSound() {
        System.out.println(name + "汪汪叫");
    }
    
    @Override
    public void move() {
        System.out.println(name + "正在奔跑");
    }
    
    // 子类特有方法
    public void wagTail() {
        System.out.println(name + "摇尾巴表示友好");
    }
    
    public void fetch(String item) {
        if (isTrained) {
            System.out.println(name + "去捡" + item);
        } else {
            System.out.println(name + "还没学会捡东西");
        }
    }
    
    public void train() {
        if (!isTrained) {
            isTrained = true;
            System.out.println(name + "完成了训练");
        } else {
            System.out.println(name + "已经训练过了");
        }
    }
    
    // Getter和Setter
    public String getBreed() {
        return breed;
    }
    
    public void setBreed(String breed) {
        this.breed = breed;
    }
    
    public boolean isTrained() {
        return isTrained;
    }
    
    // 重写getInfo方法
    @Override
    public String getInfo() {
        return super.getInfo() + ",品种:" + breed + ",训练状态:" + (isTrained ? "已训练" : "未训练");
    }
}
/**
 * 鸟类 - 继承自Animal
 */
public class Bird extends Animal {
    private boolean canFly;  // 是否会飞
    private double wingspan;  // 翼展
    
    public Bird() {
        super();
        this.canFly = true;
        this.wingspan = 0.0;
    }
    
    public Bird(String name, int age, String species, boolean canFly, double wingspan) {
        super(name, age, species);
        this.canFly = canFly;
        this.wingspan = wingspan;
    }
    
    // 重写父类方法
    @Override
    public void makeSound() {
        System.out.println(name + "啁啾鸣叫");
    }
    
    @Override
    public void move() {
        if (canFly) {
            System.out.println(name + "正在飞翔");
        } else {
            System.out.println(name + "正在地面行走");
        }
    }
    
    // 子类特有方法
    public void fly() {
        if (canFly) {
            System.out.println(name + "展开" + wingspan + "米的翅膀飞翔");
        } else {
            System.out.println(name + "不会飞");
        }
    }
    
    public void buildNest() {
        System.out.println(name + "正在筑巢");
    }
    
    // Getter和Setter
    public boolean isCanFly() {
        return canFly;
    }
    
    public void setCanFly(boolean canFly) {
        this.canFly = canFly;
    }
    
    public double getWingspan() {
        return wingspan;
    }
    
    public void setWingspan(double wingspan) {
        if (wingspan >= 0) {
            this.wingspan = wingspan;
        }
    }
    
    @Override
    public String getInfo() {
        return super.getInfo() + ",飞行能力:" + (canFly ? "会飞" : "不会飞") + ",翼展:" + wingspan + "米";
    }
}

4.3.3 继承演示

public class InheritanceDemo {
    public static void main(String[] args) {
        System.out.println("=== 继承演示 ===");
        
        // 创建不同类型的动物
        Animal animal = new Animal("通用动物", 5, "未知");
        Dog dog = new Dog("旺财", 3, "金毛");
        Bird bird = new Bird("小黄", 2, "金丝雀", true, 0.15);
        
        // 基本信息
        System.out.println("动物信息:");
        System.out.println(animal.getInfo());
        System.out.println(dog.getInfo());
        System.out.println(bird.getInfo());
        
        System.out.println("\n=== 共同行为 ===");
        // 所有动物都有的行为
        animal.eat();
        dog.eat();
        bird.eat();
        
        System.out.println();
        animal.sleep();
        dog.sleep();
        bird.sleep();
        
        System.out.println("\n=== 重写的方法 ===");
        // 重写的方法表现不同
        animal.makeSound();
        dog.makeSound();
        bird.makeSound();
        
        System.out.println();
        animal.move();
        dog.move();
        bird.move();
        
        System.out.println("\n=== 子类特有方法 ===");
        // 狗的特有行为
        dog.wagTail();
        dog.fetch("球");
        dog.train();
        dog.fetch("飞盘");
        
        System.out.println();
        // 鸟的特有行为
        bird.fly();
        bird.buildNest();
        
        System.out.println("\n=== 多态演示 ===");
        // 多态:父类引用指向子类对象
        Animal[] animals = {
            new Animal("普通动物", 1, "未知"),
            new Dog("小黑", 2, "拉布拉多"),
            new Bird("小白", 1, "鸽子", true, 0.3)
        };
        
        for (Animal a : animals) {
            System.out.println(a.getInfo());
            a.makeSound();  // 调用各自重写的方法
            a.move();
            System.out.println();
        }
        
        System.out.println("=== instanceof检查 ===");
        for (Animal a : animals) {
            System.out.println(a.getName() + ":");
            System.out.println("  是Animal: " + (a instanceof Animal));
            System.out.println("  是Dog: " + (a instanceof Dog));
            System.out.println("  是Bird: " + (a instanceof Bird));
            
            // 类型转换
            if (a instanceof Dog) {
                Dog d = (Dog) a;
                d.wagTail();
            } else if (a instanceof Bird) {
                Bird b = (Bird) a;
                b.fly();
            }
            System.out.println();
        }
    }
}

4.4 多态

4.4.1 方法重写和动态绑定

/**
 * 形状基类 - 演示多态
 */
public abstract class Shape {
    protected String color;
    protected double x, y;  // 位置坐标
    
    public Shape(String color, double x, double y) {
        this.color = color;
        this.x = x;
        this.y = y;
    }
    
    // 抽象方法 - 子类必须实现
    public abstract double calculateArea();
    public abstract double calculatePerimeter();
    public abstract void draw();
    
    // 具体方法
    public void move(double deltaX, double deltaY) {
        this.x += deltaX;
        this.y += deltaY;
        System.out.println("形状移动到位置: (" + x + ", " + y + ")");
    }
    
    public String getColor() {
        return color;
    }
    
    public void setColor(String color) {
        this.color = color;
    }
    
    public double getX() {
        return x;
    }
    
    public double getY() {
        return y;
    }
    
    // 模板方法模式
    public void displayInfo() {
        System.out.println("=== 形状信息 ===");
        System.out.println("类型: " + this.getClass().getSimpleName());
        System.out.println("颜色: " + color);
        System.out.println("位置: (" + x + ", " + y + ")");
        System.out.println("面积: " + calculateArea());
        System.out.println("周长: " + calculatePerimeter());
        draw();
        System.out.println();
    }
}

4.4.2 具体子类实现

/**
 * 圆形类
 */
public class Circle extends Shape {
    private double radius;
    
    public Circle(String color, double x, double y, double radius) {
        super(color, x, y);
        this.radius = radius;
    }
    
    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
    
    @Override
    public double calculatePerimeter() {
        return 2 * Math.PI * radius;
    }
    
    @Override
    public void draw() {
        System.out.println("绘制一个半径为" + radius + "的" + color + "圆形");
    }
    
    public double getRadius() {
        return radius;
    }
    
    public void setRadius(double radius) {
        if (radius > 0) {
            this.radius = radius;
        }
    }
}
/**
 * 矩形类
 */
public class Rectangle extends Shape {
    private double width;
    private double height;
    
    public Rectangle(String color, double x, double y, double width, double height) {
        super(color, x, y);
        this.width = width;
        this.height = height;
    }
    
    @Override
    public double calculateArea() {
        return width * height;
    }
    
    @Override
    public double calculatePerimeter() {
        return 2 * (width + height);
    }
    
    @Override
    public void draw() {
        System.out.println("绘制一个" + width + "x" + height + "的" + color + "矩形");
    }
    
    public double getWidth() {
        return width;
    }
    
    public void setWidth(double width) {
        if (width > 0) {
            this.width = width;
        }
    }
    
    public double getHeight() {
        return height;
    }
    
    public void setHeight(double height) {
        if (height > 0) {
            this.height = height;
        }
    }
}
/**
 * 三角形类
 */
public class Triangle extends Shape {
    private double side1, side2, side3;
    
    public Triangle(String color, double x, double y, double side1, double side2, double side3) {
        super(color, x, y);
        if (isValidTriangle(side1, side2, side3)) {
            this.side1 = side1;
            this.side2 = side2;
            this.side3 = side3;
        } else {
            throw new IllegalArgumentException("无效的三角形边长");
        }
    }
    
    private boolean isValidTriangle(double a, double b, double c) {
        return a + b > c && a + c > b && b + c > a;
    }
    
    @Override
    public double calculateArea() {
        // 使用海伦公式
        double s = calculatePerimeter() / 2;
        return Math.sqrt(s * (s - side1) * (s - side2) * (s - side3));
    }
    
    @Override
    public double calculatePerimeter() {
        return side1 + side2 + side3;
    }
    
    @Override
    public void draw() {
        System.out.println("绘制一个边长为" + side1 + ", " + side2 + ", " + side3 + "的" + color + "三角形");
    }
    
    public double getSide1() {
        return side1;
    }
    
    public double getSide2() {
        return side2;
    }
    
    public double getSide3() {
        return side3;
    }
}

4.4.3 多态演示

import java.util.ArrayList;
import java.util.List;

public class PolymorphismDemo {
    public static void main(String[] args) {
        System.out.println("=== 多态演示 ===");
        
        // 创建不同形状的对象
        Shape[] shapes = {
            new Circle("红色", 0, 0, 5),
            new Rectangle("蓝色", 10, 10, 4, 6),
            new Triangle("绿色", 20, 20, 3, 4, 5)
        };
        
        // 多态:同一接口,不同实现
        System.out.println("=== 形状信息展示 ===");
        for (Shape shape : shapes) {
            shape.displayInfo();  // 调用各自的实现
        }
        
        // 计算总面积
        double totalArea = calculateTotalArea(shapes);
        System.out.println("所有形状的总面积: " + String.format("%.2f", totalArea));
        
        // 动态类型检查和转换
        System.out.println("=== 动态类型检查 ===");
        for (Shape shape : shapes) {
            System.out.println("处理" + shape.getClass().getSimpleName() + ":");
            
            if (shape instanceof Circle) {
                Circle circle = (Circle) shape;
                System.out.println("  圆的半径: " + circle.getRadius());
                System.out.println("  圆的直径: " + (circle.getRadius() * 2));
            } else if (shape instanceof Rectangle) {
                Rectangle rectangle = (Rectangle) shape;
                System.out.println("  矩形宽度: " + rectangle.getWidth());
                System.out.println("  矩形高度: " + rectangle.getHeight());
                System.out.println("  是否为正方形: " + (rectangle.getWidth() == rectangle.getHeight()));
            } else if (shape instanceof Triangle) {
                Triangle triangle = (Triangle) shape;
                System.out.println("  三角形三边: " + triangle.getSide1() + ", " + 
                                 triangle.getSide2() + ", " + triangle.getSide3());
            }
            System.out.println();
        }
        
        // 使用List集合演示多态
        System.out.println("=== 使用List集合 ===");
        List<Shape> shapeList = new ArrayList<>();
        shapeList.add(new Circle("黄色", 0, 0, 3));
        shapeList.add(new Rectangle("紫色", 5, 5, 2, 8));
        shapeList.add(new Triangle("橙色", 15, 15, 6, 8, 10));
        
        // 对所有形状进行操作
        moveAllShapes(shapeList, 10, 10);
        
        // 找出面积最大的形状
        Shape largestShape = findLargestShape(shapeList);
        System.out.println("面积最大的形状:");
        largestShape.displayInfo();
        
        // 按类型分组统计
        countShapesByType(shapeList);
    }
    
    // 计算总面积的方法
    public static double calculateTotalArea(Shape[] shapes) {
        double total = 0;
        for (Shape shape : shapes) {
            total += shape.calculateArea();
        }
        return total;
    }
    
    // 移动所有形状
    public static void moveAllShapes(List<Shape> shapes, double deltaX, double deltaY) {
        System.out.println("移动所有形状:");
        for (Shape shape : shapes) {
            shape.move(deltaX, deltaY);
        }
        System.out.println();
    }
    
    // 找出面积最大的形状
    public static Shape findLargestShape(List<Shape> shapes) {
        if (shapes.isEmpty()) {
            return null;
        }
        
        Shape largest = shapes.get(0);
        for (Shape shape : shapes) {
            if (shape.calculateArea() > largest.calculateArea()) {
                largest = shape;
            }
        }
        return largest;
    }
    
    // 按类型统计形状数量
    public static void countShapesByType(List<Shape> shapes) {
        System.out.println("=== 形状类型统计 ===");
        int circleCount = 0, rectangleCount = 0, triangleCount = 0;
        
        for (Shape shape : shapes) {
            if (shape instanceof Circle) {
                circleCount++;
            } else if (shape instanceof Rectangle) {
                rectangleCount++;
            } else if (shape instanceof Triangle) {
                triangleCount++;
            }
        }
        
        System.out.println("圆形数量: " + circleCount);
        System.out.println("矩形数量: " + rectangleCount);
        System.out.println("三角形数量: " + triangleCount);
        System.out.println("总数量: " + shapes.size());
    }
}