問題一:我聲明了什么!
String s = "Hello world!";
許多人都做過這樣的事情,但是,我們到底聲明了什么?回答通常是:一個String,內容是"Hello world!"。這樣模糊的回答通常是概念不清的根源。如果要準確的回答,一半的人大概會回答錯誤。
這個語句聲明的是一個指向對象的引用,名為“s”,可以指向類型為String的任何對象,目前指向"Hello world!"這個String類型的對象。這就是真正發生的事情。我們并沒有聲明一個String對象,我們只是聲明了一個只能指向String對象的引用變量。所以,如果在剛才那句語句后面,如果再運行一句:
String string = s;
我們是聲明了另外一個只能指向String對象的引用,名為string,并沒有第二個對象產生,string還是指向原來那個對象,也就是,和s指向同一個對象。
問題二:"=="和equals方法究竟有什么區別?
==操作符專門用來比較變量的值是否相等。
int a=10;
int b=10;
則a==b將是true。
但:
String a=new String("foo");
String b=new String("foo");
則a==b將返回false。
對象變量其實是一個引用,它們的值是指向對象所在的內存地址,而不是對象本身。a和b都使用了new操作符,意味著將在內存中產生兩個內容為"foo"的字符串,既然是“兩個”,它們自然位于不同的內存地址。a和b的值其實是兩個不同的內存地址的值,所以使用"=="操作符,結果會是false。雖然,a和b所指的對象,它們的內容都是"foo",應該是“相等”,但是==操作符并不涉及到對象內容的比較。
對象內容的比較應使用Object對象的equals方法。
boolean equals(Object o){
return this==o;
}
Object對象默認使用了==操作符。所以如果你自創的類沒有覆蓋equals方法,那你的類使用equals和使用==會得到同樣的結果。同樣也可以看出,Object的equals方法沒有達到equals方法應該達到的目標:比較兩個對象內容是否相等。因為答案應該由類的創建者決定,所以Object把這個任務留給了類的創建者。
例如:
Class MyClass{
private String content;
...
boolean equals(Object another){
return true;
}
}
MyClass類中的equals方法覆蓋了父類object類的equals方法。這個實現會導致無論MyClass類的實例內容如何,它們之間的比較永遠返回true。
所以當使用equals方法判斷對象的內容是否相等,請不要想當然。因為可能你認為相等,而這個類的創建者不這樣認為,而類的equals方法的實現是由他掌握的。
問題三:String和StringBuffer的區別
String類被設計成不可變(immutable)類,所以它的所有對象都是不可變對象。請看下列代碼:
String s = "Hello";
s = s + " world!";
s所指向的對象是否改變了呢?在這段代碼中,s原先指向一個String對象,內容是"Hello",然后我們對s進行了+操作,那么s所指向的那個對象是否發生了改變呢?答案是沒有。這時,s不指向原來那個對象了,而指向了另一個String對象,內容為"Hello world!",原來那個對象還存在于內存之中,只是s這個引用變量不再指向它了。
所以,如果經常對字符串進行各種各樣的修改,使用String類來代表字符串的話會引起很大的內存開銷。因為String對象建立之后不能再改變,所以對于每一個不同的字符串,都需要一個String對象來表示。這時,應該考慮使用StringBuffer類,它允許修改,而不是每個不同的字符串都要生成一個新的對象。并且,這兩種類的對象轉換十分容易。
同時,我們還可以知道,如果要使用內容相同的字符串,不必每次都new一個String。例如我們要在構造器中對一個名叫s的String引用變量進行初始化,把它設置為初始值,應當這樣做:
public class Demo {
private String s;
...
public Demo {
s = "Initial value";
}
...
}
而非
s = new String("Initial value");
后者每次都會調用構造器,生成新對象,性能低下且內存開銷大,并且沒有意義,因為String對象不可改變,所以對于內容相同的字符串,只要一個String對象來表示就可以了。也就說,多次調用上面的構造器創建多個對象,他們的String類型屬性s都指向同一個對象。
上面的結論還基于這樣一個事實:對于字符串常量,如果內容相同,Java認為它們代表同一個String對象。而用關鍵字new調用構造器,總是會創建一個新的對象,無論內容是否相同。
至于為什么要把String類設計成不可變類,是它的用途決定的。其實不只String,很多Java標準類庫中的類都是不可變的。在開發一個系統的時候,我們有時候也需要設計不可變類,來傳遞一組相關的值,這也是面向對象思想的體現。不可變類有一些優點,比如因為它的對象是只讀的,所以多線程并發訪問也不會有任何問題。當然也有一些缺點,比如每個不同的狀態都要一個對象來代表,可能會造成性能上的問題。所以Java標準類庫還提供了一個可變版本,即StringBuffer。
問題四:邏輯與/或 和 短路與/或的區別
邏輯操作符:&(與運算),^(異或運算),|(或運算)。
短路邏輯操作符:&&(并且),||(或者).
短路與/或運算符和邏輯與/或一樣可以實現邏輯運算,但是此時有一個重要的區別:用邏輯與/或運算時,不管操作符兩邊的條件表達式成不成立,它都要進行運算判斷,而短路與/或運算不一樣了,如果通過左側的操作數就可以進行它們需要的判斷,那么它就不會再計算右側的操作數了,請看下面的例子:
……
double value=0;
if(value!=0 && 1/value<1000){
System.out.println("The value is not too small.");
}
else{
System.out.println("The value is too small.");
}
……
運行結果:
The value is too small.
結果分析:
照理說應會出現除數為0的錯誤,但由于條件邏輯操作符是短路操作符,顯然,value!=0條件不成立,立即就可作出判斷應執行else后的語句,所以它就不再會運算判斷1/value<1000了.如果不懂請再看一例:
......
double int1=0,int2=1,int3=1;
if(int1!=0 & (int2=2)==1){
}
System.out.println("int2="+int2);
if(int1!=0 && (int3=2)==1){
}
System.out.println("int3="+int3);
......
運行結果:
int2=2.0
int3=1.0
問題五.實例變量與類變量(靜態變量)
可以通過兩種方法在類中存儲數據──作為實例變量和類變量.實例變量是特定于對象的,如果你有兩個對象(即一個類的兩個實例),每一個對象中的實例變量獨立于另一個對象中的實例變量的;另一方面,兩個對象的類變量均指向相同的數據,并因此面保存相同的值,換句話說,類變量被類中的所有對象共享,類變量在聲明時比實例變量多一個static.
class Data{
public int intdata=0;//intdata在這兒是實例變量
}
public class Test{
public static void main(String[] args){
Data a,b;
a=new Data ();
b=new Data ();
a.intdata=1;
System.out.println("b.indata="+b.intdata);
}
}
運行結果:
b.intdata=0
結果分析:
可以看出,a.intdata的值雖然變了,但并沒有影響b.intdata.但是如果在data類中聲明intdata時,在其前面加上static就變成類變量了(即:public static int intdata=0;),則此時運行結果會變為:b.intdata=1
這次a.intdata值的改變可把b.intdata影響了,事實上,對象a和b的類變量均指向相同的數據,所有值一樣,這就是類變量的作用.
問題六:實例方法,類方法(靜態方法),構造器方法
我們通常所說的方法系指實例方法。類方法(即靜態方法)與實例方法最大的區別是:在形式上類方法多一個static,在用法上不必創建對象就可直接調用類方法(而實例方法卻一定要先創建對象,再通過對象調用)。
class Add{
static int addNum(int op1,int op2){
return op1+op2;
}
}
public class Test{
public static void main(String[] args){
//直接用類名作為對象調用類方法
System.out.println("addem(2,2)="+Add.addNum(2,2));
}
}
注: 也可按通常的方法,即先創建對象,再調用方法,不過,這時static就無任何意義了。
構造器方法,它是用來初始化對象中的數據的一種方法,創建很容易,只需在類中加上一個與這個類同名的方法,不需要在前面加任何訪問說明符或者返回類型,另外,構造器也一樣可以象方法一樣傳遞參數.
class Data{
private String data1;//事先聲明
Data(String s){
data1=s; /*通過接收數據來初始化變量.(注:不能在構造器內聲明變
量,事先在外就要聲明.)*/
}
public String getData(){
return data1;
}
}
public class Test{
public static void main(String[] args){
/*通過傳遞參數調用構造器新建一個對象,再通過對象調用方法得到數據*/
System.out.println((new Data("I love you")).getData());
}
}
問題七:接口與類
類是對一類特定對象的規格說明,我們可以定義類,然后創建類的對象,通過創建類的對象來組合所有屬于該類的組件,而接口不能這樣做.而接口實質上就是一個常量和抽象方法的集合,要使用一個接口,就需要在類中實現這個接口,然后作為類定義的一部分,編寫接口中聲明的每一個方法,接口中的方法永遠是public,abstract,接口中的常量永遠是public static和final,因此不需要為它們說明屬性.因為在Java中不支持多重繼承,但是,可以用接口來實現類似的功能,這是接口的重要作用之一。
interface anyone{ //定義一個接口
final double PI=3.1416;
void setNumber(int number);
int getNumber();
}
interface anyother{ //定義另一個接口
void setString(String str);
String getString();
}
class Test implement anyone,anyother{ //定義一個類,并使用兩個接口
int number;
String str;
public Test(){
}
void setNumber(int number){
this.number=number;
}
void setString(String str){
this.str=str;
}
void int getNumber(){
}//可以為一個空實現.
void String getString(){
}
}
posted on 2008-07-07 11:29
wahaha 閱讀(144)
評論(0) 編輯 收藏 所屬分類:
Java 語言入門筆記