爲什麼需要封裝,封裝的作用和含義?
面向對象共有三個特徵:封裝,繼承,多態。接下來我們具體學習封裝。我們程序設計追求“高內聚,低耦合”。
- 高內聚 :類的內部數據操作細節自己完成,不允許外部干涉;
- 低耦合 :僅對外暴露少量的方法用於使用。
隱藏對象內部的複雜性,只對外公開簡單的接口。便於外界調用,從而提高系統的可擴展性、可維護性。通俗的說,把該隱藏的隱藏起來,該暴露 的暴露出來。這就是封裝性的設計思想。
原則
- 將屬性隱藏起來,若需要訪問某個屬性,提供公共方法對其訪問。
封裝表現:
- 方法就是一個最基本封裝體。
- 類其實也是一個封裝體。
封裝的基本步驟
- 使用 private 關鍵字來修飾成員變量。
- 對需要訪問的成員變量,提供對應的一對 getXxx 方法 、 setXxx 方法。
Java中通過將數據聲明爲私有的(private),再提供公共的(public) 方法:getXxx()和setXxx()實現對該屬性的操作,以實現下述目的:
- 隱藏一個類中不需要對外提供的實現細節;
- 使用者只能通過事先定製好的方法來訪問數據,可以方便地加入控制邏輯, 限制對屬性的不合理操作;
- 便於修改,增強代碼的可維護性;
代碼示例
package demo04;
/*
* 描述現實生活中的人的事物
* 屬性: 姓名 年齡
* 功能: 說話
*
* 出現安全問題: age問題,可能出現賦值爲負數的情況
* 負數不會導致程序問題,違反生活中的真實情況
*
* 提高安全問題: 讓外面的類,不允許直接調用我的成員變量
* 新的關鍵字 private 私有 屬於成員修飾符,不能修飾局部變量
* 被private修飾的成員,只能在自己的本類中被使用
*
* 對私有變量,提供公共的訪問方式: 方法
*/
public class Person {
//人的姓名,成員變量
String name;
//人的年齡,成員變量
private int age ;
//變量age被私有,提供方法,讓外面的類使用
//定義方法,對age變量進行賦值,方法名字,必須set開頭
public void setAge(int a){
//對變量參數a進行範圍的限制
if(a<0 || a > 200){
//如果a超過範圍,手動將age賦值爲20
age = 20;
}else{
//如果a沒有超過範圍,直接對age賦值
age = a;
}
}
//定義方法,對變量age獲取值使用,方法名字get
public int getAge(){
return age;
}
//定義人的說話功能,方法中,要求說出自己的姓名和年齡
public void speak(){
System.out.println(name+"..."+age);
}
}
定義測試類
package demo04;
/*
* 定義好的Person類進行測試
* 創建對象,對象調用屬性和方法
*/
public class PersonTest {
public static void main(String[] args) {
//創建Person類的對象 new
Person p = new Person();
//對成員變量賦值
//p.age = -200;
//對成員變量age賦值,只能調用Set方法賦值
p.setAge(50);
p.name = "張三";
//調用類中方法
p.speak();
//輸出成員變量age值,必須調用get方法
System.out.println(p.getAge());
}
}
四種訪問權限修飾符
我們發現 setXxx 方法中的形參名字並不符合見名知意的規定,那麼如果修改與成員變量名一致,是否就見名知意了呢?經過修改和測試,我們發現新的問題,成員變量賦值失敗了。也就是說,在修改了 setXxx() 的形參變量名後,方法並沒有給成員變量賦值!這是由於形參變量名與成員變量名重名,導致成員變量名被隱藏,方法中的變量名,無法訪問到成員變量,從而賦值失敗。所以,我們只能使用this關鍵字,來解決這個重名問題。
關鍵字this是什麼?
在Java中,它的作用和其詞義很接近。
- 它在方法內部使用,即這個方法所屬對象的引用;
- 它在構造器內部使用,表示該構造器正在初始化的對象。
- this 可以調用類的屬性、方法和構造器
什麼時候使用this關鍵字呢?
- 當在方法內需要用到調用該方法的對象時,就用this。 具體的:我們可以用this來區分屬性和局部變量。
我們來改造一下什麼的Person類
package demo05;
/*
* 類描述人:
* 屬性: 姓名和年齡
* 方法: 說話
*
* 私有化所有的屬性 (成員變量) ,必須寫對應的get/set方法
* 凡是自定義的類,自定義成員變量,應該私有化,提供get/set
*
* this關鍵字:
* 區分成員變量和局部變量同名情況
* 方法中,方位成員變量,寫this.
*/
public class Person {
private String name;
private int age;
// set方法,變量name,age賦值
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
// get方法,變量name,age獲取值
public int getAge() {
return age;
}
public String getName() {
return name;
}
public void speak() {
String name = "哈哈";
int age = 16;
System.out.println("人在說話 " + this.name + "..." + this.age);
}
}
定義測試類
package demo05;
public class PersonTest {
public static void main(String[] args) {
Person p = new Person();
//調用set方法,對成員變量賦值
p.setAge(18);
p.setName("旺財");
p.speak();
//調用get方法,獲取成員變量的值
System.out.println(p.getName());
System.out.println(p.getAge());
}
}
經過測試變量重名問題就解決了。
構造方法
我們對封裝已經有了基本的瞭解,接下來我們來看一個新的問題,依然以Person爲例,由於Person中的屬性都被private了,外界無法直接訪問屬性,必須對外提供相應的set和get方法。當創建人對象的時候,人對象一創建就要明確其姓名和年齡,那該怎麼做呢?使用構造方法,那什麼是構造方法呢?從字面上理解即爲構建創造時用的方法,即就是對象創建時要執行的方法。既然是對象創建時要執行的方法,那麼只要在new對象時,知道其執行的構造方法是什麼,就可以在執行這個方法的時候給對象進行屬性賦值。
構造方法的作用:
當一個對象被創建時候,構造方法用來初始化該對象,給對象的成員變量賦初始值。
定義格式
構造器的特徵
- 它具有與類相同的名稱
- 它不聲明返回值類型。(與聲明爲void不同)
- 不能被static、final、synchronized、abstract、native修飾,不能有 return語句返回值
注 意:
- Java語言中,每個類都至少有一個構造器
- 默認構造器的修飾符與所屬類的修飾符一致
- 一旦顯式定義了構造器,則系統不再提供默認構造器
- 一個類可以創建多個重載的構造器
- 父類的構造器不可被子類繼承
代碼示例
/*
* 自定義的Person類.成員變量,name age
* 要求在 new Person的同時,就指定好name,age的值
* 實現功能,利用方法去實現, 構造方法,構造器 Constructor
* 作用: 在new 的同時對成員變量賦值, 給對象的屬性初始化賦值 new Person 對屬性 name,age賦值
*
* 構造方法的定義格式
* 權限 方法名(參數列表){
* }
* 方法的名字,必須和類的名字完全一致
* 構造方法不允許寫返回值類型 , void 也不能寫
*
* 構造方法在什麼時候,運行呢, 在new 的時候,自動執行
* 只運行一次,僅此而已
*
* 每個class必須擁有構造方法,構造方法不寫也有
* 編譯的時候,javac, 會自動檢查類中是否有構造方法
* 如果有,就這樣的
* 如果沒有,編譯器就會自動添加一個構造方法
* 編譯器自動添加的構造方法: public Person(){}
* 自己手寫了構造方法,編譯的時候,不會自動添加構造方法!
*/
public class Person {
private String name;
private int age;
//定義出Person類的構造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
//System.out.println("我是一個空參數構造方法");
}
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;
}
}
定義測試類
package demo01;
/*
* new 對象的時候,就是在調用對象的構造方法
* new Person(); 調用的是類中的空參數構造方法
* new Person("張三",20); 調用類中的有參數構造方法
*/
public class Test {
public static void main(String[] args) {
Person p = new Person("張三", 20);
//對象p 調用方法getName,getAge
System.out.println(p.getName());
System.out.println(p.getAge());
}
}
構造方法和一般方法區別
- 構造方法在對象創建時就執行了,而且只執行一次。
- 一般方法是在對象創建後,需要使用時才被對象調用,並可以被多次調用。
this調用構造方法
構造方法之間的調用,可以通過this關鍵字來完成。格式:this(參數列表);
總結一下this的用法
- this可以在普通方法之間相互調用
- this.的方式,區分局部變量和成員變量同名情況
- this在構造方法之間的調用
代碼示例
package demo02;
public class Person {
private String name;
private int age;
public Person(){
//調用了有參數的構造方法
//參數李四,20傳遞給了變量name,age
//this可以在構造方法之間進行調用
this("李四",20);
}
/*
* 構造方法,傳遞String,int
* 在創建對象的同時爲成員變量賦值
*/
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
//this.的方式,區分局部變量和成員變量同名情況
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void method1(){
System.out.println("我是方法1");
//this在普通方法之間的調用
this.method2();
}
public void method2(){
System.out.println("我是方法2");
}
}
注意:
- 可以在類的構造器中使用"this(形參列表)"的方式,調用本類中重載的其 他的構造器!
- 明確:構造器中不能通過"this(形參列表)"的方式調用自身構造器
- 如果一個類中聲明瞭n個構造器,則最多有 n - 1個構造器中使用了 "this(形參列表)"
- "this(形參列表)"必須聲明在類的構造器的首行!
- 在類的一個構造器中,最多隻能聲明一個"this(形參列表)"
總結:屬性賦值過程
截止到目前,我們講到了很多位置都可以對類的屬性賦值。現總結這幾個位 置,並指明賦值的先後順序。
- ① 默認初始化
- ② 顯式初始化
- ③ 構造器中初始化
- ④ 通過“對象.屬性“或“對象.方法”的方式賦值