1 封装的含义
- 一层含义是把对象的属性和行为看成为一个密不可分的整体,将这两者封装在一个不可分割的独立单位(即对象)中。
- 另一层含义指信息隐藏,把不需要让外界知道的信息隐藏起来,有些对象的属性及行为允许外界用户知道或使用,但不允许更改,而另一些属性和行为则不允许外界知晓或只允许使用对象的功能,而尽可能隐藏对象的功能实现细节。
2 信息隐藏的必要性
成员变量封装加上 private,对外提供公开的用于设置对象属性的 public 方法,并在方法中加上逻辑判断,过滤掉非法数据,从而:
- 隐藏了类的具体实现
- 操作简单
- 提高对象数据的安全性
- 减少了冗余代码,数据校验等写在方法里,可以复用
3 访问控制修饰符
访问控制分四种类别:
- 公开
public对外公开。 - 受保护
protected向子类以及同一个包中的类公开。 - 默认 向同一个包中的类公开。
- 私有
private只有类本身可以访问,不对外公开
| 修饰符 | 同一个类 | 同一个包 | 子类 | 整体 |
|---|---|---|---|---|
private |
yeah | |||
default |
yeah | yeah | ||
protected |
yeah | yeah | yeah | |
public |
yeah | yeah | yeah | yeah |
3.1 protected
1 包内可见
2 子类可见(子类和父类在同一个包:通过自己访问、通过父类访问。在不同包:仅可通过自己访问。)
若子类与父类不在同一包中,那么在子类中
- 子类实例可以访问其从父类继承而来的
protected方法 - 不能访问父类实例的
protected方法 - 不能通过另一个子类引用访问共同基类的
protected方法。
// 父类
package com.protectedaccess.parentpackage;
public class Parent{
protected String protect = "protect field";
protected void getMessages(){
System.out.println("i am parent");
}
}
// 不同包下,可以访问其从父类继承而来的`protected`方法
package com.protectedaccess.parentpackage.sonpackage1;
import com.protectedaccess.parentpackage.Parent;
public class son1 extends Parent {
public static void main(String[] args) {
Son1 son1 = new Son1();
son1.getMessage(); // 输出:i am parent
}
private void message() {
getMessage(); // 如果子类重写了该方法,则输出重写方法中的内容
super.getMessage(); // 输出父类该方法中的内容
}
}
// 不同包下,不能访问**父类实例**的`protected`方法
package com.protectedaccess.parentpackage.sonpackage1;
import com.protectedaccess.parentpackage.Parent;
public class son1 extends Parent {
public static void main(String[] args) {
Parent parent1 = new Parent();
// parent1.getMessage(); // 错误
Parent parent2 = new Parent();
// parent2.getMessage(); // 错误
}
}
// 不同包下,不能通过**另一个子类引用**访问共同基类的`protected`方法。
package com.protectedaccess.parentpackage.sonpackage2;
import com.protectedaccess.parentpackage.Parent;
public class son2 extends Parent {
}
package com.protectedaccess.parentpackage.sonpackage1;
import com.protectedaccess.parentpackage.Parent;
import com.protectedaccess.parentpackage.sonpackage2.Son2;
public class son1 extends Parent {
public static void main(String[] args) {
Son2 son2 = new Son2();
// son2.getMessage(); // 错误
}
}
3 protected 的 static 成员对所有子类可见。
对于protected修饰的静态变量,无论是否同一个包,在子类中均可直接访问;在不同包的非子类中则不可访问。
// 父类
package com.protectedaccess.parentpackage;
public class Parent{
protected String protect = "protect field";
protected static void getMessages(){
System.out.println("i am parent");
}
}
// 无论是否同一个包,在子类中均可直接访问
package com.protectedaccess.parentpackage.sonpackage1;
import com.protectedaccess.parentpackage.Parent;
public class son3 extends Parent {
public static void main(String[] args) {
Parent.getMessage(); // 输出:i am parent
}
}
// 在不同包的非子类中则不可访问
package com.protectedaccess.parentpackage.sonpackage1;
import com.protectedaccess.parentpackage.Parent;
public class son4 {
public static void main(String[] args) {
// Parent.getMessage(); // 错误
}
}
3.2 类的访问级别
Java中,类可以是public和默认访问级别
public级别的类可以被所有其他类访问- 默认级别的类只能被同一个包中的类访问
package mpack1;
class Base1{}
public class Base2{}
package mpack2;
import mpack1.*;
class Sub1 extends Base1{} // 非法
class Sub2 extends Base2{}
public class Guset{
public void test(){
Base1 b1 = new Sub1(); // 非法
Base2 b2 = new Sub2();
}
}
4 案例分析
4.1 单例模式
- 一个类在内存中只有一个实例(对象)存在,该类一般没有属性
- 无法继承,所以无法扩展,无法更改它的实现
4.1.1 “饿汉式”单实例模式
- 不Lazy初始化
- 多线程安全
- 优点:没有加锁,执行效率会提高。
- 缺点:类加载时就初始化,容易产生垃圾对象,浪费内存。
- 特点:它基于classloder机制避免了多线程的同步问题
public class Singleton {
// 静态的。保留自身的引用,类加载时就初始化
private static Singleton test = new Singleton();
// 必须是私有的构造函数
private Singleton() {}
// 公共的静态方法
public static Singleton getInstance() {
return test;
}
}
4.1.2 “懒汉式”单实例模式
- Lazy初始化
- 不多线程安全
- 描述:最大的问题是不支持多线程,因为没有加锁synchronized,所以严格意义上不算单例模式
public class Singleton {
// 静态的。保留自身的引用
private static Singleton test = null;
// 必须是私有的构造函数
private Singleton() {}
// 公共的静态方法
public static Singleton getInstance() {
if(test == null) {
test = new Singleton();
}
return test;
}
}
// 修改上述代码避免多线程中的安全问题
public class Singleton {
private static Singleton test = null;
private Singleton() {}
public static Singleton getInstance() {
if(test == null) {
synchronized(Single.class){
if(test == null){
test = new Singleton();
}
}
}
return test;
}
}