类与字段
一个类可以定义多个字段,这些字段用于存储对象的状态信息。例如,我们可以为Person
类定义两个字段:name
和age
。
class Person {
public String name;
public int age;
}
然而,将字段设置为public
会使它们对外公开,这可能会破坏类的封装性。为了保护字段不被外部代码直接访问和修改,我们通常会将字段声明为private
。
class Person {
private String name;
private int age;
}
这样做的好处是,我们可以控制对这些字段的访问,确保对象的状态不会被外部代码随意改变,从而维护了对象的完整性和一致性。
方法的定义和参数
方法的定义遵循以下语法:
修饰符 返回类型 方法名(参数列表) {
// 方法体
return 返回值;
}
方法可以有返回值,也可以没有。如果没有返回值,则返回类型为void
。
方法的参数允许我们传递值给方法。调用方法时,必须按照参数的定义顺序传递相应的值。
this变量
当我们在方法中访问一个与实例字段同名的局部变量时,Java会优先考虑局部变量。为了访问实际的实例字段,我们需要使用this
关键字。例如:
class Person {
private String name;
public String getName() {
// 这里不需要使用this,因为name没有同名的局部变量
return name; // 访问实例字段name
}
public void setName(String name) {
// 这里的name是方法参数,与实例字段name同名
// 为了区分它们,我们需要使用this来指代实例字段
this.name = name; // 设置实例字段name的值
}
}
在上面的setName
方法中,我们通过this.name
明确地告诉编译器我们想要操作的是实例字段name
,而不是方法参数name
。在没有命名冲突的情况下,this
是可以省略的。例如,在getName
方法中,由于没有局部变量与实例字段name
同名,我们可以直接使用name
而不需要this.name
。
方法的使用
当我们将字段设置为private
后,外部代码将无法直接访问这些字段。为了能够对这些字段进行赋值和读取,我们需要定义公共的方法(public methods)。
public class Main {
public static void main(String[] args) {
Person ming = new Person();
ming.setName("Xiao Ming"); // 使用方法设置name
ming.setAge(12); // 使用方法设置age
System.out.println(ming.getName() + ", " + ming.getAge()); // 使用方法获取并打印name和age
}
}
class Person {
private String name;
private int age;
// 获取name的方法(访问器)
public String getName() {
return this.name;
}
// 设置name的方法(修改器)
public void setName(String name) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("invalid name");
}
this.name = name.strip(); // 去除首尾空格
}
// 获取age的方法(访问器)
public int getAge() {
return this.age;
}
// 设置age的方法(修改器)
public void setAge(int age) {
if (age < 0 || age > 100) {
throw new IllegalArgumentException("Age must be between 0 and 100.");
}
this.age = age;
}
}
在这个例子中,setName
和setAge
是修改器方法,它们允许外部代码设置name
和age
字段的值。同时,getName
和getAge
是访问器方法,它们允许外部代码获取这些字段的值。通过这些方法,我们可以在内部进行逻辑检查,确保传入的参数是有效的,从而避免了不合理的值被赋给字段。
方法参数
当定义一个方法时,我们可以指定每个参数的类型和顺序。例如,setNameAndAge
方法接受两个参数,第一个是String
类型,第二个是int
类型。
class Person {
...
public void setNameAndAge(String name, int age) {
...
}
}
在调用这个方法时,必须提供两个参数,类型也要匹配:
Person ming = new Person();
ming.setNameAndAge("Xiao Ming", 30); // 正确调用,提供了两个参数
可变参数
Java允许使用可变参数,这是一种特殊的参数,可以接受任意数量的参数。可变参数使用省略号(...
)来定义,实际上是一个数组。
class Group {
private String[] names;
public void setNames(String... names) {
this.names = names; // 将可变参数names赋值给数组names
}
}
使用可变参数,可以简化方法调用,不需要手动创建数组:
Group g = new Group();
g.setNames("Xiao Ming"); // 传入1个String
g.setNames("Xiao Ming", "Xiao Hong"); // 传入2个String
g.setNames(); // 传入0个String,names数组将被设置为空数组
参数绑定
基本类型的参数传递是通过值的复制来实现的。这意味着方法接收的是参数值的一个副本,而不是参数本身。因此,方法内部对参数的任何修改都不会影响到原始变量。
public class Main {
public static void main(String[] args) {
Person p = new Person();
int n = 15;
p.setAge(n);
System.out.println(p.getAge()); // 输出 15
n = 20;
System.out.println(p.getAge()); // 输出仍然是 15,因为n的修改不影响p的age字段
}
}
在这个例子中,n
的值被复制给了setAge
方法的参数。即使n
的值之后被改变,p
对象的age
字段保持不变,因为它们是独立的副本。
与基本类型不同,引用类型的参数传递实际上是对引用的传递。这意味着方法接收的是对象引用的副本,而不是对象本身的副本。
public class Main {
public static void main(String[] args) {
Person p = new Person();
String[] fullname = new String[] { "Homer", "Simpson" };
p.setName(fullname);
System.out.println(p.getName()); // 输出 "Homer Simpson"
fullname[0] = "Bart";
System.out.println(p.getName()); // 输出 "Bart Simpson",因为fullname和p.name指向同一个数组
}
}
在这个例子中,fullname
数组的引用被传递给了setName
方法。因此,当fullname
数组的内容被修改后,p
对象的name
字段也会受到影响,因为它们指向同一个数组。
字符串(String
类型)在Java中是不可变的(immutable)。这意味着一旦字符串被创建,它的内容就不能被改变。
public class Main {
public static void main(String[] args) {
Person p = new Person();
String bob = "Bob";
p.setName(bob);
System.out.println(p.getName()); // 输出 "Bob"
bob = "Alice";
System.out.println(p.getName()); // 输出仍然是 "Bob",因为字符串内容不可变
}
}
在这个例子中,尽管bob
变量的引用被改变了,指向了一个新的字符串,但这不影响p
对象的name
字段,因为setName
方法保存的是字符串的引用,而不是字符串本身。由于字符串内容不可变,p.getName()
方法返回的仍然是原始的字符串内容”Bob”。