改善程序151建议

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cheidou123/article/details/77898716

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

package Advise151.advise3;
public class test3 {
    public static void main(String[] args) {
		int i=80;
		String s=String.valueOf(i<100?90:100);
		String s2=String.valueOf(i<100?90:100.0);
		System.out.println(s);
		System.out.println(s2);
	    System.out.println(s.equals(s2));
	    }
}
output:

90
90.0
false
100.0是浮点数,导致int变为浮点数

保证三元运算符操作数类型一致,即可减少错误的发生

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

package Advise151.advise4;

import java.text.NumberFormat;

public class Client4 {
   private void calPrice(int price,int... discounts) {   //记住:如果这里改为int[] discounts 第一个调用编译通不过
		// TODO Auto-generated method stub
       float sum=price;
       for(int discount:discounts)
    	   sum=sum*discount/100;
       System.out.println("复杂折扣"+fom(sum));
   }
   private void calPrice(int price,int discount) {
		// TODO Auto-generated method stub
		   float sum=price*discount/100.0F;
		   System.out.println("简单折扣"+fom(sum));
	 }
   private String fom(float price) {
	   return NumberFormat.getCurrencyInstance().format(price/100);
   }     
   
   public static void main(String[] args) {
	   Client4 client4=new Client4();
	   client4.calPrice(49900, 75,44); 
	   client4.calPrice(49900, 75); 
   }
}
output:

复杂折扣¥164.67
简单折扣¥374.25
在   client4.calPrice(49900, 75); 这个调用中, 编译器会找最简单的,int是基本数据类型,所以会调用简单折扣那个方法
3.建议5:别让NULL值和空值威胁到变长方法

左边这俩编译器都不知道该调用哪一个方法,所以编译不通过

如果改成这样

package Advise151.advise5;

public class Client {
   public void methodA(String str,Integer...is){
	   System.out.println("111");
   }
   public void methodB(String str){}
   public void methodB(Integer str){}
   public static void main(String[] args) {
	Client A=new Client();
	A.methodA("china", 0);
	A.methodA("china");
	A.methodA("china",null);
	}
}
就会输出

111
111
111

4.建议7:警惕自增的陷阱

看一段代码别的啥也不说了

package Advise151.advise7;

public class Client {
  public static void main(String[] args) {
	int count=0;
	int count2=0;
	for(int i=0;i<10;i++)
		count++;
	System.out.println(count);
	for(int j=0;j<10;j++)
		count2=count2++;
	System.out.println(count2);
}
}
output:

10
0
5.建议11:显示声明UID
JVM进行反序列化时,会比较数据流中的serialVersionID和类的serialVersionID是否相同,如果不相同,抛出InvalidClassException
举例:
Address类:

package xulihua;

import java.io.Serializable;

public class Address implements Serializable {

	   private static final long serialVersionUID = 2L;   //显示声明段

	   String street;
	   String country;
	   String province;                                  //实验属性<span style="color:#ff0000;">
</span>
	   public void setStreet(String street){
		   this.street = street;
	   }

	   public void setCountry(String country){
		   this.country = country;
	   }

	   public String getStreet(){
		   return this.street;
	   }

	   public String getCountry(){
		   return this.country;
	   }
	   
	 

	   public String getProvince() {
		return province;
	}

	  public void setProvince(String province) {
	 	this.province = province;
	   }<span style="color:#ff0000;">
</span>
	@Override
	   public String toString() {
 	   return new StringBuffer(" Street : ")
 	   .append(this.street)
 	   .append(" Country : ")
 	   .append(this.country).toString();
	   }
}
对象序列化过程:

