[笔记]改善Java程序的151个建议---第一章 Java开发中通用的方法和准则

第一章 Java开发中通用的方法和准则

建议1:不要在常量和变量中出现易混淆的字母

字母“l”务必大写,字母“o”增加注释


建议2:莫让常量蜕变成变量

常量就是常量,在编译期必须确定其值,不应该在运行期更改,否则程序在运行期发生各种事情。

如:public static final int RAND_CONST = new Random().nextInt();


建议3:三元操作符的类型务必一致

类型不一致如:i < 100 : 90: 100.0(float)


建议4:避免带有变长参数的方法重载

变长参数,必须是方法的最后一个参数,一个方法不能定义多个变长参数。

避免变长参数重载,如下:

public void calPrice(int price, int discount)

public void calPrice(int price, int... discount)


建议5: 别让null值和空值威胁到变长方法

如:

public void method(String str, Integer... is)

public void method(String str, String... is)

client.method("China");

client.method("China", null);


正确做法:

String strs = null;

client.method("China", strs);


建议6:覆写变长方法也要遵循规矩

@Override必须满足如下条件:

从写方法不能缩小访问权限;

参数列表必须与被重写方法相同;(参数数量相同,类型相同,顺序相同)

返回类型必须与被重写的方法的相同或者是其子类;

重新方法不能抛新的异常,或者超出父类范围的异常,但是可以抛出更少,更有限的异常,或者不抛异常。


建议7:警惕自增的陷阱

错误语句,如:

int count = 0;

count= count++;  //结果是count的初始值0


建议8:不要让旧语法困扰你

goto语句

break,continue后面可以加上冒号做跳转,实现goto功能。


建议9:少用静态导入

如: import static java.lang.Math.PI


建议10:不要在本类中覆盖静态导入的变量和方法

//常量名誉静态导入的PI相同

public final static String PI="祖冲之";


建议11:养成良好习惯,显式声明UID

序列化标志接口

Serial Version ID流标识符(类的版本定义),如果没有的话,序列化和反序列化所对应的类版本发生了变化,JVM不能把数据流转换为实例对象。

private static final long serialVersionUID = *****L;

隐式声明会根据类的各种属性,方法,关系,类名,包名,参数,返回值等计算得出。


建议12:避免用序列化类在构造函数中卫不变量赋值(final)

static序列化时候根本没有保存到数据流中

下面错误,如:

public class Person implements Serializable{

     public final String name;

     Person(){

          name = "aa";

     }

}


建议13:避免为final变量复杂赋值

final变量赋值还有一种方式,通过方法赋值。

public final String  name = initName();


final会被重新赋值,是指简单对象,8个基本类型,数组,字符串(不通过new生成的String),但是不能方法赋值。


保存到磁盘(网络传输)上的对象文件包括2个部分:

类描述信息:包路径,继承关系,访问权限,变量描述,变量访问权限,方法签名,返回值,变量关联类信息。不记录方法,构造函数,static等。

transient,static的实例变量值

关联类也必须序列化


总结,反序列化时候final变量在以下情况下不会被重新赋值。

通过构造函数为final变量赋值

通过方法返回值为final变量赋值

final修饰的属性不是基本类型


建议14:使用序列化类的私有方法巧妙解决部分属性持久化问题

部分属性持久化,把不需要持久化的属性前面加上transient


姓名,基本工资可以序列化

绩效工资保密,不能序列化


实现了Serializable接口的类可以实现两个私有方法:writeObject和readObject。用来影响和控制序列化和反序列化的过程。

序列化的独有机制:序列化回调。Java调用ObjectOutputStream类把一个对象转换成流数据的时候,会通过反射检查被序列化的类是否有writeObject方法,并且检查其是否符合私有、无返回值的特征。如果有,则会委托该方法进行对象序列化。如没有,则由ObjectOutputStream按照默认规则继续序列化。readObject也一样。


import java.io.Serializable;



public class Salary implements Serializable {


        private static final long serialVersionUID = -5402065593284392992L;

        //基本工资

        private int basePay ;

        //绩效工资

        private int bonus ;

       

       Salary( int basePay , int bonus){

               this.basePay = basePay ;

               this.bonus = bonus ;

       }

       

        public int getBasePay() {

               return basePay ;

       }

       

        public void setBasePay(int basePay) {

               this.basePay = basePay ;

       }

       

        public int getBonus() {

               return bonus ;

       }

       

