java中的final關鍵字有哪些用法,Java: static,final,代碼塊 的詳解
java中的final關鍵字有哪些用法,Java: static,final,代碼塊 的詳解
Java: static,final,代碼塊 的詳解
每博一文案
山本文緒說過這樣一句話:哪些決定放棄了的事,就請放棄得干干凈凈。哪些決定再也不見面的人,就真
的不要再見面了,不要再做背叛自己的事,如果想要愛別人,就先好好愛自己。
人生是一場有去無回的旅行,舊日種種,結成過往,我們只能回過頭看,卻不能從頭走,好的也罷,壞的也罷,
過去的都已成定局了。不管是遺憾還是慶幸,都要帶著這份烙印,繼續走向漫漫人生。
正如那句極有名的禪語:無論你遇見誰,他都是對的人,無論發生什么事,那都是唯一會發生的事。
不管事情開始于那個時刻都是對的,時刻已經結束的,就是結束了。如果沒有失去,就不會懂得珍惜。
但是你知道嗎?如果已經失去了,就該學會放棄,沒有什么是注定會屬于我們的,得到或者失去,都是自有它的安排。
面對哪些注定要說再見到人或物,與其拼命拉扯,淚影婆娑,倒不如落落大方道一句“珍重”。
從從容容德告個別,然后留下一個瀟灑離開的背影,從今以后更加堅定自己的路,將自己的人生過得有聲有色。
愿你可飲最烈的酒,也能放開該放的手,從前不回頭,往后強求。—————— 一禪心靈廟語
文章目錄
- Java: static,final,代碼塊 的詳解
- 每博一文案
- 1. static 關鍵字
- 1.1 static 修飾屬性
- 1.2 static 修飾方法
- 1.3 static 修飾代碼塊
- 1.4 static 修飾內部類
- 1.5 開發中如何合理的使用 staitc 關鍵字
- 2. main 方法的探究
- 3. 代碼塊
- 3.1 程序中成員變量賦值的執行順序
- 4. final 關鍵字
- 5. 總結:
- 6. 最后:
1. static 關鍵字
java中的final關鍵字有哪些用法?當我們編寫一個類時,其實就是在描述其對象的屬性和行為,而并沒有產生實質上 的對象,只有通過new關鍵字才會產生出對象,這時系統才會分配內存空間給對象, 其方法才可以供外部調用。我們有時候希望無論是否產生了對象或無論產生了多少 對象的情況下,某些特定的數據在內存空間里只有一份,例如所有的中國人都有個國家名稱,每一個中國人都共享這個國家名稱,不必在每一個中國人的實例對象中都單獨分配一個用于代表國家名稱的變量。這里我們就可以用到 static
關鍵字的使用了。
static
: 表示的含義是 :靜態的 。
在Java類當中, static 可以修飾屬性,方法,代碼塊,內部類 。
1.1 static 修飾屬性
static
修飾屬性(變量): 按是否使用 static 修飾,可以分為為 靜態屬性(類屬性) VS 非靜態屬性(實例屬性) 。
類中static代碼段,非靜態屬性(實例屬性): 我們創建了類的多個對象,每個對象都獨立的擁有一套 類中的 非靜態屬性,非靜態變量對應不同的實例對象(new) 自身的修改,不會影響到其他的 對象當中的一套非靜態屬性。
靜態屬性(類屬性): 我們創建了類的多個對象,但是多個對象共享同一個靜態屬性,當通過某一個對象修改靜態屬性時,會導致其他對象調用此靜態變量時,是最后一次修改過的結果。
被 static 修飾的屬性的特點:
- 靜態屬性隨著類的加載而加載,早于 new 對象的加載創建,因為 new 對象,要先加載類,再調用類中的構造方法創建對應的對象。
- 在訪問權限允許的情況下,靜態屬性可以通過**“類名.靜態屬性名”** 的方式進行調用。
- 由于類只會加載一次,則靜態屬性(靜態變量) 在內存(方法區)當中也只會存在一份,存在方法區的靜態域當中。
- 靜態屬性有:System.out; Math.PI;
package blogs.blog2;public class StaticTest {public static void main(String[] args) {Chinese c1 = new Chinese();c1.nation = "CHN";Chinese c2 = new Chinese();c2.nation = "CHINA";System.out.println("c2.nation: "+c1.nation);System.out.println("c2.nation: "+c2.nation);// 可以通過 類名.靜態屬性 ()Chinese.nation = "中國";System.out.println("Chinese.nation: "+Chinese.nation);}
}class Chinese{String name;int age;static String nation; // 被 static 修飾的靜態屬性}
java 靜態塊和構造的執行順序,
1.2 static 修飾方法
被 staitc 修飾的方法,被稱為是 ”靜態方法“ 或者是 類方法。
靜態方法的特點:
- 同樣靜態方法也是和隨著類的加載而加載到內存(方法區)當中的。
- 在訪問權限允許的情況下,靜態屬性可以通過**“類名.靜態方法名”** 的方式進行調用。
- 靜態方法只能調用 靜態的方法/屬性 ,無法調用非靜態的方法/屬性,除非 實例化對象(new)。因為一個(靜態方法)是已經在內存當中存在了的,調用一個(非靜態的)還沒有加載到內存當中的,JVM 是不允許的。
- 在靜態的方法內:不能使用 'this’關鍵字 以及 super 關鍵字,他倆都是表示對象的引用.
- 非靜態方法可以調用非靜態方法或屬性,也可以調用靜態方法或屬性。
- 被 static 修飾的方法無法重寫
靜態變量static。
package blogs.blog2;public class StaticTest {public static void main(String[] args) {// 靜態方法可以直接使用 “類名.靜態方法”調用Chinese.show();}
}class Chinese{String name;int age;static String nation; // 被 static 修飾的靜態屬性public static void show(){System.out.println("我是靜態方法");// name = "TOM"; // 靜態方法無法直接調用非靜態的屬性// eat(); // 靜態方法無法直接調用非靜態的方法,可以通過 new 實例對象,調用/*this.name = "Tom";super.clone();*/ // 靜態方法中無法使用 this./super.// 靜態方法可以直接調用靜態屬性,靜態方法nation = "Hello"; // 靜態屬性sleep(); // 靜態方法}public void eat(){System.out.println("吃飯");System.out.println("***************************");// 非靜態方法可以直接調用 靜態方法/屬性,因為加載內存的先后原因nation = "World"; // 靜態屬性sleep(); // 靜態方法}public static void sleep() {System.out.println("睡覺");}
}
static int?被 stiatic 修飾的方法,繼承時,無法被重寫,就算你按照重寫的要求,編寫好了沒有報錯,但是運行的時候,調用的是父類中沒有被重寫的方法 因為 stiatic 是類方法,和類一起加載到內存當中的,而多態中的重寫是,運行時類型,只有運行了。才
package blogs.blog2;public class StaticTest {public static void main(String[] args) {Earth earth = new Chinese(); // 多態earth.human();}}class Chinese extends Earth{public static void human() {System.out.println("中國人");}
}class Earth{public static void human(){System.out.println("人類");}}
1.3 static 修飾代碼塊
被 static 修飾的代碼塊,被稱為 “靜態代碼塊”。
具體詳細,繼續往下看,在 3.代碼塊 這一段有詳細說明
1.4 static 修飾內部類
關于這一部分內容,大家可以移步至:🔜🔜🔜
1.5 開發中如何合理的使用 staitc 關鍵字
開發中:如何確定一個屬性是否聲明未 static ???
如果一個屬性可以被多個對象共享的,不會隨著對象不同而不同的,就可以聲明為 staitc
一般 static 是和 final 一起修飾 變量的。
開發中:如何確定一個方法是否要聲明為 static 的 ???
一般是 stiatic 的屬性,通常對應的 set/get 方法也是 stiatic 的
工具類中的方法,習慣上聲明為 static 的,比如:Math,Arras,Collections
2. main 方法的探究
public
權限修飾符為什么是 public ???
由于Java虛擬機需要調用類的 main() 方法,所以該方法的訪問權限 必須 是
public
才行。
- 為什么 main 方法的修飾符要是
static
靜態的 ???
因為Java虛擬機在執行 main() 方法時,不必創建對象,所以該方法必須是
static
,可以直接調用。又因為 main 方法是 static 靜態的,靜態方法無法直接調用非靜態的方法/屬性,
必須創建對應類的實例對象(new)后,才能通過這個對象去訪問該對應類中的非靜態方法/屬性。
- main 方法中的形參列表是
string[] args
字符串數組
該形參接收一個 String 類型的數組參數,該字符串數組中保存執行 java 命令時,所傳遞給所運行類的參數。
main 也是一個方法,特殊之處就是,它是一個程序入口的方法。不同的類中的是可以相互調用 main 的方法的,如下
因為有兩個類,每個類中都有一個 main() 方法,所以執行的時候,你需要選擇,想要運行的是哪一個告知編譯器。
package blogs.blog2;public class MainTest {public static void main(String[] args) {System.out.println("我是 MainTest 中的 main()方法");}
}class Main2{public static void main(String[] args) {System.out.println("我要調用 MainTest中的main()方法");// 調用 MainTest 中的main()方法MainTest.main(args);}
}
3. 代碼塊
代碼塊(或初始化塊)的作用: 對Java類或者對象進行初始化
代碼塊的分類:
- 一個類中代碼塊中若有修飾符,則只能被
static
修飾,被 static 修飾的代碼塊被稱為 靜態代碼塊。 - 沒有任何修飾符,修飾的代碼塊,被稱為非靜態代碼塊
靜態代碼塊的特點:
- 可以有輸出語句
- 可以對類的屬性,類的聲明進行初始化操作
- 不可以對非靜態的屬性初始化,無法直接調用非靜態的的方法/屬性,想要 實例化對象 new
- 靜態代碼塊隨著類的加載而加載,并執行,且執行一次。所以靜態代碼塊的執行要先于非靜態代碼塊
- 一個類中可以有多個靜態代碼塊,若有多個靜態的代碼塊,那么按照從上到下的順序依次執行。
- 靜態的代碼塊的諸多限制和 “staitc" 是一樣的。
非靜態代碼塊的特點:
- 和靜態代碼塊一樣可以有輸出語句
- 可以對類的數,類的聲明進行初始化操作。
- 不僅可以調用靜態的方法/屬性,也可以調用非靜態的方法 / 屬性
- 一個類中可以有多個非靜態代碼塊,若有多個非靜態的代碼塊,那么按照從上到下的順序依次執行。
- 注意的是非靜態代碼塊是,創建對象 (new) 的時候,才會執行,而且每創建一次對象都會執行一次,并且先于構造器執行。
無論是靜態代碼塊/還是非靜態代碼塊,都是加載后并執行的,并不是加載到內存當中不執行的。
注意: 非靜態代碼塊,不是創建對象 (new) 是不會加載調用執行非靜態代碼塊的
package blogs.blog2;public class BlockTest {public static void main(String[] args) {Person.show();}
}class Person{String name;static String desc;public void eat() {System.out.println("非靜態方法");}public static void show(){System.out.println("靜態方法");}{System.out.println("非靜態代碼塊");}static{System.out.println("靜態代碼塊");// name = "Tom"; // 靜態代碼塊無法直接調用非靜態的// eat();}
}
靜態代碼塊優先比非靜態代碼塊優先被執行,并且靜態代碼塊僅僅只是執行一次(加載類的那一次)
package blogs.blog2;public class BlockTest {public static void main(String[] args) {Person person = new Person();Person person2 = new Person();}
}class Person{String name;static String desc;public void eat() {System.out.println("非靜態方法");}public static void show(){System.out.println("靜態方法");}{System.out.println("非靜態代碼塊");}static{System.out.println("靜態代碼塊");// name = "Tom"; // 靜態代碼塊無法直接調用非靜態的// eat();}
}
練習: 觀察如下代碼的運行結果:
先執行父類,再執行靜態代碼塊
package day15;public class LeafTest {public static void main(String[] args) {new Leaf();System.out.println("*************************");new Leaf();}
}class Root{static {System.out.println("Root的靜態初始化塊");}{System.out.println("Root的普通初始化塊");}public Root(){super();System.out.println("Root的無參數的構造器");}}class Mid extends Root{static{System.out.println("Mid的靜態初始化塊");}{System.out.println("Mid的普通初始化塊");}public Mid() {super();System.out.println("Mid的無參數的構造器");}public Mid(String msg) {// 通過this調用一類中的重載的構造器this();System.out.println("Mid的帶參數構造器,其參數數值: "+msg);}
}class Leaf extends Mid{static{System.out.println("Leaf的靜態初始化塊");}{System.out.println("Leaf的普通初始化塊");}public Leaf() {// 通過super調用父類中有一個字符串參數的構造器super("尚硅谷");System.out.println("Leaf的構造器");}
}
package day15;public class Son extends Father {static {System.out.println("44444444444444444");}{System.out.println("55555555555555");}public Son() {System.out.println("66666666666666");}public static void main(String[] args) {System.out.println("77777777777");System.out.println("**********************************");new Son();System.out.println("*********************************");/*new Son();System.out.println("*************************************");new Father();*/}
}class Father{static{System.out.println("11111111111");}{System.out.println("222222222222");}public Father() {System.out.println("33333333333333");}
}
3.1 程序中成員變量賦值的執行順序
如下是對屬性賦值的先后所執行的順序
- 默認初始化
- 顯式初始化
- 在代碼塊中賦值
- 構造器中初始化
- 有了對象以后,可以通過 ”對象.屬性,或對象.方法“ 的方式,進行賦值。
public class OrderTest {public static void main(String[] args) {Order order = new Order();System.out.println(order.orderId);}
}class Order{{orderId = 4;}int orderId = 3;public Order() {this.orderId = 5;}}
把構造器初始化注釋掉,結果是
4. final 關鍵字
final
: 最終的含義。
final
可以用來修飾:類,方法,變量,局部變量(形參)。
final
修飾一個類,此類就不能被其他類所繼承了
比如:System 類,String 類,StringBuffer類:因為里面的方法基本都實現了,沒有必要再通過繼承擴展了。
final 修飾方法:表明此方法不可以被重寫,比如:Object 類中的 getClass()
final 修飾屬性:可以考慮賦值的位置有:顯示初始化,代碼塊初始化,構造器,注意是:賦值不是修改,被 final 修飾的變量無法被修改了。
顯示初始化
package blogs.blog2;public class FinalTest {final int num = 10;public static void main(String[] args) {FinalTest f = new FinalTest();System.out.println(f.num);}
}
代碼塊初始化:
package blogs.blog2;public class FinalTest {final int LEFT;{LEFT = 100;}public static void main(String[] args) {FinalTest f = new FinalTest();System.out.println(f.LEFT);}
}
構造器初始化:
package blogs.blog2;public class FinalTest {final int num;public FinalTest(int n) {num = n;}public static void main(String[] args) {FinalTest f = new FinalTest(1000);System.out.println(f.num);}
}
final 修飾局部變量(形參) 表明此形參時一個常量,當我們調用此方法時,給常量形參賦值一實參,一旦賦值以后,就只能在方法體中使用此形參,并且不能再進行重新的賦值操作了。
package blogs.blog2;public class FinalTest {final int num;public FinalTest(final int n) {num = n;}public static void main(String[] args) {FinalTest f = new FinalTest(1000);System.out.println(f.num);}
}
一般是 static, final
用來修飾屬性,全局常量,需要注意的是一般 static 的成員屬性,方法也是 static 的
練習:
觀察如下代碼是否會出現報錯,如果不會報錯,運行的結果又是什么???
package blogs.blog2;public class FinalTest {public static void main(String[] args) {AA aa = new AA();aa.test(aa);}}class AA {String name;int age ;public void test(final AA aa) {aa.age = 10; // 這里是否或報錯,能否編譯成功System.out.println(aa.age);}
}
解析:
答:不會報錯,結果是 10 。
因為
final
修飾的是 AA 這個引用類型的形參,并不會作用于 AA 類中的屬性。如果我修改 AA 這個引用類型的地址,可能就會報錯了。
5. 總結:
- static 修飾屬性,方法,代碼塊,內部類的不同的作用,
- static 靜態的隨著類一起加載到內存(方法區)當中(僅僅加載一次,所有對象共有),早于 new 對象的加載創建,因為 new 對象,要先加載類,再調用類中的構造方法創建對應的對象。
- static 靜態的屬性,方法,代碼塊 都無法直接的訪問非靜態的方法/屬性,需要通過 創建實例對象(new)的方式訪問
- 被 static 修飾的方法,無法被重寫,就算編寫成了重寫的方式了,但是調用的時候,執行的不是子類重寫的方法,而是父類中沒有被重寫的方法。
- main 方法的 public , static ,String[] args 的作用。
- 代碼塊:靜態代碼塊,非靜態代碼塊。
- 靜態代碼塊:隨著類一起加載到內存(方法區)當中,加載的同時,并執行代碼塊,并且(無論加載多少次類)只執行一次(加載類的那一次)。優先于非靜態代碼塊,優先于構造器。
- 非靜態代碼塊:只有當創建實例對象(new)的時候才會被加載并執行,創建多少次對象就會執行多少次非靜態代碼塊。
- final 最終的,可以修飾:屬性(不可修改),形參(不可修改),方法(無法重寫),類(無法繼承)。
6. 最后:
限于自身水平,其中存在的錯誤,希望大家給予指教,韓信點兵——多多益善,謝謝大家,后會有期,江湖再見!!!