零基础学Java——第三章:面向对象编程

第三章:面向对象编程

在前两章中,我们学习了Java的基础知识和核心语法。现在,我们将深入探讨Java最重要的特性之一:面向对象编程(Object-Oriented Programming,简称OOP)。面向对象编程是一种编程范式,它使用"对象"这一概念来组织和构建程序。本章将通过生活中的例子,帮助你理解面向对象编程的核心概念。

1. 类与对象

1.1 类与对象的基本概念

在面向对象编程中,"类"和"对象"是两个最基本的概念:

  • 类(Class):类是对象的模板或蓝图,定义了对象的属性(数据)和行为(方法)。
  • 对象(Object):对象是类的实例,是类的具体表现。

想象一下,如果"汽车"是一个类,那么你的丰田卡罗拉和你朋友的特斯拉Model 3就是这个类的两个不同对象。它们都有品牌、型号、颜色等属性,也都能启动、停止、加速等行为,但具体的属性值和行为表现可能不同。

基本语法
// 定义类
public class 类名 {
    // 属性(成员变量)
    数据类型 属性名1;
    数据类型 属性名2;
    // 更多属性...
    
    // 方法
    返回类型 方法名1(参数列表) {
        // 方法体
    }
    
    返回类型 方法名2(参数列表) {
        // 方法体
    }
    // 更多方法...
}

// 创建对象
类名 对象名 = new 类名();
生活例子:汽车类
public class Car {
    // 属性
    String brand;        // 品牌
    String model;        // 型号
    String color;        // 颜色
    int year;            // 年份
    double mileage;      // 里程数
    boolean isRunning;   // 是否在运行
    
    // 方法
    void start() {
        if (!isRunning) {
            isRunning = true;
            System.out.println(brand + " " + model + " 启动了。引擎声:轰轰轰...");
        } else {
            System.out.println("车已经在运行中!");
        }
    }
    
    void stop() {
        if (isRunning) {
            isRunning = false;
            System.out.println(brand + " " + model + " 停止了。");
        } else {
            System.out.println("车已经停止了!");
        }
    }
    
    void accelerate() {
        if (isRunning) {
            System.out.println(brand + " " + model + " 加速中...");
        } else {
            System.out.println("请先启动车辆!");
        }
    }
    
    void displayInfo() {
        System.out.println("汽车信息:");
        System.out.println("- 品牌:" + brand);
        System.out.println("- 型号:" + model);
        System.out.println("- 颜色:" + color);
        System.out.println("- 年份:" + year);
        System.out.println("- 里程数:" + mileage + "公里");
        System.out.println("- 运行状态:" + (isRunning ? "运行中" : "已停止"));
    }
}
使用汽车类创建对象
public class CarDemo {
    public static void main(String[] args) {
        // 创建第一个汽车对象
        Car myCar = new Car();
        myCar.brand = "丰田";
        myCar.model = "卡罗拉";
        myCar.color = "银色";
        myCar.year = 2020;
        myCar.mileage = 15000.5;
        myCar.isRunning = false;
        
        // 创建第二个汽车对象
        Car friendsCar = new Car();
        friendsCar.brand = "特斯拉";
        friendsCar.model = "Model 3";
        friendsCar.color = "红色";
        friendsCar.year = 2022;
        friendsCar.mileage = 5000.0;
        friendsCar.isRunning = false;
        
        // 使用汽车对象
        System.out.println("===== 我的车 =====");
        myCar.displayInfo();
        myCar.start();
        myCar.accelerate();
        myCar.stop();
        
        System.out.println("\n===== 朋友的车 =====");
        friendsCar.displayInfo();
        friendsCar.accelerate(); // 尝试在未启动的情况下加速
        friendsCar.start();
        friendsCar.accelerate();
    }
}

1.2 构造方法

构造方法是一种特殊的方法,用于初始化对象。当使用new关键字创建对象时,构造方法会被自动调用。

基本语法
public class 类名 {
    // 无参构造方法
    public 类名() {
        // 初始化代码
    }
    
    // 带参数的构造方法
    public 类名(参数列表) {
        // 初始化代码
    }
}
生活例子:改进的汽车类
public class Car {
    // 属性
    String brand;
    String model;
    String color;
    int year;
    double mileage;
    boolean isRunning;
    
    // 无参构造方法
    public Car() {
        System.out.println("创建了一个新的汽车对象!");
        // 设置默认值
        brand = "未知品牌";
        model = "未知型号";
        color = "白色";
        year = 2023;
        mileage = 0.0;
        isRunning = false;
    }
    
    // 带参数的构造方法
    public Car(String brand, String model, String color, int year) {
        this.brand = brand;
        this.model = model;
        this.color = color;
        this.year = year;
        this.mileage = 0.0;
        this.isRunning = false;
        System.out.println("创建了一个" + color + "的" + brand + " " + model + "!");
    }
    
    // 方法(与前面相同)
    void start() {
        if (!isRunning) {
            isRunning = true;
            System.out.println(brand + " " + model + " 启动了。引擎声:轰轰轰...");
        } else {
            System.out.println("车已经在运行中!");
        }
    }
    
    void stop() {
        if (isRunning) {
            isRunning = false;
            System.out.println(brand + " " + model + " 停止了。");
        } else {
            System.out.println("车已经停止了!");
        }
    }
    
    void accelerate() {
        if (isRunning) {
            System.out.println(brand + " " + model + " 加速中...");
        } else {
            System.out.println("请先启动车辆!");
        }
    }
    
    void displayInfo() {
        System.out.println("汽车信息:");
        System.out.println("- 品牌:" + brand);
        System.out.println("- 型号:" + model);
        System.out.println("- 颜色:" + color);
        System.out.println("- 年份:" + year);
        System.out.println("- 里程数:" + mileage + "公里");
        System.out.println("- 运行状态:" + (isRunning ? "运行中" : "已停止"));
    }
}
使用构造方法创建对象
public class CarDemoWithConstructor {
    public static void main(String[] args) {
        // 使用无参构造方法创建对象
        Car defaultCar = new Car();
        System.out.println("默认汽车信息:");
        defaultCar.displayInfo();
        
        System.out.println("\n-----------------------\n");
        
        // 使用带参数的构造方法创建对象
        Car myCar = new Car("丰田", "卡罗拉", "银色", 2020);
        myCar.mileage = 15000.5; // 设置里程数
        System.out.println("我的汽车信息:");
        myCar.displayInfo();
        
        System.out.println("\n-----------------------\n");
        
        Car friendsCar = new Car("特斯拉", "Model 3", "红色", 2022);
        System.out.println("朋友的汽车信息:");
        friendsCar.displayInfo();
    }
}

1.3 this关键字

this关键字指代当前对象,常用于区分局部变量和成员变量,特别是当它们同名时。

在上面的带参数构造方法中,我们已经使用了this关键字:

public Car(String brand, String model, String color, int year) {
    this.brand = brand;  // this.brand是成员变量,brand是参数
    this.model = model;  // this.model是成员变量,model是参数
    this.color = color;  // this.color是成员变量,color是参数
    this.year = year;    // this.year是成员变量,year是参数
    this.mileage = 0.0;
    this.isRunning = false;
}

2. 封装

封装是面向对象编程的四大特性之一,它指的是将数据(属性)和操作数据的方法捆绑在一起,对外部隐藏实现细节,只暴露必要的接口。

2.1 访问修饰符

Java提供了四种访问修饰符,用于控制类、变量、方法和构造方法的访问权限:

  • public:公共的,可以被任何其他类访问
  • protected:受保护的,可以被同一包内的类和所有子类访问
  • 默认(无修饰符):包私有的,只能被同一包内的类访问
  • private:私有的,只能在定义它的类内部访问

2.2 封装的实现

封装的典型实现方式是:

  1. 将类的属性设为私有(private)
  2. 提供公共的getter和setter方法来访问和修改这些属性
生活例子:银行账户
public class BankAccount {
    // 私有属性
    private String accountNumber;
    private String accountHolder;
    private double balance;
    private String password;
    
    // 构造方法
    public BankAccount(String accountNumber, String accountHolder, String password) {
        this.accountNumber = accountNumber;
        this.accountHolder = accountHolder;
        this.balance = 0.0;
        this.password = password;
    }
    
    // Getter方法
    public String getAccountNumber() {
        return accountNumber;
    }
    
    public String getAccountHolder() {
        return accountHolder;
    }
    
    public double getBalance() {
        return balance;
    }
    
    // 注意:没有提供getPassword方法,因为密码是敏感信息
    
    // Setter方法
    public void setAccountHolder(String accountHolder) {
        this.accountHolder = accountHolder;
    }
    
    // 注意:没有提供setBalance方法,余额只能通过存款和取款方法修改
    
    // 业务方法
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("存款成功!存入:" + amount + "元,当前余额:" + balance + "元");
        } else {
            System.out.println("存款金额必须大于0!");
        }
    }
    
    public boolean withdraw(double amount, String inputPassword) {
        // 验证密码
        if (!password.equals(inputPassword)) {
            System.out.println("密码错误,取款失败!");
            return false;
        }
        
        // 验证余额
        if (amount <= 0) {
            System.out.println("取款金额必须大于0!");
            return false;
        }
        
        if (amount > balance) {
            System.out.println("余额不足,取款失败!当前余额:" + balance + "元");
            return false;
        }
        
        // 执行取款
        balance -= amount;
        System.out.println("取款成功!取出:" + amount + "元,当前余额:" + balance + "元");
        return true;
    }
    
    public void displayInfo() {
        System.out.println("账户信息:");
        System.out.println("- 账号:" + accountNumber);
        System.out.println("- 户主:" + accountHolder);
        System.out.println("- 余额:" + balance + "元");
    }
}
使用银行账户类
public class BankDemo {
    public static void main(String[] args) {
        // 创建银行账户
        BankAccount account = new BankAccount("6225 8888 8888 8888", "张三", "123456");
        
        // 显示账户信息
        account.displayInfo();
        
        // 存款
        account.deposit(1000);
        account.deposit(-100); // 尝试存入负数
        
        // 取款
        account.withdraw(500, "123456"); // 密码正确
        account.withdraw(600, "654321"); // 密码错误
        account.withdraw(1000, "123456"); // 余额不足
        
        // 尝试直接访问私有属性(这会导致编译错误)
        // System.out.println(account.balance); // 错误:balance是私有的
        // account.balance = 1000000; // 错误:balance是私有的
        
        // 使用公共方法访问私有属性
        System.out.println("当前余额:" + account.getBalance() + "元");
        
        // 修改账户持有人
        account.setAccountHolder("张三丰");
        account.displayInfo();
    }
}

2.3 封装的好处

  1. 数据隐藏:防止外部直接访问和修改对象的内部状态
  2. 数据验证:可以在setter方法中添加验证逻辑,确保数据的有效性
  3. 灵活性:可以改变内部实现而不影响外部代码
  4. 安全性:可以控制对敏感数据的访问

3. 继承

继承是面向对象编程的另一个重要特性,它允许一个类(子类)获取另一个类(父类)的属性和方法。

3.1 继承的基本概念

基本语法
// 父类
public class 父类名 {
    // 属性和方法
}

// 子类
public class 子类名 extends 父类名 {
    // 子类特有的属性和方法
}
生活例子:交通工具继承体系
// 父类:交通工具
public class Vehicle {
    // 共有属性
    protected String brand;
    protected String model;
    protected int year;
    protected double speed;
    protected boolean isMoving;
    
    // 构造方法
    public Vehicle(String brand, String model, int year) {
        this.brand = brand;
        this.model = model;
        this.year = year;
        this.speed = 0.0;
        this.isMoving = false;
    }
    
    // 共有方法
    public void start() {
        if (!isMoving) {
            isMoving = true;
            System.out.println(brand + " " + model + " 启动了。");
        } else {
            System.out.println("已经在运行中!");
        }
    }
    
    public void stop() {
        if (isMoving) {
            isMoving = false;
            speed = 0.0;
            System.out.println(brand + " " + model + " 停止了。");
        } else {
            System.out.println("已经停止了!");
        }
    }
    
    public void accelerate(double amount) {
        if (isMoving) {
            speed += amount;
            System.out.println(brand + " " + model + " 加速到 " + speed + " km/h。");
        } else {
            System.out.println("请先启动!");
        }
    }
    
    public void displayInfo() {
        System.out.println("交通工具信息:");
        System.out.println("- 品牌:" + brand);
        System.out.println("- 型号:" + model);
        System.out.println("- 年份:" + year);
        System.out.println("- 当前速度:" + speed + " km/h");
        System.out.println("- 运行状态:" + (isMoving ? "运行中" : "已停止"));
    }
}

// 子类:汽车
public class Car extends Vehicle {
    // 特有属性
    private int numberOfDoors;
    private boolean airConditionerOn;
    
    // 构造方法
    public Car(String brand, String model, int year, int numberOfDoors) {
        super(brand, model, year); // 调用父类构造方法
        this.numberOfDoors = numberOfDoors;
        this.airConditionerOn = false;
    }
    
    // 特有方法
    public void turnOnAirConditioner() {
        if (!airConditionerOn) {
            airConditionerOn = true;
            System.out.println("空调已开启。");
        } else {
            System.out.println("空调已经开启了!");
        }
    }
    
    public void turnOffAirConditioner() {
        if (airConditionerOn) {
            airConditionerOn = false;
            System.out.println("空调已关闭。");
        } else {
            System.out.println("空调已经关闭了!");
        }
    }
    
    // 重写父类方法
    @Override
    public void displayInfo() {
        System.out.println("汽车信息:");
        System.out.println("- 品牌:" + brand);
        System.out.println("- 型号:" + model);
        System.out.println("- 年份:" + year);
        System.out.println("- 门数:" + numberOfDoors);
        System.out.println("- 当前速度:" + speed + " km/h");
        System.out.println("- 运行状态:" + (isMoving ? "运行中" : "已停止"));
        System.out.println("- 空调状态:" + (airConditionerOn ? "开启" : "关闭"));
    }
}

// 子类:自行车
public class Bicycle extends Vehicle {
    // 特有属性
    private int numberOfGears;
    private boolean hasBasket;
    
    // 构造方法
    public Bicycle(String brand, String model, int year, int numberOfGears, boolean hasBasket) {
        super(brand, model, year); // 调用父类构造方法
        this.numberOfGears = numberOfGears;
        this.hasBasket = hasBasket;
    }
    
    // 特有方法
    public void ringBell() {
        System.out.println("叮铃铃!自行车铃声响起。");
    }
    
    // 重写父类方法
    @Override
    public void start() {
        if (!isMoving) {
            isMoving = true;
            System.out.println(brand + " " + model + " 自行车开始骑行。");
        } else {
            System.out.println("已经在骑行中!");
        }
    }
    
    @Override
    public void accelerate(double amount) {
        if (isMoving) {
            // 自行车速度增加较慢,所以只增加一半的量
            speed += amount / 2;
            System.out.println(brand + " " + model + " 自行车加速到 " + speed + " km/h。");
        } else {
            System.out.println("请先开始骑行!");
        }
    }
    
    @Override
    public void displayInfo() {
        System.out.println("自行车信息:");
        System.out.println("- 品牌:" + brand);
        System.out.println("- 型号:" + model);
        System.out.println("- 年份:" + year);
        System.out.println("- 档位数:" + numberOfGears);
        System.out.println("- 是否有篮子:" + (hasBasket ? "是" : "否"));
        System.out.println("- 当前速度:" + speed + " km/h");
        System.out.println("- 运行状态:" + (isMoving ? "骑行中" : "已停止"));
    }
}
使用继承类
public class VehicleDemo {
    public static void main(String[] args) {
        // 创建汽车对象
        Car myCar = new Car("丰田", "卡罗拉", 2020, 4);
        System.out.println("===== 我的汽车 =====");
        myCar.displayInfo();
        myCar.start();
        myCar.accelerate(60);
        myCar.turnOnAirConditioner();
        myCar.displayInfo();
        
        System.out.println("\n===== 我的自行车 =====");
        // 创建自行车对象
        Bicycle myBicycle = new Bicycle("捷安特", "ATX 620", 2021, 21, true);
        myBicycle.displayInfo();
        myBicycle.start();
        myBicycle.accelerate(10);
        myBicycle.ringBell();
        myBicycle.displayInfo();
        
        // 使用父类引用指向子类对象(多态,将在下一节讨论)
        System.out.println("\n===== 交通工具数组 =====");
        Vehicle[] vehicles = new Vehicle[2];
        vehicles[0] = myCar;        // 汽车是一种交通工具
        vehicles[1] = myBicycle;    // 自行车也是一种交通工具
        
        for (int i = 0; i < vehicles.length; i++) {
            System.out.println("\n交通工具 #" + (i + 1) + ":");
            vehicles[i].displayInfo();  // 调用的是各自重写的displayInfo方法
        }
    }
}

3.2 super关键字

super关键字用于引用父类的成员(属性和方法)。在上面的例子中,我们已经使用了super来调用父类的构造方法:

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

super也可以用来调用父类的方法:

@Override
public void displayInfo() {
    super.displayInfo(); // 调用父类的displayInfo方法
    System.out.println("- 门数:" + numberOfDoors);
    System.out.println("- 空调状态:" + (airConditionerOn ? "开启" : "关闭"));
}

3.3 方法重写(Override)

方法重写是指子类提供一个与父类方法签名(名称、参数列表)相同但实现不同的方法。在上面的例子中,CarBicycle类都重写了displayInfo()方法。

重写方法时,可以使用@Override注解,这不是必须的,但它可以帮助编译器检查是否正确重写了父类方法。

4. 多态

多态是面向对象编程的第三个重要特性,它允许使用父类类型的引用指向子类对象,并且调用方法时会执行子类的实现。

4.1 多态的基本概念