        public void setBonus(int bonus) {

               this.bonus = bonus ;

       }

       

       

}


import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;



public class Person implements Serializable{


        /**

        * Person serialVersionUID

        */

        private static final long serialVersionUID = 7592930394427200495L;

        //姓名

        private String name ;

        //薪水

        private transient Salary salary ;

       

       Person(String name, Salary salary){

               this.name = name ;

               this.salary = salary ;

       }


        public String getName() {

               return name ;

       }


        public void setName(String name) {

               this.name = name ;

       }


        public Salary getSalary() {

               return salary ;

       }


        public void setSalary(Salary salary) {

               this.salary = salary ;

       }

       

        //序列化委托方法

        private void writeObject(ObjectOutputStream out) throws IOException{

               out.defaultWriteObject();

               out.writeInt( this.salary .getBasePay());

       }

       

        //反序列化委托方法

        private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException{

               in.defaultReadObject();

               this.salary = new Salary(in.readInt(), 0);

       }

}


import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;



public class SerializationUtils {

        private static String FILE_NAME = "d:/a.bin";

        //序列化

        public static void writeObject(Serializable s) {

              ObjectOutputStream oos;

               try {

                      oos = new ObjectOutputStream(new FileOutputStream(FILE_NAME ));

                      oos.writeObject( s);

                      oos.close();

              } catch (FileNotFoundException e ) {

                      // TODO Auto-generated catch block

                      e.printStackTrace();

              } catch (IOException e ) {

                      // TODO Auto-generated catch block

                      e.printStackTrace();

              }


       }

       

        public static Object readObject(){

              Object obj = null;

               //反序列化

               try {

                     

                     ObjectInputStream input = new ObjectInputStream(new FileInputStream(FILE_NAME ));

                      obj = input.readObject();

                      input.close();

              } catch (IOException | ClassNotFoundException e ) {

                      // TODO Auto-generated catch block

                      e.printStackTrace();

              }

               return obj ;          

       }

}


//序列化Client

public class SerializeClient {


        public static void main(String[] args) {

               //基本工资1000,绩效工资25000

              Salary salary = new Salary(1000, 25000);

               //记录人员信息

              Person person = new Person("张三" , salary );

              SerializationUtils. writeObject(person);

       }


}


//反序列化

public class DeSerializationClient {


        public static void main(String[] args) {

               //反序列化并打印信息

              Person p = (Person)SerializationUtils. readObject();

              StringBuffer sb = new StringBuffer();

               sb.append( "姓名 : " + p .getName());

               sb.append( "\t基本工资: " + p .getSalary().getBasePay());

               sb.append( "\t绩效工资: " + p .getSalary().getBonus());

              System. out.println(sb );

       }

}


建议15:break万万不可忘
switch... case...
Performaces->Java->Compiler->Errors/Warnings->Potential Programming problems->'switch' case fall-through->Errors

建议16:易变业务使用脚本语言编写
脚步语言:灵活,便捷,简单。如Groovy

建议17:慎用动态编译

建议18:避免instanceof非预期结果
判断一个对象是否是一个类的实例

建议19:断言不是鸡肋
防御式编程中经常用断言Assertion对参数和环境做出判断,避免程序因不当的输入或错误的环境产生逻辑异常。断言是为调试程序而用。
assert默认是不启用的;assert抛出的异常AssertionErro是继承自Error。
防御式编程特点,所有外部因素都是邪恶的,存在企图摧毁程序的罪恶本源。为了抵制,要在程序中处处检验,设卡,不满足条件就不再执行后面的程序。保护主程序的正确性。
不能用断言在公开方法中做输入校验。
不能用断言在布尔表达式中执行逻辑代码

正确使用断言:按照正常执行逻辑不可能达到的代码区域放置断言
(1)私有方法中放置断言,作为输入参数的校验
(2)流程控制中不可能达到的区域,意义:程序执行到这里就是错误的。
(3)建立程序探针,断言变量之间的关系

建议20:不要只替换一个类
一个常量接口(类),编译器认为它是稳定态,所以编译时候直接把值编译到字节码中。以后无论怎么修改这个常量接口(类)里面的值,只要不重新编译,输出的还是原来的值。
发布应用系统时候,禁止使用类文件替换方式,整体WAR包发布是完全之策。
public class Constant{
     public final static int MAX_AGE = 100;
}


你可能感兴趣的:([笔记]改善Java程序的151个建议---第一章 Java开发中通用的方法和准则)