版权声明:本文为博主原创文章,未经博主允许不得转载。 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.避免对象的浅拷贝