JAVA重難點問題剖析-實例講解好理解_第1頁
JAVA重難點問題剖析-實例講解好理解_第2頁
JAVA重難點問題剖析-實例講解好理解_第3頁
JAVA重難點問題剖析-實例講解好理解_第4頁
JAVA重難點問題剖析-實例講解好理解_第5頁
已閱讀5頁,還剩37頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

JAVA重難點問題剖析(■)

這次主要剖析以卜.四個問題:

構(gòu)造函數(shù)定義問題、類實例化時的歧義、equals和==、protected成員的訪問問題

(1)構(gòu)造函數(shù)前不加void,否則變?yōu)榉椒ǎ荒芗涌稍L問性修飾。

publicclassName{

voidNameO(

System,out.printIn(''Method^);

)

publicstaticvoidmain(String[]args){

Namen=newName();

}

)

無輸出,去void則輸入Method;

(2)如果需要在不同包的類中使用構(gòu)造函數(shù)創(chuàng)建對象必須前面加上public;

二、類實例化時的歧義問題

(1)單類形導(dǎo)入(即importpl.TestC而非importpl.*)時用此類實類化:

(2)完全標示名解決歧義問題pl.TestCb=ne\vpl.TestC()

packagepl;〃定義第一個類

publicclassTestC{

publicTestCO{System.out.printin(^pl^);}

}

packagep2;〃定義第二個類