多态有两种主要形式:

  1. 编译时多态(静态多态):通过方法重载实现
  2. 运行时多态(动态多态):通过方法重写和继承实现

重载‌:
参数列表必须不同(数量、类型或顺序)。
返回类型可以不同,但仅返回类型不同不构成重载。‌
重写‌:
参数列表和返回类型必须与父类方法相同(或返回类型为父类返回类型的子类型)。
访问修饰符不能比父类更严格(如父类为protected,子类不能为private)

在这里,我们主要讨论运行时多态。

生活例子:动物声音
// 父类:动物
public class Animal {
    protected String name;
    protected int age;
    
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public void makeSound() {
        System.out.println("动物发出声音");
    }
    
    public void displayInfo() {
        System.out.println("动物名称:" + name);
        System.out.println("年龄:" + age + "岁");
    }
}

// 子类:狗
public class Dog extends Animal {
    private String breed; // 品种
    
    public Dog(String name, int age, String breed) {
        super(name, age);
        this.breed = breed;
    }
    
    @Override
    public void makeSound() {
        System.out.println(name + "汪汪汪!");
    }
    
    @Override
    public void displayInfo() {
        System.out.println("狗的信息:");
        super.displayInfo();
        System.out.println("品种:" + breed);
    }
    
    // 特有方法
    public void fetch() {
        System.out.println(name + "在捡球。");
    }
}

// 子类:猫
public class Cat extends Animal {
    private boolean isIndoor; // 是否室内猫
    
    public Cat(String name, int age, boolean isIndoor) {
        super(name, age);
        this.isIndoor = isIndoor;
    }
    
    @Override
    public void makeSound() {
        System.out.println(name + "喵喵喵!");
    }
    
    @Override
    public void displayInfo() {
        System.out.println("猫的信息:");
        super.displayInfo();
        System.out.println("是否室内猫:" + (isIndoor ? "是" : "否"));
    }
    
    // 特有方法
    public void climb() {
        System.out.println(name + "在爬树。");
    }
}

// 子类:鸟
public class Bird extends Animal {
    private boolean canFly; // 是否会飞
    
    public Bird(String name, int age, boolean canFly) {
        super(name, age);
        this.canFly = canFly;
    }
    
    @Override
    public void makeSound() {
        System.out.println(name + "叽叽喳喳!");
    }
    
    @Override
    public void displayInfo() {
        System.out.println("鸟的信息:");
        super.displayInfo();
        System.out.println("是否会飞:" + (canFly ? "是" : "否"));
    }
    
    // 特有方法
    public void fly() {
        if (canFly) {
            System.out.println(name + "在飞翔。");
        } else {
            System.out.println(name + "不会飞。");
        }
    }
}

// 使用多态
public class AnimalDemo {
    public static void main(String[] args) {
        // 创建动物对象
        Animal dog = new Dog("旺财", 3, "金毛");
        Animal cat = new Cat("咪咪", 2, true);
        Animal bird = new Bird("小黄", 1, true);
        
        // 创建动物数组
        Animal[] animals = {dog, cat, bird};
        
        // 使用多态调用方法
        for (Animal animal : animals) {
            System.out.println("\n-----------------------");
            animal.displayInfo();
            animal.makeSound();
            
            // 特有方法需要类型转换
            if (animal instanceof Dog) {
                ((Dog) animal).fetch();
            } else if (animal instanceof Cat) {
                ((Cat) animal).climb();
            } else if (animal instanceof Bird) {
                ((Bird) animal).fly();
            }
        }
    }
}

4.2 多态的好处

  1. 代码灵活性:可以使用父类类型的变量引用不同的子类对象,使代码更加灵活
  2. 可扩展性:可以方便地添加新的子类,而不需要修改使用父类引用的代码
  3. 接口统一:可以统一处理不同类型的对象,简化代码

4.3 向上转型与向下转型

  • 向上转型(Upcasting):将子类引用赋值给父类变量,这是自动的

    Animal dog = new Dog("旺财", 3, "金毛"); // 向上转型
    
  • 向下转型(Downcasting):将父类引用转换为子类类型,这需要显式转换,并且在运行时可能会失败

    Animal animal = new Dog("旺财", 3, "金毛");
    Dog dog = (Dog) animal; // 向下转型,正确
    
    Animal anotherAnimal = new Cat("咪咪", 2, true);
    Dog anotherDog = (Dog) anotherAnimal; // 运行时会抛出ClassCastException
    

为了避免向下转型失败,可以使用instanceof运算符进行类型检查:

if (animal instanceof Dog) {
    Dog dog = (Dog) animal; // 安全的向下转型
    dog.fetch();
}

5. 抽象类

抽象类是一种不能被实例化的类,它可以包含抽象方法(没有实现的方法)和具体方法(有实现的方法)。抽象类主要用于定义子类的通用特性和行为。

5.1 抽象类的基本概念

基本语法
// 抽象类
public abstract class 抽象类名 {
    // 属性
    
    // 构造方法
    
    // 抽象方法(没有方法体)
    public abstract 返回类型 方法名(参数列表);
    
    // 具体方法(有方法体)
    public 返回类型 方法名(参数列表) {
        // 方法体
    }
}
生活例子:交通工具抽象类
// 抽象类:交通工具
public abstract class Vehicle {
    // 属性
    protected String brand;
    protected String model;
    protected double speed;
    
    // 构造方法
    public Vehicle(String brand, String model) {
        this.brand = brand;
        this.model = model;
        this.speed = 0.0;
    }
    
    // 抽象方法(子类必须实现)
    public abstract void start();
    public abstract void stop();
    
    // 具体方法(子类可以继承或重写)
    public void accelerate(double amount) {
        speed += amount;
        System.out.println(brand + " " + model + " 加速到 " + speed + " km/h。");
    }
    
    public void brake(double amount) {
        if (speed >= amount) {
            speed -= amount;
        } else {
            speed = 0;
        }
        System.out.println(brand + " " + model + " 减速到 " + speed + " km/h。");
    }
    
