Java 类的属性与方法
面向对象编程(OOP)围绕着“对象”这一概念展开,对象是封装了数据和行为的独立实体。理解如何定义对象所持有的数据(它的属性或字段)以及它可以执行的操作(它的方法),是精通 Java 面向对象编程的基础。
本章将对属性和方法进行全面深入的探讨。我们将建立在上一章介绍的类和对象的概念之上,进一步深入了解如何定义这些对象的特征和行为。
1. 定义属性(字段)
属性(Attributes),也被称为字段(fields)或实例变量(instance variables),代表着与对象相关联的数据。它们定义了对象的特征或性质。例如,一个 Car(汽车)对象可能拥有 color(颜色)、model(型号)、year(年份)和 currentSpeed(当前速度)等属性。
1.1 声明属性
在 Java 中声明属性,你需要在类的定义中指定它的数据类型和名称。属性通常声明在类的开头部分,位于类声明之后,任何方法之前。
public class Car {
String color; // 用于存储汽车颜色的属性
String model; // 用于存储汽车型号的属性
int year; // 用于存储汽车生产年份的属性
int currentSpeed; // 用于存储汽车当前速度的属性
}在这个例子中:
String color;声明了一个名为color、类型为String的属性。String model;声明了一个名为model、类型为String的属性。int year;声明了一个名为year、类型为int(整数)的属性。int currentSpeed;声明了一个名为currentSpeed、类型为int的属性。
1.2 访问修饰符
访问修饰符控制属性(以及方法)的可见性和访问权限。主要的访问修饰符包括:
- public(公开): 可以从任何类访问。
- private(私有): 只能在同一个类的内部访问。
- protected(受保护): 可以在同一个类、子类以及同一个包内的类中访问。
- (默认)(无修饰符): 只能在同一个包内访问。
封装(Encapsulation):通常认为将属性声明为 private 是良好的编程实践,这能强制实现封装。这意味着对象的内部状态对外部世界是隐藏的,从而保护数据不被直接修改,并允许你通过方法(稍后我们将讨论的 Getter 和 Setter)来控制数据的访问和修改方式。
public class Car {
private String color;
private String model;
private int year;
private int currentSpeed;
}现在,color、model、year 和 currentSpeed 只能在 Car 类的内部被直接访问。
1.3 数据类型
属性可以是任何有效的 Java 数据类型,包括基本数据类型(如 int、double、boolean、char)和引用数据类型(如 String、数组和其他类)。数据类型的选择取决于属性将要存储的数据种类。
使用不同数据类型的示例:
public class Product {
private String name; // String:用于存储产品名称
private double price; // double:用于存储产品价格
private int quantity; // int:用于存储库存数量
private boolean inStock; // boolean:用于指示产品是否有货
}1.4 初始化属性
属性可以在声明时初始化,也可以在类的构造方法(将在下一章讲解)中初始化,或者使用 Setter 方法进行初始化。如果一个属性没有被显式地初始化,Java 会为它分配一个默认值:
- 数字类型(int, double 等)为
0 - 布尔类型(boolean)为
false - 字符类型(char)为
\u0000(空字符) - 引用类型(String, 对象等)为
null
在声明时初始化属性的示例:
public class Car {
private String color = "红色"; // 将颜色初始化为 "红色"
private String model = "未知"; // 将型号初始化为 "未知"
private int year = 2023; // 将年份初始化为 2023
private int currentSpeed = 0; // 将当前速度初始化为 0
}在这个例子中,除非后续被修改,否则每一个被创建的 Car 对象都会拥有这些默认的属性值。
2. 定义方法
方法(Methods)定义了对象的行为——即对象可以执行的操作。它们是执行特定任务的代码块。例如,一个 Car 对象可能拥有 accelerate()(加速)、brake()(刹车)和 honk()(鸣笛)等方法。
2.1 声明方法
一个方法的声明包含以下几个部分:
- 访问修饰符: (例如
public、private、protected)决定了方法的可见性。 - 返回类型: 方法返回值的确切数据类型。如果方法不返回任何值,返回类型需设为 void。
- 方法名称: 方法的描述性名称(应遵循驼峰命名规范)。
- 参数列表: (可选)方法作为输入接收的参数列表(包含数据类型和名称)。
- 方法体: 包含在花括号
{}中的代码块,包含了方法要执行的指令。
方法的基本语法:
访问修饰符 返回类型 方法名称(参数列表) {
// 方法体 - 要执行的代码
return 返回值; // 如果返回类型不是 void
}Car 类的方法示例:
public class Car {
private String color;
private String model;
private int year;
private int currentSpeed;
// 加速方法
public void accelerate(int increment) {
currentSpeed += increment;
System.out.println("汽车加速了。当前速度: " + currentSpeed);
}
// 刹车方法
public void brake(int decrement) {
currentSpeed -= decrement;
if (currentSpeed < 0) {
currentSpeed = 0; // 确保速度不会变成负数
}
System.out.println("汽车刹车了。当前速度: " + currentSpeed);
}
// 获取汽车当前速度的方法
public int getCurrentSpeed() {
return currentSpeed;
}
}在这个例子中:
accelerate(int increment)是一个public方法,接收一个名为increment的int参数,并增加currentSpeed属性的值。它返回void,因为它不返回任何值。brake(int decrement)是一个public方法,接收一个名为decrement的int参数,并减少currentSpeed属性的值。它同样返回void。getCurrentSpeed()是一个public方法,不接收任何参数,并以int类型返回currentSpeed属性的当前值。
3. 在方法内访问属性
在方法内部,你可以直接使用属性的名称来访问对象的属性。如果属性是 private 的,我们就需要使用 Getter 和 Setter 方法来分别读取和修改它们。
3.1 Getter 和 Setter 方法
Getter 和 Setter 方法(也称为访问器和修改器方法)用于访问和修改 private 属性的值。它们提供了对对象内部状态的受控访问,这完全符合封装的原则。
- Getter(访问器)方法: 返回属性的值。命名规范通常是
get变量名()。 - Setter(修改器)方法: 设置(修改)属性的值。命名规范通常是
set变量名(数据类型 变量名)。
Car 类中 color 属性的 Getter 和 Setter 方法示例:
public class Car {
private String color;
private String model;
private int year;
private int currentSpeed;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color; // 使用 'this' 来区分参数和属性
}
// 其他方法(accelerate, brake, getCurrentSpeed)保持不变
}在这个例子中:
getColor()是color属性的Getter方法。它返回color属性的值。setColor(String color)是color属性的Setter方法。它接收一个名为color的String参数,并将color属性的值设置为该参数的值。this关键字用于引用实例变量color,以区别于局部变量(参数)color。
3.2 this 关键字
this 关键字是对当前对象的引用。它主要用于:
- 区分实例变量和局部变量: 正如上面的
setColor方法所示,当方法参数与实例变量同名时,使用this来特指实例变量。 - 在同一个类中调用另一个构造方法 (将在下一章讲解构造方法时详细说明)。
- 从方法中返回当前对象。
演示 this 关键字用法的示例:
public class Car {
private String color;
public Car(String color) {
this.color = color; // 'this.color' 指的是实例变量,'color' 指的是参数
}
public Car changeColor(String color) {
this.color = color;
return this; // 在改变颜色后返回当前的 Car 对象
}
}在这个例子中:
- 在构造方法
Car(String color)中,this.color指的是Car对象的color属性,而color指的是传递给构造方法的参数。 changeColor方法修改了汽车的颜色,然后使用return this;返回了Car对象本身。这种写法允许进行方法链式调用(Method Chaining)。
4. 方法参数与重载
4.1 方法参数
方法可以接收参数,这些参数是在调用方法时传入的数值。参数允许你向方法传递数据,从而定制其行为。
多参数示例:
public class Calculator {
public int add(int num1, int num2) {
return num1 + num2;
}
public double divide(double dividend, double divisor) {
if (divisor == 0) {
System.out.println("除数不能为零。");
return 0; // 处理除以零的情况
}
return dividend / divisor;
}
}在这个例子中:
add方法接收两个int参数num1和num2,并返回它们的和。divide方法接收两个double参数dividend(被除数)和divisor(除数),并返回它们的商。它还包含了一个错误处理机制,以防止除以零的错误。
4.2 方法重载
方法重载(Method Overloading)允许你在同一个类中定义多个名称相同但参数列表不同(参数数量不同、参数数据类型不同或参数顺序不同)的方法。编译器会根据方法调用时传入的参数来决定具体执行哪一个方法。这是一个非常实用的特性,我们将在后续章节详细讨论。
5. 调用方法
要在对象上调用方法,你需要使用点运算符(.),后跟方法名称和括号 ()。如果方法需要参数,你要把参数放在括号里传递进去。
示例:
public class Main {
public static void main(String[] args) {
Car myCar = new Car(); // 假设 Car 有一个默认构造方法
myCar.setColor("蓝色");
myCar.accelerate(50);
myCar.brake(20);
int currentSpeed = myCar.getCurrentSpeed();
System.out.println("当前速度: " + currentSpeed);
}
}在这个例子中:
myCar.setColor("蓝色")调用了myCar对象上的setColor方法,并传入字符串"蓝色"作为参数。myCar.accelerate(50)调用了myCar对象上的accelerate方法,并传入整数 50 作为参数。myCar.brake(20)调用了myCar对象上的brake方法,并传入整数 20 作为参数。int currentSpeed = myCar.getCurrentSpeed();调用了myCar对象上的getCurrentSpeed方法,并将返回的值赋给了currentSpeed变量。
6. 综合实战演示
让我们来看一个稍微复杂一点的 BankAccount(银行账户)类的例子:
public class BankAccount {
private String accountNumber;
private String accountHolderName;
private double balance;
public BankAccount(String accountNumber, String accountHolderName) {
this.accountNumber = accountNumber;
this.accountHolderName = accountHolderName;
this.balance = 0.0; // 初始余额为 0
}
public String getAccountNumber() {
return accountNumber;
}
public String getAccountHolderName() {
return accountHolderName;
}
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("成功存款 " + amount + "。最新余额: " + balance);
} else {
System.out.println("无效的存款金额。");
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("成功取款 " + amount + "。最新余额: " + balance);
} else {
System.out.println("余额不足或取款金额无效。");
}
}
}
public class Main {
public static void main(String[] args) {
BankAccount myAccount = new BankAccount("1234567890", "张三");
System.out.println("账号: " + myAccount.getAccountNumber());
System.out.println("账户持有人姓名: " + myAccount.getAccountHolderName());
System.out.println("初始余额: " + myAccount.getBalance());
myAccount.deposit(1000.0);
myAccount.withdraw(500.0);
System.out.println("最终余额: " + myAccount.getBalance());
}
}这个 BankAccount 类展示了以下几点:
- 属性:
accountNumber、accountHolderName和balance(全部设为private)。 - 构造方法: 初始化了
accountNumber和accountHolderName,并将初始balance设为 0。 - Getter 方法:
getAccountNumber()、getAccountHolderName()和getBalance()提供了对属性的只读访问。 - 行为方法:
deposit(double amount)和withdraw(double amount)在账户上执行操作,从而修改了balance(余额)。