编写高质量代码:改善java程序的151个建议--[78~92](代码片段)

androidsuperman androidsuperman     2022-12-21     422

关键词:

编写高质量代码:改善Java程序的151个建议 --[78~92]

HashMap中的hashCode应避免冲突
多线程使用Vector或HashTable

Vector是ArrayList的多线程版本,HashTable是HashMap的多线程版本。

非稳定排序推荐使用List

对于变动的集合排序

  1. set=new TreeSet
  2. 使用TreeSet是希望实现自动排序,即使修改也能自动排序,既然它无法实现,那就用List来代替,然后使用Collections.sort()方法对List排序
import java.util.ArrayList;
import java.util.SortedSet;
import java.util.TreeSet;

public class Client69 
    public static void main(String[] args) 
        SortedSet<Person> set = new TreeSet<Person>();
        // 身高180CM
        set.add(new Person(180));
        // 身高175CM
        set.add(new Person(175));
        set.first().setHeight(185);
        set=new TreeSet<Person>(new ArrayList<Person>(set));
        for (Person p : set) 
            System.out.println("身高:" + p.getHeight());
        
    

    static class Person implements Comparable<Person> 
        // 身高
        private int height;

        public Person(int _height) 
            height = _height;
        

        public int getHeight() 
            return height;
        

        public void setHeight(int height) 
            this.height = height;
        

        // 按照身高排序
        @Override
        public int compareTo(Person o) 
            return height - o.height;
        

    

在项目中推荐使用枚举常量代替接口常量或类常量
enum Season 
        Spring, Summer, Autumn, Winter;
        public static Season getComfortableSeason()
            return Spring;
        
    
使用构造函数协助描述枚举项
enum Role 
    Admin("管理员", new LifeTime(), new Scope()), User("普通用户", new LifeTime(), new Scope());
    private String name;
    private LifeTime lifeTime;
    private Scope scope;
    /* setter和getter方法略 */

    Role(String _name, LifeTime _lifeTime, Scope _scope) 
        name = _name;
        lifeTime = _lifeTime;
        scope = _scope;
    



class LifeTime 

class Scope 

name:表示的是该角色的中文名称
lifeTime:表示的是该角色的生命周期,也就是多长时间该角色失效
scope:表示的该角色的权限范围

小心switch带来的空指针异常
在switch的default代码块中增加AssertionError错误
使用valueOf前必须进行校验

枚举.valueOf(name)
没有匹配找到指定值报错:

Exception in thread "main" java.lang.IllegalArgumentException: No enum ...
    at java.lang.Enum.valueOf(Unknown Source)

两种避免的方式:
(1)、使用try......catch捕捉异常

try
       枚举.valueOf(name)
    catch(Exception e)
        e.printStackTrace();
        System.out.println("无相关枚举项");
    

(2)、扩展枚举类

enum Season 
        Spring, Summer, Autumn, Winter;
        // 是否包含指定的枚举项
        public static boolean isContains(String name) 
            // 所有的枚举值
            Season[] season = values();
            for (Season s : season) 
                if (s.name().equals(name)) 
                    return true;
                
            
            return false;
        
    
用枚举实现工厂方法模式更简洁

枚举非静态方法实现工厂方法模式

enum CarFactory 
    // 定义生产类能生产汽车的类型
    FordCar, BuickCar;
    // 生产汽车
    public Car create() 
        switch (this) 
        case FordCar:
            return new FordCar();
        case BuickCar:
            return new BuickCar();
        default:
            throw new AssertionError("无效参数");
        
    

通过抽象方法生成产品

enum CarFactory 
    // 定义生产类能生产汽车的类型
    FordCar
        public Car create()
            return new FordCar();
        
    ,
    BuickCar
        public Car create()
            return new BuickCar();
        
    ;
    //抽象生产方法
    public abstract Car create();

