浅析Java 中的封装性
2012-09-06荆悦
荆悦
摘要: OOP 思想是整个应用型软件开发的核心,不管使用.NET 还是Java,只有掌握了OOP 思想,才能快速有效地为客户提供企业级的解决方案,而封装性又是OOP 思想中不可缺少的基础组成部分,所以在使用Java 时,理解和掌握封装性是非常重要的,对以后学习和掌握其他面向对象的编程技术也尤为重要。
关键词: Java;封装性;软件开发
中图分类号:TP311.5文献标识码:A 文章编号:
1. 引言
目前, 面向对象的思想被软件开发界广为追捧, 其实,面向对象思想不是高深的理论, 而是根据前人大量编程项目总结出来的一套分析和解决编程方面问题的方法。以面向对象思想为指导, 可以优化Java 代码的结构, 更可以让数据库访问模块变得富有弹性—即让代码和模块能更好地适应项目需求的频繁变化。面向对象编程(Object Oriented Programming, 简称OOP)描述的是对象之间的相互作用。OOP 编程主要有3 大思想,分别是封装、继承、多态。在面向对象编程中, 类作为最小程序单元, 就像以往面向过程编程中函数作为最小程序单元一样。
封装性是面向对象程序设计的原则之一。它规定对象应对外部环境隐藏它们的内部工作方式。良好的封装提高了代码的模块性, 它防止了对象之间不良的相互影响, 这样就使得未来的开发工作变得容易。
2. 定义
封装就是隐藏实现细节, 将属性私有化, 提供公有方法访问私有属性。
类是基于面向对象思想编程语言的基础, 程序员可以把具有相同业务性质的代码封装到一个类里, 通过接口方法向外部代码提供服务, 同时向外部代码屏蔽类里服务的具体实现方式。对象是类的实例, 类一般是个抽象名词, 比如“人类”, 而对象则是具体的物质存在, 比如“张三” 这个人。在现实生活中, 经常会遇到“类” 和“对象” 这些概念, 比如封装了能实现“全自动洗衣机” 功能的洗衣机类。这里, 用户可以使用面板上的按钮, 使用该类里提供的洗衣等服务,并且, 由于该类实现了封装, 所以在使用的时候, 用户可以不用了解其中的自动洗衣原理以及实现机制。
类是同一种类型的对象的抽象, 是某种类型对象的概述和定义; 而对象则是某个类的实例化结果或者叫一种类型的实体。在使用面向对象的思想进行软件开发的过程中, 首先得抽出项目的实体-对象模型:
即首先是实体类的定义、封装。
在Java 中, 最基本的封装单元是类, 一个类定义着将由一组对象所共享的行为(数据和代码) .一个类的每个对象均包含它所定义的结构与行为, 这些对象就好象是一个模子铸造出来的。在定义一个类时, 需要指定构成该类的代码与数据,
特别是类所定义的对象叫做成员变量或实例变量。操作数据
的代码叫做成员方法。方法定义怎样使用成员变量, 这意味着类的行为和接口要由操作实例数据的方法来定义。由于类的用途是封装复杂性, 所以类的内部有隐藏实现复杂性的机制。所以Java 中提供了私有和公有的访问模式, 类的公有接口代表外部的用户应该知道或可以知道的每件东西。私有的方法数据只能通过该类的成员代码来访问, 这就可以确保不会发生不希望的事情。
3.必要性
建构具有良好封装性能的对象的第一个步骤就是getter/setter 对访问私有数据域。通过一个控制的机制来要求那些要对对象的域进行读和写的其他对象这样去做, 可以加强法定值和内部数据的连贯性。如果有一个域名为duration, 它应该保存一个正整数, 当另一个对象试图进行setDuration (-4) 时,就可以给出一个IllegalArgumentException。如果将duration 成员设置为公有,就不能防止某人调用yourObject.duration=-4 并搞乱数据的内部连贯性。通过一个例子来理解这个问题: 编写一个教师类, 要求:
(1) 具有属性: 姓名、年龄
(2) 具有行为: 自我介绍
(3) 教师的最小年龄要求: 22 岁
public class Teacher {
public String name; // 教员姓名
public int age; //年龄
/**
* 返回自我介绍的内容
*/
public String introduction () {
return " 大家好!我是" + name + ",我今年" + age+"" ;
}}
编写一个测试类, 要求: 实例化一个教员对象, 并对其初始化, 并在控制台输出该教员的自我介绍。
public class TeacherTest {
public static void main (String [ ] args) {
Teacher teacher = new Teacher () ;
teacher.name = " 张三" ;
teacher.age =9;
System.out.println (teacher.introduction ()) ;
}
}
运行结果为: 大家好! 我是张三, 我今年9 岁
因为没对属性进行封装, 所以运行结果不满足教员的最小年龄要求, 要满足此要求, 就需要通过对属性的封装来实现,修改Teacher 类如下:
public class Teacher {
private String name; // 教员姓名
private int age; //年龄
public int getAge () {
return age;
}
public void setAge (int age) {
if (age<22) {
System.out.println (" 错误!最小年龄应为22 岁!") ;
this.age = 22; //如果不符合年龄要求,则赋予默认值
} else {
this.age = age;
}}
public String getName () {
return name; // 返回教员姓名
}
public void setName (String name) {
this.name = name; // 设定教员姓名
}
public String introduction () {
return " 大家好!我是" + name + ",我今年" + age+"岁" ;
}}
现在同样执行以上测试类TeacherTest, 运行结果为: 错误! 最小年龄应为22 岁!
大家好! 我是张三, 我今年22 岁
4. 封装
从以上事例可得出: 使用封装, 增加了数据访问限制,增强了程序的可维护性, 其封装步骤为:
(1) 修改name、age 属性的可见性为private 来限制直接对属性的访问。
(2) 为每个属性创建一对赋值(setter) 方法和取(getter)方法, 用于间接对这些属性的访问。
(3) 在setter 和getter 方法中, 加入对属性的存取限制。
5. 避免Java 封装性问题
对象通过方式返回值实现的数据不应是可修改的, 它不能改变对象的内部状态。当返回一个Char, 其他人可以对他们收到的Char 进行变更而不会修改这个Char, 因为Java 使用值的传递来返回原始值。
然而, 在返回对象时, 必须认真地监视它们的变动性。当一个对象的值在事例之后不能被变更, 这就是说它是不可变的。在java.lang 数据包等级中的很多对象都是不可变的,包括String、Char 和Short。
使一个对象不可变需要有3 个步骤:
(1) 数据成员必须是私有的, 防止被外部的代码直接地操纵。
(2) 不能提供setter 方式, 目的就是不允许对被封装的数据的变更。
(3) 类本身必须是最终的, 声明类为最终的可以防止另一个类创建不可变类的可变子类。
返回可变对象使得在对象外部的代码可以修改对象内部的数据而不会出现警告或是可能的干涉。例如, 有一个代表一份办公信件的Letter 对象, 它包括一个含有地址名称的Recipient对象, 如果这个Recipient 对象是可变的, 任何从前调用过getRecipient 方式的对象都可以修改信中的Recipient, 而不需要setRecipient 方式, 仅仅是通过更改返回的Recipient 对象中存储的数据。
返回对象的集合会使良好的封装性出现更多的问题。不论是否在Collections API 中使用legacy Hashtable 和Vector 对象或是它们的替代物, 都需要牢牢记住集合本身和它们的内容的可变性的级别。
对于返回集合要实现良好封装性的第一步就是要确保集合本身是不可变的或是无害可变的, 就是说, 它不更改对象的内容。最容易的但不是最有效的途径就是返回任一个集合的.clone (), 如果类具有一个HashSet 字符串对象, 返回the-Set.clone () 而不是theSet 本身就可以防止这个集合的recipient更改对象中名为theSet 的集合的内容。
返回一个产生被返回集合的内容的迭代器是实现不可变性的另一个途径。迭代器具有一套小型的给定方式, 且只有一个方式可以变更源数据。此外, 在API 之中的任何集合都可以很容易地获得一个Iterator, 将迭代器设置为不可变。
6. 结语
类的封装性即不能让外面的类随意修改一个类的成员变量; 在定义一个类的成员(包括变量和方法) 时, 使用private关键字说明这个成员的访问权限, 只能被这个类的其他成员方法调用, 而不能被其他的类中的方法所调用; 为实现封装性, 常将类的成员变量声明为private, 再通过public 的方法来对这个变量进行访问。对一个变量的操作, 一般都有读取和赋值操作, 一般定义两个方法来实现这两种操作, 即:getXxx () 与setXxx () ;一个类就是一个模块, 应该让模块仅仅公开必须要让外界知道的内容, 而隐藏其他的一切内容。在进行程序设计时, 应尽量避免一个模块直接修改或操作另一个模块的数据, 模块设计追求强内聚, 弱耦合。
参考文献
[1] 孙卫琴. Java 面向对象编程. 电子工业出版社, 2006.
[2] 甘玲, 等. 解析Java 程序设计. 清华大学出版社, 2006.
[3] 秦凤梅, 林旭东. Java 程序设计实用教程. 西南师范大学
出版社, 2006.
注:文章内所有公式及图表请以PDF形式查看。