為什么java中一切都是對象,還要有static? [復制鏈接]

2019-11-13 10:09
技術小兵 閱讀:474 評論:0 贊:1
Tag:  static

Java是一種面向對象編程的語言,而對象是客觀存在的事物,對同類對象抽象出其共性,便是Java中的類,類是對象的模子,具有相同屬性和方法的一組對象的集合。有了類的定義,我們就可以用類來描述世上任何東西。然而,有一個特殊的東西不屬于對象,它就是static。

1. static的“由來”

咱們先來分析一下以下代碼:

public class Demo {
public static void main(String[] args) {
//實例化一個p1
Person p1=new Person();
p1.name="xiaoming";  
p1.speak();
//實例化一個p2
Person p2=new Person();
p2.name="xiaohong";  
p2.speak();
}
}

class Person{
String name;
//CN是顯示初始化值
String country="China";
void speak(){
System.out.println("My name is "+name+",my country is "+country);
}
}

??通過分析上面的代碼,不難發現p1和p2兩個對象中,存在一個相同的屬性值country="China"。接下來我們通過內存示意圖來觀察下:

為什么java中一切都是對象,還要有static?

  從上圖中我們可以注意到紅色框中的內容是重復存在于堆中的。倘若對象越建越多,那么堆中的重復數據也會越來越多,這樣就會導致內存造成不必要的消耗。既然各個對象中都存在同樣的數據,那么何不將這些相同的數據單獨拿出來并且單獨存放呢?現在將Person類中的country字段加上static,其他不做改動:

class Person{
String name;
//將數據值相同且共用的用static修飾,成為類變量
static String country="CN"; 
void speak(){
System.out.println("My name is "+name+",my country is "+country);
}
}

  于是,我們將相同的數據放到靜態存儲區內,并讓靜態存儲區中的數據共享給所有對象,這樣就可以大大減少了不必要的內存損耗,可見static關鍵字的作用。

2.static的使用場景

在《Java編程思想》有這樣一段話:
  “static方法就是沒有this的方法。在static方法內部不能調用非靜態方法,反過來是可以的。而且可以在沒有創建任何對象的前提下,僅僅通過類本身來調用static方法。這實際上正是static方法的主要用途。”
  這段話雖然只是說明了static方法的特殊之處,但是可以看出static關鍵字的基本作用,簡而言之,一句話來描述就是:方便在沒有創建對象的情況下來進行調用(方法/變量)。下面我們就來探討一下static的幾個使用場景。

1)修飾成員變量和成員方法

??用static修飾成員變量可以說是該關鍵字最常用的一個功能,通常將用static修飾的成員變量稱為類成員或者靜態成員。本文就是通過該使用場景拋磚引玉,描述了static關鍵字的“由來”。在jvm的內存構造中,會在堆中開辟一塊內存空間,專門用來存儲用static修飾的成員變量,稱為靜態存儲區,無論我們創建多少個對象,用 static 修飾的成員變量有且只有一份存儲在靜態存儲區中,所以該靜態變量的值是以最后創建對象時設置該靜態變量的值為準。并且任何對象都可以對靜態存儲區中的成員變量做出修改,靜態存儲區中的成員變量的值以最后一次修改為準。值得一提的是,在JDK1.8以前,靜態存儲區是存放在方法區的,而方法區不屬于堆,在JDK1.8之后,方法區中的靜態存儲區改為到堆中存儲。
  用static修飾方法一般稱作靜態方法,由于靜態方法不依賴于任何對象就可以進行訪問,因此對于靜態方法來說,是沒有this指針的,因為它不依附于任何對象,既然都沒有對象,就談不上this了。并且由于這個特性,在靜態方法中不能訪問類的非靜態成員變量和非靜態成員方法,因為非靜態成員方法/變量都是必須依賴具體的對象才能夠被調用。但是要注意的是,雖然在靜態方法中不能訪問非靜態成員方法和非靜態成員變量,但是在非靜態成員方法中是可以訪問靜態成員方法/變量的。

2)靜態代碼塊

??靜態代碼塊定義在類中方法外, 靜態代碼塊在非靜態代碼塊之前執行,執行順序為靜態代碼塊—>非靜態代碼塊—>構造方法—>靜態方法。我們來運行以下程序:

Public class Main {
Static {
System.out.println(“靜態代碼塊”);
}
{
System.out.println(“非靜態代碼塊”);
}
public Main(){
System.out.println(“構造方法”);
}
public static void eat(){
System.out.println(“靜態方法”);
}
public static void main(String[] args) {
Main main = new Main();
Main.eat();
}
}
運行結果:
靜態代碼塊
非靜態代碼塊
構造方法
靜態方法

??而在java繼承關系中靜態代碼塊、代碼塊、構造方法的執行順序為父類靜態代碼塊—>子類靜態代碼塊—>父類代碼塊—>父類構造—>子類代碼塊—>子類構造,可以通過下列代碼來判斷:

public class Demo {
public static void main(String[] args) {
new H2();
}
}
//父類H1
class H1{
{
System.out.println("父類代碼塊");
}
public H1(){
System.out.println("父類構造");
}
static{
System.out.println("父類靜態代碼塊");
}
}
//H2繼承H1
class H2 extends H1{
static{
System.out.println("子類靜態代碼塊");
}
{
System.out.println("子類代碼塊");
}
public H2(){
System.out.println("子類構造");
}
}
運行結果:
父類靜態代碼塊
子類靜態代碼塊
父類代碼塊
父類構造
子類代碼塊
子類構造

??static關鍵字可以利用這些特性,通過形成靜態代碼塊來優化程序性能。靜態代碼塊可以存在于類的任何一個地方,并且一個類可以有多個靜態代碼塊,在類初次被加載的時候,會按照靜態代碼塊聲明的順序來依此執行,并且只會執行一次。那么,我們就可以利用靜態代碼塊只加載一次的特性,將不需要重復加載的數據放入靜態代碼塊中來優化程序。

3)靜態內部類

??內部類指的是定義在一個類的內部的類,包含內部類的類成為外部類,而靜態內部類指的是用 static 修飾的內部類。定義內部類的好處是外部類可以訪問內部類的所有方法和屬性,包括私有方法和私有屬性。下面通過代碼來理解靜態內部類的使用:

//定義一個普通外部類
public class OutClass {
//定義一個普通內部類
public class InnerClass{

}
}

??這里我們要想訪問普通內部類,就必須先創建外部類的對象,然后通過外部類名.new 創建內部類的實例,例如第一步:OuterClass oc = new OuterClass();第二步:OuterClass.InnerClass in = oc.new InnerClass();。顯然,這樣的做法不是很友好。

//定義一個普通外部類
public class OutClass {
//定義一個靜態內部類
public static class InnerClass{

}
}

??如果將普通內部類改為靜態內部類,添加static關鍵字,我們就不需要創建外部類的對象,而是可以直接通過外部類名.內部類名來創建實例,例如OuterClass.InnerClass sic = new OuterClass.InnerClass();。這樣就會顯得簡單很多。

3.關于static的常見問題

1)static關鍵字會改變類中成員的訪問權限嗎?

不會。Java中的static關鍵字不會影響到變量或者方法的作用域。在Java中能夠影響到訪問權限的只有private、public、protected(包括包訪問權限)這幾個關鍵字。

2)靜態變量可以存在于普通方法中嗎?

可以。普通方法必須通過對象來調用,靜態變量可以直接通過類名來調用,也可以通過對象來調用,所以是可以存在于普通方法中的。

3)靜態方法能存在普通變量嗎?

不能。因為靜態方法可以直接通過類名來直接調用,不用創建對象,而普通變量是必須通過對象來調用的。

4)static 可以用來修飾局部變量嗎?

不能。在C/C++中static是可以作用域局部變量的,但是在Java中切記:static是不允許用來修飾局部變量。不要問為什么,這是Java語法的規定。

4.總結

??總的來說,static關鍵字主要作用就是方便在沒有創建對象的情況下進行調用方法/變量。當static修飾成員變量和方法時,該變量就成為了所有對象的公共數據,可以直接通過類名來調用而不用創建對象;當static修飾代碼塊時,該代碼塊會在類初次被加載的時候被加載,且只會加載一次,這里需要注意加載順序的問題;當static修飾內部類時,可以使得操作內部類變得想對簡單。另外,根據static的特性,產生了一種重要的設計模式——單例設計模式。這里做個簡單介紹,程序實例如下:

class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}

??單例設計模式的特點是該類只能有一個實例,為了實現這一功能,必須隱藏類的構造函數,即把構造函數聲明為private,并提供一個創建對象的方法,由于構造對象被聲明為private,外界無法直接創建這個類型的對象,這里就可以把創建對象的方法聲明為static,通過使用類名來調用方法創建對象,從而完成該模式的設計


我來說兩句
您需要登錄后才可以評論 登錄 | 立即注冊
facelist
所有評論(0)
領先的中文移動開發者社區
18620764416
7*24全天服務
意見反饋:[email protected]

掃一掃關注我們

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粵ICP備15117877號 )

龙江福彩p62开奖 鸿利彩票游戏 528千炮捕鱼游戏 开采石子厂很赚钱 来玩安徽麻将作弊器 出租证书哪个赚钱 抖音怎么发广告赚钱 51678炮金蟾捕鱼下载 做大型工地的电线电缆赚钱吗 全民麻将下载全集 魔兽世界地图 有哪些手机app可以赚钱的 麻将棋牌辅助器免费版 白天有轿车怎样赚钱 3m之类的是怎么赚钱 88彩票网址 星际战甲怎末赚钱