    public void displayInfo() {
        System.out.println("交通工具信息:");
        System.out.println("- 品牌:" + brand);
        System.out.println("- 型号:" + model);
        System.out.println("- 当前速度:" + speed + " km/h");
    }
}

// 具体子类:汽车
public class Car extends Vehicle {
    private boolean engineRunning;
    
    public Car(String brand, String model) {
        super(brand, model);
        this.engineRunning = false;
    }
    
    // 实现抽象方法
    @Override
    public void start() {
        engineRunning = true;
        System.out.println(brand + " " + model + " 汽车启动,引擎运转中。");
    }
    
    @Override
    public void stop() {
        engineRunning = false;
        speed = 0;
        System.out.println(brand + " " + model + " 汽车停止,引擎已关闭。");
    }
    
    // 特有方法
    public void honk() {
        System.out.println(brand + " " + model + " 汽车鸣笛:嘟嘟!");
    }
}

// 具体子类:自行车
public class Bicycle extends Vehicle {
    private boolean isMoving;
    
    public Bicycle(String brand, String model) {
        super(brand, model);
        this.isMoving = false;
    }
    
    // 实现抽象方法
    @Override
    public void start() {
        isMoving = true;
        System.out.println(brand + " " + model + " 自行车开始骑行。");
    }
    
    @Override
    public void stop() {
        isMoving = false;
        speed = 0;
        System.out.println(brand + " " + model + " 自行车停止骑行。");
    }
    
    // 重写具体方法
    @Override
    public void accelerate(double amount) {
        // 自行车加速较慢
        super.accelerate(amount / 2);
    }
    
    // 特有方法
    public void ringBell() {
        System.out.println(brand + " " + model + " 自行车铃声:叮铃铃!");
    }
}

// 使用抽象类
public class VehicleDemo {
    public static void main(String[] args) {
        // Vehicle vehicle = new Vehicle("未知", "未知"); // 错误:抽象类不能被实例化
        
        Vehicle car = new Car("丰田", "卡罗拉");
        Vehicle bicycle = new Bicycle("捷安特", "ATX 620");
        
        System.out.println("===== 汽车 =====");
        car.start();
        car.accelerate(50);
        car.brake(20);
        car.displayInfo();
        ((Car) car).honk(); // 需要向下转型才能调用特有方法
        car.stop();
        
        System.out.println("\n===== 自行车 =====");
        bicycle.start();
        bicycle.accelerate(20); // 自行车加速较慢,实际只增加10
        bicycle.displayInfo();
        ((Bicycle) bicycle).ringBell(); // 需要向下转型才能调用特有方法
        bicycle.stop();
    }
}

5.2 抽象类的特点

  1. 不能被实例化:抽象类不能直接创建对象
  2. 可以包含抽象方法:抽象方法没有方法体,只有声明
  3. 子类必须实现所有抽象方法:除非子类也是抽象类
  4. 可以包含具体方法:有完整实现的方法
  5. 可以包含构造方法:虽然不能直接实例化,但构造方法可以被子类调用
  6. 可以包含成员变量:可以是任何访问修饰符

5.3 抽象类的应用场景

  1. 定义通用特性:当多个类共享某些特性和行为,但具体实现不同时
  2. 强制子类实现某些方法:通过抽象方法确保子类提供特定功能
  3. 提供部分实现:通过具体方法提供通用实现,减少代码重复
  4. 作为框架的基础:许多框架使用抽象类定义基本组件

6. 接口

接口是一种完全抽象的类型,它只定义方法的签名,不提供实现。接口主要用于定义对象的行为规范,而不关心对象的具体类型。

6.1 接口的基本概念

接口可以看作是一种特殊的抽象类,它比抽象类的抽象程度更高。在现实生活中,接口就像是设备之间的连接标准,例如USB接口、HDMI接口等,它们定义了设备之间如何交互,但不关心设备的具体实现。

基本语法
// 接口
public interface 接口名 {
    // 常量(默认是public static final)
    数据类型 常量名 =;
    
    // 抽象方法(默认是public abstract)
    返回类型 方法名(参数列表);
    
    // 默认方法(Java 8及以上)
    default 返回类型 方法名(参数列表) {
        // 方法体
    }
    
    // 静态方法(Java 8及以上)
    static 返回类型 方法名(参数列表) {
        // 方法体
    }
    
    // 私有方法(Java 9及以上)
    private 返回类型 方法名(参数列表) {
        // 方法体
    }
}
生活例子:遥控器接口
// 接口:遥控器
public interface RemoteControl {
    // 常量
    int MAX_VOLUME = 100;
    int MIN_VOLUME = 0;
    
    // 抽象方法
    void powerOn();
    void powerOff();
    void setVolume(int volume);
    void channelUp();
    void channelDown();
    
    // 默认方法
    default void mute() {
        System.out.println("将音量设置为0(静音)");
        setVolume(MIN_VOLUME);
    }
    
    // 静态方法
    static void printInstructions() {
        System.out.println("遥控器使用说明:");
        System.out.println("- 按电源键开关设备");
        System.out.println("- 使用音量键调节音量");
        System.out.println("- 使用频道键切换频道");
    }
}

// 实现接口:电视遥控器
public class TVRemote implements RemoteControl {
    private boolean isPoweredOn;
    private int volume;
    private int channel;
    
    public TVRemote() {
        this.isPoweredOn = false;
        this.volume = 30; // 默认音量
        this.channel = 1; // 默认频道
    }
    
    @Override
    public void powerOn() {
        isPoweredOn = true;
        System.out.println("电视已开机");
    }
    
    @Override
    public void powerOff() {
        isPoweredOn = false;
        System.out.println("电视已关机");
    }
    
    @Override
    public void setVolume(int volume) {
        if (!isPoweredOn) {
            System.out.println("电视未开机,无法调节音量");
            return;
        }
        
        if (volume > MAX_VOLUME) {
            this.volume = MAX_VOLUME;
        } else if (volume < MIN_VOLUME) {
            this.volume = MIN_VOLUME;
        } else {
            this.volume = volume;
        }
        
        System.out.println("电视音量设置为:" + this.volume);
    }
    
