面向对象特征一:封装性(encapsulation)
为什么需要封装?
- 我要用洗衣机,只需要按一下开关和洗涤模式就可以了。有必要了解洗衣机内部的结构吗?有必要碰电动机吗?
- 我要开车,我不需要懂离合、油门、制动等原理和维修也可以驾驶。
- 客观世界里每一个事物的内部信息都隐藏在其内部,外界无法直接操作和修改,只能通过指定的方式进行访问和修改。
随着我们系统越来越复杂,类会越来越多,那么类之间的访问边界必须把握好,面向对象的开发原则要遵循“高内聚、低耦合”。
高内聚、低耦合是软件工程中的概念,也是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()); } } }