Java基礎知識篇02——封裝

白夜的白發表於2024-04-12

大家好,我是白夜,今天給大家聊聊物件導向的三大特徵——封裝

一、包(package)

1.1、包的引入

先來看看我們之前寫的程式碼結構

圖片01

以上程式碼存在的問題

  1. 所有類寫在一個目錄下面,非常難管理,因為以後專案不可能只有這麼幾個類,當類數量很大的時候,就不容易管理了。
  2. 不能寫同名但是不同需求的類。

為了解決這些問題,我們需要把不同的東西放到專門的裡面,進行分類管理。也就是透過資料夾進行管理,Java 中的資料夾就是

1.2、定義包

包命名規範:一般是公司域名反寫.專案名.模組名字.子模組名;

要求:包名是全英文小寫。

例如 : itsource.cn 域名

package cn.itsource.erp.oa.domain; 自動化辦公

package cn.itsource.erp.sa.entity; 系統管理

package cn.itsource.packagedemo;// 宣告包

/**
 * 包package
 */
public class PackageDemo {

	public static void main(String[] args) {
		new java.util.Date();
		new java.sql.Date(1L);
		System.out.println();
	}
}

1.3、匯入包

當我們需要使用別人或者 JDK 中的類的時候,就需要告知 JVM 從什麼地方去載入這個類,這個過程就是導包

其實本質是匯入包中的類。

匯入包的語法 :

import 包名.子包名.類名

注意:

  1. 匯入包的程式碼應該在宣告包(就是該類所在的包)的後面,宣告類的前面。

  2. import java.util; 表示匯入本包中所有會使用到的 util 包中的類,只會匯入 util 包下面直接的型別,不包括util 包的子包中的型別。

  3. java.lang是核心包,下面的直接型別是自動匯入的;

    例如:String、System類,lang 包的子包中的型別不會自動匯入,需要手動匯入。

  4. 在一個類中會用到同名不同包的時候必須使用全限定路徑

    例如:同時使用 java.util.Date 和 java.sql.Date

package cn.itsource.packagedemo;//宣告包
import java.util.*;// 只會匯入util包下使用到的類

/**
 * 包package
 */
public class PackageDemo {

	public static void main(String[] args) {// String是java.lang核心包下的,程式會自動匯入;
		// 使用Arrays類中的toString方法
//		String str = java.util.Arrays.toString(new int[]{1, 2, 3});// 每次都寫全限定類名比較麻煩
		
		// 使用Arrays類中的sort方法排序
//		java.util.Arrays.sort(new int[]{3, 2, 1});
		
		// 用自動導包可以簡化上面程式碼
		String str = Arrays.toString(new int[]{1, 2, 3});// 每次都寫全限定類名比較麻煩
		System.out.println(str);
		Arrays.sort(new int[]{3, 2, 1});
		
		// 當使用同名不同包的多個類怎麼用? 必須用全限定類名
		new java.util.Date();// java.util包下
		new java.sql.Date(1L);// java.sql包下
	}
}

1.4、Java中常用的包(瞭解)

java/ javax(java 增強包)

java.lang (java 的核心包--基本包) 幫我們自動導包的

java.util(java 的工具包 --集合框架 ArrayList LinkedList)

java.io(java IO包input-output 讀寫檔案)

java.net (網路程式設計)

java.awt/javax.swing(java的圖形化介面)

java.math 數學相關的包

java.sql 資料庫相關的包

java.text 是文字格式化相關的包

java.time 時間相關的包

二、封裝

2.1、為什麼要封裝

來看下面一個案例

package cn.itsource.potting1;

/**
 *	賬戶Account類
 *	封裝引入
 */
public class Account {
	/** String型別成員變數姓名name */
	protected String name;
	
	/** String型別成員變數密碼pwd */
	String pwd;
	
	/** double型別成員變數餘額money */
	double money;
	
	/** boolean型別成員變數是否是vip使用者 */
	boolean vip;
	
	/**
	 * Account類無參構造
	 */
	public Account() {}
	
