深入介紹Java對(duì)象初始化
前言
在Java中,一個(gè)對(duì)象在可以被使用之前必須要被正確地初始化,這一點(diǎn)是Java規(guī)范規(guī)定的。
自動(dòng)初始化(默認(rèn)值)
一個(gè)類的所有基本數(shù)據(jù)成員都會(huì)得到初始化,運(yùn)行下面的例子可以查看這些默認(rèn)值:
class Default{
boolean t;
char c;
byte b;
short s;
int i;
long l;
float f;
double d;
public void show() {
System.out.println("基本類型 初始化值\n"+
"boolean<----->" + t +"\n" +
"char<----->" + c +"\n" +
"byte<----->" + b + "\n" +
"short<----->" + s + "\n" +
"int<----->" + i + "\n" +
"long<----->" + l + "\n" +
"float<----->" + f + "\n" +
"double<----->" + d + "\n"
);
}
}
public class InitValue {
public static void main(String[] args) {
Default d = new Default();
d.show();
}
}
【運(yùn)行結(jié)果】:
基本類型 初始化值
boolean<----->false
char<----->
byte<----->0
short<----->0
int<----->0
long<----->0
float<----->0.0
double<----->0.0
其中,char類型的默認(rèn)值為空(null)。
對(duì)于非基本數(shù)據(jù)類型而言,對(duì)象的句柄也會(huì)被初始化:
class Person {
private String name;
// setter
}
class Default {
Person p;
public void show() {
System.out.println("Person<----->" + p);
}
}
public class InitValue {
public static void main(String[] args) {
Default d = new Default();
d.show();
}
}
【運(yùn)行結(jié)果】:
Person<----->null
可見(jiàn),句柄初始化值為null。這就是說(shuō),如果沒(méi)有為p指定初始化值就調(diào)用類似于p.setName的方法,就會(huì)出現(xiàn)異常。
規(guī)定初始化
如果需要自己為變量賦一個(gè)初始值,可以在定義變量的同時(shí)賦值。
class Default{
boolean t = true;
char c = 'A';
byte b = 47;
short s = 0xff;
int i = 24;
long l = 999;
float f = 1.2f;
double d = 1.732;
public void show() {
System.out.println(
"boolean<----->" + t +"\n" +
"char<----->" + c +"\n" +
"byte<----->" + b + "\n" +
"short<----->" + s + "\n" +
"int<----->" + i + "\n" +
"long<----->" + l + "\n" +
"float<----->" + f + "\n" +
"double<----->" + d + "\n"
);
}
}
public class InitValue {
public static void main(String[] args) {
Default d = new Default();
d.show();
}
}
甚至可以通過(guò)一個(gè)方法來(lái)進(jìn)行初始化;
class Person {
int i = set();
//...
}
這些方法也可以使用自變量:
class Person {
int i;
int j = set(i);
//...
}
構(gòu)建器初始化
構(gòu)建器進(jìn)行初始化的優(yōu)點(diǎn)是可以在運(yùn)行期決定初始化值。例如:
class Person {
int age;
Person() {
age = 89;
}
}
age首先會(huì)初始化為0,然后變成89。對(duì)于所有基本類型以及對(duì)象的句柄,這種情況都是成立的。
初始化順序
在一個(gè)類里,初始化的順序是由變量在類內(nèi)的定義順序決定的。即使變量定義大量遍布于方法定義的中間,那么變量仍然會(huì)在調(diào)用任何方法(包括構(gòu)造函數(shù))之前得到初始化。例如:
class Pet {
Pet(int age) {
System.out.println("Pet(" + age + ")");
}
}
class Person {
Pet t1 = new Pet(1);
Person() {
System.out.println("---Person()---");
t3 = new Pet(33);
}
Pet t2 = new Pet(2);
void show() {
System.out.println("show----running");
}
Pet t3 = new Pet(3);
}
public class OrderOfInitialization {
public static void main(String[] args) {
Person p = new Person();
p.show();
}
}
【運(yùn)行結(jié)果】:
Pet(1)
Pet(2)
Pet(3)
---Person()---
Pet(33)<br/>
show----running
上例中,雖然t1、t2、t3的定義遍布于類中,但是初始化的先后順序是由t1、t2、t3的定義順序決定的(自己動(dòng)手調(diào)換t1、t2、t3看看結(jié)果),且初始化優(yōu)先于構(gòu)建器執(zhí)行,當(dāng)調(diào)用Person的構(gòu)建器時(shí),t3重新初始化。
靜態(tài)數(shù)據(jù)的初始化
如果數(shù)據(jù)是靜態(tài)的(static),同樣的過(guò)程也會(huì)執(zhí)行。若屬于基本類型,而且未對(duì)其進(jìn)行初始化,就會(huì)自動(dòng)獲得自己的標(biāo)準(zhǔn)基本類型初始值;若它是指向一個(gè)對(duì)象的句柄,除非創(chuàng)建一個(gè)對(duì)象同它連接起來(lái),否則得到一個(gè)空值(null)。如果在定義時(shí)初始化,采取的方式與非靜態(tài)值是不同的,這是因?yàn)閟tatic只有一個(gè)存儲(chǔ)區(qū)域。例如:
class Bowl {
Bowl(int marker) {
System.out.println("Bowl(" + marker + ")");
}
void f(int marker) {
System.out.println("f(" + marker + ")");
}
}
class Table {
static Bowl b1 = new Bowl(1);
Table() {
System.out.println("Table()");
b2.f(1);
}
void f2(int marker) {
System.out.println("f2(" + marker + ")");
}
static Bowl b2 = new Bowl(2);
}
class Cupboard {
Bowl b3 = new Bowl(3);
static Bowl b4 = new Bowl(4);
Cupboard() {
System.out.println("Cupboard()");
b4.f(2);
}
void f3 (int marker) {
System.out.println("f3(" + marker + ")");
}
static Bowl b5 = new Bowl(5);
}
public class StaticInitialization {
public static void main(String[] args) {
System.out.println("Creating new Cupboard() in main");
new Cupboard();
System.out.println("Creating new Cupboard() in main");
new Cupboard();
t2.f2(1);
t3.f3(1);
}
static Table t2 = new Table();
static Cupboard t3 = new Cupboard();
}
【運(yùn)行結(jié)果】:
Bowl(1)
Bowl(2)
Table()
f(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard()
f(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f(2)
f2(1)
f3(1)
靜態(tài)代碼塊
Java允許將其他static初始化工作劃分到類內(nèi)一個(gè)特殊的代碼塊中,這種代碼塊的形式為static關(guān)鍵字,后面跟著一個(gè)方法主體,稱為靜態(tài)代碼塊。靜態(tài)代碼塊只有在第一次生成那個(gè)類的對(duì)象或首次訪問(wèn)屬于那個(gè)類的static成員時(shí)執(zhí)行。例如:
class Person {
Person(int age) {
System.out.println("Person(" + age + ")");
}
void f(int age) {
System.out.println("f(" + age + ")");
}
}
class Persons {
static Person p1;
static Person p2;
static {
p1 = new Person(1);
p2 = new Person(2);
}
Persons() {
System.out.println("Persons()");
}
}
public class ExplicitStatic {
public static void main(String[] args) {
System.out.println("Inside main()");
Persons.p1.f(18);//1
}
static Persons x = new Persons();//2
static Persons y = new Persons();//2
}
在標(biāo)記為1的行內(nèi)訪問(wèn)static對(duì)象p1的時(shí)候,或在行1被注釋而行2未被注釋是,用于Persons的static初始化模塊就會(huì)運(yùn)行。若1和2都被注釋掉,則用于Persons的靜態(tài)代碼塊不會(huì)執(zhí)行。
靜態(tài)屬性和靜態(tài)代碼塊執(zhí)行的先后順序
class Person {
Person(int age) {
System.out.println("Person("+age+")");
}
}
class Persons {
static Person p = new Person(2); // 1
static {
p = new Person(3);
}
static Person p = new Person(2); // 2
}
public class CompStaticInit {
public static void main(String[] args) {
}
static Persons x = new Persons();
}
根據(jù)注釋1保留2,注釋2保留1的結(jié)果分析可知,靜態(tài)屬性和靜態(tài)代碼塊的執(zhí)行順序取決于編碼的順序。誰(shuí)在前面就先執(zhí)行誰(shuí)。
非靜態(tài)屬性的初始化
class Animal {
Animal(int age) {
System.out.println("Animal(" + age + ")");
}
void f(int age) {
System.out.println("f(" + age + ")");
}
}
public class NotStaticInit {
Animal a1;
Animal a2;
{
a1 = new Animal(1);
a2 = new Animal(2);
System.out.println("a1 & a2 initialized");
}
NotStaticInit() {
System.out.println("NotStaticInit");
}
public static void main(String[] args) {
System.out.println("Inside main()");
NotStaticInit x = new NotStaticInit();
}
}
類似于靜態(tài)代碼塊,匿名代碼塊與非靜態(tài)屬性的初始化順序取決于編碼順序。
繼承中的對(duì)象初始化過(guò)程
class Insect {
int i = 1;
int j;
Insect() {
prt("i = " + i + ", j = " + j);
j = 2;
}
static int x1 = prt("static Insect.x1 initialized");
static int prt(String s) {
System.out.println(s);
return 3;
}
}
public class Beetle extends Insect {
int k = prt("Beeklt.k initialized");
Beetle() {
prt("k = " + k);
prt("j = " + j);
}
static int x2 = prt("static Bootle.x2 initialized");
static int prt(String s) {
System.out.println(s);
return 4;
}
public static void main(String[] args) {
prt("Beetle constructor");
Beetle b = new Beetle();
}
}
【運(yùn)行結(jié)果】:
static Insect.x1 initialized
static Bootle.x2 initialized
Beetle constructor
i = 1, j = 0
Beeklt.k initialized
k = 4
j = 2
對(duì)Beetle運(yùn)行Java時(shí),發(fā)生的第一件事情是裝載程序到外面找到那個(gè)類。在裝載過(guò)程中,裝載程序發(fā)現(xiàn)一個(gè)基礎(chǔ)類,所以隨之將其載入。無(wú)論是否生成基礎(chǔ)類的對(duì)象,這一過(guò)程都將執(zhí)行。如果基礎(chǔ)類含有另一個(gè)基礎(chǔ)類,則另一個(gè)基礎(chǔ)類隨即也會(huì)載入,以此類推。接下來(lái)就在根基礎(chǔ)類中執(zhí)行static初始化,再在下一個(gè)衍生類中執(zhí)行,以此類推。這是因?yàn)檠苌惖某跏蓟赡芤蕾囉趯?duì)基礎(chǔ)類成員的初始化。
當(dāng)類都裝載完畢,就能創(chuàng)建對(duì)象。首先,這個(gè)對(duì)象中的所有基本數(shù)據(jù)類型都會(huì)設(shè)置成為他們的默認(rèn)值,對(duì)象句柄設(shè)為null。然后執(zhí)行基礎(chǔ)類的構(gòu)建器。這種情況是自動(dòng)完成的(衍生類的構(gòu)造函數(shù)中默認(rèn)調(diào)用了super(),也可以通過(guò)super指定基類的構(gòu)建器)?;A(chǔ)類構(gòu)建器完成后,衍生類實(shí)例變量就會(huì)按本來(lái)的順序得到初始化,然后執(zhí)行構(gòu)建器的剩余的主體部分。
總結(jié)對(duì)象創(chuàng)建的過(guò)程:
靜態(tài)只在類加載的時(shí)候執(zhí)行且只執(zhí)行一次;
非靜態(tài)只有在實(shí)例化的時(shí)候執(zhí)行,每次創(chuàng)建對(duì)象都執(zhí)行;
靜態(tài)在非靜態(tài)之前執(zhí)行,基類靜態(tài)優(yōu)先于衍生類靜態(tài)執(zhí)行;
靜態(tài)屬性和靜態(tài)代碼塊的執(zhí)行屬性取決于它們?cè)陬愔械奈恢茫l(shuí)在前先執(zhí)行誰(shuí);
非靜態(tài)屬性和構(gòu)造塊的執(zhí)行順序取決于它們?cè)陬愔械奈恢?,誰(shuí)在前執(zhí)行誰(shuí)。
總結(jié)
通過(guò)上面的介紹,我們對(duì)Java中初始化對(duì)象的幾種方式以及通過(guò)何種方式執(zhí)行初始化代碼有了了解,同時(shí)也對(duì)何種情況下我們可能會(huì)使用到未經(jīng)初始化的變量進(jìn)行了介紹。在對(duì)這些問(wèn)題有了詳細(xì)的了解之后,就可以在編碼中規(guī)避一些風(fēng)險(xiǎn),保證一個(gè)對(duì)象在可見(jiàn)之前是完全被初始化的。
相關(guān)文章
SpringBoot2.x設(shè)置Session失效時(shí)間及失效跳轉(zhuǎn)方式
這篇文章主要介紹了SpringBoot2.x設(shè)置Session失效時(shí)間及失效跳轉(zhuǎn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Spring Boot應(yīng)用開(kāi)發(fā)初探與實(shí)例講解
這篇文章主要介紹了Spring Boot應(yīng)用開(kāi)發(fā)初探與實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
關(guān)于springboot中對(duì)sqlSessionFactoryBean的自定義
這篇文章主要介紹了springboot中對(duì)sqlSessionFactoryBean的自定義方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
RabbitMQ 的消息持久化與 Spring AMQP 的實(shí)現(xiàn)詳解
這篇文章主要介紹了RabbitMQ 的消息持久化與 Spring AMQP 的實(shí)現(xiàn)剖析詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08
詳細(xì)介紹idea如何設(shè)置類頭注釋和方法注釋(圖文)
本篇文章主要介紹了idea如何設(shè)置類頭注釋和方法注釋(圖文),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12
Java并發(fā)編程之ReentrantLock實(shí)現(xiàn)原理及源碼剖析
ReentrantLock 是常用的鎖,相對(duì)于Synchronized ,lock鎖更人性化,閱讀性更強(qiáng),文中將會(huì)詳細(xì)的說(shuō)明,請(qǐng)君往下閱讀2021-09-09
Java SpringBoot使用guava過(guò)濾器
這篇文章主要介紹了Java SpringBoot使用guava過(guò)濾器,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容戒殺,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09

