一、static方法
#static方法一般稱為靜態方法,由於靜態方法不依賴任何對象就可以進行訪問,因此對於靜態方法來說,是沒有this的,因為它不依附於任何對象,既然都沒有對象,就談不上this了。而由於這個特性,在靜態方法中不能存取類別的非靜態成員變數和非靜態成員方法,因為非靜態成員方法/變數都是必須依賴特定的物件才能夠被呼叫。
但是要注意的是,雖然在靜態方法中不能存取非靜態成員方法和非靜態成員變量,但是在非靜態成員方法中是可以存取靜態成員方法/變數的。舉個簡單的例子:
在上面的程式碼中,由於print2方法是獨立於物件存在的,可以直接用過類別名稱呼叫。假如說可以在靜態方法中存取非靜態方法/變數的話,那麼如果在main方法中有下面一條語句:
MyObject.print2();
此時物件都沒有,str2根本就不存在,所以就會產生矛盾了。同樣對於方法也是一樣,由於你無法預知在print1方法中是否存取了非靜態成員變量,所以也禁止在靜態成員方法中存取非靜態成員方法。
而對於非靜態成員方法,它存取靜態成員方法/變數顯然是毫無限制的。
因此,如果說想在不建立物件的情況下呼叫某個方法,就可以將這個方法設為static。我們最常見的static方法就是main方法,至於為什麼main方法必須是static的,現在就很清楚了。因為程式在執行main方法的時候沒有建立任何對象,因此只有透過類別名稱來存取。
也記住,關於建構器是否為static方法可參考:http://blog.csdn.net/qq_17864929/article/details/48006835
#二、static變數
static變數也稱為靜態變量,靜態變數和非靜態變數的差異是:靜態變數被所有的物件共享,在記憶體中只有一個副本【存放在方法區】,它當且僅當在類別初次載入時會被初始化【加final和不加final的static變數初始化的位置不一樣】。而非靜態變數是物件所擁有的,在創建物件的時候被初始化,存在多個副本,各個物件擁有的副本互不影響。
static成員變數的初始化順序依照定義的順序進行初始化。
三、static程式碼區塊
static關鍵字還有一個比較關鍵的功能就是用來形成靜態程式碼區塊以優化程式效能。 static區塊可以置於類別中的任何地方,類別中可以有多個static區塊。在類別初次載入的時候,會按照static塊的順序來執行每個static塊,並且只會執行一次【根據class加載原理 每個類別加載一次 使用雙親委託加載】。
初始化的順序靜態程式碼區塊> 建構程式碼區塊> 建構子
public class Client { {//构造代码块 System.out.println("执行构造代码块"); } }
為什麼說static區塊可以用來最佳化程式效能,是因為它的特性:只會在類別中加載的時候執行一次。下面看個例子:
class Person{ private Date birthDate; public Person(Date birthDate) { this.birthDate = birthDate; } boolean isBornBoomer() { Date startDate = Date.valueOf("1946"); Date endDate = Date.valueOf("1964"); return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0; } }
isBornBoomer是用來這個人是否是1946-1964年出生的,而每次isBornBoomer被調用的時候,都會生成startDate和birthDate兩個對象,造成了空間浪費,如果改成這樣效率會更好,其實就是利用了靜態程式碼區塊在記憶體中值載入一次的機制:
class Person{ private Date birthDate; private static Date startDate,endDate; static{ startDate = Date.valueOf("1946"); endDate = Date.valueOf("1964"); } public Person(Date birthDate) { this.birthDate = birthDate; } boolean isBornBoomer() { return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0; } }
因此,很多時候會將一些只需要進行一次的初始化操作都放在static代碼塊中進行。
四、靜態內部類別
這個地方不單獨寫靜態內部類,透過和普通的內部類別對比來加深對靜態內部類別的理解:
為何要用內部類別?
1. 內部類別一般只為其外部類別使用;【供外部類別使用說的很好舉例hashmap集合中有一個內部類別Entry 就是轉為hashmap 儲存來使用】
# 2. 內部類別提供了某種進入外部類別的窗戶,內部類別存在外部類別的引用,所以內部類別可以直接存取外部類別的屬性;
3. 也是最吸引人的原因,每個內部類別都能獨立地繼承一個接口,而無論外部類別是否已經繼承了某個接口。因此,內部類別使多重繼承的解決方案變得更加完整。
定義在一個類別內部的類別叫內部類,包含內部類別的類別稱為外部類別。內部類別可以聲明public、protected、private等存取限制,可以聲明 為abstract的供其他內部類別或外部類別繼承與擴展,或聲明為static、final的,也可以實作特定的介面。
外部类按常规的类访问方式(以对象的方式)使用内部 类,唯一的差别是外部类可以访问内部类的所有方法与属性,包括私有方法与属性,外部类访问内部类,需要创建对象访问;有一点需要注意,内部类不能访问外部类所在的局部变量,只能访问final修饰的局部变量。
在方法内定义内部类时,如果内部类调用了方法中的变量,那么该变量必须申明为final类型,百思不得其解,后来想到应该是生命周期的原因,因为方法内定义的变量是局部变量,离开该方法,变量就失去了作用,也就会自动被消除,而内部类却不会离开它所在方法就失去作用,它有更广的生命周期。
(1)创建实例
OutClass.InnerClass obj = outClassInstance.new InnerClass(); //注意是外部类实例.new,内部类 AAA.StaticInner in = new AAA.StaticInner();//注意是外部类本身,静态内部类
(2)内部类中的this
内部类中的this与其他类一样是指的本身。创建内部类对象时,它会与创造它的外围对象有了某种联系,于是能访问外围类的所有成员,不需任何特殊条件,可理解为内部类链接到外部类。 用外部类创建内部类对象时,此内部类对象会秘密的捕获一个指向外部类的引用,于是,可以通过这个引用来访问外围类的成员。
(3)外部类访问内部类
内部类类似外部类的属性,因此访问内部类对象时总是需要一个创建好的外部类对象。外部类对象通过‘外部类名.this.xxx’的形式访问内部类的属性与方法。如:
System.out.println("Print in inner Outer.index=" + pouter.this.index); System.out.println("Print in inner Inner.index=" + this.index);
(4)内部类向上转型
内部类也可以和普通类一样拥有向上转型的特性。将内部类向上转型为基类型,尤其是接口时,内部类就有了用武之地。如果内部类是private的,只可以被它的外部类问,从而完全隐藏实现的细节。
(5)方法内的类
方法内创建的类(注意方法中也能定义类),不能加访问修饰符。另外,方法内部的类也不是在调用方法时才会创建的,它们一样也被事先编译了。
(6)静态内部类
定义静态内部类:在定义内部类的时候,可以在其前面加上一个权限修饰符static。此时这个内部类就变为了静态内部类。
通常称为嵌套类,当内部类是static时,意味着:
[1]要创建嵌套类的对象,并不需要其外围类的对象;
[2]不能从嵌套类的对象中访问非静态的外围类对象(不能够从静态内部类的对象中访问外部类的非静态成员);
嵌 套类与普通的内部类还有一个区别:普通内部类的字段与方法,只能放在类的外部层次上,所以普通的内部类不能有static数据和static字段, 也不能包含嵌套类。但是在嵌套类里可以包含所有这些东西。也就是说,在非静态内部类中不可以声明静态成员,只有将某个内部类修饰为静态类,然后才能够在这 个类中定义静态的成员变量与成员方法。
另外,在创建静态内部类时不需要将静态内部类的实例绑定在外部类的实例上。普通非静态内部类的 对象是依附在外部类对象之中的,要在一个外部类中定义一个静态的内部类,不需要利用关键字new来创建内部类的实例。静态类和方法只属于类本身,并不属于 该类的对象,更不属于其他外部类的对象。
(7)内部类标识符
每个类会产生一个.class文件,文件名即为类名。同样,内部类也会产生这么一个.class文件,但是它的名称却不是内部类的类名,而是有着严格的限制:外围类的名字,加上$,再加上内部类名字。
代码具体:
public class OutClassTest { static int a; int b; public static void test() { System.out.println("outer class static function"); } public static void main(String[] args) { // new一个外部类 OutClassTest oc1 = new OutClassTest(); // 通过外部类的对象new一个非静态的内部类 OutClassTest.InnerClass no_static_inner = oc1.new InnerClass(); // 调用非静态内部类的方法 System.out.println(no_static_inner.getKey()); // 调用静态内部类的静态变量 System.out.println(OutClassTest.InnerStaticClass.static_value); // 不依赖于外部类实例,直接实例化内部静态类 OutClassTest.InnerStaticClass inner = new OutClassTest.InnerStaticClass(); // 调用静态内部类的非静态方法 System.out.println(inner.getValue()); // 调用内部静态类的静态方法 System.out.println(OutClassTest.InnerStaticClass.getMessage()); } private class InnerClass { // 只有在静态内部类中才能够声明或定义静态成员 // private static String tt = "0"; private int flag = 0; public InnerClass() { // 三.非静态内部类的非静态成员可以访问外部类的非静态变量和静态变量 System.out.println("InnerClass create a:" + a); System.out.println("InnerClass create b:" + b); System.out.println("InnerClass create flag:" + flag); // System.out.println("InnerClass call outer static function"); // 调用外部类的静态方法 test(); } public String getKey() { return "no-static-inner"; } } private static class InnerStaticClass { // 静态内部类可以有静态成员,而非静态内部类则不能有静态成员。 private static String static_value = "0"; private int flag = 0; public InnerStaticClass() { System.out.println("InnerClass create a:" + a); // 静态内部类不能够访问外部类的非静态成员 // System.out.println("InnerClass create b:" + b); System.out.println("InnerStaticClass flag is " + flag); System.out.println("InnerStaticClass tt is " + static_value); } public int getValue() { // 静态内部类访问外部类的静态方法 test(); return 1; } public static String getMessage() { return "static-inner"; } } public OutClassTest() { // new一个非静态的内部类 InnerClass ic = new InnerClass(); System.out.println("OuterClass create"); } }
有就是类名ClassName后面多了个.* ,意思是导入这个类里的静态方法。当然,也可以只导入某个静态方法,只要把 .* 换成静态方法名就行了。然后在这个类中,就可以直接用方法名调用静态方法,而不必用ClassName.方法名 的方式来调用。
好处:这种方法的好处就是可以简化一些操作,例如打印操作System.out.println(…);就可以将其写入一个静态方法print(…),在使用时直接print(…)就可以了。但是这种方法建议在有很多重复调用的时候使用,如果仅有一到两次调用,不如直接写来的方便
example:
在Java 5中,import语句得到了增强,以便提供甚至更加强大的减少击键次数功能,虽然一些人争议说这是以可读性为代价的。这种新的特性成为静态导入。当你想使用static成员时,可以使用静态导入(在API中的类和你自己的类上,都可以使用该特性)。下面是静态导入前后的代码实例:
在静态导入之前:
public class TestStatic { public static void main(String[] args) { System.out.println(Integer.MAX_VALUE); System.out.println(Integer.toHexString(42)); } }
在静态导入之后:
import static java.lang.System.out; import static java.lang.Integer.*; public class TestStaticImport { public static void main(String[] args) { out.println(MAX_VALUE); out.println(toHexString(42)); } }
让我们看一下使用静态导入特性的代码中将发生什么:
1、雖然該特性通常稱為“靜態導入”,但語法必須是import static,後面跟你想導入的static成員的完全限定名稱,或者通配符。在本例中,我們在System類別的out物件上進行靜態導入。
2、在本例中,我們可能想要使用java.lang.Integer類別的幾個static成員。這個靜態導入語句使用通配符來表達「我想在此類中的所有靜態成員上進行靜態導入」。
3、現在我們終於看到靜態導入特性的好處!我們不必在System.out.println中鍵入System。太好了!另外,我們不必在Integer.MAX_VALUE中鍵入Integer。因此,在這行程式碼中,我們能夠將捷徑用於靜態方法和一個常數。
4、最後,我們進行更多的快捷操作,這次針對Integer類別的方法。
關於這個特性,我們已經有點諷刺了,但不只我們是這樣的。我們不認為節省少量的擊鍵次數會讓程式碼難以閱讀一點,但許多開發人員要求將它添加到語言中。
以下是使用靜態導入的幾個原則:
你必須說import static,你不能說static import。
提防含糊不清的命名static成員。例如,如果你對Integer類別和Long類別執行了靜態導入,引用MAX_VALUE將導致一個編譯器錯誤,因為Integer和Long都有一個MAX_VALUE常數,並且Java不會知道你在引用哪個MAX_VALUE。
你可以在static物件參考、常數(記住,它們是static 或final)和static方法上進行靜態導入。
眾多java訓練影片,盡在PHP中文網,歡迎線上學習!
以上是java static是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!