	/**
	 * 有4個引數構造方法
	 * @param n
	 * @param p
	 * @param m
	 * @param v
	 */
	public Account(String n, String p, double m, boolean v) {
		name = n;// 將區域性變數n賦值給成員變數name
		pwd = p;// 將區域性變數p賦值給成員變數pwd
		money = m;// 將區域性變數m賦值給成員變數money
		vip = v;// 將區域性變數v賦值給成員變數vip
	}
	
	/**
	 * 獲取當前成員變數money的值
	 * @return
	 */
	public double getMoney() {
		return money;
	}
}

測試類:
package cn.itsource.potting1;

/**
 *	Account測試類
 *	封裝的引入
 */
public class AccountTest {

	public static void main(String[] args) {
		// 建立Account物件,呼叫無參構造
		Account acc1 = new Account();
		// 給acc1成員變數賦值
		acc1.name = "某菲";
		acc1.pwd = "6969";
		acc1.money = 1.0;
		acc1.vip = false;
		// 列印acc1成員變數的值
		System.out.println(acc1.name);
		System.out.println(acc1.pwd);
		System.out.println(acc1.money);
		System.out.println(acc1.vip);
		// acc1呼叫getMoney方法
		double money = acc1.getMoney();// 因為getMoney方法是非static修飾,並且有返回值,所以用acc1呼叫,用double變數接收
		System.out.println(money);
		
		// 需求:當money達到50000.00的時候,會將vip升級為true。現在,能不能沒有達到5萬就不能升級。
		acc1.vip = true;// 這裡沒有經過任何許可權判斷,直接修改了值,不安全。所以,用封裝解決這個問題。
		System.out.println(acc1.vip);// true		
	}
}

而在 Java 中是透過封裝來完成保護內部成員的目的

2.2、封裝的作用

封裝是為了保護內部資料的安全:

  1. 不希望在外部類中隨意訪問類中的成員變數
  2. 達到許可權要求的才能訪問。
  3. 只是獲取資料的時候。例如:單例模式。
  4. 我們在程式設計的時候就追求“高內聚、低耦合”:
  • 高內聚:類的內部資料操作細節自己完成,不允許外部干涉;
  • 低耦合:僅對外部暴露少量的方法用於使用。

