總結
本章寫了封裝、static成員以及代碼塊。
一、封裝
1.封裝的概念
封裝簡單來說就是被密封起來(不讓我們看見的東西),即被隱藏。
對于用戶來說,并不需要關心的類,所實現的細節就會被封裝(隱藏)。
猶如密封的紙盒子
2.封裝的實現方式
1.隱藏內部狀態(屬性私有化)
將類的成員變量(屬性)用private修飾,使其只能在當前內部訪問,外部無法直接訪問修改或者讀取。
2.暴露公共接口(提供訪問方法)
通過public修飾的getter(讀取)和setter(修改)方法,對外提供屬性的訪問入口,并在方法中可以添加邏輯校驗,控制數據的合法性。
實例:
定義了一個People類中含有封裝
public class People {public String name;
// 1.屬性私有化(外部無法直接訪問)private int age;/*private只能在當前類被訪問,當前類的私有屬性*/// 2.提供公共的getter方法(讀取屬性)public int getAge() {return age;}// 3.提供公共的setter方法(修改屬性可添加校驗邏輯 )public void setAge(int age) {
// 校驗年齡為合理范圍內if (age >= 0 && age <= 200) {this.age = age;}else {System.out.println("年齡無效");}}
}
再次定義了一個測試類運行它
public class Test {public static void main(String[] args) {People people = new People();people.name = "小華";
// 無法直接訪問private屬性people.setAge(23);System.out.println("請告訴我這個人叫什么?"+" "+people.name);System.out.println("多少歲?"+" "+people.getAge());}
}
getter方法也可以叫做取值方法,setter方法也可以叫做賦值方法。上一篇文章有詳細的介紹5.Java類與對象。
3.封裝的核心作用
1.數據的安全性
禁止外部直接修改屬性通過setter方法的校驗邏輯保證數據的合法。舉個例子:銀行里面存的錢,他人看不見我所存的具體金額,我也修改不了我的數據,這樣就保證了社會的穩定,也保證了我的錢的安全性。
2.隱藏實現的細節
外部只需要知道如何通過公共方法使用類,無需關心內部屬性的存儲方式或邏輯降低耦合度。
3.代碼的可維護性
若內部實現需要修改(如校驗規則調整),只需修改類內部的方法,外部調用代碼無需變動。
4.控制訪問權限
3.訪問權限控制符
public(公共的):全局可見,可以理解成一個人的外貌特征,誰都可以看見
protected:本包或者子類可見,主要是在繼承中
default(包訪問權限):本包可見,對于自己家族(同一個包中)不是秘密,對于他人來說就是隱私
private:僅本類可見
4.包
為了更好的管理類,把多個類搜集在一起成為一組,稱為軟件包。
在Java中也引入了包,包是對類、接口等的封裝機制的體現,是一種對類或者接口的很好的組織方式。使用包主要是避免類名的沖突、便于代碼管理和訪問控制,確定類的唯一性。
假如兩個程序員不約而同地建立了 Employee類。 只要將這些類放置在不同的包中, 就不會產生沖突。
1.本質:包其實就是相當于類的“文件夾”,通過層級結構(類似文件系統的目錄)組織類。
2.命名規范:通常使用小寫字母,采用反轉的域名作為前綴(避免全局沖突),例如:com.exmple.myapp。
層級之間用? .? 分隔,對應文件系統的目錄層級(如com/example/myapp)。
3.包的聲明
在Java源文件的第一行,用package語句聲明該類所屬的包。猶如上圖。
若未聲明package,類會被放入默認包(不推薦,易導致沖突)
5.導入包中的類
一個類可以使用所屬包中的所有類, 以及其他包中的公有類(public class)。我們可以采用兩種方式訪問另一個包中的公有類。
1.在每個類名之前添加? ? 完整的包名 + 類名
2.使用 import 語句(一旦使用了 import 語句,在使用類時,就不必寫出包的全名了)
可以使用 import 語句導人一個特定的類或者整個包。import 語句應該位于源文件的頂部(但位于 package 語句的后面)。
3.導入整個包的所有類(不推薦,可能引發沖突)
import com.test.demo1.*;//導入demo1這個包中所有的類
在大多數情況下, 只導入所需的包, 并不必過多地理睬它們。但在發生命名沖突的時候, 就不能不注意包的名字了。 例如,java.util 和java.sql 包都有日期 ( Date) 類。 如果在程序中導入了這兩個包:
import java.util.*;
import java.sql.*;
就會報錯
此時編譯器無法確定程序使用的是哪一個 Date 類。
那我們怎么解決?
可以采用增加一個特定的 import 語句來解決這個問題:
這樣就不會報錯。
如果這兩個 Date 類都需要使用, 又該怎么辦呢?
答案是,在每個類名的前面加上完整的包名。
4.靜態導入
import 語句不僅可以導人類,還增加了導人靜態方法和靜態域的功能。
就可以使用 Math?類的靜態方法和靜態域,而不必加類名前綴
import static java.lang.Math.*;
public class TestDemo3 {public static void main(String[] args) {double a = 1.69;double b = 1.96;
// 不導入靜態包的寫法
// double sqrtSum = Math.sqrt(a)+Math.sqrt(b);/*(sqrt是根號)1.3+1.4*/double sqrtSum = sqrt(a)+sqrt(b);System.out.println(sqrtSum);}
}
5.常見的包
1. java.lang:系統常用基礎類(String、Object),此包從JDK1.1后自動導入。核心類(String、Object、基本類型包裝類)
2. java.lang.reflect:java 反射編程包;
3. java.net:進行網絡編程開發包。
4. java.sql:進行數據庫開發的支持包。
5. java.util:是java提供的工具程序包。(集合類等) 非常重要
6. java.io:I/O編程開發包,輸入輸出類(文件操作、流等)
二、static成員
1.static修飾成員變量
static修飾的成員變量,被稱為靜態成員變量。
靜態成員變量最大的特性:不屬于具體的對象,是所有對象所共享的。
1.特性
①屬于類的本身,而非某個具體的對象,所有對象共享一份數據。
②生命周期伴隨類的一生(即:隨類的加載而創建,隨類的卸載而銷毀)
③即可通過對象訪問,也可以通過類名訪問,但一般更推薦使用類名訪問
public class Cat {public String name;public int age;public String color;public double weight;public static int foot = 4;public void eat(){System.out.println(name+"正在吃貓糧");}public void miao(){System.out.println(name+"會喵喵叫");}public static void main(String[] args) {Cat cat1 = new Cat();Cat cat2 = new Cat();cat1.name = "布丁";cat2.name = "大橘";
// 1.通過類名訪問System.out.println(Cat.foot);
// 2.通過對象訪問System.out.println(cat1.foot);System.out.println(cat2.foot);}
}
④類變量存儲在方法區內
2.static修飾成員方法
1.定義
用static修飾的方法,屬于類,不依賴于對象存在。
靜態方法代表類級別的行為,而不是某個對象的行為。
2.訪問方式
通過類名.方法名調用,不能直接訪問非靜態成員(非靜態成員屬于對象)
注意:靜態方法中不能使用this或者super(因為與對象無關,而this和super需要傳遞參數)
3.static成員變量初始化
注意:靜態成員變量一般不會放在構造方法中來初始化,構造方法中初始化的是與對象相關的實例屬性
1. 就地初始化
就地初始化指的是:在定義時直接給出初始值
2.靜態代碼塊初始化
下方有講
三、代碼塊
1.普通代碼塊
定義在方法里面的代碼塊(直接使用{}定義)
public class TestDemo4 {public static void main(String[] args) {{
// 直接定義在{}里面的代碼塊,普通代碼塊int a = 10;System.out.println(a);}int a = 100;System.out.println(a);}
}
2.構造塊
構造塊:定義在類中的代碼塊(不加修飾符)。也叫:實例代碼塊。構造代碼塊一般用于初始化實例成員變量。
?
public class Student {
// 實例變量public String name;public int age;public String grage;public double height;public double weight;public void doClass(){System.out.println(name+"正在學習");}{
// 2.實例代碼塊this.name = "小楊";this.age = 18;this.grage = "高三";this.height = 180.4;this.weight = 130.5;System.out.println(name+"正在吃飯");}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", grage='" + grage + '\'' +", height=" + height +", weight=" + weight +'}';}public static void main(String[] args) {Student student = new Student();System.out.println(student.toString());}
}
注意:實例代碼塊只有創建對象時才會執行
3.靜態塊
定義:使用static定義的代碼塊
靜態塊是Java類中用于初始化靜態成員的特殊代碼塊,它在類加載時自動執行。靜態塊的主要特點包括:
-
語法結構: static { // 初始化代碼 }
-
執行時機:
- 在類被首次加載時執行
- 在靜態變量初始化之后執行
- 在main方法執行之前執行
-
重要特性:
- 靜態代碼塊不管生成了多少個對象,其只會執行一次
- 靜態成員變量是類的屬性,因此在JVM加載類時開辟空間并初始化的
- 每個類可以有多個靜態塊,按聲明順序執行(合并)
- 不能訪問非靜態成員
- 不能拋出已檢查異常
- 不能包含return語句
public class Cat {public String name;public int age;public String color;public double weight;public static int foot = 4;public static int averageLifespan;// 靜態代碼塊:初始化靜態屬性,類加載時執行static {System.out.println("Cat類正在加載...");averageLifespan = 15;/*假設*/System.out.println("靜態代碼已執行:初始化壽命為"+averageLifespan+"年");}
// 構造方法(創建實例時執行)public Cat(String name) {this.name =name;System.out.println("創建了貓:"+name);}public static void main(String[] args) {// 第一次使用Cat類靜態代碼塊執行Cat cat = new Cat("布丁");System.out.println("貓平壽:"+averageLifespan+"年");
// 靜態代碼塊不再執行System.out.println("=====");Cat cat1 = new Cat("犀利哥");}}
Qustion:當靜態代碼塊和構造代碼塊(實例代碼塊)以及構造方法同時存在時,怎么被執行的?其順序是怎樣的?
思考一下,下面的代碼將被怎么執行?
public class Dog {public String name;public String species;public String color;public int age;static {System.out.println("靜態代碼塊2");}static {System.out.println("靜態代碼塊1");}static {System.out.println("靜態代碼塊3");}{System.out.println("實例代碼塊2");}{System.out.println("實力代碼塊1");}public Dog(String name, String species, String color, int age) {this.name = name;this.species = species;this.color = color;this.age = age;System.out.println("構造方法");}public static void main(String[] args) {Dog dog = new Dog("小花","中華田園犬","黑白",12);System.out.println("=============下面是第二次執行");Dog dog1 = new Dog("小黃","中華田園犬","黃色",13);}
}
其結果是這樣的
第一次時
靜態代碼塊被執行(按順序執行)
實例代碼塊被執行
構造代碼塊被執行
第二次
實例代碼塊被執行
構造代碼塊被執行
即靜態代碼塊只被執行一次。
4.同步代碼塊
四、方法toString?
toString方法是Object類的一個方法,用于返回對象的字符串表示。方便調試、打印或者日志記錄。
簡單來說toString方法就像是構造方法的輸出語句,即可以直接輸出像Student類中的name, age, grage , height , weight 這些即成員屬性。
public class Student {
// 實例變量public String name;public int age;public String grage;public double height;public double weight;public void doClass(){System.out.println(name+"正在學習");}{
// 2.實例代碼塊this.name = "小楊";this.age = 18;this.grage = "高三";this.height = 180.4;this.weight = 130.5;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", grage='" + grage + '\'' +", height=" + height +", weight=" + weight +'}';}public static void main(String[] args) {Student student = new Student();System.out.println(student.toString());}
}
toString 方法可以在IDEA中有快捷生成方法鍵
就可以生成了。
1.默認行為(不重寫)
我們可以點擊輸出中的println,他來看看他的底層代碼(Ctrl鍵+鼠標右擊)
我們進入了他的代碼中,再點擊Object類看看(Ctrl鍵+鼠標右擊)
就進入到了Object類中,這個就是Object類中默認的toString實現方式。可以看見打印對象時,會輸出類名( getClass() )全限定名(.getName() )@ 哈希碼( Integer.toHexString(hashCode() )
可以看見默認行為,其可讀性差,僅能區分“不同對象”,無法體現對象具體數據。
2.為什么重寫ToString 方法
重寫后,可自定義對象的字符串格式,讓打印日志輸出更直觀,直接體現對象的屬性值,方便調試和理解對象狀態。
可以看一下不重寫toString時是這樣:
重寫之后就是這樣了(上方有源代碼)