对象和类是Java面向对象编程(OOP)的核心概念。本教程将详细介绍Java中类和对象的概念、创建和使用方法,并提供丰富的代码示例。
1. 面向对象编程(OOP)基础
在深入学习类和对象之前,先了解OOP的四个基本特性:
- 封装(Encapsulation):隐藏对象内部细节,只暴露必要的接口
- 继承(Inheritance):子类继承父类的属性和方法
- 多态(Polymorphism):同一操作作用于不同对象有不同的解释
- 抽象(Abstraction):提取关键特征而忽略非关键细节
2. 类(Class)的概念
类是创建对象的蓝图或模板,它定义了对象的属性和行为。
2.1 定义一个简单的类
java// Person.java
public class Person {
// 成员变量(属性)
String name;
int age;
// 成员方法(行为)
public void introduce() {
System.out.println("你好,我叫" + name + ",今年" + age + "岁。");
}
}
关键点:
- 类名通常采用大驼峰命名法(每个单词首字母大写)
- 成员变量定义对象的属性
- 成员方法定义对象的行为
3. 对象(Object)的概念
对象是类的实例,具有类定义的状态和行为。
3.1 创建和使用对象
javapublic class Main {
public static void main(String[] args) {
// 创建对象
Person person1 = new Person();
// 访问对象属性并赋值
person1.name = "张三";
person1.age = 25;
// 调用对象方法
person1.introduce();
// 创建第二个对象
Person person2 = new Person();
person2.name = "李四";
person2.age = 30;
person2.introduce();
}
}
输出结果:
你好,我叫张三,今年25岁。
你好,我叫李四,今年30岁。
4. 构造方法(Constructor)
构造方法用于在创建对象时初始化对象。
4.1 定义构造方法
javapublic class Person {
String name;
int age;
// 无参构造方法
public Person() {
this.name = "未知";
this.age = 0;
}
// 带参构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void introduce() {
System.out.println("你好,我叫" + name + ",今年" + age + "岁。");
}
}
4.2 使用构造方法
javapublic class Main {
public static void main(String[] args) {
// 使用无参构造方法
Person person1 = new Person();
person1.introduce();
// 使用带参构造方法
Person person2 = new Person("王五", 28);
person2.introduce();
}
}
输出结果:
你好,我叫未知,今年0岁。
你好,我叫王五,今年28岁。
5. 方法详解
5.1 方法的基本结构
java访问修饰符 返回类型 方法名(参数列表) {
// 方法体
return 返回值; // 如果返回类型不是void
}
5.2 方法重载(Overloading)
同一个类中可以有多个同名方法,但参数列表必须不同。
javapublic 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) {
Calculator calc = new Calculator();
System.out.println(calc.add(5, 3)); // 输出: 8
System.out.println(calc.add(5, 3, 2)); // 输出: 10
System.out.println(calc.add(5.5, 3.2)); // 输出: 8.7
}
}
5.3 参数传递
Java中参数传递都是值传递:
javapublic class ParameterTest {
// 基本类型参数传递
public void changePrimitive(int value) {
value = 100;
}
// 引用类型参数传递
public void changeObject(StringBuilder builder) {
builder.append(" World!");
}
public static void main(String[] args) {
ParameterTest test = new ParameterTest();
// 基本类型测试
int num = 10;
test.changePrimitive(num);
System.out.println(num); // 输出: 10 (未改变)
// 引用类型测试
StringBuilder sb = new StringBuilder("Hello");
test.changeObject(sb);
System.out.println(sb); // 输出: Hello World! (改变了)
}
}
6. 封装和访问控制
6.1 访问修饰符
修饰符 | 类内 | 同一包内 | 子类 | 其他包 |
private | ✓ | |||
(默认/包级) | ✓ | ✓ | ||
protected | ✓ | ✓ | ✓ | |
public | ✓ | ✓ | ✓ | ✓ |
6.2 实现封装
javapublic class BankAccount {
// 私有成员变量
private String accountNumber;
private double balance;
// 公有构造方法
public BankAccount(String accountNumber, double initialBalance) {
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
// 公有方法访问私有变量
public double getBalance() {
return balance;
}
// 存款方法
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("存款成功,当前余额: " + balance);
} else {
System.out.println("存款金额必须大于0");
}
}
// 取款方法
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("取款成功,当前余额: " + balance);
} else {
System.out.println("取款金额无效或余额不足");
}
}
}
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount("123456789", 1000);
account.deposit(500);
account.withdraw(200);
// 尝试直接访问私有变量会编译错误
// System.out.println(account.balance); // 错误
// 必须通过公有方法访问
System.out.println("当前余额: " + account.getBalance());
}
}
7. static关键字
7.1 静态变量
静态变量属于类,而不是某个对象。
javapublic class Employee {
private String name;
private static int nextId = 1;
private int id;
public Employee(String name) {
this.name = name;
this.id = nextId++;
}
public void printInfo() {
System.out.println("员工ID: " + id + ", 姓名: " + name);
}
public static int getNextId() {
return nextId;
}
}
public class Main {
public static void main(String[] args) {
Employee emp1 = new Employee("张三");
Employee emp2 = new Employee("李四");
emp1.printInfo(); // 员工ID: 1, 姓名: 张三
emp2.printInfo(); // 员工ID: 2, 姓名: 李四
System.out.println("下一个员工ID: " + Employee.getNextId()); // 3
}
}
7.2 静态方法
静态方法属于类,可以直接通过类名调用。
javapublic class MathUtils {
// 静态方法
public static double circleArea(double radius) {
return Math.PI * radius * radius;
}
// 实例方法
public double rectangleArea(double length, double width) {
return length * width;
}
}
public class Main {
public static void main(String[] args) {
// 调用静态方法
double area = MathUtils.circleArea(5.0);
System.out.println("圆的面积: " + area);
// 调用实例方法需要先创建对象
MathUtils utils = new MathUtils();
double rectArea = utils.rectangleArea(4.0, 6.0);
System.out.println("矩形的面积: " + rectArea);
}
}
8. final关键字
8.1 final变量
final变量一旦赋值就不能再修改。
javapublic class FinalTest {
final int MAX_VALUE = 100; // final实例变量
static final double PI = 3.1415926; // final静态变量
public void printValues() {
// MAX_VALUE = 150; // 编译错误,不能修改final变量
System.out.println("最大值: " + MAX_VALUE);
System.out.println("圆周率: " + PI);
}
public static void main(String[] args) {
FinalTest test = new FinalTest();
test.printValues();
}
}
8.2 final方法
final方法不能被子类重写。
javapublic class Parent {
public final void showMessage() {
System.out.println("这是final方法,不能被重写");
}
}
public class Child extends Parent {
// 尝试重写final方法会导致编译错误
/*
public void showMessage() {
System.out.println("尝试重写final方法");
}
*/
}
8.3 final类
final类不能被继承。
javapublic final class FinalClass {
public void display() {
System.out.println("这是一个final类");
}
}
// 尝试继承final类会导致编译错误
/*
public class SubClass extends FinalClass {
}
*/
9. 对象比较
9.1 equals()方法
默认的equals()
方法(继承自Object类)比较的是对象引用,通常需要重写。
javapublic class Book {
private String title;
private String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
// 重写equals方法
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Book book = (Book) obj;
return title.equals(book.title) && author.equals(book.author);
}
// 通常重写equals时也重写hashCode
@Override
public int hashCode() {
return Objects.hash(title, author);
}
}
public class Main {
public static void main(String[] args) {
Book book1 = new Book("Java编程思想", "Bruce Eckel");
Book book2 = new Book("Java编程思想", "Bruce Eckel");
Book book3 = new Book("Effective Java", "Joshua Bloch");
System.out.println(book1.equals(book2)); // true
System.out.println(book1.equals(book3)); // false
}
}
9.2 ==运算符
==
比较的是对象引用是否相同,而不是内容。
javapublic class Main {
public static void main(String[] args) {
String s1 = new String("Hello");
String s2 = new String("Hello");
String s3 = s1;
System.out.println(s1 == s2); // false,不同对象
System.out.println(s1.equals(s2)); // true,内容相同
System.out.println(s1 == s3); // true,同一对象
}
}
10. 对象克隆
10.1 实现Cloneable接口
javapublic class Address implements Cloneable {
private String city;
private String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
// 重写clone方法
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
// getter和setter方法...
@Override
public String toString() {
return city + "市" + street + "街";
}
}
public class Person implements Cloneable {
private String name;
private int age;
private Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
// 深拷贝实现
@Override
public Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
cloned.address = (Address) address.clone(); // 克隆address对象
return cloned;
}
@Override
public String toString() {
return "姓名: " + name + ", 年龄: " + age + ", 地址: " + address;
}
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("北京", "朝阳");
Person person1 = new Person("张三", 30, address);
// 浅拷贝
Person person2 = (Person) person1.clone();
System.out.println("原始对象: " + person1);
System.out.println("克隆对象: " + person2);
// 修改克隆对象的地址
person2.getAddress().setCity("上海");
System.out.println("修改后原始对象: " + person1);
System.out.println("修改后克隆对象: " + person2);
}
}
浅拷贝 vs 深拷贝:
- 浅拷贝:只复制基本类型和对象引用,不复制引用指向的对象
- 深拷贝:复制所有对象,包括引用指向的对象
11. 记录类(Record Class) - Java 14+
Java 14引入了record类型,用于简化不可变数据类的定义。
java// 定义一个record类
public record Point(int x, int y) {
// 可以添加静态方法
public static Point origin() {
return new Point(0, 0);
}
// 可以添加实例方法
public double distanceFromOrigin() {
return Math.sqrt(x * x + y * y);
}
// 不能添加实例字段
// 不能重写equals, hashCode, toString等方法(编译器会自动生成)
}
public class Main {
public static void main(String[] args) {
Point p1 = new Point(3, 4);
System.out.println(p1); // Point[x=3, y=4]
System.out.println(p1.x()); // 3 (自动生成的访问方法)
System.out.println(p1.distanceFromOrigin()); // 5.0
Point p2 = Point.origin();
System.out.println(p2); // Point[x=0, y=0]
}
}
12. 最佳实践
- 始终使用访问修饰符:明确指定类成员的访问级别
- 封装内部实现:将成员变量设为private,通过方法访问
- 为类编写有意义的文档注释:使用Javadoc格式
- 实现equals()和hashCode()方法:如果对象需要比较或存储在集合中
- 考虑不可变性:对于不需要改变的对象,设计为不可变
- 合理使用final:在需要防止修改的地方使用final
- 避免过度设计:从简单开始,根据需要添加复杂性
13. 完整示例:银行账户管理系统
javaimport java.util.ArrayList;
import java.util.List;
// 银行账户类
public class BankAccount {
// 账户状态枚举
public enum AccountStatus {
ACTIVE, FROZEN, CLOSED
}
// 私有成员变量
private final String accountNumber;
private String accountHolder;
private double balance;
private AccountStatus status;
private List<String> transactionHistory;
// 构造方法
public BankAccount(String accountNumber, String accountHolder, double initialBalance) {
this.accountNumber = accountNumber;
this.accountHolder = accountHolder;
this.balance = initialBalance;
this.status = AccountStatus.ACTIVE;
this.transactionHistory = new ArrayList<>();
logTransaction("账户创建,初始余额: " + initialBalance);
}
// 存款方法
public void deposit(double amount) {
if (status != AccountStatus.ACTIVE) {
System.out.println("账户未激活,无法存款");
return;
}
if (amount <= 0) {
System.out.println("存款金额必须大于0");
return;
}
balance += amount;
logTransaction("存款: +" + amount);
System.out.println("存款成功,当前余额: " + balance);
}
// 取款方法
public void withdraw(double amount) {
if (status != AccountStatus.ACTIVE) {
System.out.println("账户未激活,无法取款");
return;
}
if (amount <= 0) {
System.out.println("取款金额必须大于0");
return;
}
if (amount > balance) {
System.out.println("余额不足");
return;
}
balance -= amount;
logTransaction("取款: -" + amount);
System.out.println("取款成功,当前余额: " + balance);
}
// 转账方法
public void transfer(BankAccount targetAccount, double amount) {
if (this == targetAccount) {
System.out.println("不能向同一账户转账");
return;
}
this.withdraw(amount);
targetAccount.deposit(amount);
logTransaction("转账到账户 " + targetAccount.getAccountNumber() + ": -" + amount);
}
// 记录交易历史
private void logTransaction(String description) {
String entry = String.format("[%s] %s (余额: %.2f)",
java.time.LocalDateTime.now(), description, balance);
transactionHistory.add(entry);
}
// 显示交易历史
public void displayTransactionHistory() {
System.out.println("\n=== 账户交易历史 ===");
System.out.println("账户号码: " + accountNumber);
System.out.println("账户持有人: " + accountHolder);
System.out.println("当前状态: " + status);
for (String transaction : transactionHistory) {
System.out.println(transaction);
}
System.out.println("==================\n");
}
// getter方法
public String getAccountNumber() {
return accountNumber;
}
public String getAccountHolder() {
return accountHolder;
}
public double getBalance() {
return balance;
}
public AccountStatus getStatus() {
return status;
}
// setter方法(有限制)
public void setAccountHolder(String accountHolder) {
this.accountHolder = accountHolder;
}
public void setStatus(AccountStatus status) {
this.status = status;
logTransaction("账户状态变更为: " + status);
}
// 重写toString方法
@Override
public String toString() {
return String.format("BankAccount[number=%s, holder=%s, balance=%.2f, status=%s]",
accountNumber, accountHolder, balance, status);
}
}
// 主程序
public class BankSystem {
public static void main(String[] args) {
// 创建账户
BankAccount account1 = new BankAccount("1001", "张三", 1000);
BankAccount account2 = new BankAccount("1002", "李四", 500);
// 执行交易
account1.deposit(200);
account1.withdraw(150);
account1.transfer(account2, 300);
// 尝试无效操作
account1.withdraw(2000); // 余额不足
account1.transfer(account1, 100); // 同一账户转账
// 冻结账户
account1.setStatus(BankAccount.AccountStatus.FROZEN);
account1.deposit(100); // 应该失败
// 显示交易历史
account1.displayTransactionHistory();
account2.displayTransactionHistory();
// 打印账户信息
System.out.println(account1);
System.out.println(account2);
}
}
总结
本教程全面介绍了Java中类和对象的核心概念:
- 类的定义和对象的创建
- 构造方法的使用
- 方法的定义和重载
- 封装和访问控制
- static和final关键字的使用
- 对象比较和克隆
- 记录类(Record)的新特性
通过学习这些概念和示例,您应该能够:
- 设计并实现自己的类
- 创建和使用对象
- 理解对象之间的交互
- 编写可维护、可扩展的面向对象代码
记住,面向对象编程的核心是设计出高内聚、低耦合的类,合理划分职责,并通过对象间的协作实现复杂功能。