大家好,我是白夜,今天給大家聊聊物件導向的三大特徵——封裝
一、包(package)
1.1、包的引入
先來看看我們之前寫的程式碼結構
以上程式碼存在的問題
- 所有類寫在一個目錄下面,非常難管理,因為以後專案不可能只有這麼幾個類,當類數量很大的時候,就不容易管理了。
- 不能寫同名但是不同需求的類。
為了解決這些問題,我們需要把不同的東西放到專門的裡面,進行分類管理。也就是透過資料夾進行管理,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 包名.子包名.類名
注意:
-
匯入包的程式碼應該在宣告包(就是該類所在的包)的後面,宣告類的前面。
-
import java.util; 表示匯入本包中所有會使用到的 util 包中的類,只會匯入 util 包下面直接的型別,不包括util 包的子包中的型別。
-
java.lang是核心包,下面的直接型別是自動匯入的;
例如:String、System類,lang 包的子包中的型別不會自動匯入,需要手動匯入。
-
在一個類中會用到同名不同包的時候必須使用全限定路徑
例如:同時使用 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、封裝的作用
封裝是為了保護內部資料的安全:
- 不希望在外部類中隨意訪問類中的成員變數
- 達到許可權要求的才能訪問。
- 只是獲取資料的時候。例如:單例模式。
- 我們在程式設計的時候就追求“高內聚、低耦合”:
- 高內聚:類的內部資料操作細節自己完成,不允許外部干涉;
- 低耦合:僅對外部暴露少量的方法用於使用。
2.3、封裝的使用
-
如何控制程式中的訪問 ?
透過給類中的成員(欄位,方法,構造方法)新增訪問許可權修飾符來實現封裝(訪問控制)。
-
什麼是訪問許可權:簡單的認為訪問許可權就是不同級別的人能夠幹不同級別的事,不同級別的人能看到的頁面是不同的。
例子:比如做一個系統,不同人登入進去的訪問許可權不一樣;
-
訪問許可權修飾符:
public 最大許可權,被其修飾的成員,在任意目錄下,都可以訪問到 (所有類)
protected 在同包類和子類中都可以訪問
預設不寫 只能在同包類中訪問
private 只能在當前類中訪問
-
封裝的步驟:
- 將類的屬性設定為 private,這樣外部就不能直接訪問。
- 提供公共的 getter 方法用於獲取屬性值,通常是 public 的。
- 提供公共的 setter 方法用於設定屬性值,也是 public 的,並可以在其中新增邏輯檢查。
- 提供一個無參構造,有參構造根據需求確定是否要寫。
- 該類用 public 修飾。
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、 封裝的注意事項
- 不是隻有 private 才叫封裝,private 只是最大限度的封裝而已。
- get 和 set 方法都是隻能獲取或者賦值一個成員變數,**不能 **set(String n, double m, boolean v) 賦值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方法靈活性更高。