publicclassTestC{

TestCO{System.out.printIn(,,p2/z);}

packagep2;〃定義第三個類

importpl.*;//(1)

publicclassTestB{

publicstaticvoidmain(String[]args){

TestCc=newTestCO;//(2)

)

輸出p2,若要輸出pl,可將(1)處改為importpl.TestC或者將(2)處改為pl.TestCb=newpl.TestCO;

三、數(shù)值、對象比較問題

java.lang,object類中的equal()方法:

publicbooleanequals(Objectobj){

return(this=obj);

)

java.lang.string類中覆蓋object中的equals。方法:

publicbooleanequals(ObjectanObject){

if(this==anObject){

returntrue;

)

if(anObjectinstanceofString){

StringanotherString=(String)anObject;

intn=count;

if(n==anotherString.count){

charvl[]=value;

charv2[]=anotherString.value;

inti=offset;

intj=anotherString.offset;

while(n—!=0){

if(vl[i++]!=v2[j++])

returnfalse;

)

returntrue;

}

)

returnfalse;

)

例子:

classTest{

Strings;

Test(Strings){

this.s=s;

)

)

publicclassFyz{

publicstaticvoidmain(String[]args){

Stringsl=newString(z,hello/,);

Strings2=newStringCzhello/,);

TestTl=newTest("JAVA");

TestT2=newTest("JAVA");

System.out.printin(si.equals(s2));//true:String^Boolean等類的equals覆蓋了object的,比

較的不再是對象而是數(shù)值

System.out.printin(Tl.equals(T2));//false:沒有覆蓋object的,所以比較的仍然是對象

System,out.printin(Sl==s2);//false:二二比較的是對象,無論是什么對象;

System.out.println(Tl==T2);//false:同上

T2=T1;〃使引用變量Tl、T2都成為同一對象的別名

System,out.printin(Tl.equals(T2));〃true:對象相同

System.out.printin(T1==T2);//true:同上

)

}

四、若在不同包中子類使用對象引用來訪問父類的protected成員,必須將對象實例化為子類對象,而不

能實例為父類對象來訪問,即子類只能以實現(xiàn)繼承層次中的身份訪問其超類的protected成員。相同包子

類中使用對象引用的類型都可以。

packagetestl;

publicclassA{

publicvoidpublicMethod。{System.out.printin("public方法”);}

protectedvoidprotectedMethod(){System,out.printIn("protected方法”);}

voidfriendlyMethod(){System.out.printin("friendly方法〃);}

privatevoidprivateMethodO{System,out.printIn("private方法”);}

packagetestl;

publicclassExtendsAextendsA(

publicstaticvoidmain(String[]args){

AAl=newA();

Al.publicMethod();

Al.protectedMethod();

Al.friendlyMethod();

//Al.privateMethodO;

ExtendsAExtendsAl=newExtendsA0;

ExtendsAl.publicMethod();

ExtendsAl.protectedMethod();

ExtendsAl.friendlyMethod();

//ExtendsAl.privateMethodO;

)

publicvoidprintinfoO{

publicMethod();

protectedMethod();

friendlyMethodO;

//privateMethod();

)

packagetest2;

importtestl.A;

publicclassExtendsAextendsA{

publicstaticvoidmain(String[]args){

AAl=newA();

Al.publicMethod();

//Al.protectedMethod();調(diào)用不行

//Al.friendlyMethodO;

//Al.privateMethodO;

ExtendsAExtendsAl=newExtendsA();

ExtendsAl.publicMethodO;

ExtendsAl.protectedMethod();〃可以調(diào)用

//ExtendsAl.friendlyMethodO;

//ExtendsAl.privateMethodO;

)

publicvoidprintinfo(){

publicMethodO;

protectedMethod。;〃可以直接調(diào)用

//friendlyMethodO;

//privateMethod0;

)

多態(tài)與類型轉(zhuǎn)化原理分析:

?、多態(tài)性:超類引用在運行時既能代衣超類本身的對象,也能代表其子類的對象的能力。

類的一個成員若想表現(xiàn)多態(tài)必須可以被覆蓋:

對于成員變量而言,不會發(fā)生覆蓋現(xiàn)象(會隱藏),在子類出現(xiàn)相同變量的定義時只會隱藏父類變量,因此

不會表現(xiàn)多態(tài)。同時變量調(diào)用在編譯時就會解析,不符合動態(tài)綁定的特征;

在成員方法中,靜態(tài)方法和final方法(private方法)也不會發(fā)生覆蓋現(xiàn)象(會隱藏),因此也不會表

現(xiàn)多態(tài)性。

因此只有除靜態(tài)方法和final方法以外的方法才會表現(xiàn)多態(tài)性。

:、向上類型轉(zhuǎn)化時:

丟失添加的方法和字段,剩余的為:

基類字段

基類靜態(tài)方法或final方法〃前二者為不能被覆蓋的成員,因此保留,無多態(tài)性

基類其他方法(若被子類覆蓋則為子類覆蓋的新方法)

packagetest3;

classoopsuper{

staticStringstr="父類字段〃;

publicvoidpublicMethodO{System.out.printing父類public方法”);}

protectedvoidprotectedMethodO{System.out.printIn("父類protected方法”);}〃(1)

privatevoidprivateMethodO{System.out.printIn(〃父類private方法〃);}

staticvoidstaticMethodO{System.out.printing父類靜態(tài)方法”);}

}

publicclassoopsubextendsoopsuper{

Stringstr=〃子類字段〃;

publicvoidpublicMethodO{System.out.printIn(〃子類public方法〃);}

protectedvoidprotectedMethodO{System.out.printIn("子類protected方法”);}//(2)

privatevoidprivateMethod(){System,out.prinlln("子類private方法”);}

staticvoidstaticMethodO{System.out.printin(“子類靜態(tài)方法”);}

publicstaticvoidmain(String[]args){

oopsuperupcast=newoopsub();

System.out.printin(upcast,str);〃方法調(diào)用才具有多態(tài)性,而域沒有多態(tài)性

//能被覆蓋的方法的行為才仃多態(tài)特性

upcast,publicMethodO;

upcast.protectedMethodO;〃若注釋掉(1)則有錯誤;若注釋掉(2)則輸出:子類protected方法

//不能被覆蓋的方法[final方法(含私有方法)、靜態(tài)方法]的行為不具有多態(tài)特性

//upcast.privateMethodO;訪問的是父類的私有方法,不能訪問,不具有多態(tài)現(xiàn)象

upcast.staticMethodO;

}

JAVA重難點問題剖析(二)

Byfengyuzhe發(fā)表于2007-12-1412:44:00

0

這次主要剖析以下問題:

抽象類與接口的區(qū)別、別名、局部變量的語句塊作用域、this構(gòu)造和super構(gòu)造

一、抽象類與接口的區(qū)別:

*1.抽象類中可以定義所有成員變量(含實例變量和靜態(tài)變量[含常量])和非空方法,而接口中只能定義

常量和空方法;

*2.抽象類在定義抽象方法時必須加abstract,而在接口中可以加但不需要加;

*3.接口允許多繼承:一個接口可以基層多個接口,實現(xiàn)接口的類也可以繼承多個接口,但JAVA的類僅

支持單繼承。

packagetest;

interfaceobject1{

//intvarl;

//staticintvarls;

intVARI=2;//接口中只能定義常量,等同于finalstaticintVAR1=2;

intinterfaceMethodl();

abstractintinterfaceMethod2();〃接口中的方法其實就是抽象方法,但在接口中一般不加abstra

ct

//intinferfaceMethod3(){)接口中不能含有非抽象方法

}

abstractclassobject2{

intvar2;

staticintvar2s;

finalstaticintVAR2=2;〃抽象類中可以定義變量也可以定義常量

abstractintabstractMethodl();

//intabstractMethod2();空方法必須加abstract修飾符

//abstractintabstractMethod3(){}抽象方法不能指定方法體

voidabstractMethod4(){}〃抽象類中可以含有非抽象方法

)

二、卜.面中的引用變量都是別名

packagetest2;

publicclasstest{

inta;

publictest(inti){

this,a=i;

)

publicstaticvoidmain(String[]args){

testtest1=newtest(10);

testtest2=testl;

System,out.printin(testl.a);

System,out.println(test2.a);

testl=null;

//System.out.println(testl.a);

System,out.printIn(test2.a);

)

三、局部變量的語句塊作用域:

語句塊{}中所聲明的變量的作用域處在聲明該變量的語句塊中,語句塊外部不能訪問

publicclasshong{

publicstaticvoidmain(String[]args){

intvarl=1;

{

intvar2=2;

}

System,out.printIn(varl);

//System.out.println(var2);

//var2不能別解析,因為var2的作用域處在語句塊。中

)

)

四、this構(gòu)造和super構(gòu)造

二者必須位于構(gòu)造函數(shù)的第?行,this。構(gòu)造用于串鏈同個類中的構(gòu)造函數(shù),而super()構(gòu)造用于激活超

類的構(gòu)造函數(shù),如果構(gòu)造函數(shù)的第一句不是this。構(gòu)造或者super()構(gòu)造,則會插入一條指向超類默認構(gòu)

造函數(shù)的super()調(diào)用

classtest{

inti,j;

test(inti,intj){

this,i=i;

this,j=j;

)

)

publicclassSuperDemoextendstest{

intk,1;

SuperDemo(){

thisdl,12);

)

/*錯誤,插入插入一條指向超類默認構(gòu)造函數(shù)的super()調(diào)用

SuperDemo(inti,intj){

this,k=i;

this.1=j;

)

*/

publicstaticvoidmain(String[]args){

SuperDemosd=newSuperDemo();

System,out.print(sd.i+"〃+sd.j+sd.k+sd.1);

)

一.Input和Output

1.stream代表的是任何有能力產(chǎn)出數(shù)據(jù)的數(shù)據(jù)源,或是任何有能力接收數(shù)據(jù)的接收源。在Java的I0中,

所有的stream(包括Input和Outstream)都包括兩種類型:

1.1以字節(jié)為導(dǎo)向的stream

以字節(jié)為導(dǎo)向的stream,表示以字節(jié)為單位從stream中讀取或往stream中寫入信息。以字節(jié)為導(dǎo)向

的stream包括卜.面幾種類型:

1)inputstream:

1)ByteArraylnputStream:把內(nèi)存中的一個緩沖區(qū)作為Inputstream使用

2)StringBufferInputStream:把一個String對象作為Inputstream

3)FilelnputStream:把一個文件作為Inputstream,實現(xiàn)對文件的讀取操作

4)PipedlnputStream:實現(xiàn)了pipe的概念,主要在線程中使用

5)Sequenceinputstream:把多個Inputstream合并為一個InputStream

2)Outstream

1)ByteArrayOutputStream:把信息存入內(nèi)存中的一個緩沖區(qū)U」

2)FileOutputStream:把信息存入文件中

3)PipedOutputStream:實現(xiàn)了pipe的概念,主要在線程中使用

4)SequenceOutputStream:把多個OutStream合并為OutStream

1.2以Unicode字符為導(dǎo)向的stream

以Unicode字符為導(dǎo)向的stream,表示以Unicode字符為單位從stream中讀取或往stream中寫入

信息。以Unicode字符為導(dǎo)向的stream包括下面幾種類型:

1)InputStream

1)CharArrayReader:與ByteArraylnputStream對應(yīng)

2)StringReader:StringBufferlnputStream對應(yīng)

3)FileReader:與FilelnputStream對應(yīng)

4)PipedReader:與PipedlnputStream對應(yīng)

2)OutStream

1)CharArrayWrite:與ByteArrayOutputStream對應(yīng)

2)StringWrite:無與之對應(yīng)的以字節(jié)為導(dǎo)向的stream

3)FileWrite:與FileOutputStream對應(yīng)

4)PipedWrite:與PipedOutputStream對應(yīng)

以字符為導(dǎo)向的stream基本上對有與之相對應(yīng)的以字節(jié)為導(dǎo)向的stream。兩個對應(yīng)類實現(xiàn)的功能相同,

字是在操作時的導(dǎo)向不同。如CharArrayReader:和ByteArraylnputStream的作用都是把內(nèi)存中的

一個緩沖區(qū)作為Inputstream使用,所不同的是前者每次從內(nèi)存中讀取一個字節(jié)的信息,而后者每次從

內(nèi)存中讀取一個字符。

1.3兩種不現(xiàn)導(dǎo)向的stream之間的轉(zhuǎn)換

InputstreamR6ader和OutputstreamReader:把一個以字節(jié)為導(dǎo)向的stream轉(zhuǎn)換成一個以字符為

導(dǎo)向的stream。

2.stream添加屬性

2.1"為stream添加屬性”的作用

運用上面介紹的Java中操作I0的API,我們就可完成我們想完成的任何操作了。但通過

Filterinputstream和FilterOutStream的子類,我們可以為stream添加屬性。F面以一個例子來說

明這種功能的作用。

如果我們要往一個文件中寫入數(shù)據(jù),我們可以這樣操作:

FileOutStreamfs=newFileOutStream(Mtest.txt");

然后就可以通過產(chǎn)生的fs對象調(diào)用write。函數(shù)來往test.txt文件中寫入數(shù)據(jù)了。但是,如果我們想實現(xiàn)

“先把要寫入文件的數(shù)據(jù)先緩存到內(nèi)存中,再把緩存中的數(shù)據(jù)寫入文件中”的功能時,I二面的API就沒有一

個能滿足我們的需求了。但是通過Filterinputstream和FilterOutStream的子類,FileOutStream

添加我們所需要的功能。

2.2Filterinputstream的各種類型

2.2.1用于封裝以字節(jié)為導(dǎo)向的Inputstream

1)DatalnputStream:從stream中讀取基本類型(int、char等)數(shù)據(jù)。

2)BufferedlnputStream:使用緩沖區(qū)

3)LineNumberlnputStream:會記錄inputstream內(nèi)的行數(shù),然后可以調(diào)用getLineNumber()和

setLineNumber(int)

4)PushbacklnputStream:很少用到,一般用于編譯器開發(fā)

2.2.2用于封裝以字符為導(dǎo)向的Inputstream

1)沒有與DatalnputStream對應(yīng)的類。除非在要使用readLine。時改用BufferedReader,否則使用

DatalnputStream

2)BufferedReader:與BufferedlnputStream對應(yīng)

3)LineNumberReader:與LineNumberlnputStream對應(yīng)

4)PushBackReader:與PushbacklnputStream對應(yīng)

2.3FilterOutStream的各種類型

2.2.3用于封裝以字節(jié)為導(dǎo)向的Outputstream

1)DatalOutStream:往stream輸出基本類型(int、char等)數(shù)據(jù)。

2)BufferedOutStream:使用緩沖區(qū)

3)Printstream:產(chǎn)生格式化輸出

