- final,static,this,super 關鍵字總結
- static 關鍵字詳解
- static 關鍵字主要有以下四種使用場景
- 補充內容
final,static,this,super 關鍵字總結
final 關鍵字
final關鍵字主要用在三個地方:變量、方法、類。
- 對於一個final變量,如果是基本數據類型的變量,則其數值一旦在初始化之後便不能更改;如果是引用類型的變量,則在對其初始化之後便不能再讓其指向另一個對象。
- 當用final修飾一個類時,表明這個類不能被繼承。final類中的所有成員方法都會被隱式地指定爲final方法。
- 使用final方法的原因有兩個。第一個原因是把方法鎖定,以防任何繼承類修改它的含義;第二個原因是效率。在早期的Java實現版本中,會將final方法轉爲內嵌調用。但是如果方法過於龐大,可能看不到內嵌調用帶來的任何性能提升(現在的Java版本已經不需要使用final方法進行這些優化了)。類中所有的private方法都隱式地指定爲final。
static 關鍵字
static 關鍵字主要有以下四種使用場景:
- 修飾成員變量和成員方法: 被 static 修飾的成員屬於類,不屬於單個這個類的某個對象,被類中所有對象共享,可以並且建議通過類名調用。被static 聲明的成員變量屬於靜態成員變量,靜態變量 存放在 Java 內存區域的方法區。調用格式:
類名.靜態變量名``類名.靜態方法名()
- 靜態代碼塊: 靜態代碼塊定義在類中方法外, 靜態代碼塊在非靜態代碼塊之前執行(靜態代碼塊—>非靜態代碼塊—>構造方法)。 該類不管創建多少對象,靜態代碼塊只執行一次.
- 靜態內部類(static修飾類的話只能修飾內部類): 靜態內部類與非靜態內部類之間存在一個最大的區別: 非靜態內部類在編譯完成之後會隱含地保存着一個引用,該引用是指向創建它的外圍類,但是靜態內部類卻沒有。沒有這個引用就意味着:1. 它的創建是不需要依賴外圍類的創建。2. 它不能使用任何外圍類的非static成員變量和方法。
- 靜態導包(用來導入類中的靜態資源,1.5之後的新特性): 格式爲:
import static
這兩個關鍵字連用可以指定導入某個類中的指定靜態資源,並且不需要使用類名調用類中靜態成員,可以直接使用類中靜態成員變量和成員方法。
this 關鍵字
this關鍵字用於引用類的當前實例。 例如:
class Manager {
Employees[] employees;
void manageEmployees() {
int totalEmp = this.employees.length;
System.out.println("Total employees: " + totalEmp);
this.report();
}
void report() { }
}
在上面的示例中,this關鍵字用於兩個地方:
- this.employees.length:訪問類Manager的當前實例的變量。
- this.report():調用類Manager的當前實例的方法。
此關鍵字是可選的,這意味着如果上面的示例在不使用此關鍵字的情況下表現相同。 但是,使用此關鍵字可能會使代碼更易讀或易懂。
super 關鍵字
super關鍵字用於從子類訪問父類的變量和方法。 例如:
public class Super {
protected int number;
protected showNumber() {
System.out.println("number = " + number);
}
}
public class Sub extends Super {
void bar() {
super.number = 10;
super.showNumber();
}
}
在上面的例子中,Sub 類訪問父類成員變量 number 並調用其其父類 Super 的 showNumber()
方法。
使用 this 和 super 要注意的問題:
- 在構造器中使用
super()
調用父類中的其他構造方法時,該語句必須處於構造器的首行,否則編譯器會報錯。另外,this 調用本類中的其他構造方法時,也要放在首行。 - this、super不能用在static方法中。
簡單解釋一下:
被 static 修飾的成員屬於類,不屬於單個這個類的某個對象,被類中所有對象共享。而 this 代表對本類對象的引用,指向本類對象;而 super 代表對父類對象的引用,指向父類對象;所以, this和super是屬於對象範疇的東西,而靜態方法是屬於類範疇的東西。
static 關鍵字詳解
static 關鍵字主要有以下四種使用場景
- 修飾成員變量和成員方法
- 靜態代碼塊
- 修飾類(只能修飾內部類)
- 靜態導包(用來導入類中的靜態資源,1.5之後的新特性)
修飾成員變量和成員方法(常用)
被 static 修飾的成員屬於類,不屬於單個這個類的某個對象,被類中所有對象共享,可以並且建議通過類名調用。被static 聲明的成員變量屬於靜態成員變量,靜態變量 存放在 Java 內存區域的方法區。
方法區與 Java 堆一樣,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。雖然Java虛擬機規範把方法區描述爲堆的一個邏輯部分,但是它卻有一個別名叫做 Non-Heap(非堆),目的應該是與 Java 堆區分開來。
HotSpot 虛擬機中方法區也常被稱爲 “永久代”,本質上兩者並不等價。僅僅是因爲 HotSpot 虛擬機設計團隊用永久代來實現方法區而已,這樣 HotSpot 虛擬機的垃圾收集器就可以像管理 Java 堆一樣管理這部分內存了。但是這並不是一個好主意,因爲這樣更容易遇到內存溢出問題。
調用格式:
- 類名.靜態變量名
- 類名.靜態方法名()
如果變量或者方法被 private 則代表該屬性或者該方法只能在類的內部被訪問而不能在類的外部被訪問。
測試方法:
public class StaticBean {
String name;
靜態變量
static int age;
public StaticBean(String name) {
this.name = name;
}
靜態方法
static void SayHello() {
System.out.println(Hello i am java);
}
@Override
public String toString() {
return StaticBean{ +
name=' + name + ''' + age + age +
'}';
}
}
public class StaticDemo {
public static void main(String[] args) {
StaticBean staticBean = new StaticBean(1);
StaticBean staticBean2 = new StaticBean(2);
StaticBean staticBean3 = new StaticBean(3);
StaticBean staticBean4 = new StaticBean(4);
StaticBean.age = 33;
StaticBean{name='1'age33} StaticBean{name='2'age33} StaticBean{name='3'age33} StaticBean{name='4'age33}
System.out.println(staticBean+ +staticBean2+ +staticBean3+ +staticBean4);
StaticBean.SayHello();Hello i am java
}
}
靜態代碼塊
靜態代碼塊定義在類中方法外, 靜態代碼塊在非靜態代碼塊之前執行(靜態代碼塊—非靜態代碼塊—構造方法)。 該類不管創建多少對象,靜態代碼塊只執行一次.
靜態代碼塊的格式是
static {
語句體;
}
一個類中的靜態代碼塊可以有多個,位置可以隨便放,它不在任何的方法體內,JVM加載類時會執行這些靜態的代碼塊,如果靜態代碼塊有多個,JVM將按照它們在類中出現的先後順序依次執行它們,每個代碼塊只會被執行一次。
靜態代碼塊對於定義在它之後的靜態變量,可以賦值,但是不能訪問.
靜態內部類
靜態內部類與非靜態內部類之間存在一個最大的區別,我們知道非靜態內部類在編譯完成之後會隱含地保存着一個引用,該引用是指向創建它的外圍類,但是靜態內部類卻沒有。沒有這個引用就意味着:
- 它的創建是不需要依賴外圍類的創建。
- 它不能使用任何外圍類的非static成員變量和方法。
Example(靜態內部類實現單例模式)
public class Singleton {
聲明爲 private 避免調用默認構造方法創建對象
private Singleton() {
}
聲明爲 private 表明靜態內部該類只能在該 Singleton 類中被訪問
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance() {
return SingletonHolder.INSTANCE;
}
}
當 Singleton 類加載時,靜態內部類 SingletonHolder 沒有被加載進內存。只有當調用 getUniqueInstance()
方法從而觸發 SingletonHolder.INSTANCE
時 SingletonHolder 纔會被加載,此時初始化 INSTANCE 實例,並且 JVM 能確保 INSTANCE 只被實例化一次。
這種方式不僅具有延遲初始化的好處,而且由 JVM 提供了對線程安全的支持。
靜態導包
格式爲:import static
這兩個關鍵字連用可以指定導入某個類中的指定靜態資源,並且不需要使用類名調用類中靜態成員,可以直接使用類中靜態成員變量和成員方法
Math. --- 將Math中的所有靜態資源導入,這時候可以直接使用裏面的靜態方法,而不用通過類名進行調用
如果只想導入單一某個靜態方法,只需要將換成對應的方法名即可
import static java.lang.Math.;
換成import static java.lang.Math.max;具有一樣的效果
public class Demo {
public static void main(String[] args) {
int max = max(1,2);
System.out.println(max);
}
}
補充內容
靜態方法與非靜態方法
靜態方法屬於類本身,非靜態方法屬於從該類生成的每個對象。 如果您的方法執行的操作不依賴於其類的各個變量和方法,請將其設置爲靜態(這將使程序的佔用空間更小)。 否則,它應該是非靜態的。
Example
class Foo {
int i;
public Foo(int i) {
this.i = i;
}
public static String method1() {
return An example string that doesn't depend on i (an instance variable);
}
public int method2() {
return this.i + 1; Depends on i
}
}
你可以像這樣調用靜態方法:Foo.method1()
。 如果您嘗試使用這種方法調用 method2 將失敗。 但這樣可行:Foo bar = new Foo(1);bar.method2();
總結:
- 在外部調用靜態方法時,可以使用”類名.方法名”的方式,也可以使用”對象名.方法名”的方式。而實例方法只有後面這種方式。也就是說,調用靜態方法可以無需創建對象。
- 靜態方法在訪問本類的成員時,只允許訪問靜態成員(即靜態成員變量和靜態方法),而不允許訪問實例成員變量和實例方法;實例方法則無此限制
static{}靜態代碼塊與{}非靜態代碼塊(構造代碼塊)
相同點: 都是在JVM加載類時且在構造方法執行之前執行,在類中都可以定義多個,定義多個時按定義的順序執行,一般在代碼塊中對一些static變量進行賦值。
不同點: 靜態代碼塊在非靜態代碼塊之前執行(靜態代碼塊—非靜態代碼塊—構造方法)。靜態代碼塊只在第一次new執行一次,之後不再執行,而非靜態代碼塊在每new一次就執行一次。 非靜態代碼塊可在普通方法中定義(不過作用不大);而靜態代碼塊不行。
一般情況下,如果有些代碼比如一些項目最常用的變量或對象必須在項目啓動的時候就執行的時候,需要使用靜態代碼塊,這種代碼是主動執行的。如果我們想要設計不需要創建對象就可以調用類中的方法,例如:Arrays類,Character類,String類等,就需要使用靜態方法, 兩者的區別是 靜態代碼塊是自動執行的而靜態方法是被調用的時候才執行的.
Example
public class Test {
public Test() {
System.out.print(默認構造方法!--);
}
非靜態代碼塊
{
System.out.print(非靜態代碼塊!--);
}
靜態代碼塊
static {
System.out.print(靜態代碼塊!--);
}
public static void test() {
System.out.print(靜態方法中的內容! --);
{
System.out.print(靜態方法中的代碼塊!--);
}
}
public static void main(String[] args) {
Test test = new Test();
Test.test();靜態代碼塊!--靜態方法中的內容! --靜態方法中的代碼塊!--
}
當執行 Test.test();
時輸出:
靜態代碼塊!--靜態方法中的內容! --靜態方法中的代碼塊!--
當執行 Test test = new Test();
時輸出:
靜態代碼塊!--非靜態代碼塊!--默認構造方法!--
非靜態代碼塊與構造函數的區別是: 非靜態代碼塊是給所有對象進行統一初始化,而構造函數是給對應的對象初始化,因爲構造函數是可以多個的,運行哪個構造函數就會建立什麼樣的對象,但無論建立哪個對象,都會先執行相同的構造代碼塊。也就是說,構造代碼塊中定義的是不同對象共性的初始化內容。