(一)什么情況下我們要使用static呢暑竟?
1、只想用一個(gè)存儲區(qū)域來保存一個(gè)特定的數(shù)據(jù)——無論要創(chuàng)建多少個(gè)對象症概,甚至根本不創(chuàng)建對象蕾额。
2、我們需要一個(gè)特殊的方法彼城,它沒有與這個(gè)類的任何對象關(guān)聯(lián)诅蝶。也就是說,即使沒有創(chuàng)建對象募壕,也需要一個(gè)能調(diào)用的方法调炬。
(二)static表示“全局”或者“靜態(tài)”的意思,用來修飾成員變量和成員方法舱馅,也可以形成靜態(tài)static代碼塊筐眷,但是Java語言中沒有全局變量的概念。
被static修飾的成員變量和成員方法獨(dú)立于該類的任何對象习柠。也就是說,它不依賴類特定的實(shí)例照棋,被類的所有實(shí)例共享资溃。只要這個(gè)類被加載,Java虛擬機(jī)就能根據(jù)類名在運(yùn)行時(shí)數(shù)據(jù)區(qū)的方法區(qū)內(nèi)定找到他們烈炭。因此溶锭,static對象可以在它的任何對象創(chuàng)建之前訪問,無需引用任何對象符隙。
(三)(1)用public修飾static成員趴捅,表示它們是全局成員(成員變量和成員方法),當(dāng)生成類的對象時(shí)霹疫,不為每一個(gè)對象生成static變量的副本拱绑,Java只為static變量生成一個(gè)副本,所有的類的對象共享這一個(gè)副本丽蝎;當(dāng)不存在任何類對象時(shí)猎拨,要訪問一個(gè)public的static成員,用類名.成員訪問。
(2)用private修飾的static成員红省,只能通過該類的方法進(jìn)行訪問额各。如果不存在任何類的對象時(shí)訪問一
個(gè)private的static成員,則必須提供一個(gè)public的static方法吧恃,并且在調(diào)用該方法時(shí)虾啦,必須添加類名和點(diǎn)運(yùn)算
符以進(jìn)行限制。
(四)static修飾的成員變量和成員方法習(xí)慣上稱為靜態(tài)變量和靜態(tài)方法痕寓,可以直接通過類名來訪問傲醉,訪問語
法為:
(1)類名.靜態(tài)方法名(參數(shù)列表...)
(2)類名.靜態(tài)變量名
(3)static {
// whatever code is needed for initialization goes here
}
形如這樣的式子----用static修飾的代碼塊表示靜態(tài)代碼塊,當(dāng)Java虛擬機(jī)(JVM)加載類時(shí)厂抽,就會執(zhí)行該代碼塊
(五)static變量
按照是否靜態(tài)的對類成員變量進(jìn)行分類可分兩種:一種是被static修飾的變量需频,叫靜態(tài)變量或類變量;另一種是沒有被static修飾的變量筷凤,叫實(shí)例變量昭殉。兩者的區(qū)別是: 對于靜態(tài)變量在內(nèi)存中只有一個(gè)拷貝(節(jié)省內(nèi)存),JVM只為靜態(tài)分配一次內(nèi)存藐守,在加載類的過程中完成靜態(tài)變量的內(nèi)存分配挪丢,可用類名直接訪問(方便),當(dāng)然也可以通過對象來訪問(但是這是不推薦的)卢厂。 對于實(shí)例變量乾蓬,沒創(chuàng)建一個(gè)實(shí)例,就會為實(shí)例變量分配一次內(nèi)存慎恒,實(shí)例變量可以在內(nèi)存中有多個(gè)拷貝任内,互不影響(靈活)。
(六)靜態(tài)方法
靜態(tài)方法可以直接通過類名調(diào)用融柬,任何的實(shí)例也都可以調(diào)用死嗦,因此靜態(tài)方法中不能用this和super關(guān)鍵字,不能直接訪問所屬類的實(shí)例變量和實(shí)例方法(就是不帶static的成員變量和成員成員方法)粒氧,只能訪問所屬類的靜態(tài)成員變量和成員方法越除。因?yàn)閷?shí)例成員與特定的對象關(guān)聯(lián)!這個(gè)需要去理解外盯,想明白其中的道理摘盆,不是記憶!
因?yàn)閟tatic方法獨(dú)立于任何實(shí)例饱苟,因此static方法必須被實(shí)現(xiàn)孩擂,而不能是抽象的abstract。
(七)static代碼塊
static代碼塊也叫靜態(tài)代碼塊箱熬,是在類中獨(dú)立于類成員的static語句塊肋殴,可以有多個(gè)囤锉,位置可以隨便放,它不在任何的方法體內(nèi)护锤,JVM加載類時(shí)會執(zhí)行這些靜態(tài)的代碼塊官地,如果static代碼塊有多個(gè),JVM將按照它們在類中出現(xiàn)的先后順序依次執(zhí)行它們烙懦,每個(gè)代碼塊只會被執(zhí)行一次驱入。例如:
public class Test5 {
private static int a;
private int b;
static{
Test5.a=3;
System.out.println(a);
Test5 t=new Test5();
t.f();
t.b=1000;
System.out.println(t.b);
}
static{
Test5.a=4;
System.out.println(a);
}
public static void main(String[] args) {
// TODO 自動生成方法存根
}
static{
Test5.a=5;
System.out.println(a);
}
public void f(){
System.out.println("hhahhahah");
}
}
運(yùn)行結(jié)果:
3
hhahhahah
1000
4
5
這個(gè)例子的運(yùn)行順序是:首先Java虛擬機(jī)調(diào)用main方法(因?yàn)閙ian方法是程序的入口),main方法位于Test5類中氯析,此main方法是靜態(tài)的亏较,此時(shí)Java解釋器查找路徑,找到Test5.class文件掩缓,加載Test5.class雪情。于此同時(shí)靜態(tài)初始化開始了(包括靜態(tài)變量和靜態(tài)代碼塊的初始化,靜態(tài)方法(除了main)不可以初始化你辣,只可以用實(shí)例方法巡通、靜態(tài)方法調(diào)用或者類名、對象調(diào)用)舍哄。
(八)總結(jié)static變量和方法的訪問限制:
(1)實(shí)例方法可以訪問實(shí)例變量和實(shí)例方法
(2)實(shí)例方法可以訪問靜態(tài)變量和靜態(tài)方法
(3)靜態(tài)方法可以訪問靜態(tài)變量和靜態(tài)方法
(4)靜態(tài)方法不能訪問實(shí)例變量和實(shí)例方法宴凉。靜態(tài)方法中不能使用關(guān)鍵字this和super
(九)static final一起修飾的作用
從字面意思看,表示全局常量表悬。static final修飾的變量只占據(jù)“一段”存儲空間弥锄,這個(gè)存儲空間不可以改變。也就是賦予它的值是不可以改變的蟆沫。如果是基本類型籽暇,那么數(shù)值不可變;如果是引用變量饭庞,那么引用變量不變戒悠,一旦引用被初始化指向一個(gè)對象,就不可以把它指向另一個(gè)對象但绕,但是這個(gè)對象的內(nèi)容可以改變,也就是說這個(gè)對象里面的變量的值可以改變惶看。比如:下面標(biāo)記***的地方
//: c06:FinalData.java
import com.bruceeckel.simpletest.;
import java.util.;
class Value {
int i; // Package access
public Value(int i) { this.i = i; } // 注意;這里的i就是類的內(nèi)容可以改變
}
public class FinalData {
private static Test monitor = new Test();
private static Random rand = new Random();
private String id;
public FinalData(String id) { this.id = id; }
// Can be compile-time constants:
private final int VAL_ONE = 9;
private static final int VAL_TWO = 99;
// Typical public constant:
public static final int VAL_THREE = 39;
// Cannot be compile-time constants:
private final int i4 = rand.nextInt(20);
static final int i5 = rand.nextInt(20);
private Value v1 = new Value(11);
private final Value v2 = new Value(22);
private static final Value v3 = new Value(33);
// Arrays:
private final int[] a = { 1, 2, 3, 4, 5, 6 };
public String toString() {
return id + ": " + "i4 = " + i4 + ", i5 = " + i5;
}
public static void main(String[] args) {
FinalData fd1 = new FinalData("fd1");
//! fd1.VAL_ONE++; // Error: can't change value
fd1.v2.i++; // Object isn't constant!
fd1.v1 = new Value(9); // OK -- not final
for(int i = 0; i < fd1.a.length; i++)
fd1.a[i]++; // Object isn't constant!
//! fd1.v2 = new Value(0); // Error: Can't
//! fd1.v3 = new Value(1); // change reference
//! fd1.a = new int[3];
System.out.println(fd1);
System.out.println("Creating new FinalData");
FinalData fd2 = new FinalData("fd2");
System.out.println(fd1);
System.out.println(fd2);
monitor.expect(new String[] {
"%% fd1: i4 = //d+, i5 = //d+",
"Creating new FinalData",
"%% fd1: i4 = //d+, i5 = //d+",
"%% fd2: i4 = //d+, i5 = //d+"
});
}
} ///:~
例(二)
package c06.net;
class Value{
static int c=0;
Value(){
c=15;
}
Value(int i){
c=i;
}
static void inc(){
c++;
}
}
class Count{
public static void prt(String s){
System.out.println(s);
}
Value v=new Value(10);
static Value v1,v2;
static{
prt( "v1.c= "+v1.c+ " v2.c= "+v2.c);
v1=new Value(27);
prt( "v1.c= "+v1.c+ " v2.c= "+v2.c);
v2=new Value(15);
prt( "v1.c= "+v1.c+ " v2.c= "+v2.c);
}
public static void main(String[] args){
Count ct=new Count();
prt( "ct.c= "+ct.v.c);
prt( "v1.c= "+v1.c+ " v2.c= "+v2.c);
v1.inc();
prt( "v1.c= "+v1.c+ " v2.c= "+v2.c);
prt( "ct.c= "+ct.v.c);
}
}
運(yùn)行結(jié)果:
v1.c=0 v2.c=0
v1.c=27 v2.c=27
v1.c=15 v2.c=15
ct.c=10
v1.c=10 v2.c=10
v1.c=11 v2.c=11
ct.c=11