2.2.4用于封裝以字符為導(dǎo)向的Outputstream

1)BufferedWrite:與對應(yīng)

2)PrintWrite:與對應(yīng)

3.RandomAccessFile

1)可通過RandomAccessFile對象完成對文件的讀寫操作

2)在產(chǎn)生一個對象時,可指明要打開的文件的性質(zhì):r,只讀:w,只寫;rw可讀寫

3)可以直接跳到文件中指定的位置

4.I/O應(yīng)用的一個例子

importjava.io.*;

publicclassTestlO{

publicstaticvoidmain(String[]args)

throwsIOException{

〃1.以行為單位從一個文件讀取數(shù)據(jù)

BufferedReaderin=

newBufferedReader(

newFileReader("F:\\nepalon\\TestlO.java"));

Strings,s2=newString();

while((s=in.readLine())!=null)

s2+=s+M\nM;

in.close();

//1b.接收鍵盤的輸入

BufferedReaderstdin=

newBufferedReader(

newInputStreamReader(System.in));

System.out.println(HEnteraline:");

System.out.println(stdin.readLine());

//2.從一個String對象中讀取數(shù)據(jù)

StringReaderin2=newStringReader(s2);

intc;

while((c=in2.read())!=-1)

System.out.printin((char)c);

in2.close();

//3.從內(nèi)存取出格式化輸入

try{

DatalnputStreamin3=

newDatalnputStream(

newByteArraylnputStream(s2.getBytes()));

while(true)

System.out.printin((char)in3.readByte());

)

catch(EOFExceptione){

System.out.println(,'Endofstream");

)

//4.輸出到文件

try(

BufferedReaderin4=

newBufferedReader(

newStringReader(s2));

PrintWriterout1=

newPrintWriter(

newBufferedWriter(

newFileWriter("F:\\nepalon\\TestlO.out")));

intlineCount=1;

while((s=in4.readLine())!=null)

Java本質(zhì)論之關(guān)于Java棧與堆的思考

原文地址http:〃/edu/ShowArticle.asD?ArticlelD=1539

1.棧(stack)與堆(heap)都是Java用來在Ram中存放數(shù)據(jù)的地方。與C++不同,Java自動

管理棧和堆,程序員不能直接地設(shè)置?;蚨?。

2.棧的優(yōu)勢是,存取速度比堆要快,僅次于直接位于CPU中的寄存器。但缺點是,存在棧

中的數(shù)據(jù)大小與生存期必須是確定的,缺乏靈活性。另外,棧數(shù)據(jù)可以共享,詳見第3點。

堆的優(yōu)勢是可以動態(tài)地分配內(nèi)存大小,生存期也不必事先告訴編譯器,Java的垃圾收集器

會自動收走這些不再使用的數(shù)據(jù)。但缺點是,由于要在運行時動態(tài)分配內(nèi)存,存取速度較慢。

3.Java中的數(shù)據(jù)類型有兩種。

一種是基本類型(primitivetypes),共有8種,即int,short,long,byte,float,double,boolean,

char(注意,并沒有string的基本類型)。這種類型的定義是通過諸如inta=3;longb=255L;

的形式來定義的,稱為自動變量。值得注意的是,自動變量存的是字面值,不是類的實例,

即不是類的引用,這里并沒有類的存在。如inta=3;這里的a是一個指向int類型的引用,

指向3這個字面值。這些字面值的數(shù)據(jù),由于大小可知,生存期可知(這些字面值固定定義

在某個程序塊里面,程序塊退出后,字段值就消失了),出于追求速度的原因,就存在于棧

中。

另外,棧有一個很重要的特殊性,就是存在棧中的數(shù)據(jù)可以共享。假設(shè)我們同時定義:

inta=3;

intb=3;

編譯器先處理inta=3;首先它會在棧中創(chuàng)建一個變量為a的引用,然后查找有沒有字面值

為3的地址,沒找到,就開辟一個存放3這個字面值的地址,然后將a指向3的地址。接

著處理intb=3;在創(chuàng)建完b的引用變量后,山于在棧中已經(jīng)有3這個字面值,便將b直接

指向3的地址。這樣,就出現(xiàn)了a與b同時均指向3的情況。

特別注意的是,這種字面值的引用與類對象的引用不同。假定兩個類對象的引用同時指向一

個對象,如果一個對象引用變量修改了這個對象的內(nèi)部狀態(tài),那么另一個對象引用變量也即

刻反映出這個變化。相反,通過字面值的引用來修改其值,不會導(dǎo)致另一個指向此字面值的

引用的值也跟著改變的情況。如上例,我們定義完a與b的值后,再令a=4;那么,b不會

等于4,還是等于3。在編譯器內(nèi)部,遇到a=4;時,它就會重新搜索棧中是否有4的字面

值,如果沒有,重新開辟地址存放4的值;如果已經(jīng)有了,則直接將a指向這個地址。因

此a值的改變不會影響到b的值。

另一種是包裝類數(shù)據(jù),如Integer,String,Double等將相應(yīng)的基本數(shù)據(jù)類型包裝起來的類。

這些類數(shù)據(jù)全部存在于堆中,Java用new()語句來顯示地告訴編譯器,在運行時才根據(jù)需

要動態(tài)創(chuàng)建,因此比較靈活,但缺點是要占用更多的時間。4.String是一個特殊的包裝類

數(shù)據(jù)。即可以用Stringstr=newString("abc");的形式來創(chuàng)建,也可以用Stringstr="abe";

的形式來創(chuàng)建(作為對比,在JDK5.0之前,你從未見過Integeri=3;的表達式,因為類與

字面值是不能通用的,除了String。而在JDK5.0中,這種表達式是可以的!因為編譯器在

后臺進行Integeri=newlnteger(3)的轉(zhuǎn)換)。前者是規(guī)范的類的創(chuàng)建過程,即在Java中,

一切都是對象,而對象是類的實例,全部通過new()的形式來創(chuàng)建。Java中的有些類,如

DateFormat類,可以通過該類的getlnstance()方法來返回一個新創(chuàng)建的類,似乎違反了此

原則。其實不然。該類運用了單例模式來返回類的實例,只不過這個實例是在該類內(nèi)部通過

new()來創(chuàng)建的,而getlnstance()向外部隱藏了此細節(jié)。那為什么在Stringstr="abc";中,

并沒有通過new()來創(chuàng)建實例,是不是違反了上述原則?其實沒有。

5.關(guān)于Stringstr="abc"的內(nèi)部工作。Java內(nèi)部將此語句轉(zhuǎn)化為以下幾個步驟:

⑴先定義一個名為str的對String類的對象引用變量:Stringstr;

(2)在棧中查找有沒有存放值為"abc"的地址,如果沒有,則開辟一個存放字面值為"abc"的地

址,接著創(chuàng)建一個新的String類的對象o,并將。的字符串值指向這個地址,而且在棧中這

個地址旁邊記下這個引用的對象。。如果已經(jīng)有了值為"abc"的地址,則查找對象。,并返回

o的地址。

⑶將str指向?qū)ο髈的地址。

值得注意的是,一般String類中字符串值都是直接存值的。但像Stringstr="abc";這種場

合下,其字符串值卻是保存了一個指向存在棧中數(shù)據(jù)的引用!

為了更好地說明這個問題,我們可以通過以下的幾個代碼進行驗證。

Stringstr1="abc";

Stringstr2="abc";

System.out.println(str1==str2);//true

注意,我們這里并不用str1.equals(st⑵;的方式,因為這將比較兩個字符串的值是否相等。

==號,根據(jù)JDK的說明,只有在兩個引用都指向了同一個對象時才返回真值。而我們在這

里要看的是,str1與str2是否都指向了同一個對象。

結(jié)果說明,JVM創(chuàng)建了兩個引用str1和str2,但只創(chuàng)建了一個對象,而且兩個引用都指向

了這個對象。

我們再來更進一步,將以上代碼改成:

Stringstr1="abc";

Stringstr2="abc";

str1="bed";

System.out.println(str1++str2);//bed,abe

System.out.println(str1==str2);//false

這就是說,賦值的變化導(dǎo)致了類對象引用的變化,str1指向了另外一個新對象!而str2仍

舊指向原來的對象。上例中,當我們將str1的值改為"bed"時,JVM發(fā)現(xiàn)在棧中沒有存放該

值的地址,便開辟了這個地址,并創(chuàng)建了一個新的對象,其字符串的值指向這個地址。

事實上,String類被設(shè)計成為不可改變(immutable)的類。如果你要改變其值,可以,但JVM

在運行時根據(jù)新值悄悄創(chuàng)建了一個新對象,然后將這個對象的地址返回給原來類的引用。這

個創(chuàng)建過程雖說是完全自動進行的,但它畢竟占用了更多的時間。在對時間要求比較敏感的

環(huán)境中,會帶有一定的不良影響。

再修改原來代碼:

Stringstr1=HabcH;

Stringstr2="abc";

str1="bed”;

Stringstr3=str1;

System.out.println(str3);//bed

Stringstr4="bed”;

System.out.println(str1==str4);//true

str3這個對象的引用直接指向str1所指向的對象(注意,str3并沒有創(chuàng)建新對象)。當str1改

完其值后,再創(chuàng)建一個String的引用str4,并指向因str1修改值而創(chuàng)建的新的對象。可以

發(fā)現(xiàn),這回str4也沒有創(chuàng)建新的對象,從而再次實現(xiàn)棧中數(shù)據(jù)的共享。

我們再接著看以下的代碼。

Stringstr1=newString("abc");

Stringstr2="abc";

System.out.println(str1==str2);//false

創(chuàng)建了兩個引用。創(chuàng)建了兩個對象。兩個引用分別指向不同的兩個對象。

Stringstr1="abc";

Stringstr2=newString("abc");

System.out.println(str1==str2);//false

創(chuàng)建了兩個引用。創(chuàng)建了兩個對象。兩個引用分別指向不同的兩個對象。

以上兩段代碼說明,只要是用new()來新建對象的,都會在堆中創(chuàng)建,而且其字符串是單獨

存值的,即使可棧中的數(shù)據(jù)相同,也不會與棧中的數(shù)據(jù)共享。

6.數(shù)據(jù)類型包裝類的值不可修改。不僅僅是String類的值不可修改,所有的數(shù)據(jù)類型包裝

類都不能更改其內(nèi)部的值。7.結(jié)論與建議:

⑴我們在使用諸如Stringstr="abc";的格式定義類時,總是想當然地認為,我們創(chuàng)建了

String類的對象str。擔心陷阱!對象可能并沒有被創(chuàng)建!唯一可以肯定的是,指向String

類的引用被創(chuàng)建了。至于這個引用到底是否指向了一個新的對象,必須根據(jù)上下文來考慮,

除非你通過new()方法來顯要地創(chuàng)建一個新的對象。因此,更為準確的說法是,我們創(chuàng)建了

一個指向String類的對象的引用變量str,這個對象引用變量指向了某個值為"abc”的String

類。清醒地認識到這一點對排除程序中難以發(fā)現(xiàn)的bug是很有幫助的。

⑵使用Stringstr="abc";的方式,可以在一定程度上提高程序的運行速度,因為JVM會

自動根據(jù)棧中數(shù)據(jù)的實際情況來決定是否有必要創(chuàng)建新對象。而對于Stringstr=new

String("abc");的代碼,則一概在堆中創(chuàng)建新對象,而不管其字符串值是否相等,是否有必

要創(chuàng)建新對象,從而加重了程序的負擔。這個思想應(yīng)該是享元模式的思想,但JDK的內(nèi)部

在這里實現(xiàn)是否應(yīng)用了這個模式,不得而知。

(3)當比較包裝類里面的數(shù)值是否相等時,用equals。方法;當測試兩個包裝類的引用是否

指向同一個對象時,用==。

(4)由于String類的immutable性質(zhì),當String變量需要經(jīng)常變換其值時,應(yīng)該考慮使用

StringBuffer類,以提高程序效率。

Java本質(zhì)論之關(guān)于Java棧與堆的思考

原文地址http:〃/edu/ShowArticle.asp?ArticlelD=1539

1.棧(stack)與堆(heap)都是Java用來在Ram中存放數(shù)據(jù)的地方。與C++不同,Java自動

管理棧和堆,程序員不能直接地設(shè)置棧或堆。

2.棧的優(yōu)勢是,存取速度比堆要快,僅次于直接位于CPU中的寄存器。但缺點是,存在棧

中的數(shù)據(jù)大小與生存期必須是確定的,缺乏靈活性。另外,棧數(shù)據(jù)可以共享,詳見第3點。

堆的優(yōu)勢是可以動態(tài)地分配內(nèi)存大小,生存期也不必事先告訴編譯器,Java的垃圾收集器

會自動收走這些不再使用的數(shù)據(jù)。但缺點是,由于要在運行時動態(tài)分配內(nèi)存,存取速度較慢。

3.Java中的數(shù)據(jù)類型有兩種。

一種是基本類型(primitivetypes),共有8種,即int,short,long,byte,float,double,boolean,

char(注意,并沒有string的基本類型)。這種類型的定義是通過諸如inta=3;longb=255L;

的形式來定義的,稱為自動變量。值得注意的是,自動變量存的是字面值,不是類的實例,

即不是類的引用,這里并沒有類的存在。如inta=3;這里的a是一個指向int類型的引用,

指向3這個字面值。這些字面值的數(shù)據(jù),由于大小可知,生存期可知(這些字面值固定定義

在某個程序塊里面,程序塊退出后,字段值就消失了),出于追求速度的原因,就存在于棧

中。

另外,棧有一個很重要的特殊性,就是存在棧中的數(shù)據(jù)可以共享。假設(shè)我們同時定義:

inta=3;

intb=3;

編譯器先處理inta=3;首先它會在棧中創(chuàng)建一個變量為a的引用,然后查找有沒有字面值

為3的地址,沒找到,就開辟一個存放3這個字面值的地址,然后將a指向3的地址。接

著處理intb=3;在創(chuàng)建完b的引用變量后,由于在棧中已經(jīng)有3這個字面值,便將b直接

指向3的地址。這樣,就出現(xiàn)了a與b同時均指向3的情況。

特別注意的是,這種字面值的引用與類對象的引用不同。假定兩個類對象的引用同時指向一

個對象,如果一個對象引用變量修改了這個對象的內(nèi)部狀態(tài),那么另一個對象引用變量也即

刻反映出這個變化。相反,通過字面值的引用來修改其值,不會導(dǎo)致另一個指向此字面值的

引用的值也跟著改變的情況。如上例,我們定義完a與b的值后,再令a=4;那么,b不會

等于4,還是等于3。在編譯器內(nèi)部,遇到a=4;時,它就會重新搜索棧中是否有4的字面

值,如果沒有,重新開辟地址存放4的值;如果已經(jīng)有了,則直接將a指向這個地址。因

此a值的改變不會影響到b的值。

另一種是包裝類數(shù)據(jù),如Integer,String,Double等將相應(yīng)的基本數(shù)據(jù)類型包裝起來的類。

這些類數(shù)據(jù)全部存在于堆中,Java用new()語句來顯示地告訴編譯器,在運行時才根據(jù)需

要動態(tài)創(chuàng)建,因此比較靈活?,但缺點是要占用更多的時間。4.String是一個特殊的包裝類

數(shù)據(jù)。即可以用Stringstr=newString("abc");的形式來創(chuàng)建,也可以用Stringstr="abc";

的形式來創(chuàng)建(作為對比,在JDK5.0之前,你從未見過Integeri=3;的表達式,因為類與

字面值是不能通用的,除了String。而在JDK5.0中,這種表達式是可以的!因為編譯器在

后臺進行Integeri=newlnteger(3)的轉(zhuǎn)換)。前者是規(guī)范的類的創(chuàng)建過程,即在Java中,

一切都是對象,而對象是類的實例,全部通過new()的形式來創(chuàng)建。Java中的有些類,如

DateFormat類,可以通過該類的getlnstance()方法來返回一個新創(chuàng)建的類,似乎違反了此

原則。其實不然。該類運用了單例模式來返回類的實例,只不過這個實例是在該類內(nèi)部通過

new()來創(chuàng)建的,而getlnstance()向外部隱藏了此細節(jié)。那為什么在Stringstr="abc";中,

并沒有通過new()來創(chuàng)建實例,是不是違反了上述原則?其實沒有。

5.關(guān)于Stringstr="abc"的內(nèi)部工作。Java內(nèi)部將此語句轉(zhuǎn)化為以下幾個步驟:

⑴先定義一個名為str的對String類的對象引用變量:Stringstr;

(2)在棧中查找有沒有存放值為"abc"的地址,如果沒有,則開辟一個存放字面值為"abc"的地

址,接著創(chuàng)建一個新的String類的對象o,并將。的字符串值指向這個地址,而且在棧中這

個地址旁邊記下這個引用的對象。。如果已經(jīng)有了值為"abc"的地址,則查找對象。,并返回

o的地址。

(3)將str指向?qū)ο髈的地址。

值得注意的是,一般String類中字符串值都是直接存值的。但像Stringstr="abc";這種場

合下,其字符串值卻是保存了一個指向存在棧中數(shù)據(jù)的引用!

為了更好地說明這個問題,我們可以通過以下的幾個代碼進行驗證。

Stringstr1="abc";

Stringstr2="abc";

System.out.println(str1==str2);//true

注意,我們這里并不用str1.equals(str2);的方式,因為這將比較兩個字符串的值是否相等。

==號,根據(jù)JDK的說明,只有在兩個引用都指向了同一個對象時才返回真值。而我們在這

里要看的是,str1與str2是否都指向了同個對象。

結(jié)果說明,JVM創(chuàng)建了兩個引用str1和str2,但只創(chuàng)建了一個對象,而且兩個引用都指向

了這個對象。

我們再來更進一步,將以上代碼改成:

Stringstr1="abc";

Stringstr2="abc";

str1="bed";

System.out.println(str1++str2);//bed,abc

System.out.println(str1==str2);//false

這就是說,賦值的變化導(dǎo)致了類對象引用的變化,str1指向了另外一個新對象!而str2仍

舊指向原來的對象。上例中,當我們將str1的值改為"bed"時,JVM發(fā)現(xiàn)在棧中沒有存放該

值的地址,便開辟了這個地址,并創(chuàng)建了一個新的對象,其字符串的值指向這個地址。

事實上,String類被設(shè)計成為不可改變(immutable)的類。如果你要改變其值,可以,但JVM

在運行時根據(jù)新值悄悄創(chuàng)建了一個新對象,然后將這個對象的地址返回給原來類的引用。這

個創(chuàng)建過程雖說是完全自動進行的,但它畢竟占用了更多的時間。在對時間要求比較敏感的

環(huán)境中,會帶有一定的不良影響。

再修改原來代碼:

Stringstr1="abc";

Stringstr2=,'abcH;

str1=“bed”;

Stringstr3=str1;

System.out.println(str3);//bed

Stringstr4="bed”;

System.out.println(str1==str4);//true

str3這個對象的引用直接指向str1所指向的對象(注意,str3并沒有創(chuàng)建新對象)。當str1改

完其值后,再創(chuàng)建一個String的引用str4,并指向因str1修改值而創(chuàng)建的新的對象??梢?/p>

發(fā)現(xiàn),這回str4也沒有創(chuàng)建新的對象,從而再次實現(xiàn)棧中數(shù)據(jù)的共享。

我們再接著看以下的代碼。

Stringstr1=newString("abc");

Stringstr2="abc";

System.out.println(str1==str2);//false

創(chuàng)建了兩個引用。創(chuàng)建了兩個對象。兩個引用分別指向不同的兩個對象。

Stringstr1=,'abcH;

Stringstr2=newString("abc");

System.out.println(str1==str2);//false

創(chuàng)建了兩個引用。創(chuàng)建了兩個對象。兩個引用分別指向不同的兩個對象。

以上兩段代碼說明,只要是用new()來新建對象的,都會在堆中創(chuàng)建,而且其字符串是單獨

存值的,即使與棧中的數(shù)據(jù)相同,也不會與棧中的數(shù)據(jù)共享。

6.數(shù)據(jù)類型包裝類的值不可修改。不僅僅是String類的值不可修改,所有的數(shù)據(jù)類型包裝

類都不能更改其內(nèi)部的值。7.結(jié)論與建議:

⑴我們在使用諸如Stringstr="abc";的格式定義類時,總是想當然地認為,我們創(chuàng)建了

String類的對象str。擔心陷阱!對象可能并沒有被創(chuàng)建!唯一可以肯定的是,指向String

類的引用被創(chuàng)建了。至于這個引用到底是否指向了一個新的對象,必須根據(jù)上下文來考慮,

除非你通過new()方法來顯要地創(chuàng)建一個新的對象。因此,更為準確的說法是,我們創(chuàng)建了

一個指向String類的對象的引用變量str,這個對象引用變量指向了某個值為"abc"的String

類。清醒地認識到這一點對排除程序中難以發(fā)現(xiàn)的bug是很有幫助的。

⑵使用Stringstr="abc";的方式,可以在一定程度上提高程序的運行速度,因為JVM會

自動根據(jù)棧中數(shù)據(jù)的實際情況來決定是否有必要創(chuàng)建新對象。而對于Stringstr=new

String("abc");的代碼,則一概在堆中創(chuàng)建新對象,而不管其字符串值是否相等,是否有必

要創(chuàng)建新對象,從而加重了程序的負擔。這個思想應(yīng)該是享元模式的思想,但JDK的內(nèi)部

在這里實現(xiàn)是否應(yīng)用了這個模式,不得而知。

⑶當比較包裝類里面的數(shù)值是否相等時,用equals。方法;當測試兩個包裝類的引用是否

指向同一個對象時,用==。

(4)由于String類的immutable性質(zhì),當String變量需要經(jīng)常變換其值時,應(yīng)該考慮使用

StringBuffer類,以提高程序效率。

入門必看的5個JAVA經(jīng)典實例

1-個飼養(yǎng)員給動物喂食物的例子體現(xiàn)JAVA中的面向?qū)ο笏枷?,接?抽象類)的用處

packagecom.softeem.demo;

/**

*@authorleno

*動物的接口

*/

interfaceAnimal{

publicvoideat(Foodfood);

)

/**

authorleno

*一種動物類:貓

*/

classCatimplementsAnimal{

publicvoideat(Foodfood){

System.out.println("小貓吃"+food.getName());

)

I

/**

*@authorleno

木?種動物類:狗

*/

classDogimplementsAnimal{

publicvoideat(Foodfood){

System.out.println("小狗啃"+food.getName());

)

}

/**

authorleno

*食物抽象類

*/

abstractclassFood(

protectedStringname;

publicStringgetName(){

returnname;

publicvoidsetName(Stringname){

=name;

authorleno

*?種食物類:魚

*/

classFishextendsFood{

publicFish(Stringname){

=name;

)

}

/**

*@authorleno

*一種食物類:骨頭

*/

classBoneextendsFood{

publicBone(Stringname){

=name;

/**

*@authorleno

*飼養(yǎng)員類

*

*/

classFeeder(

/**

*飼養(yǎng)員給某種動物喂某種食物

*@paramanimal

*@paramfood

*/

publicvoidfeed(Animalanimal,Foodfood){

animal.eat(food);

authorleno

*測試飼養(yǎng)員給動物喂食物

*/

publicclassTestFeeder{

publicstaticvoidmain(String[]args){

Feederfeeder=newFeeder();

Animalanimal=newDog();

Foodfood=newBone("肉骨頭)

feeder.fecdfanimal.food);〃給狗喂肉骨頭

animal=newCat();

food=newFish("魚”);

feeder.feed(animal,food);〃給貓喂魚

}

}

2.做一個單子模式的類,只加載一次屬性文件

packagecom.softeem.demo;

importjava.io.FilelnputStream;

importjava.io.FileNotFoundException:

importjava.io.IOExccption;

importjava.io.InputStream;

importjava.util.Properties;

/**

*@authorleno單子模式,保證在整個應(yīng)用期間只加載一次配置屬性文件

*/

publicclassSingleton{

privatestaticSingletoninstance;

privatestaticfinalStringCONFIG_FILE_PATH="E:\\perties'1;

privatePropertiesconfig;

privateSingleton(){

config=newProperties();

InputStreamis;

try(

is=newFileInputStream(CONFIG_FILE_PATH);

config.load(is);

is.close();

}catch(FileNotFoundExceptione){

//TODOAuto-generatedcatchblock

e.printStackTrace();

}catch(lOExceptione){

//TODOAuto-generatcdcatchblock

e.printStackTrace();

1

)

publicstaticSingletongctlnstance(){

if(instance==null){

instance=newSingleton();

)

returninstance;

publicPropertiesgetConfigO{

returnconfig;

)

publicvoidsetConfig(Propertiesconfig){

this.config=config;

)

1

3.用JAVA中的多線程示例銀行取款問題

packagecom.softeem.demo;

/**

*@authorleno

*賬戶類

*默認有余額,可以取款

*/

classAccount{

privatefloatbalance=1000;

publicfloatgetBalance(){

returnbalance;

)

publicvoidsetBalance(floatbalance){

this.balance=balance;

/**

*取款的方法需要同步

*@parammoney

*/

publicsynchronizedvoidwithdrawals(floatmoney){

if(balance>=money){

System.out.printin("被取走"+money+"元!");

try(

Thread.sleep(1000);

}catch(InterruptedExceptione){

//TODOAuto-generatedcatchblock

e.printStackTrace();

)

balance-=money;

)else{

System.out.println("對不起,余額不足!)

authorleno

銀行卡

*/

classTestAccount1extendsThread{

privateAccountaccount;

publicTestAccount1(Accountaccount){

this.account=account;

}

@Override

publicvoidrun(){

account.withdrawals(800);

System.out.println("余額為:"+account.getBalance()+"元!”);

*@authorleno

*存折

*/

classTestAccount?extendsThread{

privateAccountaccount;

publicTestAccount2(Accountaccount){

this.account=account;

@Override

publicvoidrun(){

account.withdrawals(700);

System.out.println("余額為:"+account.getBalance()+"元!”);

}

I

publicclassTest{

publicstaticvoidmain(String|]args){

Accountaccount=newAccount();

TestAccountltestAccount1=newTestAccount1(account);

testAccount1.start();

TestAccount2testAccount2=newTestAccount2(account);

testAccount2.starl();

)

)

4.用JAVA中的多線程示例生產(chǎn)者和消費者問題

packagecom.softeem.demo;

classProducerimplementsRunnable{

privateSyncStackstack;

publicProducer(SyncStackstack){

this.stack=stack;

publicvoidrun(){

for(inti=0;i<stack.getProducts().length;i++){

Stringproduct="產(chǎn)品"+i;

stack.push(product)

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論