目錄
引言
要明白Java中變量的初始化,那麼首先需要知道Java中存在哪些變量。一般來說,在Java中存在2種變量,一種是類的成員變量,另一種則是定義在函數(方法)裏的局部變量。正如Java編程思想中所述,Java保證所有變量在使用前一定被初始化。對於類的成員變量,即使我們不進行初始化,Java也會進行默認初始化。而對於方法中的局部變量,Java以編譯時錯誤的形式貫徹這種保證。
默認初始化
Java中基本數據類型的初始化,如下表所展示的,而對於引用類型,則會初始化爲null。
基本數據類型 |
默認值 |
boolean |
false |
char |
‘\u0000’(null) |
byte |
(byte)0 |
short |
(short)0 |
int |
0 |
long |
0L |
float |
0.0f |
double |
0.0d |
相關概念
1.靜態變量
使用static修飾的成員變量。
static People p1 = new People();
static int count;
靜態變量屬於類,可直接通過類名進行調用,也可使用引用(對象名)進行調用。但是靜態變量只在類初次加載到JVM時纔會進行初始化,並且只初始化一次,無論之後創建了該類的對象有多少個,初始化動作都只做一次,靜態代碼塊也同樣如此。
類初次加載到JVM
那麼什麼叫做類初次加載到JVM呢?這裏分2種情況,1)首次生成該類的對象;2)使用到該類的靜態數據成員,而此時並未生成該類的對象。這一點與實例變量有所不同,實例變量的初始化在創建該類的對象纔會進行。並且每次創建一個對象,這些實例變量都會進行初始化。
2.靜態代碼塊
使用static{ ...//代碼}進行表示。
3.實例變量
即沒有使用static修飾的成員變量,它屬於實例(對象),每次創建對象都會生成該實例變量,這與靜態初始化動作(靜態變量及靜態代碼塊)是有區別的。
4.非靜態代碼塊
使用{...//代碼}進行表示。
代碼示例
package com.thinkinginjava.chapter05;
public class ExplicitStatic
{
public static void main(String[] args) {
System.out.println("Inside main()");
Cups cups1 = new Cups();
}
}
class Cup1
{
Cup1(int marker) {
System.out.println("Cup1("+marker+")");
}
void f(int marker) {
System.out.println("f("+marker+")");
}
}
// Cup2的定義
class Cup2
{
Cup2(int marker){
System.out.println("Cup2("+marker+")");
}
void f2(int marker) {
System.out.println("f2("+marker+")");
}
}
class Cup3
{
Cup3(int marker){
System.out.println("Cup3("+marker+")");
}
}
class Cups
{// 用來測試靜態代碼塊 ,靜態變量,實例變量,非靜態代碼塊之間的先後執行順序
Cups(){ // 構造器
System.out.println("Cups() ");
}
Cup3 cup3_2 ;
{// 非靜態代碼塊
cup3_2 = new Cup3(2);
}
Cup3 cup3_1 = new Cup3(1);
static Cup2 cup2_1;
static Cup2 cup2_2;
static { // 靜態代碼塊
cup2_1 = new Cup2(1);
cup2_2 = new Cup2(2);
}
static Cup1 cup1 = new Cup1(1);
// static Cup1 cup1; // 也初始化了但是爲null沒有調用構造器看不出效果
static Cup1 cup2 = new Cup1(2);
}
變量初始化順序
對於前者,即類的成員變量,當你只聲明變量而沒有賦值時,Java會對這類變量自動初始化,當然自動初始化也遵循着一套規則。
不存在繼承關係
在沒有繼承的前提下,他們之間的初始化規則如下。
1)首先初始化由static修飾的代碼,靜態變量和靜態代碼塊的初始化順序是誰在前,誰先初始化;
2)實例變量和非靜態代碼塊的初始化順序也是誰在前誰先初始化;
3)而無論實例變量和非靜態代碼塊是否定義在靜態變量和靜態代碼塊之前,都是靜態變量和靜態代碼塊先初始化,而後纔是實例變量和非靜態代碼塊,最後纔是構造器的初始化。
從這裏可以看出,類的成員變量一定在類的成員方法(包括構造器)之前得到初始化,無論成員變量的定義代碼在構造器的前面還是後面,都是如此。
存在繼承關係
那如果引入更復雜的基類和子類之間的繼承關係,初始化順序又該如何呢?
- 先初始化父類(基類)的靜態代碼;
- 初始化子類的靜態代碼;
- 初始化父類的非靜態代碼;
- 初始化父類的構造器;
- 初始化子類的非靜態代碼;
- 初始化子類的構造器.
父類的靜態代碼之所以先初始化,是因爲子類的靜態代碼可能會依賴於父類成員能否被正確初始化。
函數內的局部變量
而對於函數內的局部變量,則需要編程人員自己顯式地初始化,如果只聲明變量而不進行初始化(也就是賦值),則會導致程序編譯不過。