Lesson4 封装

1 封装的含义

  1. 一层含义是把对象的属性和行为看成为一个密不可分的整体,将这两者封装在一个不可分割的独立单位(即对象)中。
  2. 另一层含义指信息隐藏,把不需要让外界知道的信息隐藏起来,有些对象的属性及行为允许外界用户知道或使用,但不允许更改,而另一些属性和行为则不允许外界知晓或只允许使用对象的功能,而尽可能隐藏对象的功能实现细节。

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;  
    }  
}

5 深刻理解封装(从系统角度)

Built with MDFriday ❤️