目录
封装(encapsulation)
封装的作用和含义
封装的实现—使用访问控制符
public 访问权限修饰符:
protected 访问权限修饰符:
默认访问权限修饰符
private 访问权限修饰符
封装的一些处理
封装(encapsulation)
封装是面向对象三大特征之一。对于程序合理的封装让外部调用更加方便,更加利于写 作。同时,对于实现者来说也更加容易修正和改版代码。
封装的作用和含义
我要看电视,只需要按一下开关和换台就可以了。有必要了解电视机内部的结构吗?有 必要碰碰显像管吗?制造厂家为了方便我们使用电视,把复杂的内部细节全部封装起来,只 给我们暴露简单的接口,比如:电源开关。具体内部是怎么实现的,我们不需要操心。
需要让用户知道的才暴露出来,不需要让用户知道的全部隐藏起来,这就是封装。说的专业一点,封装就是把对象的属性和操作结合为一个独立的整体,并尽可能隐藏对象的内部 实现细节。
我们程序设计要追求“高内聚,低耦合”。 高内聚就是类的内部数据操作细节自己完成, 不允许外部干涉;低耦合是仅暴露少量的方法给外部使用,尽量方便外部调用。
编程中封装的具体优点:
- 提高代码的安全性。
- 提高代码的复用性。
- “高内聚”:封装细节,便于修改内部代码,ᨀ高可维护性。
- “低耦合”:简化外部调用,便于调用者使用,便于扩展和协作。
未进行封装:
class Person {
String name;
int age;
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public class Test {
public static void main(String[ ] args) {
Person p = new Person();
p.name = "李斯";
p.age = -23;//年龄可以通过这种方式随意赋值,没有任何限制
System.out.println(p);
}
}
我们都知道,年龄不可能是负数,也不可能超过 130 岁,但是如果没有使用封装的话, 便可以给年龄赋值成任意的整数,这显然不符合我们的正常逻辑思维。
再比如说,如果哪天我们需要将 Person 类中的 age 属性修改为 String 类型的,你会怎么 办?你只有一处使用了这个类的话那还比较幸运,但如果你有几十处甚至上百处都用到了, 那你岂不是要改到崩溃。而封装恰恰能解决这样的问题。如果使用封装,我们只需要稍微修 改下 Person 类的 setAge()方法即可,而无需修改使用了该类的客户代码。
封装的实现—使用访问控制符
Java 是使用“访问控制符”来控制哪些细节需要封装,哪些细节需要暴露的。 Java 中 4 种“访问控制符”分别为 private、default、protected、public,它们说明了面向对象的封装性, 所以我们要利用它们尽可能的让访问权限降到最低,从而ᨀ高安全性。
修饰符 | 同一个类 | 同一个包中 | 子类 | 所有类 |
private | √ | |||
default | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
- private 表示私有,只有自己类能访问
- . default 表示没有修饰符修饰,只有同一个包的类能访问
- protected 表示可以被同一个包的类以及其他包中的子类访问
- public 表示可以被该项目的所有包中的所有类访问
下面做进一步说明 Java 中 4 种访问权限修饰符的区别:首先我们创建 4 个类:Person 类、Student 类、Animal 类和 Computer 类,分别比较本类、本包、子类、其他包的区别。
public 访问权限修饰符:
访问权限—本类中访问 public 属性
public 访问权限—本包中访问 public 属性
public 访问权限—不同包中的子类访问 public 属性
public 访问权限—不同包中的非子类访问 public 属性
public 修饰符的访问权限为:该项目的所有包中的所有类。
protected 访问权限修饰符:
将 Person 类中属性改为 protected,其他类不修改。
protected 访问权限修改后的 Person 类
protected 访问权限,不同包中的非子类不能访问 protected 属性
protected 修饰符的访问权限为:同一个包中的类以及其他包中的子类。
默认访问权限修饰符
将 Person 类中属性改为默认的,其他类不修改
默认修饰符的访问权限为:同一个包中的类。
private 访问权限修饰符
将 Person 类中属性改为 private,其他类不修改。
封装的一些处理
- 一般使用 private 访问权限。
- 提供相应的 get/set 方法来访问相关属性,这些方法通常是 public 修饰的,以提供对 属性的赋值与读取操作(注意:boolean 变量的 get 方法是 is 开头!)。
- 一些只用于本类的辅助性方法可以用 private 修饰,希望其他类调用的方法用 public 修饰。
public class Person {
// 属性一般使用 private 修饰
private String name;
private int age;
private boolean flag;
// 为属性ᨀ供 public 修饰的 set/get 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isFlag() {// 注意:boolean 类型的属性 get 方法是 is 开头的
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
年龄非法赋值的问题
public class PersonTest {
public static void main(String[ ] args) {
Person p1 = new Person();
//p1.name = "小红"; //编译错误
//p1.age = -45; //编译错误
p1.setName("小红");
p1.setAge(-45);
System.out.println(p1);
Person p2 = new Person("小白", 300);
System.out.println(p2);
}
}
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
// this.age = age;//构造方法中不能直接赋值,应该调用 setAge 方法
setAge(age);
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
//在赋值之前先判断年龄是否合法
if (age > 130 || age < 0) {
this.age = 18;//不合法赋默认值 18
} else {
this.age = age;//合法才能赋值给属性 age
}
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}