static的用法,較比原文添加了static靜態內部類。java
另外添加了靜態內部類和非靜態內部類的用法:jvm
--------------------------------------------------------函數
在java的關鍵字中,static和final是兩個咱們必須掌握的關鍵字。不一樣於其餘關鍵字,他們都有多種用法,並且在必定環境下使用,能夠提升程序的運行性能,優化程序的結構。下面咱們先來了解一下static關鍵字及其用法。工具
在咱們平時的使用當中,static最經常使用的功能就是修飾類的屬性和方法,讓他們成爲類的成員屬性和方法,咱們一般將用static修飾的成員稱爲類成員或者靜態成員,這句話挺起來都點奇怪,其實這是相對於對象的屬性和方法來講的。請看下面的例子:(未避免程序太過臃腫,暫時無論訪問控制)性能
public class Person {
String name;
int age;
public String toString() {
return "Name:" + name + ", Age:" + age;
}
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "zhangsan";
p1.age = 10;
Person p2 = new Person();
p2.name = "lisi";
p2.age = 12;
System.out.println(p1);
System.out.println(p2);
}
/**Output
* Name:zhangsan, Age:10
* Name:lisi, Age:12
*///~
}優化
上面的代碼咱們很熟悉,根據Person構造出的每個對象都是獨立存在的,保存有本身獨立的成員變量,相互不會影響,他們在內存中的示意以下:spa
從上圖中能夠看出,p1和p2兩個變量引用的對象分別存儲在內存中堆區域的不一樣地址中,因此他們之間相互不會干擾。但其實,在這當中,咱們省略了一些重要信息,相信你們也都會想到,對象的成員屬性都在這了,由每一個對象本身保存,那麼他們的方法呢?實際上,不論一個類建立了幾個對象,他們的方法都是同樣的:code
從上面的圖中咱們能夠看到,兩個Person對象的方法實際上只是指向了同一個方法定義。這個方法定義是位於內存中的一塊不變區域(由jvm劃分),咱們暫稱它爲靜態存儲區。這一塊存儲區不只存放了方法的定義,實際上從更大的角度而言,它存放的是各類類的定義,當咱們經過new來生成對象時,會根據這裏定義的類的定義去建立對象。多個對象僅會對應同一個方法,這裏有一個讓咱們充分信服的理由,那就是無論多少的對象,他們的方法老是相同的,儘管最後的輸出會有所不一樣,可是方法老是會按照咱們預想的結果去操做,即不一樣的對象去調用同一個方法,結果會不盡相同。對象
咱們知道,static關鍵字能夠修飾成員變量和方法,來讓它們變成類的所屬,而不是對象的所屬,好比咱們將Person的age屬性用static進行修飾,結果會是什麼樣呢?請看下面的例子:內存
public class Person {
String name;
static int age;
/* 其他代碼不變... */
/**Output
* Name:zhangsan, Age:12
* Name:lisi, Age:12
*///~
}
咱們發現,結果發生了一點變化,在給p2的age屬性賦值時,干擾了p1的age屬性,這是爲何呢?咱們仍是來看他們在內存中的示意:
咱們發現,給age屬性加了static關鍵字以後,Person對象就再也不擁有age屬性了,age屬性會統一交給Person類去管理,即多個Person對象只會對應一個age屬性,一個對象若是對age屬性作了改變,其餘的對象都會受到影響。咱們看到此時的age和toString()方法同樣,都是交由類去管理。
雖然咱們看到static可讓對象共享屬性,可是實際中咱們不多這麼用,也不推薦這麼使用。由於這樣會讓該屬性變得難以控制,由於它在任何地方都有可能被改變。若是咱們想共享屬性,通常咱們會採用其餘的辦法:
public class Person {
private static int count = 0;
int id;
String name;
int age;
public Person() {
id = ++count;
}
public String toString() {
return "Id:" + id + ", Name:" + name + ", Age:" + age;
}
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "zhangsan";
p1.age = 10;
Person p2 = new Person();
p2.name = "lisi";
p2.age = 12;
System.out.println(p1);
System.out.println(p2);
}
/**Output
* Id:1, Name:zhangsan, Age:10
* Id:2, Name:lisi, Age:12
*///~
}
上面的代碼起到了給Person的對象建立一個惟一id以及記錄總數的做用,其中count由static修飾,是Person類的成員屬性,每次建立一個Person對象,就會使該屬性自加1而後賦給對象的id屬性,這樣,count屬性記錄了建立Person對象的總數,因爲count使用了private修飾,因此從類外面沒法隨意改變。
static的另外一個做用,就是修飾成員方法。相比於修飾成員屬性,修飾成員方法對於數據的存儲上面並無多大的變化,由於咱們從上面能夠看出,方法原本就是存放在類的定義當中的。static修飾成員方法最大的做用,就是可使用"類名.方法名"的方式操做方法,避免了先要new出對象的繁瑣和資源消耗,咱們可能會常常在幫助類中看到它的使用:
public class PrintHelper {
public static void print(Object o){
System.out.println(o);
}
public static void main(String[] args) {
PrintHelper.print("Hello world");
}
}
上面即是一個例子(如今還不太實用),可是咱們能夠看到它的做用,使得static修飾的方法成爲類的方法,使用時經過「類名.方法名」的方式就能夠方便的使用了,至關於定義了一個全局的函數(只要導入該類所在的包便可)。不過它也有使用的侷限,一個static修飾的類中,不能使用非static修飾的成員變量和方法,這很好理解,由於static修飾的方法是屬於類的,若是去直接使用對象的成員變量,它會不知所措(不知該使用哪個對象的屬性)。
在說明static關鍵字的第三個用法時,咱們有必要從新梳理一下一個對象的初始化過程。如下面的代碼爲例:
package com.dotgua.study;
class Book{
public Book(String msg) {
System.out.println(msg);
}
}
public class Person {
Book book1 = new Book("book1成員變量初始化");
static Book book2 = new Book("static成員book2成員變量初始化");
public Person(String msg) {
System.out.println(msg);
}
Book book3 = new Book("book3成員變量初始化");
static Book book4 = new Book("static成員book4成員變量初始化");
public static void main(String[] args) {
Person p1 = new Person("p1初始化");
}
/**Output
* static成員book2成員變量初始化
* static成員book4成員變量初始化
* book1成員變量初始化
* book3成員變量初始化
* p1初始化
*///~
}
上面的例子中,Person類中組合了四個Book成員變量,兩個是普通成員,兩個是static修飾的類成員。咱們能夠看到,當咱們new一個Person對象時,static修飾的成員變量首先被初始化,隨後是普通成員,最後調用Person類的構造方法完成初始化。也就是說,在建立對象時,static修飾的成員會首先被初始化,並且咱們還能夠看到,若是有多個static修飾的成員,那麼會按照他們的前後位置進行初始化。
實際上,static修飾的成員的初始化能夠更早的進行,請看下面的例子:
class Book{
public Book(String msg) {
System.out.println(msg);
}
}
public class Person {
Book book1 = new Book("book1成員變量初始化");
static Book book2 = new Book("static成員book2成員變量初始化");
public Person(String msg) {
System.out.println(msg);
}
Book book3 = new Book("book3成員變量初始化");
static Book book4 = new Book("static成員book4成員變量初始化");
public static void funStatic() {
System.out.println("static修飾的funStatic方法");
}
public static void main(String[] args) {
Person.funStatic();
System.out.println("****************");
Person p1 = new Person("p1初始化");
}
/**Output
* static成員book2成員變量初始化
* static成員book4成員變量初始化
* static修飾的funStatic方法
* ***************
* book1成員變量初始化
* book3成員變量初始化
* p1初始化
*///~
}
在上面的例子中咱們能夠發現兩個有意思的地方,第一個是當咱們沒有建立對象,而是經過類去調用類方法時,儘管該方法沒有使用到任何的類成員,類成員仍是在方法調用以前就初始化了,這說明,當咱們第一次去使用一個類時,就會觸發該類的成員初始化。第二個是當咱們使用了類方法,完成類的成員的初始化後,再new該類的對象時,static修飾的類成員沒有再次初始化,這說明,static修飾的類成員,在程序運行過程當中,只須要初始化一次便可,不會進行屢次的初始化。
回顧了對象的初始化之後,咱們再來看static的第三個做用就很是簡單了,那就是當咱們初始化static修飾的成員時,能夠將他們統一放在一個以static開始,用花括號包裹起來的塊狀語句中:
class Book{
public Book(String msg) {
System.out.println(msg);
}
}
public class Person {
Book book1 = new Book("book1成員變量初始化");
static Book book2;
static {
book2 = new Book("static成員book2成員變量初始化");
book4 = new Book("static成員book4成員變量初始化");
}
public Person(String msg) {
System.out.println(msg);
}
Book book3 = new Book("book3成員變量初始化");
static Book book4;
public static void funStatic() {
System.out.println("static修飾的funStatic方法");
}
public static void main(String[] args) {
Person.funStatic();
System.out.println("****************");
Person p1 = new Person("p1初始化");
}
/**Output
* static成員book2成員變量初始化
* static成員book4成員變量初始化
* static修飾的funStatic方法
* ***************
* book1成員變量初始化
* book3成員變量初始化
* p1初始化
*///~
}
咱們將上一個例子稍微作了一下修改,能夠看到,結果沒有二致。
相比於上面的三種用途,第四種用途可能瞭解的人就比較少了,可是實際上它很簡單,並且在調用類方法時會更方便。以上面的「PrintHelper」的例子爲例,作一下稍微的變化,便可使用靜態導包帶給咱們的方便:
上面的代碼來自於兩個java文件,其中的PrintHelper很簡單,包含了一個用於打印的static方法。而在App.java文件中,咱們首先將PrintHelper類導入,這裏在導入時,咱們使用了static關鍵字,並且在引入類的最後還加上了「.*」,它的做用就是將PrintHelper類中的全部類方法直接導入。不一樣於非static導入,採用static導入包後,在不與當前類的方法名衝突的狀況下,無需使用「類名.方法名」的方法去調用類方法了,直接能夠採用"方法名"去調用類方法,就好像是該類本身的方法同樣使用便可。
若是一個類要被聲明爲static的,只有一種狀況,就是靜態內部類。
靜態內部類實際上與普通類(即類名必須與文件名同樣的頂級類)同樣,只是靜態內部類在某一類的內部定義了而已,既然是類,要想使用就必須實例化。概念上與靜態變量、靜態方法是不同的,不要被「靜態」兩個字迷惑了(不要覺得凡是靜態的東西就不須要實例化就能夠直接使用,靜態內部類是有區別),並且只有靜態內部類,而沒有靜態類(頂級類)的概念。
例子:
public class Singleton{ private Singleton(){} private static class SingletonHolder{ private final static Singleton instance; } public Singleton getInstance(){ return SingletonHolder.instance; } }
/* 下面程序演示如何在java中建立靜態內部類和非靜態內部類 */ class OuterClass{ private static String msg = "GeeksForGeeks"; // 靜態內部類 public static class NestedStaticClass{ // 靜態內部類只能訪問外部類的靜態成員 public void printMessage() { // 試着將msg改爲非靜態的,這將致使編譯錯誤 System.out.println("Message from nested static class: " + msg); } } // 非靜態內部類 public class InnerClass{ // 不論是靜態方法仍是非靜態方法均可以在非靜態內部類中訪問 public void display(){ System.out.println("Message from non-static nested class: "+ msg); } } } class Main { // 怎麼建立靜態內部類和非靜態內部類的實例 public static void main(String args[]){ // 建立靜態內部類的實例 OuterClass.NestedStaticClass printer = new OuterClass.NestedStaticClass(); // 建立靜態內部類的非靜態方法 printer.printMessage(); // 爲了建立非靜態內部類,咱們須要外部類的實例 OuterClass outer = new OuterClass(); OuterClass.InnerClass inner = outer.new InnerClass(); // 調用非靜態內部類的非靜態方法 inner.display(); // 咱們也能夠結合以上步驟,一步建立的內部類實例 OuterClass.InnerClass innerObject = new OuterClass().new InnerClass(); // 一樣咱們如今能夠調用內部類方法 innerObject.display(); } }
static是java中很是重要的一個關鍵字,並且它的用法也很豐富,主要有四種用法: