你能想到「封装」说明有点专业
文章来源:《Head First Java》修炼感悟。
上一章,师兄们能否理解对象属性的相互关系? 本章老白想聊聊数据安全的问题,毕竟谁都不想把自己的数据暴露于大庭广众之下。 如果那样的话将毫无隐私可言,委实令人尴尬。 下面我们来了解一下什么是「封装」。
「封装」的意义
通过这段时间的修炼,相信大家都有了不小的进步,最起码打印输出的小程序应该信手拈来。 不过,老白不得不说些沮丧的话,这些代码结构很糟糕。 先前我们写的所有代码,如果仅是自己玩玩也就罢了,但如果用在正式场合,那就不得不考虑一下数据安全问题了。
试想,一个项目数以百计的类可以随意访问其它类的方法,随意修改其变量,整个程序就会面临崩溃的危险。 比如,你精心设计了一个宠物类,其中的 height
是一个很重要的实例变量。 如果你的代码可以远程访问,你无法阻止类似 cat.height = 0
这样的修改,这个程序还能正常运行吗?
同样的问题也可能出现在方法调用上。 本来一个方法仅用于当前类,如果不加控制的话所有无关的类都可以随意访问。 有些不怀好意的恶意调取方法,比如无限循环调用,那你的程序瞬间就会瘫痪。
认识到问题的严重性了吗? 现在需要寻求一种方法,把访问权限控制在合理范围,拒绝一切无关修改和访问。 万幸的是,Java 的设计者们早就想到了这个问题,提供了「封装」的技术手段。
合理保护私有数据
老白刚看到封装(Encapsulation) 这个单词时,第一个想法就是「把什么东西塞进箱子里,不轻易让人看到」,就好像邮寄快件。 事实也差不多,塞进「箱子」里的东西就是数据,别人是无权拆箱的。
「封装」并不是彻底断绝外界访问,可以通过创建公有的 Getter
和 Setter
访问器来存取变量,由不安全访问改为受保护访问。 请看下面代码:
/**
* 源文件:Cat.java
* 类描述:演示 Getter、Setter 访问器如何实现「数据封装」
*/
public class Cat {
// 声明实例变量并赋初始值
private int height = 5;
// Setter,设置高度值
public void setHeight(int height) {
if (height < 5) {
return;
}
this.height = height;
}
// Getter,返回高度值
public int getHeight() {
return height;
}
}
不要怀疑,现在正在做的事情就是「数据封装」。 可能你会说,这也没什么神秘的,就是两个方法。 的确如此,或许将来你有了更多 Java 经验之后会有更好的保护数据的方法,但目前这是我们能做到的最好的保护数据安全的手段。
上面代码有几点需要注意,老白解释一下:
- Cat 类的实例变量
height
的声明方式有点特别,使用了private
这样的关键字修饰,表示该变量只能在 Cat 类中使用,属于「私有数据」,别的类无权访问,哪怕是它的子类也不行。 - Getter 和 Setter 访问器使用
public
关键字修饰,表示允许外部访问。 - 注意 Setter 中
return
的用法,只要if
条件成立(即height < 5
),方法直接返回,不会执行下一条赋值语句this.height = height;
,杜绝了譬如height = -100
这样的恶意赋值。
善于隐藏实现细节
从现在开始养成一个习惯,设计新类时考虑一下访问权限的问题。 比如类中某个方法如果只是在当前类中使用,那就没有必要声明为 public
,而改为 private
更合适。 换句话说,你若是能把所有变量和方法都声明为「私有」的话,那是最安全的。 不过,这样做意义不大,以安全的方式「暴露数据」才是正确的选择。
可能大部分人都不希望自己程序的实现细节过度暴露,因为有经验的程序员会根据参数列表以及返回值,大致猜出实现过程,所以核心代码的「数据隐藏」尤为重要。
老白感悟: 「数据封装」的基本原则,就是把实例变量声明为私有的,通过公有的 Setter 和 Getter 实现存取数据。
即将被封装的对象
以下内容摘自《Head First Java》,希望可以帮助大家加深对「封装」的理解。
- Head First
封装有什么本事? - Object
嗯,你有没有梦到过面对 500 名观众,突然发觉自己没有穿裤子? - Head First
是有一次。 我梦到跟一群模特儿在后宫嬉戏,然后我的裤子… 呃,先不谈这个。 OK,所以你是说没有封装就像没穿裤子? 但是没有露一点出来会不会很不舒服? - Object
不会吧,大哥? 不舒服? 很不舒服? 哈哈哈哈 ~~~~ 哈哈哈 ~~~~ - Head First
这有什么好笑的? 我是很认真的。 - Object
哇哈哈哈哈 ~~~~ (在地上滚来滚去) ~~~~ (泪) ~~~~ 哈哈哈 ~~~~ - Head First
快来人啊!叫救护车,快! - Object
我没事了,~~~~ 哈 ~~~~ 啊 ~~~~ 快不行了 ~~~~ 好了,真的没事了,啊 ~~~~ (深呼吸)。 - Head First
好吧,请告诉我们封装可以怎样保护你的安全。 - Object
封装会对我的实例变量加上绝对领域,因此没有人能够恶搞我的实例变量。 - Head First
比如说? - Object
用膝盖想也能知道。 大部分的实例变量值都有一个适当的范围,比如身高就不可能是负的、佛跳墙就不可能在 3 分钟之内做好的。 - Head First
我懂你的意思了。 那封装是如何设下保护罩的? - Object
强迫其它程序一定得经过 Setter,如此 Setter 就能够检查参数并判断是否可以执行。Setter 也许可以退回不合理的值,或是抛出 Exception,或者自己进行取小数点的操作。 重点在于,你可以在 Setter 中执行任何操作,直接暴露的 public 实例变量就没有这个能耐。 - Head First
但我有看过有些 Setter 什么事情也么做,只是把值设给变量而已,这样不是只会增加执行的负担吗? - Object
这对 Getter 也是一样的,好处是你事后可以改变想法却不需要改变其它部分的程序。 假如说所有人都使用到你的类和公有变量,万一有一天你发现这个变量需要检查,只要在 Getter 中改动代码而无需强迫所有人改动。 封装的优点就是能够让你三心二意却又不会伤害别人,直接存取变量的效率是不可企及的。
《 上一篇 对象的「行为」 |
---|