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

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

包名全小写,类名首字母全大写,常量全部大写并用下划线分割,变量采用驼峰(Camel Case)命名。在变量的声明中不要引入容易混淆的字母:

long i=1l;
System.out.println("i的两倍是:"+(i+i));

如果字母和数字混合使用,字母“l”务必大写,字母“O”则增加注释。
注意,字母“l”作为长整型标志时务必大写。

莫让常量蜕变成变量

加了final和static的常量可能会变吗?

interface ConstDemo {
    public static final int RAND_CONST = new Random().nextInt();
}
System.out.println("常量会变哦:" + ConstDemo.RAND_CONST);

常量就是常量,在编译器就必须确定其值,不应该在运行期更改,负责程序的可读性会非常差,甚至连作者自己都不能确定在运行 期发生了何种神奇的事情。

三元操作符的类型务必一致

 int i = 80;
String s = String.valueOf(i < 100 ? 90 : 100);
String s1 = String.valueOf(i < 100 ? 90 : 100.0);
System.out.println("两者是否相等:" + s.equals(s1));

三元操作符类型的转换规则:
若两个操作数不可转换,则不做钻换,返回值为Object类型
若干两个操作数是明确类型额表达式(比如变量),则按正常的二进制数字来转换,int类型转换为long类型,long类型转换为float类型等。
若两个操作数中有一个是数字S,另外一个是表达式,且其类型标识为T,那么,若数字S在T的范围内,则转换为T类型;若S超出T类型的范围,则T转换为S类型
若两个操作数都是直接量数字,则返回值类型为范围较大者。

注意保证三元操作符中两个操作数类型一致,即可减少错误的发生。

避免带有变长参数的方法重载

 public void calPrice(int price, int discount) {
        float knockdownPrice = price * discount / 100.0F;
        System.out.println("简单折扣后的价格是:" + formateCurrency(knockdownPrice));
    }

    public void calPrice(int price, int... discounts) {
        float knockdownPrice = price;
        for (int discount : discounts) {
            knockdownPrice = knockdownPrice * discount / 100;
        }
        System.out.println("复杂折扣后的价格是:" + formateCurrency(knockdownPrice));
    }

    private String formateCurrency(float price) {
        return NumberFormat.getCurrencyInstance().format(price / 100);
    }

Java在编译时,首先会根据实参的数量和类型来进行处理,也就是查到calPrice(int price,int discount)方法,而且确认它是否符合方法签名条件。
int是一个原生数据类型,而数组本身是一个对象,编译器想要“偷懒”,所以会这样选择。

慎用变长参数的方法重载,让人伤脑经不说,苏红补丁哪天就陷入这类小陷阱里了。

别让null值和空值威胁到变长方法

public class Client {
    public void methodA(String str, Integer... is) {
    }

    public void methodA(String str, String... strs) {
    }

    public static void main(String[] args) {
        Client client = new Client();
        client.methodA("China", 0);
        client.methodA("China", "People");
        client.methodA("China", null);
    }
}
public static void main(String[] args){
    Client client = new Client();
    String[] strs = null;
    client.methodA("China",strs);
}

让编译器知道这个null值是String类型的,编译即可顺利通过,也就减少了错误的发生。

覆写变长方法也循规蹈矩

在Java中,子类覆写符类的方法很常见,这样做既可以修正Bug也可以提供扩展的业务功能支持,同时还符合开闭原则(Open-Closed Principle)。覆写必须满足的条件:
重写方法不能缩小访问权限。
参数列表必须与被重写方法相同。
返回类型必须与重写方法的相同或者是其子类。
重写方法不能抛出新的异常,或者超出符类范围的异常,但是可以抛出更少,更有限的异常,或者不抛出异常。

public class Client {
    public static void main(String[] args) {
        Base base = new Sub();
        base.fun(100, 50);
        Sub sub = new Sub();
        sub.fun(100, 50);
    }
}

class Base {
    void fun(int price, int... discounts) {
        System.out.println("Base.....fun");
    }
}

class Sub extends Base {
    @Override
    void fun(int price, int[] discounts) {
        System.out.println("Sub.....fun");
    }
}

警惕自增的陷阱

int count = 0;
            for (int i = 0; i < 10; i++) {
                count = count++;
            }
            System.out.println("count=" + count);

结果是0

count++是一个表达式,是由返回值的,它的返回值就是count自加前的值,Java对自加是这样处理的:首先把count的值(注意是值,不是引用)拷贝到一个临时变量区,然后对count变量加1,最后返回临时变量区的值。程序第一次玄幻时的详细处理步骤如下:
1.JVM把count的值(其值是0)拷贝到临时变量区
2.count值加1,这时候count的值是1
3.返回临时变量区的值,注意这个值是0,没修改过。
4.返回值赋值给count,此时count值被重置成0.