使用枚举类型的工厂方法模式三个优点:

  1. 避免错误调用的发生:一般工厂方法模式中的生产方法(也就是createCar方法),可以接收三种类型的参数:类型参数(如我们的例子)、String参数(生产方法中判断String参数是需要生产什么产品)、int参数(根据int值判断需要生产什么类型的的产品),这三种参数都是宽泛的数据类型,很容易发生错误(比如边界问题、null值问题),而且出现这类错误编译器还不会报警。
  2. 性能好,使用简洁:枚举类型的计算时以int类型的计算为基础的,这是最基本的操作,性能当然会快,至于使用便捷,注意看客户端的调用。
  3. 降低类间耦合:不管生产方法接收的是Class、String还是int的参数,都会成为客户端类的负担,这些类并不是客户端需要的,而是因为工厂方法的限制必须输入的,例如Class参数,对客户端main方法来说,他需要传递一个FordCar.class参数才能生产一辆福特汽车,除了在create方法中传递参数外,业务类不需要改Car的实现类。这严重违背了迪米特原则(Law of Demeter 简称LoD),也就是最少知识原则:一个对象应该对其它对象有最少的了解。
    而枚举类型的工厂方法就没有这种问题了,它只需要依赖工厂类。
枚举项的数量限制在64个以内

 为了更好地使用枚举,Java提供了两个枚举集合:EnumSet和EnumMap,这两个集合使用的方法都比较简单,EnumSet表示其元素必须是某一枚举的枚举项,EnumMap表示Key值必须是某一枚举的枚举项,由于枚举类型的实例数量固定并且有限,相对来说EnumSet和EnumMap的效率会比其它Set和Map要高。
当枚举项数量小于等于64时,创建一个RegularEnumSet实例对象,大于64时则创建一个JumboEnumSet实例对象。
枚举项数量不要超过64,否则建议拆分。

import java.util.EnumSet;

public class EnumSetTest 
    //普通枚举项,数量等于64
    enum Const
        A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,
        AA,BB,CC,DD,EE,FF,GG,HH,II,JJ,KK,LL,MM,NN,OO,PP,QQ,RR,SS,TT,UU,VV,WW,XX,YY,ZZ,
        AAA,BBB,CCC,DDD,EEE,FFF,GGG,HHH,III,JJJ,KKK,LLL
    
    //大枚举,数量超过64
    enum LargeConst
        A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,
        AA,BB,CC,DD,EE,FF,GG,HH,II,JJ,KK,LL,MM,NN,OO,PP,QQ,RR,SS,TT,UU,VV,WW,XX,YY,ZZ,
        AAAA,BBBB,CCCC,DDDD,EEEE,FFFF,GGGG,HHHH,IIII,JJJJ,KKKK,LLLL,MMMM
    
    public static void main(String[] args) 
        EnumSet<Const> cs = EnumSet.allOf(Const.class);
        EnumSet<LargeConst> lcs = EnumSet.allOf(LargeConst.class);
        //打印出枚举数量
        System.out.println("Const的枚举数量:"+cs.size());
        System.out.println("LargeConst的枚举数量:"+lcs.size());
        //输出两个EnumSet的class
        System.out.println(cs.getClass());
        System.out.println(lcs.getClass());
    

allOf调用noneOf

public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) 
        //生成一个空EnumSet
        EnumSet<E> result = noneOf(elementType);
        //加入所有的枚举项
        result.addAll();
        return result;
    
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) 
       //获得所有的枚举项
       Enum[] universe = getUniverse(elementType);
       if (universe == null)
           throw new ClassCastException(elementType + " not an enum");
       //枚举数量小于等于64
       if (universe.length <= 64)
           return new RegularEnumSet<>(elementType, universe);
       else 
           //枚举数量大于64
           return new JumboEnumSet<>(elementType, universe);
   
注意@Override不同版本的区别
interface Foo 
    public void doSomething();


class FooImpl implements Foo
    @Override
    public void doSomething() 
        
    