    @Override
    public void channelUp() {
        if (!isPoweredOn) {
            System.out.println("电视未开机,无法切换频道");
            return;
        }
        
        channel++;
        System.out.println("电视频道切换为:" + channel);
    }
    
    @Override
    public void channelDown() {
        if (!isPoweredOn) {
            System.out.println("电视未开机,无法切换频道");
            return;
        }
        
        if (channel > 1) {
            channel--;
        }
        System.out.println("电视频道切换为:" + channel);
    }
    
    // 特有方法
    public void showInfo() {
        System.out.println("电视状态:" + (isPoweredOn ? "开机" : "关机"));
        if (isPoweredOn) {
            System.out.println("当前频道:" + channel);
            System.out.println("当前音量:" + volume);
        }
    }
}

// 实现接口:音响遥控器
public class StereoRemote implements RemoteControl {
    private boolean isPoweredOn;
    private int volume;
    private String mode; // FM, AM, AUX, Bluetooth等
    
    public StereoRemote() {
        this.isPoweredOn = false;
        this.volume = 20; // 默认音量
        this.mode = "FM"; // 默认模式
    }
    
    @Override
    public void powerOn() {
        isPoweredOn = true;
        System.out.println("音响已开机");
    }
    
    @Override
    public void powerOff() {
        isPoweredOn = false;
        System.out.println("音响已关机");
    }
    
    @Override
    public void setVolume(int volume) {
        if (!isPoweredOn) {
            System.out.println("音响未开机,无法调节音量");
            return;
        }
        
        if (volume > MAX_VOLUME) {
            this.volume = MAX_VOLUME;
        } else if (volume < MIN_VOLUME) {
            this.volume = MIN_VOLUME;
        } else {
            this.volume = volume;
        }
        
        System.out.println("音响音量设置为:" + this.volume);
    }
    
    @Override
    public void channelUp() {
        if (!isPoweredOn) {
            System.out.println("音响未开机,无法切换频道");
            return;
        }
        
        System.out.println("音响频率上调");
    }
    
    @Override
    public void channelDown() {
        if (!isPoweredOn) {
            System.out.println("音响未开机,无法切换频道");
            return;
        }
        
        System.out.println("音响频率下调");
    }
    
    // 重写默认方法
    @Override
    public void mute() {
        if (!isPoweredOn) {
            System.out.println("音响未开机,无法静音");
            return;
        }
        
        System.out.println("音响已静音");
        setVolume(MIN_VOLUME);
    }
    
    // 特有方法
    public void changeMode(String mode) {
        if (!isPoweredOn) {
            System.out.println("音响未开机,无法切换模式");
            return;
        }
        
        this.mode = mode;
        System.out.println("音响模式切换为:" + mode);
    }
    
    public void showInfo() {
        System.out.println("音响状态:" + (isPoweredOn ? "开机" : "关机"));
        if (isPoweredOn) {
            System.out.println("当前模式:" + mode);
            System.out.println("当前音量:" + volume);
        }
    }
}

// 使用接口
public class RemoteControlDemo {
    public static void main(String[] args) {
        // 打印使用说明
        RemoteControl.printInstructions();
        
        System.out.println("\n===== 电视遥控器 =====");
        TVRemote tvRemote = new TVRemote();
        tvRemote.powerOn();
        tvRemote.setVolume(50);
        tvRemote.channelUp();
        tvRemote.channelUp();
        tvRemote.showInfo();
        tvRemote.mute(); // 使用默认方法
        tvRemote.showInfo();
        tvRemote.powerOff();
        
        System.out.println("\n===== 音响遥控器 =====");
        StereoRemote stereoRemote = new StereoRemote();
        stereoRemote.powerOn();
        stereoRemote.setVolume(70);
        stereoRemote.changeMode("Bluetooth");
        stereoRemote.showInfo();
        stereoRemote.mute();
        stereoRemote.showInfo();
        stereoRemote.powerOff();
        
        // 使用接口类型引用
        System.out.println("\n===== 使用接口类型引用 =====");
        RemoteControl remote = new TVRemote(); // 接口引用指向实现类对象
        remote.powerOn();
        remote.setVolume(40);
        remote.powerOff();
    }
}

6.2 接口的特点

  1. 只能包含抽象方法、默认方法、静态方法和私有方法:接口中的方法默认是public abstract
  2. 只能包含常量:接口中的变量默认是public static final
  3. 不能被实例化:接口不能直接创建对象
  4. 类可以实现多个接口:这是Java实现多重继承的方式
  5. 接口可以继承其他接口:使用extends关键字

6.3 接口与抽象类的区别

特性接口抽象类
方法只能有抽象方法、默认方法、静态方法和私有方法可以有抽象方法和具体方法
变量只能有常量(public static final)可以有任何类型的变量
构造方法不能有构造方法可以有构造方法
继承类可以实现多个接口类只能继承一个抽象类
关键字implementsextends
设计目的定义行为规范提供基础实现

6.4 接口的应用场景

  1. 定义通用行为:当多个不相关的类需要实现相同的行为时
  2. 实现多重继承:Java不支持类的多重继承,但可以通过接口实现
  3. 解耦合:接口可以降低代码的耦合度,提高灵活性
  4. 回调机制:通过接口实现回调功能

6.5 生活中的接口例子

充电接口

在现实生活中,充电接口就是一个很好的例子。不同的设备(手机、平板、笔记本电脑等)可能使用不同的充电器,但它们都需要实现相同的充电接口标准(如USB-C、Lightning等)。

// 充电接口
public interface ChargingPort {
    void connect();
    void disconnect();
    void charge();
    int getBatteryLevel();
}

// USB-C接口实现
public class USBCPort implements ChargingPort {
    private boolean isConnected;
    private int batteryLevel;
    
    public USBCPort() {
        this.isConnected = false;
        this.batteryLevel = 20; // 初始电量
    }
    
    @Override
    public void connect() {
        isConnected = true;
        System.out.println("USB-C接口已连接");
    }
    
    @Override
    public void disconnect() {
        isConnected = false;
        System.out.println("USB-C接口已断开");
    }
    
