在6.1.2节中已经讲解过类是封装对象的属性和行为的载体,而在 Java 语言中对象的属性以成员变量的形式存在,对象的方法以成员方法的形式存在。本节将讲解类在 Java 语言中是如何定义的。

成员变量

在 Java 中,对象的属性也称为成员变量,成员变量可以是任意类型,整个类中均是成员变量作用范围。下面通过一个实例来演示成员变量在类中所处的位置。

【例6.1】为书添加书名属性(实例位置:资源包\TM\sl\6\1)

创建一个 Book 类,在类中设置一个 name 属性,并为该属性编写 Getter/Setter 方法。

public class Book {// 类
	private String name; // String类型的成员边阿玲

	public String getName() { // name的Getter方法
		return name;
	}

	public void setName(String name) { // name的Setter方法
		this.name = name; // 将参数值赋予类中的成员变量
	}
}

在上面这个实例中可以看到,在 Java 语言中需要使用 class 关键字来定义类,Book 是类的名称。同时在 Book 类中定义了一个成员变量,成员变量的类型为 String 类型。其实成员变量就是普通的变量,可以为它设置初始值,也可以不设置初始值。如果不设置初始值,则会有默认值。读者应该注意到成员变量 name 前面有一个 private 关键字,它被用来定义一个私有成员(关于权限修饰符的说明将在6.2.3节中进行讲解)。

成员方法

在 Java 语言中,使用成员方法对应于类对象的行为。以 Book 类为例,它包含 getName() 和 setName() 两个方法,这两个成员方法分别为获取图书名称和设置图书名称的方法。

定义成员方法的语法格式如下:

权限修饰符 返回值类型 方法名(参数类型 参数名) {
  ...          // 方法体
  return 返回值;
}

一个成员方法可以有参数,这个参数可以是对象,也可以是基本数据类型的变量,同时成员方法有返回值和不返回任何值的选择,如果方法需要返回值,则可以在方法体中使用 return 关键字,使用这个关键字后,方法的执行将被终止。

要使 Java 代码中的成员方法无返回值,可以使用 void 关键字表示。

成员方法的返回值可以是计算结果,也可以是其他想要的数值和对象,返回值类型要与方法返回的值类型一致。

在成员方法中可以调用其他成员方法和类成员变量,如在例6.1的 getName() 方法中就调用了 setName() 方法将一个值赋予图书名称。同时,在成员方法中可以定义一个变量,这个变量为局部变量(局部变量的内容将在6.2.4节中进行讲解)。

如果一个方法中含有与成员变量同名的局部变量,则在方法中对这个变量的访问以局部变量进行。类的成员变量和成员方法也可以被统称为类成员。

权限修饰符

Java 中的权限修饰符主要包括 private、public 和 protected,这些修饰符控制着对类和类的成员变量以及成员方法的访问。如果一个类的成员变量或成员方法被修饰为 private,则该成员变量只能在本类中使用,在子类中是不可见的,并且对其他包的类也是不可见的。如果将类的成员变量和成员方法的访问权限设置为 public,那么除了可以在本类中使用这些数据,还可以在子类和其他包的类中使用这些数据。如果一个类的访问权限被设置为 private,这个类将隐藏其内的所有数据,以免用户直接访问它。如果需要使类中的数据被子类或其他包中的类使用,则可以将这个类设置为 public 访问权限。如果一个类使用 protected 修饰符,那么只有本包内的该类的子类或其他类可以访问此类中的成员变量和成员方法。

这么看来,public 和 protected 修饰的类可以由子类进行访问,如果子类和父类不在同一包中,那么只有修饰符为 public 的类可以被子类进行访问。如果父类不允许通过继承产生的子类访问它的成员变量,那么必须使用 private 声明父类的这个成员变量。表6.1中描述了 private、protected 和 public 修饰符的修饰权限。

image 2024 02 29 16 07 51 175
Figure 1. 表6.1 Java语言中的修饰符权限

当声明类时不使用 public、protected 和 private 修饰符设置类的权限,则这个类被预设为包存取范围,即只有一个包中的类可以访问这个类的成员变量或成员方法。

例如,在项目的 com.mr 包下创建 AnyClass 类,该类使用默认的访问权限。

package com.mr
class AnyClass{
    public void doString() {
        ...           // 方法体
    }
}

在上述代码中,由于类的修饰符为默认修饰符,即只有一个包内的其他类和子类可以对该类进行访问,而 AnyClass 类中的 doString() 方法却又被设置为 public 访问权限,即使这样,doString() 方法的访问权限依然与 AnyClass 类的访问权限相同。

局部变量

在6.2.2节中已经讲述过成员方法,如果在成员方法内定义一个变量,那么这个变量被称为局部变量。

实际上,方法中的形参也可作为一个局部变量。例如,在例6.1的 Book 类中定义 setName(String name) 方法,String name 这个形参就被看作是局部变量。

