“不積跬步,無以至千里,不積小流,無以成江海”,程序員如何提高代碼質量?我們不僅要知其然,還要知其所以然,我們要從點點滴滴的積累開始的。這篇帖子裏記錄了編程時的應該注意的一些細節,如有需要後續還會補充,希望通過不斷的積累,提高編程質量。
可序列化接口Serializable
類實現Serializable接口的目的是爲了可持久化,比如網絡傳輸或本地存儲,爲系統的分佈或異步部署提供先決支持條件。若沒有序列化,現在我們熟悉的遠程調用,對象數據庫都不可能存在。
序列化和反序列化是對應的,以下用代碼描述實現了序列化接口Serializable的類在磁盤上的存儲過程及反序列化,其在網絡上的傳輸道理也是一樣的。
package org.iti.wxl.serializable;
import java.io.Serializable;
/**
* 實現了Serializable的實體類
*
*/
public class Person implements Serializable{
private static final long serialVersionUID = 6388172609871883630L;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
爲模擬序列化的過程我們創建序列化工具類,如下:
package org.iti.wxl.serializable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class SerializableUtils {
private static String FILE_NAME="e:/obj.bin";
//序列化數據保存到磁盤
public static void writeObject(Serializable s){
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_NAME));
oos.writeObject(s);
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//反序列化從磁盤讀取數據
public static Object readObject(){
Object obj = null;
try {
ObjectInput input = new ObjectInputStream(new FileInputStream(FILE_NAME));
obj = input.readObject();
input.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return obj;
}
}
序列化保存數據到磁盤上
package org.iti.wxl.serializable;
public class Producer {
public static void main(String[] args) {
Person p = new Person();
p.setName("小明");
// 序列化保存到磁盤上
SerializableUtils.writeObject(p);
}
}
反序列化,從磁盤讀取數據:
package org.iti.wxl.serializable;
public class Consumer {
public static void main(String[] args) {
//反序列化
Person p =(Person)SerializableUtils.readObject();
System.out.println(p.getName());
}
}
在反序列化時,若類的版本號serialVersionUID不一致,反序列化時會報一個InvalidClassexception異常,另外在反序列化時,類中的構造函數不會執行。
奇偶校驗用偶校驗,避免使用奇校驗
JDK提供的奇偶校驗代碼模擬如下:
public class Remainder {
//dividend被除數,divisor除數
public static int remainder(int dividend, int divisor) {
return dividend - dividend/divisor*divisor;
}
}
當使用取模運算“%”做奇偶校驗時,JDK會依照以上公式進行運算,此時dividend爲要校驗的數,divisor=2我們發現當被除數爲負數且爲奇數時,餘數爲-1,如果我們的奇偶校驗公式爲i%2 =1 ? "奇數":"偶數";那麼當i爲負奇數時結果判斷會錯誤,因此我們做奇偶校驗時要用偶校驗,即:i%2 == 0 ? "偶數":"奇數";
java中的引用問題
java中沒有指針的概念,但我們要理解另一個名詞“引用”。看下面的例子:
實體類
public class IntString {
private Integer no;
private String str;
public Integer getNo() {
return no;
}
public void setNo(Integer no) {
this.no = no;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "IntString [" + (no != null ? "no=" + no + ", " : "")
+ (str != null ? "str=" + str : "") + "]";
}
}
測試類:
public class Test {
public static void main(String[] args) {
List<IntString> test = new ArrayList<IntString>();
IntString is1 = new IntString();
is1.setNo(1);
is1.setStr("一");
IntString is2 = new IntString();
is2.setNo(2);
is2.setStr("二");
IntString is3 = new IntString();
is3.setNo(3);
is3.setStr("三");
test.add(is1);
test.add(is2);
test.add(is3);
List<IntString> newtest = new ArrayList<IntString>();
newtest = test;
// List<IntString> newtest = test;
System.out.println("前" + newtest);
test.get(0).setStr("1");
System.out.println("後" + newtest);
}
}
輸出結果:
前[IntString [no=1, str=一], IntString [no=2, str=二], IntString [no=3, str=三]]
後[IntString [no=1, str=1], IntString [no=2, str=二], IntString [no=3, str=三]]
程序中改變了test實體裏的一個對象的某個參數的值,但是打印出newTest後發現它的內部對象值發生了變化,解釋如下:newtest = test時,newTest和test兩個名字雖然不一樣,但是都指向同一塊內存地址,test發生變化也就是newTest發生了變化。
java中基本類型可以分爲三類,字符類型char,布爾類型boolean以及數值類型byte、short、int、long、float、double,對於非基本類型的對象類數據都是採用引用的方式來處理,那麼在做賦值操作“=”時,是引用的賦值,兩個不用名稱的應用指向同一個地址,其中一個數值發生變化兩個就都發生變化了。
另外,對於基本類型,JVM提供常量池,用到某數值的時從常量池裏取值,這個時候用==判斷是否相等,數值相等時一定相等,“==”判斷的是地址相等。而對於非基本類型數值相等也不一定“==”,因此我們應該用equals方法來判斷值相等,而對於在內存中只存在一份的判斷可以用“==”例如字節碼等。