    @Override
    public void charge() {
        if (isConnected) {
            batteryLevel += 10;
            if (batteryLevel > 100) {
                batteryLevel = 100;
            }
            System.out.println("通过USB-C接口充电中,当前电量:" + batteryLevel + "%");
        } else {
            System.out.println("请先连接充电器");
        }
    }
    
    @Override
    public int getBatteryLevel() {
        return batteryLevel;
    }
}

// Lightning接口实现
public class LightningPort implements ChargingPort {
    private boolean isConnected;
    private int batteryLevel;
    
    public LightningPort() {
        this.isConnected = false;
        this.batteryLevel = 30; // 初始电量
    }
    
    @Override
    public void connect() {
        isConnected = true;
        System.out.println("Lightning接口已连接");
    }
    
    @Override
    public void disconnect() {
        isConnected = false;
        System.out.println("Lightning接口已断开");
    }
    
    @Override
    public void charge() {
        if (isConnected) {
            batteryLevel += 8;
            if (batteryLevel > 100) {
                batteryLevel = 100;
            }
            System.out.println("通过Lightning接口充电中,当前电量:" + batteryLevel + "%");
        } else {
            System.out.println("请先连接充电器");
        }
    }
    
    @Override
    public int getBatteryLevel() {
        return batteryLevel;
    }
}

// 使用充电接口
public class ChargingDemo {
    public static void main(String[] args) {
        System.out.println("===== 安卓手机(USB-C接口)=====");
        ChargingPort androidPhone = new USBCPort();
        System.out.println("初始电量:" + androidPhone.getBatteryLevel() + "%");
        androidPhone.charge(); // 尝试在未连接时充电
        androidPhone.connect();
        for (int i = 0; i < 5; i++) {
            androidPhone.charge();
        }
        androidPhone.disconnect();
        
        System.out.println("\n===== 苹果手机(Lightning接口)=====");
        ChargingPort iPhone = new LightningPort();
        System.out.println("初始电量:" + iPhone.getBatteryLevel() + "%");
        iPhone.connect();
        for (int i = 0; i < 5; i++) {
            iPhone.charge();
        }
        iPhone.disconnect();
    }
}

7. 内部类

内部类是定义在另一个类内部的类。Java支持多种类型的内部类,每种类型都有其特定的用途和特点。

7.1 内部类的基本概念

内部类可以看作是外部类的一个成员,它可以访问外部类的所有成员(包括私有成员),同时也可以对外隐藏。在现实生活中,内部类就像是一个组件或零件,它是某个大型设备的一部分,但也有自己的功能和特性。

7.2 内部类的类型

7.2.1 成员内部类

成员内部类是最常见的内部类,它作为外部类的一个成员存在,可以访问外部类的所有成员。

// 外部类:汽车
public class Car {
    private String brand;
    private String model;
    private Engine engine; // 引用内部类
    
    public Car(String brand, String model, int horsePower) {
        this.brand = brand;
        this.model = model;
        this.engine = new Engine(horsePower);
    }
    
    public void start() {
        System.out.println(brand + " " + model + " 启动中...");
        engine.start();
    }
    
    public void stop() {
        System.out.println(brand + " " + model + " 停止中...");
        engine.stop();
    }
    
    public void showInfo() {
        System.out.println("汽车信息:");
        System.out.println("- 品牌:" + brand);
        System.out.println("- 型号:" + model);
        System.out.println("- 引擎马力:" + engine.horsePower + "HP");
        System.out.println("- 引擎状态:" + (engine.isRunning ? "运行中" : "已停止"));
    }
    
    // 成员内部类:引擎
    private class Engine {
        private int horsePower;
        private boolean isRunning;
        
        public Engine(int horsePower) {
            this.horsePower = horsePower;
            this.isRunning = false;
        }
        
        public void start() {
            isRunning = true;
            System.out.println("引擎启动,马力:" + horsePower + "HP");
        }
        
        public void stop() {
            isRunning = false;
            System.out.println("引擎停止");
        }
    }
}

// 使用带有成员内部类的外部类
public class CarDemo {
    public static void main(String[] args) {
        Car myCar = new Car("丰田", "卡罗拉", 140);
        myCar.showInfo();
        myCar.start();
        myCar.showInfo();
        myCar.stop();
        myCar.showInfo();
        
        // 注意:不能直接创建内部类的实例
        // Car.Engine engine = new Car.Engine(100); // 错误:Engine是私有的
        // 即使Engine不是私有的,也需要通过外部类实例创建
        // Car.Engine engine = myCar.new Engine(100);
    }
}
7.2.2 静态内部类

静态内部类是使用static关键字修饰的内部类,它不依赖于外部类的实例,但可以访问外部类的静态成员。

// 外部类:数学工具
public class MathUtils {
    // 静态变量
    private static final double PI = 3.14159;
    
    // 静态方法
    public static double calculateCircleArea(double radius) {
        return PI * radius * radius;
    }
    
    // 静态内部类:几何计算器
    public static class GeometryCalculator {
        // 计算矩形面积
        public double calculateRectangleArea(double length, double width) {
            return length * width;
        }
        
        // 计算三角形面积
        public double calculateTriangleArea(double base, double height) {
            return 0.5 * base * height;
        }
        
        // 使用外部类的静态方法
        public double calculateCircleArea(double radius) {
            return MathUtils.calculateCircleArea(radius);
        }
    }
}

// 使用静态内部类
public class MathDemo {
    public static void main(String[] args) {
        // 直接创建静态内部类的实例,不需要外部类的实例
        MathUtils.GeometryCalculator calculator = new MathUtils.GeometryCalculator();
        
        // 使用静态内部类的方法
        double rectangleArea = calculator.calculateRectangleArea(5, 10);
        double triangleArea = calculator.calculateTriangleArea(4, 6);
        double circleArea = calculator.calculateCircleArea(3);
        
        System.out.println("矩形面积:" + rectangleArea);
        System.out.println("三角形面积:" + triangleArea);
        System.out.println("圆形面积:" + circleArea);
        
        // 直接使用外部类的静态方法
        double anotherCircleArea = MathUtils.calculateCircleArea(5);
        System.out.println("另一个圆形面积:" + anotherCircleArea);
    }
}
7.2.3 局部内部类

