歡迎您光臨本站 註冊首頁

java中值得注意的問題

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

  Java作為一門優秀的面向對象的程序設計語言,正在被越來越多的人使用.本文試圖列出作者在實際開發中碰到的一些Java語言的容易被人忽視的細節,希望能給正在學習Java語言的人有所幫助.

  1,位移運算越界怎麼處理考察下面的代碼輸出結果是多少?

  int a=5;System.out.println(a < <33);按照常理推測,把a左移33位應該將a的所有有效位都移出去了,那剩下的都是零啊,所以輸出結果應該是0才對啊,可是執行后發現輸出結果是10,為什麼呢?Java語言對位移運算作了優化處理,Java語言對a < <b轉化為a < <(b2)來處理,所以當要移位的位數b超過32時,實際上移位的位數是b2的值,那麼上面的代碼中a < <33相當於a < <1,所以輸出結果是10.

  2,可以讓i!=i嗎?

  當你看到這個命題的時候一定會以為我瘋了,或者Java語言瘋了.這看起來是絕對不可能的,一個數怎麼可能不等於它自己呢?或許就真的是Java語言瘋了,不信看下面的代碼輸出什麼?

  double i=0.0/0.0;if(i==i){System.out.println("Yes i==i");}else{System.out.println("No i!=i");}

  上面的代碼輸出"No i!=i",為什麼會這樣呢?關鍵在0.0/0.0這個值,在IEEE 754浮點算術規則里保留了一個特殊的值用來表示一個不是數字的數量.這個值就是NaN("Not a Number"的縮寫),對於所有沒有良好定義的浮點計算都將得到這個值,比如:0.0/0.0;其實我們還可以直接使用Double.NaN來得到這個值.在IEEE 754規範裡面規定NaN不等於任何值,包括它自己.所以就有了i!=i的代碼.

  3,怎樣的equals才安全?

  我們都知道在Java規範里定義了equals方法覆蓋的5大原則:reflexive(反身性),symmetric(對稱性),transitive(傳遞性),consistent(一致性),non-null(非空性).那麼考察下面的代碼:

  public class Student{private String name;private int age;public Student(String name,int age){this.name=name;this.age=age;}

  public boolean equals(Object obj){if(obj instanceof Student){Student s=(Student)obj;if(s.name.equals(this.name) && s.age==this.age){return true;}

  }

  return super.equals(obj);}

  }

  你認為上面的代碼equals方法的覆蓋安全嗎?表面看起來好像沒什麼問題,這樣寫也確實滿足了以上的五大原則.但其實這樣的覆蓋並不很安全,假如Student類還有一個子類CollegeStudent,如果我拿一個Student對象和一個CollegeStudent對象equals,只要這兩個對象有相同的name和age,它們就會被認為相等,但實際上它們是兩個不同類型的對象啊.問題就出在instanceof這個運算符上,這個運算符是向下兼容的,也就是說一個CollegeStudent對象也被認為是一個Student的實例.怎樣去解決這個問題呢?那就只有不用instanceof運算符,而使用對象的getClass()方法來判斷兩個對象是否屬於同一種類型,例如,將上面的equals()方法修改為:

  public boolean equals(Object obj){if(obj.getClass()==Student.class){Student s=(Student)obj;if(s.name.equals(this.name) && s.age==this.age){return true;}

  }

  return super.equals(obj);}

  這樣才能保證obj對象一定是Student的實例,而不會是Student的任何子類的實例.

  4,淺複製與深複製1)淺複製與深複製概念⑴淺複製(淺克隆)被複制對象的所有變數都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象.換言之,淺複製僅僅複製所考慮的對象,而不複製它所引用的對象.

  ⑵深複製(深克隆)被複制對象的所有變數都含有與原來的對象相同的值,除去那些引用其他對象的變數.那些引用其他對象的變數將指向被複制過的新對象,而不再是原有的那些被引用的對象.換言之,深複製把要複製的對象所引用的對象都複製了一遍.

  2)Java的clone()方法⑴clone方法將對象複製了一份並返回給調用者.一般而言,clone()方法滿足:

  ①對任何的對象x,都有x.clone() !=x//克隆對象與原對象不是同一個對象②對任何的對象x,都有x.clone().getClass()= =x.getClass()//克隆對象與原對象的類型一樣③如果對象x的equals()方法定義恰當,那麼x.clone().equals(x)應該成立.

  ⑵Java中對象的克隆①為了獲取對象的一份拷貝,我們可以利用Object類的clone()方法.

  ②在派生類中覆蓋基類的clone()方法,並聲明為public.

  ③在派生類的clone()方法中,調用super.clone().

  ④在派生類中實現Cloneable介面.

  請看如下代碼:

  class Student implements Cloneable{String name;int age;Student(String name,int age){this.name=name;this.age=age;}

  public Object clone(){Object obj=null;try{obj=(Student)super.clone();//Object中的clone()識別出你要複製的是哪一個對象.

  }

  catch(CloneNotSupportedException e){e.printStackTrace();}

  return obj;}

  }

  public static void main(String[] args){Student s1=new Student("zhangsan",18);Student s2=(Student)s1.clone();s2.name="lisi";s2.age=20;System.out.println("name=" s1.name "," "age=" s1.age);//修改學生2//后,不影響學生1的值.

  }

  說明:

  ①為什麼我們在派生類中覆蓋Object的clone()方法時,一定要調用super.clone()呢?在運行時刻,Object中的clone()識別出你要複製的是哪一個對象,然後為此對象分配空間,並進行對象的複製,將原始對象的內容一一複製到新對象的存儲空間中.

  ②繼承自java.lang.Object類的clone()方法是淺複製.以下代碼可以證明之.

  class Teacher{String name;int age;Teacher(String name,int age){this.name=name;this.age=age;}

  }

  class Student implements Cloneable{String name;int age;Teacher t;//學生1和學生2的引用值都是一樣的.

  Student(String name,int age,Teacher t){this.name=name;this.age=age;this.t=t;}

  public Object clone(){Student stu=null;try{stu=(Student)super.clone();}catch(CloneNotSupportedException e){e.printStackTrace();}

  stu.t=(Teacher)t.clone();return stu;}

  public static void main(String[] args){Teacher t=new Teacher("tangliang",30);Student s1=new Student("zhangsan",18,t);Student s2=(Student)s1.clone();s2.t.name="tony";s2.t.age=40;System.out.println("name=" s1.t.name "," "age=" s1.t.age);//學生1的老師成為tony,age為40.

  }

  }

  那應該如何實現深層次的克隆,即修改s2的老師不會影響s1的老師?代碼改進如下.

  class Teacher implements Cloneable{String name;int age;Teacher(String name,int age){this.name=name;this.age=age;}

  public Object clone(){Object obj=null;try{obj=super.clone();}catch(CloneNotSupportedException e){e.printStackTrace();}

  return obj;}

  }

   class Student implements Cloneable{String name;int age;Teacher t;Student(String name,int age,Teacher t){this.name=name;this.age=age;this.t=t;}

  public Object clone(){Student stu=null;try{stu=(Student)super.clone();}catch(CloneNotSupportedException e){e.printStackTrace();}

  stu.t=(Teacher)t.clone();return stu;}

  }

  public static void main(String[] args){Teacher t=new Teacher("tangliang",30);Student s1=new Student("zhangsan",18,t);Student s2=(Student)s1.clone();s2.t.name="tony";s2.t.age=40;System.out.println("name=" s1.t.name "," "age=" s1.t.age);//學生1的老師不改變.

  }

  3)利用串列化來做深複製把對象寫到流里的過程是串列化(Serilization)過程,Java程序員又非常形象地稱為「冷凍」或者「腌鹹菜(picking)」過程;而把對象從流中讀出來的并行化(Deserialization)過程則叫做「解凍」或者「回鮮(depicking)」過程.應當指出的是,寫在流里的是對象的一個拷貝,而原對象仍然存在於JVM裡面,因此「腌成鹹菜」的只是對象的一個拷貝,Java鹹菜還可以回鮮.

  在Java語言里深複製一個對象,常常可以先使對象實現Serializable介面,然後把對象(實際上只是對象的一個拷貝)寫到一個流里(腌成鹹菜),再從流里讀出來(把鹹菜回鮮),便可以重建對象.

  如下為深複製源代碼.

  public Object deepClone(){//將對象寫到流里ByteArrayOutoutStream bo=new ByteArrayOutputStream();ObjectOutputStream oo=new ObjectOutputStream(bo);oo.writeObject(this);//從流里讀出來ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());ObjectInputStream oi=new ObjectInputStream(bi);return(oi.readObject());}

  這樣做的前提是對象以及對象內部所有引用到的對象都是可串列化的,否則,就需要仔細考察那些不可串列化的對象可否設成transient,從而將之排除在複製過程之外.上例代碼改進如下.

  class Teacher implements Serializable{String name;int age;Teacher(String name,int age){this.name=name;this.age=age;}

  }

  class Student implements Serializable{String name;//常量對象.

  int age;Teacher t;//學生1和學生2的引用值都是一樣的.

  Student(String name,int age,Teacher t){this.name=name;this.age=age;this.p=p;}

  public Object deepClone() throws IOException,OptionalDataException,ClassNotFoundException{//將對象寫到流里ByteArrayOutoutStream bo=new ByteArrayOutputStream();ObjectOutputStream oo=new ObjectOutputStream(bo);oo.writeObject(this);//從流里讀出來ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());ObjectInputStream oi=new ObjectInputStream(bi);return(oi.readObject());}

  }

  public static void main(String[] args){Teacher t=new Teacher("tangliang",30);Student s1=new Student("zhangsan",18,t);Student s2=(Student)s1.deepClone();s2.t.name="tony";s2.t.age=40;System.out.println("name=" s1.t.name "," "age=" s1.t.age);//學生1的老師不改變.

  }


[火星人 ] java中值得注意的問題已經有495次圍觀

http://coctec.com/docs/java/show-post-61077.html