美文网首页Java java学习
面向对象三大特性(一):封装

面向对象三大特性(一):封装

作者: 许宏川 | 来源:发表于2015-07-22 02:07 被阅读219次

    面向对象有三大特性:封装、继承和多态。注意,是面向对象的三大特性而不是Java的三大特性,Java只是面向对象语言的一种。这篇文章说说其中的一个特性:封装。

    面向对象的类是把程序逻辑里需要的属性和方法抽象出来放到合适的类里。而我们在把类的成员(属性和方法)打包成类后,我们还要对类和类的成员的信息进行适当的隐藏和对访问权限进行限制。这就是封装。

    封装分两个层面,一是类本身的封装和类的成员的封装。
    实现封装的语法有以下两个

    • 访问控制符
    • getter和setter

    访问控制符##

    Java有四个访问修饰符,按访问范围从小到大排序分别是<code>private</code>、<code>default</code>、<code>protected</code>、<code>public</code>。其中default并不是有这个关键字,而是没有写访问修饰符即缺省的意思。
    这些访问修饰符可以修饰类也可以修饰类的成员。
    修饰类时控制的是这个类在什么范围内允许被实例化。类只能被public和default修饰,不能被private和protected修饰。如果用public修饰则没有限制在哪都可以被实例化,如果用defalut修饰则在同一个包下才能被实例化。实际使用中我们会把大部分类都用public修饰。什么情况用defalut修饰类呢,这个等学到内部类时再就知道了。

    修饰类的成员时控制的是这个成员在什么范围内允许被访问。四个访问修饰符都可以用于修饰类的成员,其权限如下表所示。


    用public修饰的成员在整个项目里都能访问。而用protected修饰的成员只有子类可以访问(这个等学习继承时再详细说),default是同一个包内可以访问,再次解释下没有default这个关键字,就是什么访问修饰符都不写的意思。而private范围范围最小,只有本类里可以访问。

    来看示例。例如我们有一个操作系统类OS,有以下几个属性。

    String name; //系统名称
    String language; //系统语言
    int timeZone; //时区
    

    如果我们把这些属性都用public,那是不是到处都可以通过对象名和小圆点来获取或者修改值?那如果像下面这样瞎改是不是就不安全了?

    OS myOS = new OS();
    myOS.name = "塞牙班";
    myOS.language = "@#¥%";
    myOS.timeZone = 25;
    

    所以我们需要对需要对这些属性进行权限回收。把访问修饰符改为private私有的。这下好了,因为别的地方不能访问这些属性了,但这下也糟了,也是因为别的地方不能访问这些属性了。所以这时就需要<code>getter()</code>和<code>setter()</code>出场了。

    getter()和setter()##

    我们对属性的保护完整的做法除了用private私有化外,还需要对外提供公有化的访问方法。也就是获取值得方法getter()和设置值的方法setter()。如果一个属性完全不想被其它类访问只是类内部私用那么就不要提供getter()和setter()。如果只允许访问值而不允许被修改那么只提供getter()而不提供setter(),如果允许被修改值那么可以提供setter()方法,设置成什么值通过参数传递给setter(),然后setter()可以通过传进来的参数进行判断这个值是否合法。例如地球之友24个时区,如果传进来一个25就不对了,修改失败。
    我把OS的代码写成下面这样:

    public class OS {
    
        private String name; //系统名称
        private String language; //系统语言
        private int timeZone; //时区
    
        public String getName() {
            return name;
        }
    
        public String getLanguage() {
            return language;
        }
    
        public void setLanguage(String language) { //简化,假设这个世界只有这四种文字
            if (language == "中文(简体)" || language == "中文(繁體)" || language == "English" || language == "français") {
                this.language = language;
            }
        }
    
        public int getTimeZone() {
            return timeZone;
        }
    
        public void setTimeZone(int timeZone) { //地球只有24个时区
            if (timeZone >= 0 && timeZone <= 24) {
                this.timeZone = timeZone;
            }
        }
    
        //无参数构造方法
        public OS() {
            this.name = "dos";
            this.language = "English";
            this.timeZone = 17;
        }
    
    }
    

    tips:可以通过快捷键alt + insert快捷键添加getter()和setter(),在弹出来的对话框用ctrl键多选要添加哪些属性的getter()和setter()。


    再写个测试类Test

    public class Test {
    
        public static void main(String[] args) {
            OS myOS = new OS();
            System.out.println(myOS.getName());
            myOS.setLanguage("中文(简体)");
            System.out.println(myOS.getLanguage());
            myOS.setTimeZone(8);
            System.out.println(myOS.getTimeZone());
        }
    
    }
    

    运行结果:

    <pre>
    dos
    English
    8
    </pre>

    以上是对属性进行的封装,那方法呢?
    我创建一个PC类,代码如下。

    public class PC {
        
        private String processor; //处理器
        private int memory; //内存
        private String videoCard; //显卡
        private int hardDrive; //硬盘
        private int display; //屏幕
        private com.xuhongchuan.os.OS OS; //操作系统
    
        public String getProcessor() {
            return processor;
        }
    
        public int getMemory() {
            return memory;
        }
    
        public String getVideoCard() {
            return videoCard;
        }
    
        public int getHardDrive() {
            return hardDrive;
        }
    
        public int getDisplay() {
            return display;
        }
    
        public OS getOS() {
            return OS;
        }
    
        public void setOS(OS os) {
            this.OS = os;
        }
    
        /**
         * 无参构造方法
         */
        public PC() {
            System.out.println("一台新的电脑诞生了。");
        }
    
        public void startup() {
            System.out.println("开机");
            System.out.println("硬件自检");
            System.out.println("引导操作系统启动");
            OS.startup();
        }
    
        public void shutDown() {
            System.out.println("关机");
        }
    
    }
    

    PC有处理器、内存、显卡、硬盘、屏幕、操作系统这些属性,出于安全考虑都要设置成private的。但是用户是可以知道这些属性的,所以都添加了getter()。但是一台电脑能不能自行DIY修改配置不一定,所以除了可以setOS()装系统外没有添加其它属性的setter()。那有的电脑就是可以自行升级配置setter()写在哪呢?这个等学继承时再来处理。

    然后我们再给OS类添加以下方法:

        //开机启动系统
        public void startup() {
            System.out.println(this.name + "系统启动了");
        }
    
        //关机
        public void shutdown(PC pc) {
            exitApps();
            pc.shutDown();
        }
    
        //重启
        public void restart(PC pc) {
            shutdown(pc);
            pc.startup();
        }
    
        //注销
        public void logOff() {
            exitApps();
        }
    
        //退出所有正在运行的程序
        private void exitApps() {
            System.out.println("关闭所有正在运行的程序。。。");
        }
    

    把注意力放到方法上。首先构造方法是public的,这样别的地方才能调用它进行实例化。而像开机、关机、重启、注销这些方法都是提供给用户使用的功能自然也必须是public的,那什么方法适合私有化呢。是这样的,如果一个方法是为类里的别的方法服务的时候,那么通常把这个方法定义为私有的。例如例子里的exitApps()就是私有的。因为关机、重启、注销都会关闭所有程序,但是把把关闭所有程序重复写就太冗余了。

    改一下测试代码:

        OS myOS = new OS();
        myOS.setLanguage("中文(简体)");
        myOS.setTimeZone(8);
    
        PC myPC = new PC();
        myPC.setOS(myOS);
        myPC.startup(); //开机
        myPC.getOS().restart(myPC); //重启
        myPC.getOS().shutdown(myPC); //关机
    

    运行结果:
    <pre>
    一台新的电脑诞生了。
    开机
    硬件自检
    引导操作系统启动
    dos系统启动了
    关闭所有正在运行的程序。。。
    关机
    开机
    硬件自检
    引导操作系统启动
    dos系统启动了
    关闭所有正在运行的程序。。。
    关机
    </pre>

    本文代码下载:百度网盘

    相关文章

      网友评论

        本文标题:面向对象三大特性(一):封装

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