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


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

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

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

    非稳定排序推荐使用List

    对于变动的集合排序

    1. set=new TreeSet(new ArrayList(set));重新生成一个Set对象,也就是对原有的Set对象重新排序
    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注解。

  • 相关阅读:
    javamail.providers not found
    vue.js实现购物车功能2.0版本
    vue.js实现购物车功能
    iframe高度自适应
    C语言 自定义字符串拼接函数
    php安装
    Apache安装
    python:爬虫初体验
    c:forEach 显示下拉框并回显
    数据结构 --- 线性表学习(php模拟)
  • 原文地址:https://www.cnblogs.com/androidsuperman/p/9456403.html
Copyright © 2020-2023  润新知