package xulihua;  
import java.io.FileOutputStream;  
import java.io.ObjectOutputStream;  
public class WriteObject {  
    public static void main (String args[]) {  
  
           Address address = new Address();  
           address.setStreet("wall street");  
           address.setCountry("united states");  
  
           try{  
  
            FileOutputStream fout = new FileOutputStream("c:\\address.ser");  
            ObjectOutputStream oos = new ObjectOutputStream(fout);  
            oos.writeObject(address);  
            oos.close();  
            System.out.println("Done");  
  
           }catch(Exception ex){  
               ex.printStackTrace();  
           }  
        }  
}  
反序列化过程:
package xulihua;  
import java.io.FileInputStream;  
import java.io.ObjectInputStream;  
public class ReadObject {  
    public static void main (String args[]) {  
  
           Address address;  
  
           try{  
  
               FileInputStream fin = new FileInputStream("c:\\address.ser");  
               ObjectInputStream ois = new ObjectInputStream(fin);  
               address = (Address) ois.readObject();  
               ois.close();  
  
               System.out.println(address);  
               System.out.println(address.getCountry());  
               System.out.println(address.getStreet());  
  
           }catch(Exception ex){  
               ex.printStackTrace();  
           }  
       }  
}  
当我们屏蔽显式声明段,先序列化编译,再屏蔽掉实验属性,反序列化编译,则出现异常:
java.io.InvalidClassException: xulihua.Address; local class incompatible: stream classdesc serialVersionUID = -2998289792273691725, local class serialVersionUID = -7655750428610544977  
    at java.io.ObjectStreamClass.initNonProxy(Unknown Source)  
    at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)  
    at java.io.ObjectInputStream.readClassDesc(Unknown Source)  
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)  
    at java.io.ObjectInputStream.readObject0(Unknown Source)  
    at java.io.ObjectInputStream.readObject(Unknown Source)  
    at xulihua.ReadObject.main(ReadObject.java:13)  
假如这时我们想将类反序列过来,我们可以保留显式声明段,先序列化编译,再屏蔽掉实验属性,反序列化编译,则运行正常,但是无法取到province属性:
Street : wall street Country : united states  
united states  
wall street  
当我们建立一个实现序列化接口的类时,有两种情况让我们选择:
①add default serial version ID :默认1L,一般用这个,我们可以手动递加
②add generated serial version ID:根据类的结构产生的hash值,生成一个很大的数,一般不用
6.建议12:避免用序列化类在构造函数中为不变量赋值
很重要的一个概念:反序列化的时候构造函数不会执行

public class Person implements Serializable{        
     private static final long serialVersionUID = 91282334L;    
     //不变量初始不赋值    
     public  final String name;    
     //构造函数为不变量赋值    
     public Person(){    
         name="混世魔王";    
    }    
}   
以上这种在构造函数给序列化不变量赋值的方法是错误的。
7.建议13:避免用序列化类中的final变量复杂赋值

public class Person implements Serializable{  
    private static final long serialVersionUID = 91282334L;  
    // 通过方法返回值为final 变量赋值  
    public final String name=initName();  
    // 初始化方法名  
    public String initName(){  
        return " 混世魔王";  
    }  
}  
以上这种通过方法给序列化final变量进行复杂赋值的办法是错误的
所说final 会被重新赋值,其中的“值”指的是简单对象。简单对象包括:8个基本类型,以及数组、字符串(字符串情况很复杂,不通过new 关键字生成String 对象的情况下,final 变量的赋值与基本类型相同),但是不能方法赋值。
总结一下,反序列化时final 变量在以下情况下不会被重新赋值:
通过构造函数为final 变量赋值。
通过方法返回值为final 变量赋值。
final 修饰的属性不是基本类型。

8.建议21:用偶判断不用奇判断

package AOP2;

public class TestString {
    
    public static void main(String[] args) {
		System.out.println(1%2);
		System.out.println(2%2);
		System.out.println(-1%2);
		System.out.println(-2%2);
		System.out.println(-3%2==1);
		System.out.println(-4%2==0);
		}
	}
输出;

1
0
-1
0
false
true
可以看到,如果是负奇数的话直接取余不是1,所以用==0来判断是不是偶数。
9.建议23:使用主动声明方式防止类型转换导致越界
package AOP2;

public class TestString {
    public static final int a=30*1000*10000;
    public static void main(String[] args) {
	  long dis=a*60*8;   //java是先运算再进行类型转换
	  System.out.println(dis);
	  //我们可以在运算上解决这个问题
	  long dis2=1L*a*60*8;
	  System.out.println(dis2);
		}
	}
输出:

-2028888064
144000000000
10.建议28:注意整形池

package AOP2;

public class TestString {
   