2.3、封裝的使用

  1. 如何控制程式中的訪問 ?

    透過給類中的成員(欄位,方法,構造方法)新增訪問許可權修飾符來實現封裝(訪問控制)。

  2. 什麼是訪問許可權:簡單的認為訪問許可權就是不同級別的人能夠幹不同級別的事,不同級別的人能看到的頁面是不同的。

    例子:比如做一個系統,不同人登入進去的訪問許可權不一樣;

  3. 訪問許可權修飾符:

    public 最大許可權,被其修飾的成員,在任意目錄下,都可以訪問到 (所有類)

    protected 在同包類和子類中都可以訪問

    預設不寫 只能在同包類中訪問

    private 只能在當前類中訪問

    圖片02

  4. 封裝的步驟:

    1. 將類的屬性設定為 private,這樣外部就不能直接訪問。
    2. 提供公共的 getter 方法用於獲取屬性值,通常是 public 的。
    3. 提供公共的 setter 方法用於設定屬性值,也是 public 的,並可以在其中新增邏輯檢查。
    4. 提供一個無參構造,有參構造根據需求確定是否要寫。
    5. 該類用 public 修飾。

    圖片03

    public class Account {// 5. 該類用 public 修飾
    	//1. 私有化成員變數(用 private 修飾成員變數)
    	private String name;
    	
    	private String pwd;
    	
    	// 4. 提供一個無參構造
    	public Account() {}
    	
    	// 有參構造根據實際需求,決定是否要寫
    	public Account(String n, String p) {
    		name = n;// 將區域性變數賦值給成員變數
    		pwd = p;	
    	}
    	
    	/**
    	 *  2. 為每一個成員變數提供合理的 public void setName(String n) 方法
    	 * 	給成員變數name賦值方法
    	 * @param n
    	 */
    	public void setName(String n) {
    		// 可以寫判斷條件
    		name = n;// 將區域性變數n賦值給成員變數 name
    	}
    		
    	public void setPwd(String p) { pwd = p; }
    	
    	/**
    	 * 2. 為每一個成員變數提供合理的	public String getName() 方法
    	 * 獲取成員變數 name 的值
    	 * @return
    	 */
    	public String getName() {
    		// 可以寫判斷條件
    		return name;// 直接返回成員變數 name
    	}
    	
    	public String getPwd() { return pwd; }	
    }
    
    /**
     *	Account 測試類
     *	封裝的引入
     */
    public class AccountTest {
    	public static void main(String[] args) {
    		// 建立Account物件,呼叫無參構造
    		// Account acc1 = new Account();
    		// 給 acc1 成員變數賦值
    		/*
    		 * 因為 private 修飾成員變數後,不能在其他類中使用了。
    		acc1.name = "小七";
    		acc1.pwd = "6969";
    		*/
    		// 使用有參構造賦值
    		Account acc1 = new Account("小七", "6969");
    		
    		// 列印acc1成員變數的值
    //		System.out.println(acc1.name);// 因為 private 修飾成員變數後,不能在其他類中使用了。
    //		System.out.println(acc1.pwd);
    		
    		// 因為封裝了,所以只能呼叫:public 返回值 getXxx() 獲取成員變數的值
    		String name = acc1.getName();// acc1 呼叫 getName 方法獲取成員變數 name 的值
    		String pwd = acc1.getPwd();// acc1 呼叫 getPwd 方法獲取成員變數 pwd 的值
    		// 列印上面獲取的值
    		System.out.println(name);
    		System.out.println(pwd);
    		
    		// 當需要修改建立好的物件的成員變數值,用 set 方法賦值
    		acc1.name("小八");	
    		String name2 = acc1.getName();
    		System.out.println(money2);		
    	}
    }
    

    2.4、 封裝的注意事項

    1. 不是隻有 private 才叫封裝,private 只是最大限度的封裝而已。
    2. get 和 set 方法都是隻能獲取或者賦值一個成員變數,**不能 **set(String n, double m, boolean v) 賦值3個成員變數。
    3. 單一職能原則:功能最小化,不要想著一個方法寫完所有的功能,因為程式碼複用率高。

    2.5、封裝的好處

    • 增強安全性:隱藏資料和方法的實現細節,防止外部直接訪問,降低因錯誤使用而導致的風險。
    • 提高複用性:封裝後的程式碼更容易被其他程式或模組複用,因為它們的實現細節對於使用者來說是透明的。
    • 便於維護:封裝使得程式碼修改時影響範圍侷限,提高了程式碼的可維護性。

三、this關鍵字

this 的概念:

​ this 指代當前物件,即,哪個物件呼叫就指代哪個物件

3.1、this 的使用

1. 解決區域性變數和成員變數的二義性【set 方法和有參構造中形參跟成員變數同名不同義】

public Account(String name,String pwd) {
	// 要解決二義性問題,就需要用到this,加上this,就會直接從成員變數位置找name
	this.name = name;
	this.pwd = pwd;
}

1. 本類中構造方法之間的相互呼叫,但是必須是構造方法內的第一句。

package cn.itsource.task.topic02;

public class People {
    private String name;
    private boolean sex;
    private int age;
    private int weight;
    public People(){

    }
    public People(String name, boolean sex, int age, int weight){
        this(name, sex, age);
        this.weight = weight;
    }
    public People(String name, boolean sex, int age){
        this(name, sex);
        this.age = age;
    }
    public People(String name, boolean sex){
        this(name);
        this.sex = sex;
    }
    public People(String name){
        this.name = name;
    }

    public String getName() { return name; }

    public void setName(String name) { this.name = name; }

    public boolean isSex() { return sex; }

    public void setSex(boolean sex) { this.sex = sex; }

    public int getAge() { return age; }

    public void setAge(int age) { this.age = age; }

    public int getWeight() { return weight; }

    public void setWeight(int weight) { this.weight = weight; }
}

四、構造方法和set方法的區別

  • 構造方法可以為多個成員變數賦值,set只能對一個進行賦值;
  • 構造方法由JVM自動呼叫,set需要手動呼叫;
  • 構造方法針對同一個物件只能呼叫一次,set方法針對同一個物件可以呼叫多次;
  • 構造方法用於給變數賦值,set可以給變數賦值或者修改值;
  • 構造可以使程式碼更加簡潔,set方法靈活性更高。

相關文章