anonymous的Java学习笔记(7)——Java面向对象之封装

面向对象三大特征之封装

封装(Encapsulation)是面向对象的三大特征之一(另外两个是继承和多态),它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。

对于一个类或对象实现良好封装的好处

  • 隐藏类的实现细节。
  • 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里加入控制逻辑,限制对成员变量的不合理访问。
  • 可进行数据检查,从而有利于保证对象信息的完整性。
  • 便于修改,提高代码的可维护性。

为了实现良好的封装, 需要从两个方面考虑

  • 将对象的成员变量和实现细节隐藏起来,不允许外部直接访问。
  • 把方法暴露出来,让方法来控制对这些成员变量进行安全的访问和操作。
    因此,封装实际上有两个方面的含义:把该隐藏的隐藏起来,把该暴露的暴露出来,这两个方面都需要通过使用Java提供的访问控制符来实现。

使用访问控制符

Java提供的4个访问控制符publicprivateprotecteddefault(不加任何访问控制符的访问控制级别)

Java访问控制级别由小到大如下图所示
Java访问控制符级别由小到大
图5.14中的4个访问控制级别中的default并没有对应的访问控制符,当不使用任何访问控制符来修饰类或类成员时,系统默认使用该访问控制级别。

访问控制符详解

  • private:当前类访问权限
    • 如果类里的一个成员(包括成员变量、方法和构造器等)使用private访问控制符来修饰,则这个成员只能在当前类的内部被访问。
    • private访问控制符比较适合用来修饰成员变量,使用private来修饰成员变量可以把成员变量隐藏在该类的内部。
  • default:包访问权限
    • 如果类里的一个成员(包括成员变量、方法和构造器等)或者一个外部类不使用任何访问控制符修饰,就称它是包访问权限的,default访问控制的成员或外部类可以被相同包下的其他类访问。
  • protected:子类访问权限
    • 如果一个成员(包括成员变量、方法和构造器等)使用protected访问控制符修饰,那么这个成员既可以被同一个包中的其他类访问,也可以被不同包中的子类访问。在通常情况下,如果使用protected来修饰一个方法,通常是希望其子类来重写这个方法。
  • public:公共访问权限
    • 如果一个成员(包括成员变量、方法和构造器等)或者一个外部类使用public访问控制符修饰。那么这个成员或外部类就可以被所有类访问,不管访问类和被访问类是否处于同一个包中,是否具有父子继承关系。

访问控制级别关系如下表所示
访问控制级别表

访问修饰符对于局部变量

访问控制符用于控制一个类的成员是否可以被其他类访问,对于局部变量而言,其作用域就是它所在的方法,所以局部变量不能使用访问修饰符来控制访问权限。

访问修饰符对于外部类

  • 外部类只能使用两种访问修饰符:publicdefault(默认),不能使用privateprotected来修饰。因为外部类没有处于任何类的内部,也就没有其所在类的内部、所在类的子类两个范围,因此privateprotected访问控制符对外部类没有意义
  • 使用public修饰符来修饰外部类:该外部类可以被所有类使用,如声明变量,创建实例。
  • 使用default(默认)修饰符来修饰外部类:该外部类只能被同一个包中的其他类使用。

知识点

  • 如果一个Java源文件里定义的所有类都没有使用public修饰,则这个Java源文件的文件名可以是一切合法的文件名;
  • 但如果Java源文件里定义了一个public修饰的类,则这个源文件的文件名必须与public修饰的类的类名相同。

封装代码实例

通过使用合理的访问控制符来定义一个Person2类,这个Person2类实现了良好的封装。

Person2.java

package com.abc.part_four;

/**
 * 如果一个Java类的每个实例变量都被使用private修饰,并为每个实例变量都提供了public修饰setter和getter方法,那么这个类就是一个符合JavaBean规范的类
 * JavaBean总是一个封装良好的类。
 */
public class Person2 {
    private String name;
    private int age;

    public void setName(String name) {
        if (name.length() >= 2 && name.length() <= 6) {
            this.name = name;
        } else {
            System.out.println("设置的名字长度过长或过短!");
        }
    }

    public String getName() {
        return this.name;
    }

    public void setAge(int age) {
        if (age >= 0 && age <= 100) {
            this.age = age;
        } else {
            System.out.println("设置的age不能小于0岁或大于100岁!");
        }
    }

    public int getAge() {
//        return this.age;
        return age;//this关键字可以省略

    }
}

定义了上面的Person2类之后,该类的nameage两个成员变量只有在Person2类内才可以操作和访问,Person2类之外只能通过各自对应的settergetter方法来操作和访问它们。

Person2Test.java

package com.abc.part_four;

public class Person2Test {

    public static void main(String[] args) {
        Person2 person2 = new Person2();
        person2.setName("小花花");
        person2.setAge(21);
        System.out.println("我叫" + person2.getName() + ", 今年" + person2.getAge() + "岁了!");//我叫小花花, 今年21岁了!
    }

}

例如,某个类里包含了一个名为abc的实例变量,则其对应的settergetter方法名应为setAbc()getAbc()(即将原实例变量名的首字母大写,并在前面分别增加setget动词,就变成settergetter方法名)。

Java 类里实例变量的settergetter方法的意义

  • 如果一个Java类的每个实例变量都被使用private修饰,并为每个实例变量都提供了public修饰settergetter方法,那么这个类就是一个符合JavaBean规范的类。JavaBean总是一个封装良好的类。

访问控制符使用的基本原则

  1. 类里的绝大部分成员变量都应该使用private修饰,只有一些static修饰的、类似全局变量的成员变量,才可能考虑使用public修饰。除此之外,有些方法只用于辅助实现该类的其他方法,这些方法被称为工具方法,工具方法也应该使用private修饰。
  2. 如果某个类主要用做其他类的父类,该类里包含的大部分方法可能仅希望被其子类重写,而不想被外界直接调用,则应该使用protected修饰这些方法。
  3. 希望暴露出来给其他类自由调用的方法应该使用public修饰。因此,类的构造器通过使用public修饰,从而允许在其他地方创建该类的实例。因为外部类通常都希望被其他类自由使用,所以大部分外部类都使用public修饰。

总结

  • 一个类常常就是一个小的模块,应该只让这个模块公开必须让外界知道的内容,而隐藏其他一切内容。
  • 进行程序设计时,应尽量避免一个模块直接操作和访问另一个模块的数据,模块设计追求高内聚(尽可能把模块的内部数据、功能实现细节隐藏在模块内部独立完成,不允许外部直接干预)、低耦合(仅暴露少量的方法给外部使用)。
  • 正如日常常见的内存条,内存条里的数据及其实现细节被完全隐藏在内存条里面,外部设备(如主机板)只能通过内存条的全手指(提供一些方法供外部调用)来和内存条进行交互。