不要让旧语法困扰你

Java中没有了goto关键字,但是扩展了break和continue关键字。

少用静态导入

静态导入语法(import static)其目的是为了减少字符串输入量,提高代码的可阅读性,以便更好的理解程序。但是,滥用静态导入回是程序更难于都,更难维护。会让阅读者很难弄清除其属性或方法代表何意,甚至是哪一个类的属性(方法)都要思考一番。(IDE友好提示是另说)

对于静态导入,一定要遵循两个原则:
1.不使用*(星号)通配符,除非是导入静态常量类(只包含常量的类或接口)。
2.方法名是具有明确,清晰表象意义的工具类

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

编译器有一个“最短路径”原则:如果能在奔雷中找到的变量,常量,方法,就不会到其他包或者符类,接口中查找,以确保奔雷中的属性,方法优先。

如果要变更一个被导入的方法,最好的办法是在原始类中重构,而不是在本类中覆盖。

养成良好习惯,显式声明UID

类实现Serializable接口目的是为了可持久化,比如网络传输或本地存储,为系统的分布和异构部署提供先决支持条件。若没有序列化,现在我们熟悉的远程调用,对象数据库都不可能存在。
通过SerialVersionUID,也叫做流标识符,即类的版本定义的,它可以显式声明也可以隐式声明。显式声明格式如下:
private static final long serialVersionUID = XXXXXL;
而隐式声明是我不声明,你编译器在编译的时候帮我生成。

JVM在反序列化时,会比较数据流中的serialVersionUID与类的serialVersionUID是否相同,如果相同,则认为类没有发生改变;如果不相同,JVM不干了,抛个异常InvalidClassException。

显式声明serialVersionUID可以提高代码的健壮性。

~~## 避免用序列化类在构造函数中为不变量赋值

避免为final变量复杂赋值

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

break万万不可忘

case后缺少break往往会称为bug所在。
可以再IDE中做相应的设置,将switch语句中,每个case不以break结尾的情况设置为编译错误。

易变业务使用脚本语言编写

例如我们项目中的公式。

慎用动态编译

动态编译一致是java的梦想,从Java6版本它开始支持动态编译了,可以在运行期直接编译.java文件,执行.class,并且能够获得相关的输入输出,甚至还能监听相关的事件。

使用动态编译时需要注意以下几点:
1.在框架中谨慎使用
2.不要再要求高性能的项目使用
3.动态编译要考虑安全性问题
4.记录动态编译过程。(空中编译和运行是很让人不放心的,留下些一句可以更好地优化程序)

避免instanceof非预期结果

public class Client {
    public static void main(String[] args) {
        //String对象是否是Object的实例
        boolean b1 = "Sting" instanceof Object;
        //String对象是否是String的实例
        boolean b2 = new String() instanceof String;
        //Object对象是否是String的实例
        boolean b3 = new Object() instanceof String;
        //拆箱类型是否是装箱类型的实例
        boolean b4 = 'A' instanceof Character;
        //空对象是否是String的实例
        boolean b5 = null instanceof String;
        // 类型转换后的空对象是否是String的实例
        boolean b6 = (String) null instanceof String;
        // Date对象是否是String的实例
        boolean b7 = new Date() instanceof String;
        // 在泛型类中判断String对象是否是Date的实例
        boolean b8 = new GenericClass<String>().isDateInstance("");
    }
}

class GenericClass<T> {
    //判断是否是Date类型
    public boolean isDateInstance(T t) {
        return t instanceof Date;
    }
}

'A' instanceof String无法编译通过,因为'A'是一个char类型,也就是一个基本类型,不是一个对象,instanceof只能用于对象的判断,不能用于基本类型的判断。

不要只替换一个类

对于final修饰的基本类型和String类型,编译器会认为它是稳定态(Immutable Status),所以在编译时就直接把值变异到字节码中了,编码了在运行期引用,以提高代码的执行效率。
对于final修饰的类(即非基本类型,编译器认为它是不稳定态(Mutable Status)),在编译时简历的则是引用关系,如果Client类引入的常量是一个类或实例,即使不重新编译也会输出最新值。

注意:发布应用系统时禁止使用类文件替换方式,整个WAR包或者JAR包发布才是万全之策。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,755评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,305评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,138评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,791评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,794评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,631评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,362评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,264评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,724评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,900评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,040评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,742评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,364评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,944评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,060评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,247评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,979评论 2 355

推荐阅读更多精彩内容