这段代码在Java1.6版本上编译没问题,虽然doSomething方法只是实现了接口的定义,严格来说并不是覆写,但@Override出现在这里可减少代码中出现的错误。

可如果在Java1.5版本上编译此段代码可能会出现错误:The method doSomeThing() of type FooImpl must override a superclass method。

Java1.5版本的@Override是严格遵守覆写的定义:子类方法与父类方法必须具有相同的方法名、输出参数、输出参数(允许子类缩小)、访问权限(允许子类扩大),父类必须是一个类,不能是接口,否则不能算是覆写。而这在Java1.6就开放了很多,实现接口的方法也可以加上@Override注解了,可以避免粗心大意导致方法名称与接口不一致的情况发生。
Java1.6版本的程序移植到1.5版本环境中,就需要删除实现接口方法上的@Override注解。









编写高质量代码:改善java程序的151个建议--[78~92](代码片段)

编写高质量代码:改善Java程序的151个建议--[78~92]HashMap中的hashCode应避免冲突多线程使用Vector或HashTableVector是ArrayList的多线程版本,HashTable是HashMap的多线程版本。非稳定排序推荐使用List对于变动的集合排序set=newTreeSet使用TreeSet是... 查看详情

编写高质量代码:改善java程序的151个建议--[52~64](代码片段)

