一、封装
1、什么是封装?
在一个对象中,包含了状态和行为,状态指的是对象的数据成员,而行为则对应了对象的方法。数据成员和方法都是提供给内部和外部使用的,内部通过this调用对象的数据成员和方法,在外部另一个类中则通过 对象.数据成员 和 对象.方法()来调用对象的数据成员和方法。
将要封装的对象成员(数据成员和方法),只允许从外部调用部分成员。
2、封装的作用
封装的作用是提高对象的易用性和安全性。
譬如,一个充电电筒:
图1 充电电筒一个用户即使不看说明书,也可以猜到这个电筒的操作: 开关和充电。这个电筒用一个塑料壳将用户不需要接触的内部细节隐藏起来,只保留了两个接口,开关和电插头。使用这两个接口,用户足以使用该产品在设计中想要实现的功能。如果所有的细节都同时暴露给用户,那么用户会对产品感到不知所措 (比如下面不加壳的遥控器)。因此,封装提高了产品的易用性。
对于Java来说,若不对数据成员和方法进行封装,面对茫茫多的数据成员时,我们在需要用到几个数据成员的组合值时,这里的数据成员就相当于手电筒的内部部件,数据成员的组合值,就相当于手电筒的内部部件组合成的开关功能,当我们需要这个组合值的时候,直接调用就可以了,而不是还要我们在外部一个一个成员数值组起来再构成那个组合值的功能。你看,要是我们封装起来了,是不是更容易使用了?
图2 遥控器对于安全性来说,这里我们用遥控器打比方,如果产品不封装,遥控器的许多细节会暴露在用户面前: 电池、电路、密封的橡胶等等。尽管这可以让用户更自由的对产品实施操作,比如直接给电池放电,取出一个LED灯等等。然而,用户往往要承担更大的损坏产品的风险。因此,封装提高了产品的安全性。
同样,对于Java,譬如有3个参数,int a,int b,int c,而a=b/c,若我们没有对其封装,没有对c进行判断不能为0,当我们运行程序时,输入c=0时,就会产生异常。当一个类的数据成员直接暴露给外部使用,在外部使用人员不了解或者恶意破坏的情况下,就有可能产生不可预想的后果,这里也就体现了封装的安全性。
对象成员的封装
Java通过三个关键字来控制对象的成员的外部可见性(visibility):public,private,protected。
这里我们主要讲public 和 private 这两个关键字。
public: 该成员外部可见,即该成员为接口的一部分
private: 该成员外部不可见,只能用于内部使用,无法从外部访问。
我们先来封装以前定义的Human类:
public class Test
{
public static void main(String[] args)
{
Human aPerson = new Human(160);
System.out.println(aPerson.getHeight());
aPerson.growHeight(170);
System.out.println(aPerson.getHeight());
aPerson.repeatBreath(100);
}
}
class Human
{
/**
* constructor
*/
public Human(int h)
{
this.height = h;
System.out.println("I'm born");
}
/**
* accessor
*/
public int getHeight()
{
return this.height;
}
/**
* mutator
*/
public void growHeight(int h)
{
this.height = this.height + h;
}
/**
* encapsulated, for internal use
*/
private void breath()
{
System.out.println("hu...hu...");
}
/**
* call breath()
*/
public void repeatBreath(int rep)
{
int i;
for(i = 0; i < rep; i++) {
this.breath();
}
}
private int height; // encapsulated, for internal use
}
内部方法并不受封装的影响。Human的内部方法可以调用任意成员,即使是设置为private的height和breath()
外部方法只能调用public成员。当我们在Human外部时,比如Test中,我们只能调用Human中规定为public的成员,而不能调用规定为private的成员。
通过封装,Human类就只保留了下面几个方法作为接口:
getHeight()
growHeight()
repBreath()
我们可以将Human类及其接口表示为如下图的形式:
图3 封装了的Human如果我们从main中强行调用height:
System.out.println(aPerson.height);
将会有如下错误提示:
Test.java:6: height has private access in Human
System.out.println(aPerson.height);
^
1 error
Beep, 你触电了! 一个被说明为private的成员,不能被外部调用。
在Java的通常规范中,表达状态的数据成员(比如height)要设置成private。对数据成员的修改要通过接口提供的方法进行(比如getHeight()和growHeight())。这个规范起到了保护数据的作用。用户不能直接修改数据,必须通过相应的方法才能读取和写入数据。类的设计者可以在接口方法中加入数据的使用规范。
网友评论