前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >3分钟快速阅读-《Effective Java》(三)

3分钟快速阅读-《Effective Java》(三)

作者头像
cwl_java
发布2019-10-26 20:47:46
3570
发布2019-10-26 20:47:46
举报
文章被收录于专栏:cwl_Javacwl_Java
21.用函数对象来表示策略

简而言之,就是使用接口来定义要实现的策略方法,然后具体的实现类来实现不同的接口从而实现不同的策略,这就是所谓的函数对象来表示策略

22.优先考虑静态成员类

1.嵌套类包括以下四种,除了静态成员类,其他的都可以被称为内部类

  • 1.1 静态成员类:可以被外部访问的一种辅助类,访问的时候并不需要实例化外部类,
  • 1.2 非静态成员内部类,这样的类是需要外部类实例化之后才可以使用的,往往是需要用于操作内部的一些字段进行实现的,例如集合当中的迭代器设计.
  • 1.3 匿名内部类,它的本质并不是外部类的一个成员,也不能与其他的成员一起呗声明,而是在使用的时候才被声明和实例化.常见的例如创建一个Comparator的实例,就需要覆盖它内部的方法去实现它的比较功能
  • 1.4 局部类,类似于局部变量,一般开发中不会有这样的用法.它可以跟任何局部变量一样在方法内部进行声明和初始化,使用的时候作用域跟局部变量相同
23.请不要在新代码当中使用原生态类型

原文中这一条想要告诉我们的就是泛型的不正确声明是会导致泛型的擦除和脏数据的,如下代码示例

代码语言:javascript
复制
public class Test {

    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        StackDemo stackDemo = new StackDemo();
        unsafeAdd(strings,stackDemo);
        unsafeAdd(strings,new Integer(1));
        System.out.println(strings);
    }
    //该方法会导致泛型被擦除,导致脏数据的产生
    private static void unsafeAdd(List strings, StackDemo stackDemo) {
        strings.add(stackDemo);
    }
    //该方法就会限制对应的参数操作也必须是对应的String类型,所以会编译报错不通过
    private static void unsafeAdd(List<String> strings, Integer stackDemo) {
        strings.add(stackDemo);
    }
    
    //综上所述:需要采用第二种方式来声明和使用泛型才是合理的
}
24.消除非受检警告

在开发当中会遇到很多编译器警告,例如在JDK1.8以前的编译器会对下面这段代码有警告

代码语言:javascript
复制
//JDK1.8之前的编译器要求我们必须要在实例当中声明泛型类型,否则会有编译警告
Set<String> strings = new HashSet();
//如果编译器比较早的,采用下面这个方式即可解决
Set<String> strings = new HashSet<String>();

有些警告是不好消除的,此时就可以使用注解@SuppressWarings(“unchecked”)来消除我们所信任的不必要的警告

25.集合优先于数组

使用数组的问题在编译时期比较不易被发现,但是使用集合搭配泛型就更容易发现问题,如下代码所示

代码语言:javascript
复制
public class GenericsTest {

    public static void main(String[] args) {
        //使用这种方式需要运行时才能发现它的错误
        Object[] objects = new Long[1];
        objects[0] = "不要这样操作";
        
        //使用这种方式,编译本身就不通过,异常比较容易发现
        List<Object> objectList = new ArrayList<Long>();
        objectList.add("可以这样做");
    }
}
26.优先考虑泛型的使用

如下所示我们可以对第六条当中的代码进行泛型改装,这样可以使得它的拓展性更好,而且减少了不必要的警告

代码语言:javascript
复制
public class StackDemo<E> {
    private E[] elements;
    private int size = 0;
    private final int DEFAULT_INITIAL_CAPACITY = 16;

    /**
     * 功能描述:
     * 〈初始化栈〉
     *
     * @params : []
     * @return :
     * @author : cwl
     * @date : 2019/5/5 16:59
     */
    public StackDemo(){
        elements = (E[])new Object[DEFAULT_INITIAL_CAPACITY];
    }

    /**
     * 功能描述:
     * 〈压栈〉
     *
     * @params : [o]
     * @return : void
     * @author : cwl
     * @date : 2019/5/5 16:58
     */
    public void push(E o){
        ensureCapacity();
        elements[size++] = o;
    }

    /**
     * 功能描述:
     * 〈出栈〉
     *
     * @params : []
     * @return : java.lang.Object
     * @author : cwl
     * @date : 2019/5/5 16:59
     */
    public E pop(){
        if (size == 0){
            throw new EmptyStackException();
        }
        return elements[size--];
    }

    /**
     * 功能描述:
     * 〈自动扩容〉
     *
     * @params : []
     * @return : void
     * @author : cwl
     * @date : 2019/5/5 16:59
     */
    private void ensureCapacity() {
        if(elements.length == size){
            elements = Arrays.copyOf(elements,2*size+1);
        }
    }
}

总而言之:只要时间允许,尽可能的去做到泛型化,对于代码的可拓展性会更好一些

27.优先考虑泛型方法

错误示例:当不使用泛型的时候,即便s1和s2两个是不同类型的集合此时也会被添加到一起.

代码语言:javascript
复制
    private static Set union(Set s1, Set s2) {
        s1.addAll(s2);
        return s1;
    }

正确示例:采用下列这种方式,当你想要添加一个set集合时,它会根据类型推导,要求E的类型必须都是一致的,从而来限制这个集合的数据准确,而不至于产生脏数据

