Java 高质量编程建议(笔记1)

来源:互联网 发布:东莞犀牛软件培训班 编辑:程序博客网 时间:2024/06/06 03:00

Ad-1:不要在变量和常量中出现容易混淆的数字和字母
 1)包名称通常全小写;
 2)多个单词构成的类名称,全首字母大写;
 3)变量名称和函数名称的首单词首字母小写,后续单词的首字母大写;
 4)常量全大写,单词之间用"_"连接;
 5)long型整数赋值时,需要加字母"l"或"L",建议用L;
 6)字母O与数字0 易混淆,建议常量和变量中不少使用,使用时增加注释;
 
Ad-2:莫让常量蜕变成变量
 1)常量就是常量,是在编译的时候确定了的值,在运行期间不能发生变化;
 2)常量定义会变的例子:
     public static final int RAND_CONST = new Random().nextInt();
  
Ad-3:三元操作符的类型必须一致
 1)三元操作符的操作数类型不一致的时候,会发生类型转换,可能true/false条件下,预期的结果类型不符合;
 2)三元操作符的类型转换规则:
  a)如果两个操作数不可转换,则不会转换,返回值为Object类型;
  b)如果两个操作数是明确类型的表达式, 则按照正常的二进制数字转换,int类型转long类型,long类型转float类型;
  c)如果连个操作数是直接数字量(常数),则返回值类型为范围较大者(注意是范围较大者,而不是值,java的数字类型是有符号的);
  d)如果两个操作数,一个是数字,一个是表达式,则返回值类型是这两个类型表示的范围较大者;
    总之,如果两个操作的范围之间存在包含关系(可以转换的),则返回范围较大者,否则返回Object类型;
   
Ad-4:避免带有变长参数的方法重载
 1)变长参数介绍:
  a)变长参数的使用目的是提高程序的灵活性;在Java5之前,使用容器类型的缺点是需要对空参数进行判断和筛选;
  b)变长参数的必须是方法的最后一个参数,当然一个方法不可能有多个变长参数;
 2) 变长参数的定义
  在参数类型之后,加三个".";
 3)变长参数的在方法中使用方式与数组相同;

 2)变长参数在重载的时候使用注意编译器对调用方法的选择
  例:public float calcPrice(int price, int discount);
      public float calcPrice(int price, int... discount);
  
  calcPrice(100, 80);  //非变长参数
  calcPrice(100, 90, 90); //变长参数
  
     要点:Java编译器在编译的时候,首先根据实参的类型和个数查找最符合条件的,话虽如此,参数80也可以作为{80}一个元素的数组,
    其次,Java编译器“偷懒”,当然从最简单的开始匹配,而且int 80 转换为 int[] {80}也是有成本的,因此java编译器聪明的选择了非变长参数的方法;
  注意:代码给机器执行的,但是是给人看的,所以一定要人能看得懂,要避免晦涩易误解的代码。
  
  
Ad-5:别让null值和空值威胁到变长方法
 例子:public void methodA(String str, Integer...is){ System.out.println("Integer");}
    public void methodA(String str, String...strIs){ System.out.println("String");}
  
    testFunc()
    {
  Integer intA = null;
  String str = "str";
  methodA(str, 0);  //Integer
  methodA(str, "People"); //String
  methodA(str);   //编译错误,方法模糊不清,编译器无法确定调用哪个函数
  methodA(str, null);  //编译错误,方法模糊不清,编译器无法确定调用哪个函数
  methodA(str, intA);  //Integer
 }  
 
Ad-6:覆盖变长方法也要循规蹈矩
 1)子类覆盖父类的方法条件
  a)重写方法不能缩小访问权限;
  b)参数列表必须与被重写的方法相同;
  c)返回类型必须与被重写的方法返回类型相同,或者是其子类型;
  d)重写方法不能抛出新的异常,或者超出父类范围的异常,但是可以抛出更少、范围更小的异常,或者不抛异常;
     e)注意变长参数在继承和覆盖的时候,可能出现参数的表现形式不同而导致编译失败;
  
