第07部分(字段和方法)

类可以看成是由一些数据(也叫状态)和操作这些状态的代码组成的。数据存储在字段中,操作数据的代码则组织在方法中。
字段和方法有两种不同的类型:关联在类自身上的类成员(也叫静态成员),关联在类的单个实例(即对象)身上的实例成员。因此,成员分为四类:

? 类字段
? 类方法
? 实例字段
? 实例方法
如图所示的示例定义了一个简单的类 Circle,包含所有这四种成员类型:
第07部分(字段和方法)
文章图片
一般来说公开 r 字段并不好,最好把 r 声明为私有字段,然后提供 radius()方法,获取它的值。现在,我们使用公开字段只是为了演示如何处理实例字段。


声明字段的句法
声明字段的句法和声明局部变量的句法很像,不过声明字段时还可以使用修饰符。最简单的字段声明包含字段类型和字段名。类型前面可以放零个或多个修饰符关键字或注解,名称后面可以跟着一个等号和初始化表达式,提供字段的初始值。如果两个或多个字段的类型和修饰符都相同,那么可以把一些用逗号分隔的字段名和初始化表达式放在类型后面。如下是一些有效的字段声明:
int x = 1;
private String name;
public static final int DAYS_PER_WEEK = 7;
String[] daynames = new String[DAYS_PER_WEEK];
private int a = 17, b = 37, c = 53;
字段的修饰符由零个或多个下述关键字组成。
1public、protected、private,这些访问控制修饰符指明字段是否能在定义它的类之外使用,以及能在何处使用。
2static,如果使用,这个修饰符指明字段关联在定义它的类自身上,而不是类的实例身上。
3final,这个修饰符指明,字段一旦初始化,其值就不能改变。如果字段同时使用 static 和final 修饰,那么这个字段就是编译时常量,javac 会将其内联化。final 修饰的字段也可以用来创建实例不可变的类。
4transient,这个修饰符指明字段不是对象持久状态的一部分,无需跟对象的其他内容一起序列化。
5volatile,这个修饰符指明字段有额外的语义,可被两个或多个线程同时使用。volatile 修饰符的意思是,字段的值必须始终从主存储器中读取和释放,不能被线程缓存(在寄存器或CPU 缓存中)。




类字段
类字段关联在定义它的类身上,而不是类的实例身上。下面这行代码声明一个类字段:
public static final double PI = 3.14159;
这行代码声明了一个字段,类型为 double,名称为 PI,并且把值设为 3.14159。
static 修饰符表明这个字段是类字段。因为使用了 static 修饰符,所以类字段有时也叫静态字段。final 修饰符表明这个字段的值不会改变。因为字段 PI 表示一个常量,而且声明时加上了 final,所以无法修改它的值。在 Java(以及很多其他语言)中,习惯使用大写字母命名常量,因此这个字段的名称是 PI,而不是 pi。类字段经常用来定义常量,也就是说,static 和 final 修饰符经常放在一起使用。然而,并不是所有类字段都是常量,因此字段可以声明为 static 但不声明为 final。
公开的静态字段要尽量声明为 final,因为多个线程都能修改字段的值,会导致极难调试的行为。
公开的静态字段其实就是全局变量。不过,类字段的名称会被定义它的类名限定,因此,如果不同的模块定义了同名的全局变量,Java 不会出现其他语言遇到的名称冲突问题。
关于静态字段,有个重点要理解,即字段的值只有一个副本。字段关联在类自身上,而不是类的实例身上。看一下 Circle 类中的各个方法,它们都使用了同一个字段。在Circle 类内部,可以直接使用 PI 引用这个字段。但是在类的外部,既要使用类名也要使用字段名,这样才能引用这个独一无二的字段。Circle 类外部的方法要使用 Circle.PI才能访问这个字段。




类方法
和类字段一样,类方法也使用 static 修饰符声明:
public static double radiansToDegrees(double rads) {
return rads * 180 / PI;
}
上述代码声明了一个类方法,名为 radiansToDegrees()。这个方法只有一个参数,类型为double,而且会返回一个 double 类型的值。
和类字段一样,类方法也关联在类身上,而不是对象身上。在类的外部调用类方法时,既要指定类名也要指定方法名。例如:
// 2.0弧度等于多少角度?
double d = Circle.radiansToDegrees(2.0);
如果想在定义类方法的类中调用类方法,则不用指定类名。还可以使用静态成员导入声明,减少输入的代码量。
注意,Circle.radiansToDegrees() 方法的主体使用了类字段 PI。类方法可以使用所在类(或其他类)中的任何类字段和类方法。
类方法不能使用任何实例字段或实例方法,因为类方法不关联在类的实例身上。也就是说,虽然 radiansToDegrees() 方法在 Circle 类中定义,但它不能使用 Circle 对象的任何实例成员。
【第07部分(字段和方法)】可以这样理解:在任何实例中,总有一个 this 引用指向当前对象,但类方法不关联在具体的实例身上,所以没有 this 引用,因此不能访问实例字段。
前面说过,类字段其实就是全局变量。类似地,类方法是全局方法,或全局函数。虽然radiansToDegrees() 方法不处理 Circle 对象,但还是在 Circle 类中定义,因为它是一个实用方法,处理圆时有时会用到,因此可以把它和 Circle 类的其他功能放在一起。


