美文网首页Java知识 | 解析java
重新认识java(二) ---- 面向对象之封装!

重新认识java(二) ---- 面向对象之封装!

作者: Sharember | 来源:发表于2017-01-31 17:37 被阅读2100次

    如果你认为封装仅仅是private + getter and setter,那你就大错特错了!

    什么是封装

    对于面向对象的特点,我想大家都可以倒背如流了:封装,继承,多态。很多人对这些特点的理解仅仅停留在表面。以为封装就是变量私有化,然后对外提供接口,而不知为什么要这样做。

    封装,简单的来讲就是将变量的属性私有化,在java里就是用private修饰符修饰,这样在外部产生的对象就不能直接访问到这个变量。想要对变量进行操作或者访问,就需要在类里提供外部访问的接口,也就是我们熟知的get和set方法。

    这就是大部分人对封装的理解。知道有封装这回事,知道怎么用,却不知道为什么要用,甚至觉得这是多此一举。因为明明person.name就可以访问到变量,为什么非要person.getName()呢?

    任性的使用public

    我们先来看一下不使用封装的情况。

    首先,有两个类,Man和Women:

    //Man
    public class Man {
        public String name; //名字
        public Woman wife;  //男人嘛,都有妻子
        public double money;//男人嘛,多赚点钱
    
        //还可以结个婚
        public void marry(Woman woman){
            this.wife = woman;
            woman.marry(this);
        }
    }
    //Women
    public class Woman {
        public String name; //名字
        public Man husband; //得有一个丈夫
        //也可以结个婚
        public void marry(Man man){
            this.husband = man;
        }
    }
    

    代码很精简,看着很舒服,测试一下。

    哎哟,看起来还不错。

    这个时候,来了一个小偷,这小偷不干别的,就偷别人的钱和老婆。

    //小偷
    public class Thief {
        private double stealMoney = 0;
        private List<Woman> womens = new ArrayList<>();
    
        //偷钱
        public void stealMoney(Man man){
            stealMoney += man.money;
            man.money = 0;
            System.out.println("哈哈,偷到钱了");
        }
        //偷老婆,最可气的是,偷了你的老婆还把凤姐丢给了你
        public void stealWife(Man man){
            womens.add(man.wife);
            Woman woman = new Woman();
            woman.name = "凤姐";
            man.wife = woman;
            System.out.println("哈哈哈,又偷了一个妹纸");
        }
    }
    

    有一天,来了这么一个小偷:

    傻了吧?你老婆呢?你钱呢?哈哈哈哈哈哈哈哈哈

    就这样,小偷偷走了你的钱和你的老婆并丢给了你一个凤姐,而你,却无能为力。

    你觉得必须要改变一下了!!

    封装前来报到

    封装觉得你有点惨,于是过来帮了一下你:

    //PackageMan
    public class PackageMan {
        private String name; //私有化名字
        private PackageWoman wife;//必须私有!!必须!
        private double money; //私有,统统私有!
        //我们先写个构造函数,为了方便
        public PackageMan(String name, double money) {
            this.name = name;
            this.money = money;
        }
        //结婚
        public void marry(PackageWoman woman){
            this.wife = woman;
            woman.marry(this);
        }
    
        //各种getter和setter
        public void setName(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public PackageWoman getWife() {
            return wife;
        }
    
        public double getMoney() {
            return money;
        }
    }
    
    //PackageWoman
    public class PackageWoman {
        private String name;
        private PackageMan husband;
    
        public void marry(PackageMan man){
            this.husband = man;
        }
    
        public PackageWoman(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public PackageMan getHusband() {
            return husband;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    看起来有点眼花缭乱,这样真的有效么?

    恩,看起来还行,就是代码长了点,赶紧给我来个小偷测试一下!

    什么?你想测试一下,不好意思,测试不了,因为小偷已经死在测试的路上了。因为我们根本没有对外提供设置money和wife的方法,所以小偷可以知道你有多少钱,知道你有一个漂亮的老婆,但是他却无能为力,因为他只能看着。

    细心的人也许发现了,这里面有一个很严重的问题:

    没错,小偷不能把我们的money清空了,也不能将我们的wife换成别人了。但是,如果我们要自己换呢?我的钱这辈子就这么点?还不能花?我还不能离婚了?(咳咳。。不鼓励离婚哈,就是举个例子,别打我)

    这才是封装厉害的地方

    如何解决上面的问题呢?私有化外部访问不到,自己也没法改变数据,提供set方法又会让所有人都能改,和不私有没什么区别,好纠结啊。

    等等,你刚刚说 “所有人“?真的是所有人么?

    我们来看看:

    public void setMoney(PackageMan man,double money) {
        if (man == this) {
            this.money = money;
        } else {
            System.out.println("喂,110吗?"+man.getName()+"抢钱!");
        }
    }
    

    这样呢?只有你自己可以修改,别人都不可以,测试一下:

    这样就可以了,自己可以修改,但是别人不可以。

    但是你老婆不满意了,凭什么只有你自己能改?我也想改!

    这种要求,还是应该满足一下的,怎么做呢?

    public void setMoney(Object o,double money) {
        if (o == this || o == this.wife) {
            this.money = money;
        } else {
            System.out.println("喂,110吗?有人抢钱!");
        }
    }
    

    这样就可以了。

    当然,爱思考的人肯定发现了,我把抢钱的那句话修改了,没有获取修改人的名字,因为传入的是Objects对象。当然,你也可以再写点代码,判断一下传进来的是什么,然后抢转为相应的类型,再调用相应的方法。

    除此之外,就没有别的办法了么?当然有,具体怎么做,我们下一篇文章再做分解。下一篇文章《重新认识java(三) ---- 面向对象之继承》不定期更新。

    仔细思考一下你会发现,我特么竟然是在骗你!因为当你提供了set函数以后,小偷又可以偷你的东西了。仔细看一下之前小偷是怎么偷你东西的你就知道了。

    没错,就是通过你自己。小偷通过你自己改变了你自己。听起来有点扯,但是事实上就是这样的。

    那么,有没有一种办法让小偷在只得到”你自己“的情况下怎么样都不能改变“你的属性值”,而只有你自己能改变呢?

    大家可以自己想想,具体的解决办法,我们在之后的文章里揭晓。敬请期待。

    总结一下

    以上就是面向对象的封装,封装不仅仅只是private + getter and setter。使用封装可以对setter进行更深层次的定制。你可以对可以执行setter方法的对象做规定,也可以对数据作要求,还可以做类型转换等等一系列你可以想到的。

    使用封装不仅仅是安全,更可以简化操作。不要觉得用了封装多了好多代码,看起来乱糟糟的。这只是因为我举得例子太小了。如果你写你个大的系统,一开始你的这样定义类的

    public int age;
    

    你的程序里大概有100处这样的语句:

    p.age = 10;
    

    这个时候,突然要求你把数据类型变了,改成:

    public String age;
    

    你是不是要把那100处数据都加个双引号呢?这是不是很麻烦?

    如果你用了封装,你只需要这样:

    public void setAge(int age){
        this.age = String.valueOf(age);
    }
    
    

    然后就搞定了,是不是简化了操作?

    我只是举个例子,实际开发中也不会出现改变数据类型这么操蛋的事。。

    封装还有一个好处是模块化。当你参与一个很多人实现的大型系统中,你不可能知道所有的类是怎样实现的。你只需要知道这个类给我提供了哪些方法,我需要传入什么数据,我能得到什么结果。至于怎么得到的,关我x事?

    所以说,如果你写的代码还没有用封装,改过来吧。不是因为大家都用所以你也应该用,而是这确实可以给你提供极大的便利。

    结束~


    有什么疑问或者错误可以给我留言

    下篇文章见~

    转载请注明出处!!!!!
    本文原创自csdn和简书!!
    csdn地址:http://m.blog.csdn.net/article/details?id=53381737
    简书地址:http://www.jianshu.com/p/9cc3a832fabb

    相关文章

      网友评论

      • 43f383d8ba08:作者 编辑器用的什么字体。。。感觉很舒服啊
        43f383d8ba08:@cleverfan :blush: O(∩_∩)O谢谢
        Sharember: @墨染烟雨 大概是mac的默认字体。。我不记得我改过。叫lucida grande。编译器是idea
      • 行者自若:封装不是简单的「变量私有化」,而是两点:
        1、对外隐藏细节
        2、对外屏蔽变化
      • a692c1609976:请教问题:您的文章中提到 “ 那么,有没有一种办法让小偷在只得到”你自己“的情况下怎么样都不能改变“你的属性值”,而只有你自己能改变呢?”, 我也想了许久,使用Proxy模式来实现吗? 但是还不能阻止小偷改变, 请问您有什么办法,谢谢。
      • 288d8878f975:哈哈哈,大写的服
      • 3014d79da6a9::+1: :+1: 形象生动,深入浅出,贴合实际,人才
        Sharember: @3014d79da6a9 谢谢支持~
      • Vove7:起初学习时,自身就对编程应该就要有这些概念😄
        Sharember: @M天然呆 一开始,概念是有的,但是理解的不够深入
      • 328f0e94dfc4:写的不错,以前真的没有深入的想过,多谢!
        Sharember: @笨笨熊216 不客气~~
      • HowieW:受益匪浅 加深印象
        能否出几篇应届生面试类详解?:smile:
        HowieW:@cleverfan 了解!
        Sharember: @蠢到极致自然萌 面试。。。我才大三,我也没面试过。我写这个系列就是为面试打基础的😂
      • 89a02c75dbe3:很喜欢,
        Sharember: @木宇fly 谢谢~
      • dong_note:建议延伸下技术之外话题,为啥学他!
        Sharember: @dong_note 后面会讲到的~
        dong_note:@cleverfan 具体的,比如学县城,因为他用到很多地方比如卖票!:smile:
        Sharember: @dong_note 没太理解你的意思。。为啥学java?
      • 古月XYZ:写得挺有意思的. 2个小问题建议看看:
        1. Object 实例判断相等不建议用 `==`, 建议使用 `equal` 方法. 比如文中判断 `o == this.wife`;
        2. 丈夫实例和妻子实例婚后相互引用, 是否存在内存无法销毁的风险? (可以考虑建一个 human 基类, 然后 override 一个 die 方法, 哈哈)
        古月XYZ:@若兮相言123 如果夫妻一方去世呢?去世的一方一直会被活着的一方引用,导致无法销毁。或者可以考虑使用 `weakreference` ?
        若兮相言:@古月XYZ 可以销毁的吧,有个算法直接从根节点搜索,到达不到的直接销毁
        Sharember: @古月XYZ 谢谢你提的建议~第一个我确实没有注意。第二个我在下一篇文章讲继承的时候讲了~因为这篇文章还没涉及到继承~
      • 小灰灰呢:很感谢,每期观看,虽然还不是很懂,但有个概念也好
        Sharember: @小灰灰呢 我有一篇文章,是讲百度网盘限速的。不过没有弄过来。我现在鼓捣一下,你一会看看。
        小灰灰呢: @cleverfan 嗯嗯,好,网盘限速太厉害了,都下了一整天了 😲😲
        Sharember: @小灰灰呢 哈哈,我知道你,加油呀~
      • f34d8d512024:好棒,更加理解为什么要封装了
        Sharember: @sunnytips 对你有用就好~
      • 3cc24d5f172f:写的不错,让我理解更深入了,期待下一篇。😃😃😃
        Sharember: @Java菜鸟来袭 谢谢支持~~
      • iamxcc:看了有收获,感谢👏
        Sharember: @iamxcc 谢谢支持~
      • junouba:写的挺好扥呢
        Sharember: @junouba 谢谢支持~

      本文标题:重新认识java(二) ---- 面向对象之封装!

      本文链接:https://www.haomeiwen.com/subject/dprkittx.html