封装字面意思即包装。专业点来说就是数据隐藏,是指利用抽象数据将数据和基于数据的操作封装起来,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能的隐藏细节,只保留一些对外的接口和外部进行联系。系统的其他对象只能通过包裹在这个数据外部的已经授权的方法与这个封装对象进行交流和交互。也就是说使用者不用知道这个封装对象内部的细节(当然也是无从知道的),但是可以通过这个封装对象对外提供的接口来访问该对象。
对于封装而言,它所封装的是自己的属性和方法,所以不需要依赖其他对象就可以完成自己的操作。
首先我们先来看两个类:Husband.java、Wife.java
Husband.java
public class Husband { /* * 对属性的封装一个人的姓名、性别、年龄、妻子 * 都是这个人的私有属性 */ private String name; private String sex; private int age; private Wife wife; /* * setter()、getter()是该对象对外开发的接口 */ public String getName() { return name; } publicvoid setName(String name) { this.name = name; } public String getSex() { return sex; } publicvoid setSex(String sex) { this.sex = sex; } public int getAge() { return age; } publicvoid setAge(int age) { this.age = age; } public void setWife(Wife wife) { this.wife = wife; } //wife不想让别人看见所以不设getter方法 }
Wife.java
public class Wife { private String name; private int age; private String sex; private Husband husband; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public void setAge(int age) { this.age = age; } //女人的age(年龄)是秘密所以不设置getter方法 public void setHusband(Husband husband) { this.husband = husband; } public Husband getHusband() { return husband; } }
从上面两个实例我们可以看出Husband里面wife引用是没有getter()的,同时wife的age也是没有getter()方法的。至于理由我想各位都懂的,男人嘛深屋藏娇妻嘛,没有那个女人愿意别人知道她的年龄。
通过这个我们还不能真正体会封装的好处。现在我们从程序的角度来分析封装带来的好处。如果我们不使用封装,那么该对象就没有setter()和getter(),那么Husband类应该这样写:
public class Husband { public String name ; public String sex ; public int age ; public Wife wife; }
外部调用:
Husband husband = new Husband(); husband.age = 30; husband.name = "张三"; husband.sex = "男";
但是那天如果 我们需要修改Husband,例如将age修改为String类型的呢?你只有一处使用了这个类还好,如果你有几十个甚至上百个这样地方,你是不是要改到崩溃。如果使用了封装,我们完全可以不需要做任何修改,只需要稍微改变下Husband类的setAge()方法即可。
public class Husband { /* * 对属性的封装一个人的姓名、性别、年龄、妻子都是这个人的私有属性 */ private String name; private String sex; private String age; /* 改成 String类型的 */ private Wife wife; public String getAge() { returnage; } public void setAge(int age) { // 转换即可 this.age = String.valueOf(age); } /** 省略其他属性的setter、getter **/ }
其他的地方依然那样引用(husband.setAge(22))保持不变。
到了这里我们确实可以看出-->
封装确实可以使我们容易地修改类的内部实现,而无需修改使用了该类的客户代码。
我们在看这个好处:可以对成员变量进行更精确的控制。
还是那个Husband,一般来说我们在引用这个对象的时候是不容易出错的,但是有时你迷糊了,写成了这样:
Husband husband = new Husband(); husband.age = 300;
也许你是因为粗心写成了,你发现了还好,如果没有发现那就麻烦大了,毕竟谁见过300岁的老妖怪啊!
但是使用封装我们就可以避免这个问题,我们对age的访问入口做一些控制(setter)如:
public class Husband { /* * 对属性的封装一个人的姓名、性别、年龄、妻子都是这个人的私有属性 */ private String name; private String sex; private int age; /* 改成 String类型的 */ private Wife wife; public int getAge() { return age; } public void setAge(int age) { if (age > 120) { System.out.println("ERROR:error age input...."); // 提示错误信息 } else { this.age = age; } } /** 省略其他属性的setter、getter **/ }
上面都是对setter方法的控制,其实通过使用封装我们也能够对对象的出口做出很好的控制。例如性别我们在数据库中一般都是已1、0方式来存储的,但是在前台我们又不能展示1、0,这里我们只需要在getter()方法里面做一些转换即可。
public String getSexName() { if("0".equals(sex)) { sexName = "女"; } elseif("1".equals(sex)) { sexName = "男"; } else { sexName = "人妖???"; } return sexName; }
在使用的时候我们只需要使用sexName即可实现正确的性别显示。同理也可以用于针对不同的状态做出不同的操作。
public String getCzHTML() { if("1".equals(zt)) { czHTML = "<a href='javascript:void(0)' onclick='qy("+id+")'>启用</a>"; } else { czHTML = "<a href='javascript:void(0)' onclick='jy("+id+")'>禁用</a>"; } return czHTML; }
小结
- 良好的封装可以减少耦合。
- 类内部结构可以自由修改。
- 对成员进行更精确的控制。
- 信息隐藏,实现细节。