局部变量是在方法被执行时创建,在方法执行结束时被销毁。局部变量在使用时必须进行赋值操作或被初始化,否则会出现编译错误。

【例6.2】交换两个整数的值(实例位置:资源包\TM\sl\6\2)

在 ChangeDemo 类中创建静态的 exchange() 方法,该方法可以将数组参数 arr 的前两个元素值进行互换,通过在方法中定义一个保存临时数据的局部变量 tmp,利用 tmp 交换两个元素的值。

public class ChangeDemo {

	public static int[] exchange(int[] arr) {
		int tmp = arr[0];// 创建布局变量tmp,保存数组第一个元素的值
		arr[0] = arr[1];// 第二个元素值赋给第一个元素
		arr[1] = tmp;// 第二个元素值改为tmp
		return arr;
	}

	public static void main(String[] args) {
		int arr[] = { 17, 29 };
		System.out.println("第一个值=" + arr[0] + ",第二个值=" + arr[1]);
		arr = exchange(arr);
		System.out.println("第一个值=" + arr[0] + ",第二个值=" + arr[1]);
	}

}

运行结果如下:

     第一个值=17,第二个值=29
     第一个值=29,第二个值=17

局部变量的有效范围

可以将局部变量的有效范围称为变量的作用域,局部变量的有效范围从该变量的声明开始到该变量的结束为止。图6.7描述了局部变量的作用范围。

image 2024 02 29 16 11 53 911
Figure 2. 图6.7 局部变量的作用范围

在相互不嵌套的作用域中可以同时声明两个名称和类型都相同的局部变量,如图6.8所示。

但是在相互嵌套的区域中不可以这样声明,如果将局部变量 id 在方法体的 for 循环中再次进行定义,那么编译器将会报错,如图6.9所示。

image 2024 02 29 16 12 46 704
Figure 3. 图6.8 在互不嵌套区域可以定义相同名称和类型的局部变量
image 2024 02 29 16 13 09 823
Figure 4. 图6.9 在嵌套区域中不可以定义相同名称和类型的局部变量

在作用范围外使用局部变量是一个常见的错误,因为在作用范围外没有声明局部变量的代码。

this关键字

this 关键字用于表示本类当前的对象,当前对象不是某个 new 出来的实体对象,而是当前正在编辑的类。this 关键字只能用于本类中。

例如,例6.1中图书类的 setName() 方法的代码如下:

public void setName(String name) { // 定义一个setName()方法
    this.name = name;     // 将参数值赋予类中的成员变量
}

在上述代码中可以看到,成员变量与 setName() 方法中的形式参数的名称相同,都为 name,那么该如何在类中区分使用的是哪一个变量呢?在 Java 语言中,规定使用 this 关键字来代表本类对象的引用,this 关键字被隐式地用于引用对象的成员变量和方法,如在上述代码中,this.name 指的就是 Book 类中的 name 成员变量,而 this.name = name 语句中的第二个 name 则指的是形参 name。实质上,setName() 方法实现的功能就是将形参 name 的值赋予成员变量 name。

在这里读者明白了 this 可以调用成员变量和成员方法,但 Java 语言中最常规的调用方式是使用 “对象.成员变量” 或 “对象.成员方法” 进行调用(关于使用对象调用成员变量和方法的问题,将在后续章节中进行讲述)。

既然 this 关键字和对象都可以调用成员变量和成员方法,那么 this 关键字与对象之间具有怎样的关系呢?事实上,this 引用的就是本类的一个对象。在局部变量或方法参数覆盖了成员变量时,如上面代码的情况,就要添加 this 关键字明确引用的是类成员还是局部变量或方法参数。

如果省略 this 关键字并将其直接写成 name = name,那么这只是把参数 name 赋值给参数变量本身而已,成员变量 name 的值没有改变,因为参数 name 在方法的作用域中覆盖了成员变量 name。

其实,this 除了可以调用成员变量或成员方法,还可以作为方法的返回值。例如,返回图书类本类的对象,可以写成下面这种形式:

public Book getBook() {
    return this;      // 返回 Book 类的本类对象
}

在 getBook() 方法中,因为返回值为 Book 类,所以方法体中使用 return this 这种形式返回 Book 类对象。

编程训练(答案位置:资源包\TM\sl\6\编程训练)

【训练1】汽车加油 一辆汽车的油箱为 30 升,油箱里现剩余 6 升汽油。加油站每 5 秒为这辆汽车加 2 升汽油直至加满,控制台输出加油过程和加油时间。

【训练2】交换数组元素 现有一个整型数组 int a[] = {1 , 3 , 5 , 7},编写一段代码,将这个数组的第一个元素值与第三个元素值进行交换,第二个元素值与第四个元素值进行交换,最后输出数组交换后的结果。