“不积跬步,无以至千里,不积小流,无以成江海”,程序员如何提高代码质量?我们不仅要知其然,还要知其所以然,我们要从点点滴滴的积累开始的。这篇帖子里记录了编程时的应该注意的一些细节,如有需要后续还会补充,希望通过不断的积累,提高编程质量。
可序列化接口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方法来判断值相等,而对于在内存中只存在一份的判断可以用“==”例如字节码等。