    public static void main(String[] args) {
    	
    Integer m1=new Integer(127); 
    Integer m2=new Integer(127); 
    System.out.println(m1==m2);
    Integer m3=new Integer(127); 
    Integer m4=new Integer(127); 
    System.out.println(m3==m4);
 
    Integer j1=127;
    Integer j2=127;
    System.out.println(j1==j2);
    Integer j3=128;
    Integer j4=128;
    System.out.println(j3==j4);
    	
    	
    Integer k1=Integer.valueOf(127);
    Integer k2=Integer.valueOf(127);
    System.out.println(k1==k2);
    
    Integer k3=Integer.valueOf(128);
    Integer k4=Integer.valueOf(128);
    System.out.println(k3==k4);
		}
	}
输出:

false
false
true
false
true
false
11.不要覆写静态方法
package AOP2;
public class TestString {
    public static void main(String[] args) {
      Base base=new Sub();
      base.doSomething();
      base.doSomething2();
      Sub sub=new Sub();
      sub.doSomething();
      sub.doSomething2();
   	}
}
   class Base{
       public static  void doSomething(){
     	System.out.println("父类静态方法");   
       }
       public  void doSomething2(){
       	System.out.println("父类静态方法");   
          }
    	}
   class Sub extends Base{
	   public static  void doSomething(){
    	 System.out.println("子类静态方法");   
	       }
	   public  void doSomething2(){
	     System.out.println("子类静态方法");   
	     }
	   }
输出:

父类静态方法
子类静态方法
子类静态方法
子类静态方法
静态方法是属于类的,它那不叫覆写,叫隐藏!覆写用可以用 @Override,而隐藏不可以

12.避免在构造函数初始化其他类

package AOP2;

import Buillder.SonyBuilder;

public class TestString {
    public static void main(String[] args) {
      Son s=new Son();
      s.doSomething();
   	}
   }
class Father{
	public Father() {
		// TODO Auto-generated constructor stub
		new Son();
	}
}
class Son extends Father{
	public void doSomething() {
		// TODO Auto-generated method stub
		System.out.println("hello");
	}
	}  
因为调用Son的时候会执行父类的无参构造函数,而父类的无参构造函数又new Son();导致
Exception in thread "main" java.lang.StackOverflowError
13.建议36:使用构造代码块精炼程序

先看一段程序:

package AOP;

public class Client {
	{
	 System.out.println("构造代码块");	
	}
	public Client(){
	 System.out.println("无参构造函数");
	}
	public Client(String a){
		System.out.println("有参构造函数"+a);	
	}
	public static void main(String[] args) {
	   Client aClient=new Client();
	   Client bClient=new Client("a");
	}
}
输出:

构造代码块
无参构造函数
构造代码块
有参构造函数a

其实是这样的:编译器会把构造代码块插入到构造函数之前,上面其实等同是这样的:

package AOP;

public class Client {
	public Client(){
	 System.out.println("构造代码块");	
	 System.out.println("无参构造函数");
	}
	public Client(String a){
		System.out.println("构造代码块");	
		System.out.println("有参构造函数"+a);	
	}
	public static void main(String[] args) {
	   Client aClient=new Client();
	   Client bClient=new Client("a");
	}
}
这里需要注意:构造代码块依赖于构造函数,并非是在构造函数之前执行的,这里再脑补一下什么叫构造代码块: 没有任何前缀或修饰,用{}括起来的代码片段

我们可以把构造代码块用到如下场景:

⑴初始化实例变量

如果每个构造函数都要初始化变量,可以用构造代码块来解决。

⑵初始化实例环境

一个对象必须在合适的环境下才能存在,比如HTTP Request必须首先建立HTTP Session,我们在创建HTTP Request时可以通过构造代码块检查HTTP Session是否存在,不存在就创建!

13.建议42:让工具类不可实例化


为了防止类被实例化,我们一般定义私有构造函数,但是通过反射仍可以实例化,为此,我们可以在构造函数抛出异常来防止实例化,但是要注意,这个类不要被继承,否则子类实例化会出问题

14.避免对象的浅拷贝





猜你喜欢

转载自blog.csdn.net/cheidou123/article/details/77898716
今日推荐