Ad-7:警惕自增陷阱
  int count = 0;
  for (int i = 0; i < 10; i++)
  {
   count = count++;
  }
 运行结果:count=0
 1)注意count++ 是一个表达式,是有返回值的,返回值是增加之前的值;
 2)分析一下运行过程,i=0时
  a)JVM将count=0拷贝到一个零时变量(零时工)中;
  b)count值加1,即count=1;
  c)count++表达式返回值时零时变量的值0;
  d)将表达式的值0赋值给count,这样count变为0;
 3)千万要注意在循环中,如果出现i=i++; i < K; 将出现死循环; 
  
  
Ad-9:少用静态导入
 1)静态导入语法(import static)目的是为了减少字符输入量,提高代码的可读性;
 2)静态属性和静态方法缺少类来限定范围,易被无限放大;
 3)静态导入的时候,慎用*号,避免把一个类的所有静态成员都导入;
 4)静态导入规则:
  a)不使用*(星号)通配符,除非导入的类是静态常量类;
  b)方法名是具有明确、清晰表象意义的工具类;
  
Ad-10:不要在本类中覆盖静态导入的变量和方法
 1)如果本地方法和变量与静态导入的方法和变量相同,不会导致编译失败;
 2)编译器根据“最短路径”原则:如果能在本类中找到变量、常量、方法,就不会从包含的包或者父类、接口中查找,“地方保护主义”;
 

Ad-11:养成良好习惯,显示声明UID
 1)类实现Serializable接口的目的是为了持久化(网络传输或者本地存储),为系统的分布和异构部署提供先决支持条件;
 2)通常在网络传输中,发送方进行序列化,接受方进行反序列化;
 3)在序列化和反序列化的类不一致的情形下,反序列化会报一个InvalidClassException异常, 原因是序列化和反序列化所对应的类版本发生变化,JVM不能
  把数据流转换为实例对象。
 4)JVM通过SerialVersionUID(也称:流标示符, Stream Unique Identifier),即类的版本定义,可以显示声明,也可以是隐式的;
 5)显示声明SerialVersionUID方法:private static final long serialVersionUID = xxxxxL;
 6)隐式则是编译器在编译的时候生成,根据包名、类名、继承关系、非私有方法和属性、以及参数、返回值等通过计算公式所得;
 7)JVM在反序列化的时候,会比较数据流中的serialVersionUID和类中的serialVersionUID是否相同,如果不相同,则抛InvalidClassException异常;
  否则认为类没有变化,根据数据流初始化实例对象;
 8)通过显示声明serialVersionUID可以实现分布式或者升级前后类之间的兼容; 
 

Ad-12:避免用序列化类在构造函数中为不变量赋值
Ad-13:避免位final变量复杂赋值
 1)static静态变量不会被序化,final不变量被序列化;
 2)final变量初始化方法有两种,一种是在定义的时候赋值,一种是在构造方法中赋值;
 3)序列化的基本原则是序列化和反序列化的时候,类的数据保持一致;
 4)final变量在反序列化的时候,如果类中的赋值方法是定义时简单表达式(基本类型)赋值,则会重新计算,
  如果是复杂对象, 该对象和关联类信息一起保存,并且持续递归下去(复杂对象类也实现了Serializable接口,否则会出现序列化异常),则会重新计算;
  如果定义时通过复杂赋值(函数调用返回值),则不会重新计算;
  如果是通过构造方法赋值,则不会重新计算;
 5)序列化保存的信息 
      a)类描述信息:包路径、继承关系、访问权限、变量描述、变量访问权限、方法签名、返回值、以及变量的关联类信息,但是不是class文件,不包括方法、构造方法、static变量;
  b)非瞬态(transient关键字)和非静态(static关键字)的实例变量; 
 
Ad-14:使用序列化类的私有方法巧妙解决部分属性持久化问题
 1)在实现Serializable的类中实现两个私有方法,可以影响和控制序列化,反序列化的过程:
    private void writeObject(java.io.ObjectOutputStream out) throws IOException
    {
        out.defaultWriteObject();
        //out.writeInt()
    }
   
    private void readObejct(java.io.ObjectInputStream in) throws IOException,ClassNotFoundException
    {
        in.defaultReadObject();
        //var=in.readInt();
    }