局部内部类是定义在方法或代码块内的类,它只在定义它的方法或代码块内可见。

// 外部类:玩具工厂
public class ToyFactory {
    private String factoryName;
    
    public ToyFactory(String factoryName) {
        this.factoryName = factoryName;
    }
    
    // 方法中定义局部内部类
    public void produceToy(String toyType) {
        // 局部变量
        final String productionDate = java.time.LocalDate.now().toString();
        
        // 局部内部类:玩具
        class Toy {
            private String type;
            private String manufacturer;
            
            public Toy(String type) {
                this.type = type;
                this.manufacturer = factoryName; // 访问外部类的成员
            }
            
            public void showInfo() {
                System.out.println("玩具信息:");
                System.out.println("- 类型:" + type);
                System.out.println("- 制造商:" + manufacturer);
                System.out.println("- 生产日期:" + productionDate); // 访问局部变量
            }
        }
        
        // 在方法内创建局部内部类的实例
        Toy toy = new Toy(toyType);
        System.out.println(factoryName + " 工厂生产了一个新玩具:");
        toy.showInfo();
    }
}

// 使用带有局部内部类的类
public class ToyFactoryDemo {
    public static void main(String[] args) {
        ToyFactory factory = new ToyFactory("快乐玩具厂");
        factory.produceToy("泰迪熊");
        factory.produceToy("遥控车");
        
        // 注意:不能在方法外部访问局部内部类
        // Toy toy = new Toy("积木"); // 错误:Toy类在方法外不可见
    }
}
7.2.4 匿名内部类

匿名内部类是没有名字的内部类,它通常用于创建接口或抽象类的实例。

// 接口:按钮监听器
public interface ButtonListener {
    void onClick();
    void onLongPress();
}

// 外部类:按钮
public class Button {
    private String label;
    private ButtonListener listener;
    
    public Button(String label) {
        this.label = label;
    }
    
    public void setLabel(String label) {
        this.label = label;
    }
    
    public void setListener(ButtonListener listener) {
        this.listener = listener;
    }
    
    public void click() {
        System.out.println("按钮 '" + label + "' 被点击");
        if (listener != null) {
            listener.onClick();
        }
    }
    
    public void longPress() {
        System.out.println("按钮 '" + label + "' 被长按");
        if (listener != null) {
            listener.onLongPress();
        }
    }
}

// 使用匿名内部类
public class ButtonDemo {
    public static void main(String[] args) {
        // 创建按钮
        Button loginButton = new Button("登录");
        Button registerButton = new Button("注册");
        
        // 使用匿名内部类设置按钮监听器
        loginButton.setListener(new ButtonListener() {
            @Override
            public void onClick() {
                System.out.println("执行登录操作");
            }
            
            @Override
            public void onLongPress() {
                System.out.println("显示登录帮助");
            }
        });
        
        registerButton.setListener(new ButtonListener() {
            @Override
            public void onClick() {
                System.out.println("执行注册操作");
            }
            
            @Override
            public void onLongPress() {
                System.out.println("显示注册协议");
            }
        });
        
        // 模拟按钮操作
        loginButton.click();
        registerButton.longPress();
    }
}

7.3 内部类的优点

  1. 封装性:内部类可以对外部隐藏,提高封装性
  2. 访问外部类成员:内部类可以访问外部类的所有成员,包括私有成员
  3. 提高代码可读性:将相关的类放在一起,使代码结构更清晰
  4. 实现回调:匿名内部类常用于实现回调功能

7.4 内部类的应用场景

  1. 组件关系:当一个类是另一个类的组件时,可以使用成员内部类
  2. 辅助功能:当一个类只为另一个类提供服务时,可以使用内部类
  3. 实现接口:使用匿名内部类快速实现接口
  4. 事件处理:在GUI编程中,使用匿名内部类处理事件

7.5 生活中的内部类例子

嵌套玩具

在现实生活中,俄罗斯套娃是一个很好的内部类例子。每个套娃都可以包含一个更小的套娃,形成嵌套结构。

// 俄罗斯套娃类
public class RussianDoll {
    private String color;
    private int size;
    private RussianDoll innerDoll; // 内部的套娃
    
    public RussianDoll(String color, int size) {
        this.color = color;
        this.size = size;
        this.innerDoll = null;
    }
    
    // 添加内部套娃
    public void addInnerDoll(String color, int size) {
        if (size >= this.size) {
            System.out.println("错误:内部套娃必须比外部套娃小!");
            return;
        }
        
        if (innerDoll == null) {
            innerDoll = new RussianDoll(color, size);
        } else {
            innerDoll.addInnerDoll(color, size);
        }
    }
    
    // 显示所有套娃信息
    public void displayDolls(String prefix) {
        System.out.println(prefix + "套娃 - 颜色:" + color + ",尺寸:" + size);
        if (innerDoll != null) {
            innerDoll.displayDolls(prefix + "  ");
        }
    }
}

// 使用俄罗斯套娃
public class RussianDollDemo {
    public static void main(String[] args) {
        RussianDoll outerDoll = new RussianDoll("红色", 10);
        outerDoll.addInnerDoll("蓝色", 8);
        outerDoll.addInnerDoll("绿色", 6);
        outerDoll.addInnerDoll("黄色", 4);
        outerDoll.addInnerDoll("紫色", 2);
        
        System.out.println("俄罗斯套娃结构:");
        outerDoll.displayDolls("");
    }
}

总结

在本章中,我们学习了面向对象编程的核心概念:类与对象、封装、继承、多态、抽象类、接口和内部类。这些概念是Java编程的基础,掌握它们将帮助你设计出更加灵活、可维护的程序。

通过生活中的例子,我们看到了面向对象编程如何模拟现实世界的对象和关系。在接下来的章节中,我们将学习更多Java的高级特性,如异常处理、泛型编程和集合框架等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值