导航菜单

Java面向对象编程

Java面向对象编程

Java是一种完全面向对象的编程语言,理解面向对象编程(OOP)的概念对于掌握Java至关重要。本教程将介绍Java面向对象编程的核心概念和实践。

学习目标

  • 理解面向对象编程的基本概念
  • 学习如何定义和使用类与对象
  • 掌握继承、多态和封装的应用
  • 了解抽象类和接口的使用

面向对象编程的基本概念

面向对象编程基于以下几个核心概念:

类(Class)

定义对象的属性和行为的模板或蓝图

对象(Object)

类的实例,具有状态(属性)和行为(方法)

封装(Encapsulation)

将数据和操作数据的方法绑定在一起,对外部隐藏实现细节

继承(Inheritance)

一个类可以继承另一个类的特性,实现代码重用

多态(Polymorphism)

同一个方法可以在不同的对象上有不同的行为

抽象(Abstraction)

关注对象的关键特性而忽略细节,提供更高层次的视角

类与对象

定义类

在Java中,我们使用class关键字来定义类:

public class Student {
    // 属性(成员变量)
    private String name;
    private int age;
    private String studentId;
    
    // 构造方法
    public Student(String name, int age, String studentId) {
        this.name = name;
        this.age = age;
        this.studentId = studentId;
    }
    
    // 方法
    public void study() {
        System.out.println(name + " is studying.");
    }
    
    public void displayInfo() {
        System.out.println("Name: " + name);
        System.out.println("Age: " + age);
        System.out.println("Student ID: " + studentId);
    }
}

类的组成部分:

  1. 属性:定义对象的状态或特征
  2. 构造方法:用于创建对象实例并初始化属性
  3. 方法:定义对象的行为或功能

创建对象

一旦定义了类,我们就可以创建该类的对象:

public class Main {
    public static void main(String[] args) {
        // 创建Student类的对象
        Student student1 = new Student("张三", 20, "S001");
        Student student2 = new Student("李四", 22, "S002");
        
        // 调用对象的方法
        student1.displayInfo();
        student1.study();
        
        student2.displayInfo();
        student2.study();
    }
}

构造方法

构造方法是一种特殊的方法,用于初始化对象。它具有以下特点:

  • 方法名与类名相同
  • 没有返回类型,甚至不需要void
  • 在创建对象时自动调用

我们可以定义多个构造方法,实现方法重载:

public class Student {
    private String name;
    private int age;
    private String studentId;
    
    // 默认构造方法
    public Student() {
        name = "Unknown";
        age = 0;
        studentId = "Not Assigned";
    }
    
    // 带参数的构造方法
    public Student(String name, int age, String studentId) {
        this.name = name;
        this.age = age;
        this.studentId = studentId;
    }
    
    // 部分参数的构造方法
    public Student(String name) {
        this.name = name;
        age = 0;
        studentId = "Not Assigned";
    }
}

使用示例:

Student s1 = new Student();                  // 使用默认构造方法
Student s2 = new Student("王五", 21, "S003"); // 使用带全部参数的构造方法
Student s3 = new Student("赵六");             // 使用部分参数的构造方法

访问修饰符

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

修饰符同一类同一包子类其他包
private
default (无修饰符)
protected
public

封装

封装是面向对象编程的基本原则之一,它将数据(属性)和操作数据的方法捆绑在一起,并对外部隐藏实现细节。

实现封装

在Java中,我们通过以下方式实现封装:

  1. 将类的属性声明为私有(private)
  2. 提供公共(public)的getter和setter方法访问和修改属性
public class Person {
    // 私有属性
    private String name;
    private int age;
    
    // Getter方法
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
    
    // Setter方法
    public void setName(String name) {
        this.name = name;
    }
    
    public void setAge(int age) {
        // 可以添加验证逻辑
        if (age > 0 && age < 120) {
            this.age = age;
        } else {
            System.out.println("Invalid age value");
        }
    }
}

封装的优点:

  • 数据隐藏:防止外部直接访问和修改数据
  • 灵活性:可以更改实现而不影响使用方
  • 可控性:可以在setter方法中添加验证逻辑
  • 安全性:保护对象不受外部干扰

继承

继承允许我们基于现有类创建新类,新类继承现有类的属性和方法。这促进了代码复用并建立了类之间的层次关系。

实现继承

在Java中,使用extends关键字实现继承:

// 父类(基类)
public class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public void displayInfo() {
        System.out.println("Name: " + name);
        System.out.println("Age: " + age);
    }
}

// 子类(派生类)
public class Student extends Person {
    private String studentId;
    
    public Student(String name, int age, String studentId) {
        super(name, age);  // 调用父类构造方法
        this.studentId = studentId;
    }
    
