面向对象特征一:封装性(encapsulation)

发布时间 2023-05-08 06:05:23作者: 晓枫的春天

为什么需要封装?

  • 我要用洗衣机,只需要按一下开关和洗涤模式就可以了。有必要了解洗衣机内部的结构吗?有必要碰电动机吗?
  • 我要开车,我不需要懂离合、油门、制动等原理和维修也可以驾驶。
  • 客观世界里每一个事物的内部信息都隐藏在其内部,外界无法直接操作和修改,只能通过指定的方式进行访问和修改。

随着我们系统越来越复杂,类会越来越多,那么类之间的访问边界必须把握好,面向对象的开发原则要遵循“高内聚、低耦合”。

高内聚、低耦合是软件工程中的概念,也是UNIX 操作系统设计的经典原则。

内聚,指一个模块内各个元素彼此结合的紧密程度;耦合指一个软件结构内不同模块之间互连程度的度量。内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身。

而“高内聚,低耦合”的体现之一:

  • 高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
  • 低耦合:仅暴露少量的方法给外部使用,尽量方便外部调用。

何为封装性?

所谓封装,就是把客观事物封装成抽象概念的类,并且类可以把自己的数据和方法只向可信的类或者对象开放,向没必要开放的类或者对象隐藏信息。

通俗的讲,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。

Java如何实现数据封装

  • 实现封装就是控制类或成员的可见性范围。这就需要依赖访问控制修饰符,也称为权限修饰符来控制。
  • 权限修饰符:public、protected、缺省、private。具体访问范围如下:

修饰符

本类内部

本包内

其他包的子类

其他包非子类

private

×

×

×

缺省

×

×

protected

×

public

  • 具体修饰的结构:

             外部类:public、缺省

             成员变量、成员方法、构造器、成员内部类:public、protected、缺省、private

 

封装性的体现

成员变量/属性私有化

概述:私有化类的成员变量,提供公共的get和set方法,对外暴露获取和修改属性的功能。

实现步骤:

使用 private 修饰成员变量

private 数据类型 变量名 

代码如下:

public class Person {
    private String name;
      private int age;
    private boolean marry;
}

提供 getXxx方法 / setXxx 方法,可以访问成员变量,代码如下:

public class Person {
    private String name;
      private int age;
    private boolean marry;

    public void setName(String n) {
        name = n;
    }

    public String getName() {
        return name;
    }

    public void setAge(int a) {
        age = a;
    }

    public int getAge() {
        return age;
    }
    
    public void setMarry(boolean m){
        marry = m;
    }
    
    public boolean isMarry(){
        return marry;
    }
}

测试

public class PersonTest {
    public static void main(String[] args) {
        Person p = new Person();

        //实例变量私有化,跨类是无法直接使用的
        /* p.name = "张三";
        p.age = 23;
        p.marry = true;*/

        p.setName("张三");
        System.out.println("p.name = " + p.getName());

        p.setAge(23);
        System.out.println("p.age = " + p.getAge());

        p.setMarry(true);
        System.out.println("p.marry = " + p.isMarry());
    }
}

成员变量封装的好处:

  • 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里面加入控制逻辑,限制对成员变量的不合理访问。还可以进行数据检查,从而有利于保证对象信息的完整性。
  • 便于修改,提高代码的可维护性。主要说的是隐藏的部分,在内部修改了,如果其对外可以的访问方式不变的话,外部根本感觉不到它的修改。例如:Java8->Java9,String从char[]转为byte[]内部实现,而对外的方法不变,我们使用者根本感觉不到它内部的修改。

举例

public class Employee {
    private String name;
    private int age;
    private char gender;
    private String phoneNumber;


    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public char getGender() {
        return gender;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setName(String name) {
        this.name = name;
    }

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

    public void setGender(char gender) {
        this.gender = gender;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                ", phoneNumber='" + phoneNumber + '\'' +
                '}';
    }
}

测试类

public class EmployeeTest {
    public static void main(String[] args) {
        Employee[] emp = new Employee[2];
        Scanner scanner = new Scanner(System.in);

        for (int i = 0; i < emp.length; i++) {
            emp[i] = new Employee();
            System.out.println("添加第 " + (i + 1) + "个员工 ");
            System.out.print("姓名");
            String name = scanner.nextLine();
            System.out.print("性别");
            char gender = scanner.next().charAt(0);
            System.out.print("年龄");
            int age = scanner.nextInt();
            System.out.print("号码");
            String phoneNuber = scanner.nextLine();

            //赋值
            emp[i].setName(name);
            emp[i].setGender(gender);
            emp[i].setAge(age);
            emp[i].setPhoneNumber(phoneNuber);
        }
        //遍历
        for (Employee employee : emp) {
            System.out.println(employee.toString());
        }

    }
}