实例字段
声明时没使用 static 修饰符的字段是实例字段:
public double r; // 圆的半径
实例字段关联在类的实例身上,所以创建的每个 Circle 对象都有自己的一个 double 类型 r 字段副本。在这个例子中,r 表示某个圆的半径。每个 Circle 对象的半径和其他所有Circle 对象的都不同。
在类定义内部,实例字段只通过名称引用。在实例方法 circumference() 的主体中有一个例子。在类外部,实例字段的名称前面必须加上包含这个字段的对象的引用。例如,如果变量 c 保存的是一个 Circle 对象的引用,那么可以使用表达式 c.r 引用这个圆的半径:
Circle c = new Circle(); // 创建一个Circle对象,把引用存储在c中
c.r = 2.0; // 把一个值赋值给实例字段r
Circle d =new Circle(); // 再创建一个Circle对象
d.r = c.r * 2; // 让这个圆是前一个的两倍大
实例字段是面向对象编程的关键。实例字段保存对象的状态,实例字段的值把两个对象区分开来。




实例方法
实例方法处理类的具体实例(对象),只要声明方法时没使用 static 关键字,这个方法默认就是实例方法。
实例方法这个特性让面向对象编程开始变得有趣。下面示例中定义的 Circle 类包含两个实例方法,area() 和 circumference(),分别计算指定 Circle 对象表示的圆的面积和周长。若想在定义实例方法的类之外使用实例方法,必须在方法名前加上要处理的实例引用。例如:
// 创建一个Circle对象,存储在变量c中
Circle c = new Circle();
c.r = 2.0; //设定这个对象的实例字段
double a = c.area(); // 调用这个对象的实例方法
这就是叫面向对象编程的原因,这里对象是重点,而不是函数调用。
在实例方法内部,可以自然地访问属于调用这个方法的对象的实例字段。前面说过,经常可以把对象理解为包含状态(通过对象的字段表示)和行为(处理状态的方法)的包(bundle)。
实现所有实例方法时都使用了一个隐式参数,方法签名里没显示这个参数。这个隐式参数是 this,它的值是调用这个方法的对象引用。在我们的例子中,是一个 Circle 对象。area() 和 circumference() 两个方法的主体都使用了类字段 PI。前面说过,类方法只能使用类字段和类方法,而不能使用实例字段或实例方法。实例方法没有这种限制,不管类中的成员有没有声明为 static,实例方法都可以使用。


this引用的工作方式
方法签名中不显示隐式参数 this,是因为往往用不到。只要 Java 方法在类中访问实例字段,都默认访问 this 参数指向的对象中的字段。实例方法调用同一个类中的其他实例方法时也一样,可以理解为“在当前对象上调用实例方法”。
不过,如果想明确表明方法访问的是自己的字段或方法,可以显式使用 this 关键字。例如,可以改写 area() 方法,显式使用 this 引用实例字段:
public double area() {
return Circle.PI * this.r * this.r;
}
上述代码还显式使用类名引用类字段 PI。在这样简单的方法中,一般无需如此明确。然而,遇到复杂情况时,在不强制要求使用 this 的地方使用 this,有时可以让代码的意图更明确。
不过,有些情况下必须使用 this 关键字。例如,如果方法的参数或方法中的局部变量和类中的某个字段同名,那么就必须使用 this 引用这个字段,因为只使用字段名的话,引用的是方法的参数或局部变量。
例如,可以把下述方法添加到 Circle 类中:
public void setRadius(double r) {
this.r = r; // 把参数r的值赋值给字段this.r注意,不能写成r = r
}
有些开发者会谨慎选择方法的参数名,避免和字段名冲突,因此可以最大限度地少使用this。
最后,注意,实例方法可以使用this 关键字,但类方法不能使用。这是因为类方法不关联在单个对象身上。

    推荐阅读