Java不得不說的事之Static關鍵字
紙上得來終覺淺,絕知此事要躬行
本文主要用於學習內容的記錄和整理,如有不正請儘量指正。
static關鍵字
本文以static的五個用法作為思路進行整理。
靜態變數
靜態方法
靜態程式碼段
靜態匯入
一、靜態變數
我們把java中的變數且分成靜態變數以及非靜態變數來理解。
靜態變數屬於類,非靜態變數屬於物件 我們怎麼理解這句話呢?先來一個非靜態變數的例子。
//Demo1_非靜態變數賦值
public class Book {
String name;
int price;
public String toString() {
return "Name:" + name + ", Price:" + price;
}
public static void main(String[] args) {
Book p1 = new Book();
b1.name = "Math";
p1.price = 10;
Book p2 = new Book();
p2.name = "History";
p2.price = 12;
System.out.println(p1);
System. out.println(p2);
}
/**Output
* Name:Math, Price:10
* Name:History, Price:12
*///~
}
Animal這個物件中的屬性各自進行賦值,不會相互干擾,各個物件對應的屬性的值各不相同。下面是一份靜態變數的例子。
//demo2_靜態變數
public class Person {
String name;
static int age;
public String toString() {
return "Name:" + name + ", Price:" + price;
}
public static void main(String[] args) {
Book p1 = new Book();
b1.name = "Math";
p1.price = 10;
Book p2 = new Book();
p2.name = "History";
p2.price = 12;
System.out.println(p1);
System.out.println(p2);
}
/**Output
* Name:Math, Price:12
* Name:History, Price:12
*///~
}
可以發現與非靜態變數的區別在於demo2的price變數被static修飾了。在二次賦值的時候會影響到其他物件的price屬性。 接下來我們從記憶體儲存過程的原因來分析為什麼出現這個情況。
非靜態變數price這個屬性值,屬於Book1或者book2物件,當price被static修飾之後,price移到了靜態儲存區,屬於book類
經過上圖以及程式碼的分析,我們已經可以清楚,當給price屬性加上static之後,Book物件就不再擁有price這個屬性了,這個屬性就統一交給Book類去管理了,即有多個book物件也將只有一個price值。
靜態變數有什麼用?
有某個變數在程式的任何地方都可能用到,可以將這個變數設定成靜態變數,避免每次使用到這個變數的時候都要建立一個對應的物件,浪費記憶體空間。
二、靜態方法
我依然把JAVA中的方法區分成靜態方法以及非靜態方法。 靜態方法:類的方法,用Class.MethodName進行呼叫 非靜態方法:物件的方法,用Object.MethodName進行呼叫
使用靜態方法的一個好處是可以直接使用類名.方法名的形式進行呼叫,可以不必頻繁New新的物件。
static方法中不能使用this和super關鍵字,不能呼叫非static方法,只能訪問所屬類的靜態變數和靜態方法。因為當static方法被呼叫的時候,這個類的物件可能還沒有建立,即使已經被建立了,也無法確認呼叫那個物件的方法。不能訪問非靜態方法同理。
static修飾的變數是可以用private限定的,這種情況下的變數可以出現在靜態程式碼塊中,或者類的其他靜態方法中使用,當然非靜態方法也可以。但是不能再其他類中通過類名來直接引用。需要清楚的是private是訪問許可權限定,static代表的不需要例項就可以使用,二者不衝突所以可以配合使用。
三、靜態程式碼塊
首先我們先理清一下什麼叫做程式碼塊。下來是百度知道的解釋,非常清楚了。 程式碼塊是一種常見的程式碼形式。他用大括號“{}”將多行程式碼封裝在一起,形成一個獨立的程式碼區,這就構成了程式碼塊。 在Java中程式碼塊主要分為四種:普通程式碼塊,靜態程式碼塊,同步程式碼塊和構造程式碼塊。
- 普通程式碼塊
方法名後面用{}括起來的程式碼段。普通程式碼塊是不能夠單獨存在的,它必須要緊跟在方法名後面。同時也必須要使用方法名呼叫它
- 靜態程式碼塊
靜態程式碼塊就是用static修飾的用{}括起來的程式碼段,它的主要目的就是對靜態屬性進行初始化。靜態程式碼塊可以有多個,位置可以隨便放,它不在任何的方法體內,JVM載入類時會執行這些靜態的程式碼塊,如果static程式碼塊有多個,JVM將按照它們在類中出現的先後順序依次執行它們,每個程式碼塊只會被執行一次。
- 同步程式碼塊
使用 synchronized 關鍵字修飾,並使用“{}”括起來的程式碼片段,它表示同一時間只能有一個執行緒進入到該方法塊中,是一種多執行緒保護機制。
- 構造程式碼塊
在類中直接定義沒有任何修飾符、字首、字尾的程式碼塊即為構造程式碼塊。我們明白一個類必須至少有一個建構函式,建構函式在生成物件時被呼叫。構造程式碼塊和建構函式一樣同樣是在生成一個物件時被呼叫,那麼構造程式碼在什麼時候被呼叫?如何呼叫的呢?
在CSDN上看到一個非常好的文章中找到一個很好的程式碼樣例
public class CodeBlock {
private int a = 1;
private int b ;
private int c ;
//靜態程式碼塊
static {
int a = 4;
System.out.println("我是靜態程式碼塊1");
}
//構造程式碼塊
{
int a = 0;
b = 2;
System.out.println("構造程式碼塊1");
}
public CodeBlock(){
this.c = 3;
System.out.println("建構函式");
}
public int add(){
System.out.println("count a + b + c");
return a + b + c;
}
//靜態程式碼塊
static {
System.out.println("我是靜態程式碼塊2,我什麼也不做");
}
//構造程式碼塊
{
System.out.println("構造程式碼塊2");
}
public static void main(String[] args) {
CodeBlock c = new CodeBlock();
System.out.println(c.add());
System.out.println();
System.out.println("*******再來一次*********");
System.out.println();
CodeBlock c1 = new CodeBlock();
System.out.println(c1.add());
}
}
//結果:
//我是靜態程式碼塊1
//我是靜態程式碼塊2,我什麼也不做
//構造程式碼塊1
//構造程式碼塊2
//建構函式
//count a + b + c
//6
//
//*******再來一次*********
//
//構造程式碼塊1
//構造程式碼塊2
//建構函式
//count a + b + c
//6
可以得到如下結論
- 靜態程式碼塊只會執行一次。有多個靜態程式碼塊時按順序依次執行。
- 構造程式碼塊每次建立新物件時都會執行。有多個時依次執行。
- 執行順序:靜態程式碼塊 > 構造程式碼塊 > 建構函式。
- 構造程式碼塊和靜態程式碼塊有自己的作用域,作用域內部的變數不影響作用域外部。
四、靜態導包
這個內容在整理static關鍵字之前我完全不知道有這麼一個知識點。看來整理還是有點用處的。 寫法:import static com.xxx.xxx.*; 下面用程式碼來解釋
package com.boom.static;
public class BoomHelper {
public static void print(Object o){
System.out.println(o);
}
}
import static com.boom.static.BoomHelper.*;
public class test
{
public static void main( String[] args )
{
print("Hello World!");
}
/**Output
* Hello World!
*///~
}
在使用靜態匯入之後呼叫匯入的方法時無需使用類名.方法名的形式進行,可以更加簡潔的直接使用方法名進行呼叫方法。
總結
static是java中非常重要的一個關鍵字,而且它的用法也很豐富,主要有四種用法:
用來修飾成員變數,將其變為類的成員,從而實現所有物件對於該成員的共享;
用來修飾成員方法,將其變為類方法,可以直接使用“類名.方法名”的方式呼叫,常用於工具類;
靜態塊用法,將多個類成員放在一起初始化,使得程式更加規整,其中理解物件的初始化過程非常關鍵;
靜態導包用法,將類的方法直接匯入到當前類中,從而直接使用“方法名”即可呼叫類方法,更加方便。