    // 扩展方法
    public void study() {
        System.out.println("Student is studying");
    }
    
    // 重写父类方法
    @Override
    public void displayInfo() {
        super.displayInfo();  // 调用父类方法
        System.out.println("Student ID: " + studentId);
    }
}

继承的要点:

  • 子类继承父类的所有非私有成员(属性和方法)
  • 子类可以添加新的属性和方法
  • 子类可以重写(覆盖)父类的方法
  • Java只支持单继承,一个类只能直接继承一个父类
  • 使用super关键字调用父类的构造方法和方法

方法重写(覆盖)

方法重写是指子类提供了父类方法的特定实现:

public class Animal {
    public void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

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

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

方法重写的规则:

  • 方法名、参数列表和返回类型必须相同
  • 访问修饰符不能比父类更严格
  • 不能重写被声明为final的方法
  • 子类方法不能抛出比父类方法更广泛的检查异常

多态

多态是指同一种操作或方法可以在不同的对象上有不同的行为。多态是面向对象编程的强大特性,使代码更加灵活和可扩展。

多态的类型

Java支持两种类型的多态:

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

方法重载

方法重载指在同一个类中定义多个同名但参数不同的方法:

public class Calculator {
    // 两个整数相加
    public int add(int a, int b) {
        return a + b;
    }
    
    // 三个整数相加
    public int add(int a, int b, int c) {
        return a + b + c;
    }
    
    // 两个双精度数相加
    public double add(double a, double b) {
        return a + b;
    }
}

方法重载的条件:

  • 方法名必须相同
  • 参数列表必须不同(参数类型、数量或顺序)
  • 返回类型可以相同也可以不同,但不能仅靠返回类型区分

运行时多态

运行时多态通过方法重写和继承实现:

public class Main {
    public static void main(String[] args) {
        Animal myAnimal = new Animal();
        Animal myDog = new Dog();
        Animal myCat = new Cat();
        
        myAnimal.makeSound();  // 输出: Animal makes a sound
        myDog.makeSound();     // 输出: Dog barks
        myCat.makeSound();     // 输出: Cat meows
    }
}

注意变量myDogmyCat的声明类型是Animal,但它们引用的实际对象类型分别是DogCat。Java虚拟机会根据实际对象类型调用相应的方法。

抽象类与接口

抽象类

抽象类是不能被实例化的类,用于提供一个可以被子类继承的通用形式:

// 抽象类
public abstract class Shape {
    // 普通属性
    private String color;
    
    // 构造方法
    public Shape(String color) {
        this.color = color;
    }
    
    // 普通方法
    public String getColor() {
        return color;
    }
    
    // 抽象方法(没有实现)
    public abstract double calculateArea();
    public abstract double calculatePerimeter();
}

// 具体子类
public class Circle extends Shape {
    private double radius;
    
    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }
    
    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
    
    @Override
    public double calculatePerimeter() {
        return 2 * Math.PI * radius;
    }
}

抽象类的特点:

  • 使用abstract关键字声明
  • 可以包含抽象方法和非抽象方法
  • 抽象方法没有方法体,以分号结束
  • 子类必须实现所有抽象方法,除非子类也是抽象类
  • 不能被实例化,只能被继承

接口

接口是一种完全抽象的类型,只包含抽象方法和常量:

// 定义接口
public interface Drawable {
    // 常量(默认public static final)
    String TOOL = "Pencil";
    
    // 抽象方法(默认public abstract)
    void draw();
    
    // Java 8+: 默认方法
    default void displayInfo() {
        System.out.println("Drawing with " + TOOL);
    }
    
    // Java 8+: 静态方法
    static void printVersion() {
        System.out.println("Drawable Interface v1.0");
    }
}

// 实现接口
public class Rectangle implements Drawable {
    private double width;
    private double height;
    
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    @Override
    public void draw() {
        System.out.println("Drawing Rectangle with width: " + width + " and height: " + height);
    }
}

接口的特点:

  • 使用interface关键字声明
  • 方法默认是public abstract,可以省略这些修饰符
  • 属性默认是public static final,只能是常量
  • 类通过implements关键字实现接口
  • 一个类可以实现多个接口,解决Java单继承的限制
  • Java 8引入了默认方法和静态方法,允许在接口中提供实现

抽象类与接口的比较

特性抽象类接口
关键字abstractinterface
方法可以有抽象方法和具体方法抽象方法、默认方法、静态方法
变量可以有实例变量只能有常量(public static final)
构造方法可以有构造方法不能有构造方法
继承单继承可以实现多个接口
访问修饰符可以使用所有访问修饰符方法默认为public
主要用途对相关类提供通用基础实现定义对象的行为契约

实践示例:银行账户系统

让我们通过一个银行账户系统的例子来综合应用面向对象编程的概念:

// 账户接口
interface Account {
    void deposit(double amount);
    boolean withdraw(double amount);
    double getBalance();
    String getAccountInfo();
}

// 抽象账户类
abstract class BankAccount implements Account {
    protected String accountNumber;
    protected double balance;
    protected String ownerName;
    
    public BankAccount(String accountNumber, String ownerName, double initialBalance) {
        this.accountNumber = accountNumber;
        this.ownerName = ownerName;
        this.balance = initialBalance;
    }
    
    @Override
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("存款成功: ¥" + amount);
        } else {
            System.out.println("存款金额必须大于0");
        }
    }
    
    @Override
    public boolean withdraw(double amount) {
        if (amount > 0 && balance >= amount) {
            balance -= amount;
            System.out.println("取款成功: ¥" + amount);
            return true;
        } else {
            System.out.println("取款失败: 余额不足或金额无效");
            return false;
        }
    }
    
    @Override
    public double getBalance() {
        return balance;
    }
    
    @Override
    public String getAccountInfo() {
        return "账户号: " + accountNumber + ", 户主: " + ownerName + ", 余额: ¥" + balance;
    }
    
    // 抽象方法
    public abstract String getAccountType();
}

// 储蓄账户
class SavingsAccount extends BankAccount {
    private double interestRate;
    
    public SavingsAccount(String accountNumber, String ownerName, double initialBalance, double interestRate) {
        super(accountNumber, ownerName, initialBalance);
        this.interestRate = interestRate;
    }
    
    public void addInterest() {
        double interest = balance * interestRate;
        balance += interest;
        System.out.println("已添加利息: ¥" + interest);
    }
    
    @Override
    public String getAccountType() {
        return "储蓄账户";
    }
    
    @Override
    public String getAccountInfo() {
        return super.getAccountInfo() + ", 利率: " + (interestRate * 100) + "%";
    }
}

// 支票账户
class CheckingAccount extends BankAccount {
    private double overdraftLimit;
    
    public CheckingAccount(String accountNumber, String ownerName, double initialBalance, double overdraftLimit) {
        super(accountNumber, ownerName, initialBalance);
        this.overdraftLimit = overdraftLimit;
    }
    
    @Override
    public boolean withdraw(double amount) {
        if (amount > 0 && (balance + overdraftLimit) >= amount) {
            balance -= amount;
            System.out.println("取款成功: ¥" + amount);
            if (balance < 0) {
                System.out.println("警告: 账户已透支");
            }
            return true;
        } else {
            System.out.println("取款失败: 超出透支限额或金额无效");
            return false;
        }
    }
    
    @Override
    public String getAccountType() {
        return "支票账户";
    }
    
    @Override
    public String getAccountInfo() {
        return super.getAccountInfo() + ", 透支限额: ¥" + overdraftLimit;
    }
}

// 测试类
public class BankingSystem {
    public static void main(String[] args) {
        SavingsAccount savingsAccount = new SavingsAccount("SA001", "张三", 5000, 0.03);
        CheckingAccount checkingAccount = new CheckingAccount("CA001", "李四", 3000, 2000);
        
        System.out.println("账户信息:");
        System.out.println(savingsAccount.getAccountInfo());
        System.out.println(checkingAccount.getAccountInfo());
        
        System.out.println("\n存款操作:");
        savingsAccount.deposit(1000);
        checkingAccount.deposit(500);
        
        System.out.println("\n取款操作:");
        savingsAccount.withdraw(2000);
        checkingAccount.withdraw(4000);  // 透支
        
        System.out.println("\n添加利息:");
        savingsAccount.addInterest();
        
        System.out.println("\n更新后的账户信息:");
        System.out.println(savingsAccount.getAccountInfo());
        System.out.println(checkingAccount.getAccountInfo());
    }
}

这个例子展示了:

  • 封装:属性为protected,通过方法访问和修改
  • 继承SavingsAccountCheckingAccount继承自BankAccount
  • 多态:不同账户类型对withdraw方法有不同的实现
  • 抽象BankAccount是抽象类,包含抽象方法getAccountType()
  • 接口Account接口定义了账户的基本行为

总结

面向对象编程是Java的核心范式,掌握其基本概念和应用对于成为一名优秀的Java开发者至关重要。通过本教程,我们学习了:

  • 类和对象的定义与使用
  • 封装数据和操作的方法
  • 通过继承实现代码重用
  • 利用多态使代码更加灵活
  • 抽象类和接口的应用

在下一篇教程中,我们将深入探讨Java的高级特性,如泛型、集合框架和异常处理。

实践提示

尝试扩展银行账户系统示例,添加新的账户类型或功能,如定期存款账户、贷款账户、转账功能等。这将帮助你更好地理解和应用面向对象的概念。

搜索