编写高质量代码:改善Java程序的151个建议--[52~64]推荐使用String直接量赋值Java为了避免在一个系统中大量产生String对象(为什么会大量产生,因为String字符串是程序中最经常使用的类型),于是就设计了一个字符串池(也叫作字符串常... 查看详情

转载---编写高质量代码:改善java程序的151个建议(第2章:基本类型___建议21~25)

阅读目录建议21:用偶判断,不用奇判断建议22:用整数类型处理货币建议23:不要让类型默默转换建议24:边界还是边界建议25:不要让四舍五入亏了一方                                ... 查看详情

转载--编写高质量代码:改善java程序的151个建议(第3章:类对象及方法___建议36~40)

阅读目录建议36:使用构造代码块精简程序建议37:构造代码块会想你所想建议38:使用静态内部类提高封装性建议39:使用匿名类的构造函数建议40:匿名类的构造函数很特殊建议36:使用构造代码块精简程序  什么叫做代码块... 查看详情

转载---编写高质量代码:改善java程序的151个建议(第2章:基本类型___建议26~30)

阅读目录建议26:提防包装类型的null值建议27:谨慎包装类型的大小比较建议28:优先使用整型池建议29:优先选择基本类型建议30:不要随便设置随机种子回到顶部建议26:提防包装类型的null值  我们知道Java引入包装类型(Wrapp... 查看详情

转载--编写高质量代码:改善java程序的151个建议(第5章:数组和集合___建议60~64)

阅读目录建议60:性能考虑,数组是首选建议61:若有必要,使用变长数组建议62:警惕数组的浅拷贝建议63:在明确的场景下,为集合指定初始容量建议64:多种最值算法,适时选择      噢,它明白了,河水既没有牛伯... 查看详情

转载--编写高质量代码:改善java程序的151个建议(第3章:类对象及方法___建议31~35)

阅读目录建议31:在接口中不要存在实现代码建议32:静态变量一定要先声明后赋值建议33:不要覆写静态方法建议34:构造函数尽量简化建议35:避免在构造函数中初始化其它类                  书读的多... 查看详情

转载--编写高质量代码:改善java程序的151个建议(第4章:字符串___建议52~55)

阅读目录建议52:推荐使用String直接量赋值建议53:注意方法中传递的参数要求建议54:正确使用String、StringBuffer、StringBuilder建议55:注意字符串的位置回到顶部建议52:推荐使用String直接量赋值  一般对象都是通过new关键字生... 查看详情

转载---编写高质量代码:改善java程序的151个建议(第3章:类对象及方法___建议47~51)

阅读目录建议47:在equals中使用getClass进行类型判断建议48:覆写equals方法必须覆写hashCode方法建议49:推荐覆写toString方法建议50:使用package-info类为包服务建议51:不要主动进行垃圾回收回到顶部建议47:在equals中使用getClass进行... 查看详情

转载--编写高质量代码:改善java程序的151个建议(第5章:数组和集合___建议65~69)

阅读目录建议65:避开基本类型数组转换列表陷阱建议66:asList方法产生的List的对象不可更改建议67:不同的列表选择不同的遍历算法建议68:频繁插入和删除时使用LinkList建议69:列表相等只关心元素数据回到顶部建议65:避开基... 查看详情

转载---编写高质量代码:改善java程序的151个建议(第3章:类对象及方法___建议41~46)

阅读目录建议41:让多重继承成为现实建议42:让工具类不可实例化建议43:避免对象的浅拷贝建议44:推荐使用序列化对象的拷贝建议45:覆写equals方法时不要识别不出自己建议46:equals应该考虑null值情景回到顶部建议41:让多重... 查看详情

转载--编写高质量代码:改善java程序的151个建议(第1章:java开发中通用的方法和准则___建议16~20)

阅读目录建议16:易变业务使用脚本语言编写建议17:慎用动态编译建议18:避免instanceof非预期结果建议19:断言绝对不是鸡肋建议20:不要只替换一个类回到顶部建议16:易变业务使用脚本语言编写  Java世界一直在遭受着异种... 查看详情

转载----编写高质量代码:改善java程序的151个建议(第1章:java开发中通用的方法和准则___建议1~5)

阅读目录建议1:不要在常量和变量中出现易混淆的字母建议2:莫让常量蜕变成变量  建议3:三元操作符的类型务必一致 建议4:避免带有变长参数的方法重载建议5:别让null值和空值威胁到变长方法      ... 查看详情

编写高质量代码:改善java的151个建议四(类对象方法)31-51

31.接口中不要存在实现代码  接口中不能存在实现代码(虽然可以实现,但是如果把实现代码写在接口中,那么接口就绑定了可能变化的因素,这就导致实现不在文档和可靠,是随时可能被抛弃,被修改,被重构的)packagejsont... 查看详情

转载--编写高质量代码:改善java程序的151个建议(第1章:java开发中通用的方法和准则___建议11~15)

阅读目录建议11:养成良好习惯,显示声明UID建议12:避免用序列化类在构造函数中为不变量赋值建议13:避免为final变量复杂赋值建议14:使用序列化类的私有方法巧妙解决部分属性持久化问题建议15:break万万不可忘回到顶部建... 查看详情

编写高质量代码:改善java程序的151个建议(第1章:java开发中通用的方法和准则___建议11~15)

建议11:养成良好习惯,显示声明UID我们编写一个实现了Serializable接口(序列化标志接口)的类,Eclipse马上就会给一个黄色警告:需要添加一个SerialVersionID。为什么要增加?他是怎么计算出来的?有什么用?下面就来解释该问题... 查看详情

编写高质量代码:改善java程序的151个建议(第1章:java开发中通用的方法和准则___建议16~20)

建议16:易变业务使用脚本语言编写  Java世界一直在遭受着异种语言的入侵,比如PHP,Ruby,Groovy、Javascript等,这些入侵者都有一个共同特征:全是同一类语言-----脚本语言,它们都是在运行期解释执行的。为什么Java这种强编... 查看详情

编写高质量代码:改善java程序的151个建议(第1章:java开发中通用的方法和准则___建议6~10)

建议6:覆写变长方法也循规蹈矩   在JAVA中,子类覆写父类的中的方法很常见,这样做既可以修正bug,也可以提供扩展的业务功能支持,同时还符合开闭原则(Open-ClosedPrinciple)。符合开闭原则(Open-ClosedPrinciple)的主要特征: ... 查看详情