封装十分有用,可以将一些数据信息封装起来,或者隐藏,或者做一些额外的限制操作,而这部分的行为都在内部来进行,对外只保留了适当的接口来提供调用,而类内部的各种信息可以自动修改达到很轻松可控的效果
有一个简单的Girl类
class Girl { public String name; public int age; public int id; }
类有三个成员变量,分别是姓名,年龄和工号
对于每个新来的员工,都会这样来使用(Girl obj).id来进行操作工号,类型都是int
Girl girl = new Girl(); girl.id = 6666;
如果有一天,公司大幅度扩张,疯狂招人,int工号都不够用了(太疯狂!),担心long有一天也会不够用,直接修改成员变量的类型为String,如此一来就变成这样了
class Girl { public String name; public int age; public String id; }
这下就尴尬了,其它在调用(Girl obj).id的地方,都会因为数据类型的修改,无法编译通过;还需要一个一个修改或者做转换,多加思索就觉得设计方式就有所欠妥,因为Girl类对于我的使用可扩展性太差,看看封装的效果
一开始的Girl类设计成如下
class Girl { private String name; private int age; private int id; public String getId() { return id; } public void setId(int id) { this.id = id; } }
可以看到这种设计方法,将成员变量置为private的,提供public的get和set方法来控制获取和存储操作,此时要用id信息的地方,在这种情形下,存和取的方式如下
Girl girl = new Girl(); girl.setId(6666); System.out.println(girl.getId());
通过这种方式,能够很安全的对数据进行存和取,或者暂时看不出来封装的优势,再回到上面的问题,假如int工号不够用,要将成员变量修改成String类型,而此时实例调用中setId里面依旧都是int类型,想要兼容现有所有人的用法,很自然的想法就是在setId方法里将传入的int类型做一下类型转换,让getId方法返回String类型,就达到目的了,整个如下
class Girl { private String name; private int age; private String id; public String getId() { return id; } public void setId(int id) { this.id = String.valueOf(id); } } public class Main { public static void main(String[] args) { Girl girl = new Girl(); girl.setId(6666); System.out.println(girl.getId()); } }
仔细酝酿一下就会发现所有通过setId存和getId取的地方,就算传入的是int类型,都不需要做任何修改(如果有其它int类型加减等运算应当自行处理),但就这个例子而言,还有一个问题,都已经说过了,此处的int工号已经无法满足了,虽然转换成了String类型,但setId方法传入的还是int类型,因此新来的员工还是无法适配这里,为了眼前兼容,改成long即可
class Girl { private String name; private int age; private String id; public String getId() { return id; } public void setId(long id) { this.id = String.valueOf(id); } } public class Main { public static void main(String[] args) { Girl girl = new Girl(); girl.setId(2222222222L); System.out.println(girl.getId()); } }
通过这个例子,就很通俗易懂地发现封装的一些好处,可以自定义地添加一些限制条件,再比如像有些公司,不招聘40岁以上的员工
class Girl { private String name; private int age; private String id; public String getId() { return id; } public void setId(long id) { this.id = String.valueOf(id); } public int getAge() { return age; } public void setAge(int age) { if (age > 40) { System.out.println("NO NEED"); } else { this.age = age; } } }
假如说妹纸的年龄在30到40岁之间是保密的
class Girl { private String name; private int age; private String id; public String getId() { return id; } public void setId(long id) { this.id = String.valueOf(id); } public int getAge() { return age; } public void setAge(int age) { if (age > 40) { System.out.println("NO NEED"); } else if (age > 30) { System.out.println("Secret"); } else { this.age = age; } } }
其实,还遗漏了一个比较重要的常识限制,年龄假如有人输入一个非常大的数字,显然就相当于有误的,因此可以做一下判断
class Girl { private String name; private int age; private String id; public String getId() { return id; } public void setId(long id) { this.id = String.valueOf(id); } public int getAge() { return age; } public void setAge(int age) { if (age > 200) { System.out.println("Impossible"); } else if (age > 40) { System.out.println("NO NEED"); } else if (age > 30) { System.out.println("Secret"); } else { this.age = age; } } }
从这些例子可以看出,类里可以自行进行把控,而这些细节对于实例调用的时候是不需要可见的
通过setter能够做一切可以进行的检查操作,还可以跑出Exception异常,始终记着将类成员变量设置为私有,然后提供公有的getter和setter来进行存取操作,可以保证安全性