代码语言:javascript
复制
public class Test {

    public static void main(String[] args) {
        Set<String> s1 = new HashSet<>();
        s1.addAll(Arrays.asList("1","2","3"));
        Set<String> s2 = new HashSet<>();
        s2.addAll(Arrays.asList("4","5","6"));
        Set<String> s3 = union(s1, s2);
        System.out.println(s3);
    }
    private static <E> Set<E> union(Set<E> s1, Set<E> s2) {
        s1.addAll(s2);
        return s1;
    }
}
28.利用泛型的上限和下限来提升API的灵活性

如我们之前代码例子:如果我在addAll的方法当中只是如代码示例2中的方式,那么我所要添加的类型可以是Set或者是Set都是可以被编译通过的.这样是不合理的,所以我们需要设置泛型的上限就是当前类型T,才能保证添加的类型都是属于同一类的

代码语言:javascript
复制
public class ChildHashSet<T> extends HashSet<T> {

    private int count = 0;

    public ChildHashSet() {
    }

    public ChildHashSet(int initCap , int loadFactory) {
        super(initCap,loadFactory);
    }

    @Override
    public boolean add(T t) {
        count++;
        return super.add(t);
    }
    
    @Override
    public boolean addAll(Collection<? extends T> c) {
        count = c.size();
        return super.addAll(c);
    }

    public int getCount() {
        return count;
    }
}

代码示例2:

代码语言:javascript
复制
    //错误做法
    @Override
    public boolean addAll(Collection<T> c) {
        count = c.size();
        return super.addAll(c);
    }
29.优先考虑类型安全的异构容器

异构容器:利用字节码对象来使得一个容器当中可以容下多种类型,而且还是可以合法使用的.这样就可以像一个对象一样,存储像数据库当中的行项目那样包含了不同种类的类型.具体代码实现如下:

代码语言:javascript
复制
public class Hobbies {

    private Map<Class<?>,Object> hobbies = new HashMap<>();

    /**
     * 功能描述:
     * 〈添加爱好〉
     *
     * @params : [type, instance]
     * @return : void
     * @author : cwl
     * @date : 2019/5/6 17:51
     */
    public <T> void putHobbies(Class<T> type,T instance){
        if(type == null){
            throw new NullPointerException("type is null");
        }
        hobbies.put(type,instance);
    }
    
    /**
     * 功能描述: 
     * 〈查询爱好〉
     *
     * @params : [type]
     * @return : T
     * @author : cwl
     * @date : 2019/5/6 17:52
     */
    public <T> T getHobbies(Class<T> type){
        return type.cast(hobbies.get(type));
    }

}

本质来说如果我们能够使用类来表示数据库当中的一个行项目,就不要采用这样的集合来封装想要的信息,我们要尽量做到面向对象编程,而非面向集合编程

30.使用enum代替int常量

对于枚举类,它可以使用的范围相对比int常量要广泛的多,下面列举一个枚举类的综合使用.需求是区分工作日和休息日,不同的工作时间对应不同的工作计算汇率

代码语言:javascript
复制
public enum PayrollyDay {
    MONDAY(PayType.WEEKDAY),
    TUESDAY(PayType.WEEKDAY),
    WENDESDAY(PayType.WEEKDAY),
    THURSDAY(PayType.WEEKDAY),
    FRIDAY(PayType.WEEKDAY),
    SATURDAY(PayType.WEEKEND),
    SUNDAY(PayType.WEEKEND);

    private final PayType payType;
    //当创建一个枚举对象的时候,有多少个枚举字段就会调用多少次构造器,可以通过人肉打印测试
    PayrollyDay(PayType payType){
        //System.out.println("调用构造器");
        this.payType = payType;
    }
    double pay(double hoursWorked,double payRate){
       return payType.pay(hoursWorked,payRate);
    }
    private enum PayType{

        WEEKDAY{
            double overTimePay(double hours,double payRate){
                return hours <= HOURS_PER_SHIFT ? 0 : (hours - HOURS_PER_SHIFT) * payRate / 2;
            }

        },
        WEEKEND{
            double overTimePay(double hours,double payRate){
                return hours = payRate / 2;
            }
        };
        //枚举类当中的固定写法,具体实现时会找到上面两个枚举当中的任意一个
        abstract double overTimePay(double hours,double payRate);

        private static final int HOURS_PER_SHIFT = 8;
        double pay(double hoursWorked,double payRate){
            double basePay = hoursWorked * payRate;
            return basePay + overTimePay(hoursWorked,payRate);
        }
    }
}

测试用例

代码语言:javascript
复制
    public static void main(String[] args) {
        //参数1:时薪
        //参数2:工作时间
        //参数3:加班工资计算汇率
        //此处double精度不够,有兴趣的可以自行转换成Bigdecmail计算
        double pay = PayrollyDay.MONDAY.pay(50,10, 0.8);
        System.out.println(pay);
    }
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-05-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 21.用函数对象来表示策略
  • 22.优先考虑静态成员类
  • 23.请不要在新代码当中使用原生态类型
  • 24.消除非受检警告
  • 25.集合优先于数组
  • 26.优先考虑泛型的使用
  • 27.优先考虑泛型方法
  • 28.利用泛型的上限和下限来提升API的灵活性
  • 29.优先考虑类型安全的异构容器
  • 30.使用enum代替int常量
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档