Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >3分钟快速阅读-《Effective Java》(一)

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

作者头像
cwl_java
发布于 2019-10-26 12:47:27
发布于 2019-10-26 12:47:27
39700
代码可运行
举报
文章被收录于专栏:cwl_Javacwl_Java
运行总次数:0
代码可运行

简介

Effective Java这本书针对的是有经验的程序员,本书共计78个条目,目的是告诉大家如何进行更加有效的编程

1.考虑用静态工厂方法代替构造器

静态工厂方法的优点

  • 1.1 静态工厂方法有对应方法名称,构造器则没有.所以使用静态工厂方法能够更加直接的表达想要实例化的对象
  • 1.2 静态工厂方法不用每次都创建出一个新的对象
  • 1.3 静态工厂方法可以返回父类对象或者接口对象
  • 1.5 静态工厂方法可以使用泛型来作为返回的参数

静态工厂方法的缺点

  • 1.1 如果不含有公有的或者受保护的构造器,就不能被子类化
  • 1.2 本质上跟其他静态方法没有太大区别
2.遇到多个构造器参数时考虑使用构建器

当一个对象中字段很多,那么需要实例化时如果每种不同的对象都写一个对象的构造器来进行实例化会使得代码十分冗余,如果使用无参构造器,然后使用setter方法来给每个字段设置值,会导致这个对象字段可变且不安全,所以需要考虑使用构建器.构建器代码如下所示

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 工厂实例类代码
public class FactoryDemo {

    private final Long ID;
    private final String adress;

    private final int age;
    private final String name;

    public static class Builder {
        private final int age;
        private final String name;

        private Long ID = 0L;
        private String adress = "";

        public Builder(int age, String name) {
            this.age = age;
            this.name = name;
        }

        public Builder ID(Long ID){
            this.ID = ID;
            return this;
        }

        public Builder adress(String adress){
            this.adress = adress;
            return this;
        }

        public FactoryDemo build(){
            return new FactoryDemo(this);
        }
    }

    private FactoryDemo(Builder builder){
        this.ID = builder.ID;
        this.adress = builder.adress;
        this.age = builder.age;
        this.name = builder.name;
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 客户端调用代码
FactoryDemo build = new FactoryDemo.Builder(18, "大爷").ID(35062819920914L).adress("福建漳州").build();
3.用私有构造器或者枚举类型强化Singleton类型
  • JDK1.5前可以有两种方式写单例模式,分别是以下两种
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//方式1
public class Elvis {
    public static fianl Elvis INSTANCE = new Elvis();
    private Elvis(){}
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//方式2
public class Elvis {
    public static fianl Elvis INSTANCE = new Elvis();
    private Elvis(){}
    public static Elvis getInstance(){
        return INSTANCE;
    }
}
  • 推荐使用枚举来进行实例化,可以绝对防止实例化出多个对象
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public enum Elvis {
    INSTANCE;
    //业务逻辑方法
    public void run(){
        System.out.println("处理业务逻辑");
    }
}
4.通过私有构造器强化不可实例化的能力

对于工具类而言,是不需要被实例化的,这个时候需要使用私有化构造器的方式来强化它不可被实例化的能力.不要选择不写,这样当该类被调用的时候,编译器会自动生成一个无参构造函数,或者当子类继承它并实例化子类的时候,该类也会被实例化,这样的操作都是不合理的

5.避免创建不必要的对象
  • 5.1 JDK1.5开始出现自动封装,实际开发当中能用基本数据类型就尽量不要使用包装类型,因为包装类型会无端创建出很多对象,代码示例如下所示
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Test {

    public static void main(String[] args) {

        Long sum = 0L;
        long begin = System.currentTimeMillis();
        for(int i = 0 ;i<=100000;i++){
            sum += 1;
        }
        long end = System.currentTimeMillis();
        System.out.println("Long总耗时:"+(end-begin));//5ms

        long sum1 = 0L;
        long begin1 = System.currentTimeMillis();
        for(int i = 0 ;i<=100000;i++){
            sum1 += 1;
        }
        long end1 = System.currentTimeMillis();
        System.out.println("long总耗时:"+(end1-begin1));//0ms

    }
}
  • 5.2 对于引用类型的初始化尽量不要使用new String(),特别是在for循环当中,这样也会出现过多不必要对象的创建,代码示例如下:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Test {

    public static void main(String[] args) {
        long begin = System.currentTimeMillis();
        for(int i = 0 ;i<=10000;i++){
            String str = new String("sb");
        }
        long end = System.currentTimeMillis();
        System.out.println("new总耗时:"+(end - begin));//2ms

        long begin1 = System.currentTimeMillis();
        for(int i = 0 ;i<=10000;i++){
            String str = "sb";
        }
        long end1 = System.currentTimeMillis();
        System.out.println("非new总耗时:"+(end1 - begin1));//0ms
    }
}
  • 5.3 当类A中包含字段类B,当类A进行实例化对象A时,可以考虑使用静态代码块来对关联对象B进行初始化,避免每次调用对象A时都会初始化一次对象B

备注:以上所说的情况仅仅只是针对于需求当中该对象是只需要被初始化一次,后续不需要再进行更改的情况下可以使用,如果是该对象在程序运行过程中需要被修改的,那么则不适用此规则

6.消除过期的对象引用

在JVM当中栈空间弹出的对象引用为过期引用,这一类的过期引用不会再被GC回收,那么这样的过期对象引用越来越多则会导致内存泄漏(Disk paging),严重可能导致OOM,具体代码示例如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//以下这段代码主要问题在于pop出栈的对象,还对原有的对象在内存中的地址/存在着引用,所以需要对已经出栈的对象做空引用的处理
public class StackDemo {
    private Object[] 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 = new Object[DEFAULT_INITIAL_CAPACITY];
    }
    
    /**
     * 功能描述: 
     * 〈压栈〉
     *
     * @params : [o]
     * @return : void
     * @author : cwl
     * @date : 2019/5/5 16:58
     */
    public void push(Object o){
        ensureCapacity();
        elements[size++] = o;
    }
    
    /**
     * 功能描述: 
     * 〈出栈〉
     *
     * @params : []
     * @return : java.lang.Object
     * @author : cwl
     * @date : 2019/5/5 16:59
     */
    public Object 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);
        }
    }
}
  • pop方法修改如下
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 public Object pop(){
        if (size == 0){
            throw new EmptyStackException();
        }
        elements[size] =null;//将出栈的元素设置为空引用
        return elements[size--];
 }
7.避免使用finally代码块

大多数情况下finally代码块是用来搭配一些资源的打开没有关闭,在这里进行最终的关闭的.但是finally代码块有时候并不会非常及时的去关闭资源,而且会极其消耗JVM的性能,但是如果如果需要搭配FileInputStream/FileOutputStream/Timer/Connection等,自带colse方法时,可以考虑避免文件过多的被打开而导致内存不足,而在finally代码块当中去执行这样的操作

8.覆盖equals时请遵守通用约定
  • 8.1 高质量覆盖equals方法诀窍
    • 使用==操作符检查参数是否为这个对象的引用,是则返回true
    • 使用instanceof操作符检查参数是否为正确类型.如果不是,则返回false
    • 把参数转换成正确的类型
    • 对于该类中的每个关键域,检查参数中的域是否与该对象中的对应的域相匹配
  • 8.2 覆盖equals时必须要做到的三件事
    • 覆盖equals时必须要覆盖hashCode
    • 不要企图让equals方法过于智能.不要把一些过于复杂的等价关系写到equals当中去
    • 不要讲equals声明中的Object对象替换为其他的类型
9.覆盖equals时总要覆盖hashCode

摘自Object规范[JavaSE6]第二条:

  • 如果两个对象根据equals(Object)方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果

结论:如果没有覆盖hashCode方法,那么当使用HashMap,HashSet和HashTable这种键值对结构时比较就会不准确.详情请看以下代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class PhoneNumber {
    private final short areaCode;
    private final short prefix;
    private final String lineNumber;

    public PhoneNumber(int areaCode, int prefix, String lineNumber) {
        rangeCheck(areaCode,999,"areaCode");
        this.areaCode = (short) areaCode;
        this.prefix = (short) prefix;
        this.lineNumber = lineNumber;
    }

    private void rangeCheck(int arg, int max, String name) {
        if(arg < 0 || arg > max){
            throw new IllegalArgumentException(name + ":" +arg);
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof PhoneNumber)) return false;
        PhoneNumber that = (PhoneNumber) o;
        return areaCode == that.areaCode &&
                prefix == that.prefix &&
                lineNumber.equals(that.lineNumber);
    }
}

当你想要尝试搭配map集合使用时,如果没有覆盖hashCode方法,那么当你用相同的对象去get集合Map当中的value值的时候,返回的将会是null,因为他们的hashCode值时不一样的,所以这个时候就需要我们去覆盖hashCode方法,这个方法涉及到hashCode值的计算方式,这里我们不做深究,建议直接调用父类的即可,在PhoneNumber当中添加代码示例1的方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Map<PhoneNumber,String> numberStringMap = new HashMap<>();
        numberStringMap.put(new PhoneNumber(180,286,"6849"),"Jenny");
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//代码示例1:
    @Override
    public int hashCode() {
        return Objects.hash(areaCode, prefix, lineNumber);
    }
10.始终要覆盖toString

这一条的目的非常简单,覆盖toString只是为了让我们更加方便观看而已,Object类的toString是全限定雷鸣+地址值

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/05/06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
《Effective Java》读书笔记(一)之创建和销毁对象
最近在研读《Effective Java》一书,读书不做点笔记,感觉很容易就忘掉,于是用本篇博客来记录阅读此书的笔记。 郑重声明: 由于是《Effective Java》一书的笔记,所以大部分内容基本来自此书,还有一小部分是自己的理解。
老马的编程之旅
2022/06/22
3720
《Effective Java 》系列一
编写实例受控类有几个原因。实例受控使得类可以确保他是一个Singleton或者是不可实例化的。他还使得不可变类可以确保不会存在两个相等的实例。
高广超
2018/12/12
8620
Effective Java(一)
对于类而言,为了让客户端获取它自身的一个实例,最传统的方法就是提供一个公有的构造器。还有一种方法,也应该在每个程序员的工具箱中占有一席之地。类可以提供一个公有的静态工厂方法( static factory method ),它只是一个返回类的实例的静态方法。下面是一个来自 Boolean(基本类型 boolean 装箱类)的简单示例。这个方法将 boolean 基本类型值转换成了 Boolean 对象引用:
Remember_Ray
2020/08/03
6780
笔记《Effective Java》01: 创建和销毁对象
《Effective Java》这本书可以说是程序员必读的书籍之一。这本书讲述了一些优雅的,高效的编程技巧。对一些方法或API的调用有独到的见解,还是值得一看的。刚好最近重拾这本书,看的是第三版,顺手整理了一下笔记,用于自己归纳总结使用。建议多读一下原文。今天整理第一章节:创建和销毁对象。
有一只柴犬
2025/01/24
830
笔记《Effective Java》01: 创建和销毁对象
效率编程 之「创建和销毁对象」
但是很遗憾,在标准的 JDK 中,并没有提供类似的静态工厂方法。不过,我们可以通过在项目中引入谷歌发布的Guava,使用类似上述的静态工厂方法。当然,静态工厂方法也不是尽善尽美的,也有其缺点:
CG国斌
2019/05/26
5330
《Effective Java》—— 创建与销毁对象
本篇主要总结的是《Effecticve Java》中关于创建和销毁对象的内容。 比如: 何时以及如何创建对象 何时以及如何避免创建对象 如何确保及时销毁 如何管理对象销毁前的清理动作 考虑用静态工厂方法代替构造器 使用静态工厂的优势: 有名称 不必每次调用的时候都创建一个新的对象 返回原返回类型的任何子类型对象 在创建参数化类型实例时,代码更加简洁。 使用静态工厂的缺点: 类如果不包含公有的或者受保护的构造器,就不能被子类化 与其他的静态方法实际上没有任何区别 举个例子: public class A
用户1154259
2018/01/17
8140
Effective Java通俗理解(上)
  这篇博客是Java经典书籍《Effective Java(第二版)》的读书笔记,此书共有78条关于编写高质量Java代码的建议,我会试着逐一对其进行更为通俗易懂地讲解,故此篇博客的更新大约会持续1个月左右。 第1条:考虑用静态工厂方法代替构造器   通常情况下我们会利用类的构造器对其进行实例化,这似乎毫无疑问。但“静态工厂方法”也需要引起我们的高度注意。   什么是“静态工厂方法”?这不同于设计模式中的工厂方法,我们可以理解它为“在一个类中用一个静态方法来返回这个类的实例”,例如: public st
用户1148394
2018/01/12
1.5K0
Effective Java通俗理解(上)
effective Java 创建和销毁对象篇
小伙伴们好呀,我是 小羊 ,今天来和大家分享下 《Effective Java》这本书的 第2章 —— 创建和销毁对象 。
Java4ye
2024/02/14
3000
effective Java 创建和销毁对象篇
读完《Effective Java》后我淦了 50 条开发技巧
《Effective Java》Java名著,必读。如果能严格遵从本文的原则,以编写API的质量来苛求自己的代码,会大大提升编码素质。
cxuan
2020/11/13
6580
Effective Java tips
所有对 Elvis.getInstance 的调用都返回相同的对象引用,并且不会创建其他的 Elvis 实例(与前面提到的警告相同)
只喝牛奶的杀手
2023/04/07
3000
Effective Java tips
笔记《Effective Java》02:对所有对象都通用的方法
《Effective Java》这本书可以说是程序员必读的书籍之一。这本书讲述了一些优雅的,高效的编程技巧。对一些方法或API的调用有独到的见解,还是值得一看的。刚好最近重拾这本书,看的是第三版,顺手整理了一下笔记,用于自己归纳总结使用。建议多读一下原文。今天整理第二章节:对所有对象都通用的方法。
有一只柴犬
2025/02/07
1040
笔记《Effective Java》02:对所有对象都通用的方法
Effective Java - 静态方法与构造器
传统来讲,为了使客户端能够获取它自身的一个实例,最传统的方法就是提供一个公有的构造器。像下面这样
cxuan
2019/07/10
9000
一文读懂《Effective Java》第3条:用私有构造器或者枚举类型强化Singleton属性
Singleton 指仅仅被实例化一的类,通常用于代表那些本质上唯一的系统组件,比如窗口管理器或者文件系统。
后台技术汇
2022/05/28
3430
Effective Java要点笔记
工作中如果构造函数有多个 且 特定 的话,我一般倾向写两三个函数签名不一样构造器。但是如果在构造参数很多且多变,要写一个内部构建器,用builder模式,而不是大量重叠构造器。
程序员小强
2020/04/14
4590
Effective Java要点笔记
Effective-java-读书笔记之创建和销毁对象
对于类而言, 最常用的获取实例的方法就是提供一个公有的构造器, 还有一种方法, 就是提供一个公有的静态工厂方法(static factory method), 返回类的实例.
特特
2022/10/08
4180
Effective Java(第三版)-学习笔记
这本书的目的是帮助编写清晰正确,可用的,健壮性灵活性高和可维护的代码,而非针对高性能。主要从对象,类,接口,泛型,枚举,流,并发和序列化等方面介绍。
Monica2333
2020/06/22
1.2K0
3分钟快速阅读-《Effective Java》(三)
21.用函数对象来表示策略 简而言之,就是使用接口来定义要实现的策略方法,然后具体的实现类来实现不同的接口从而实现不同的策略,这就是所谓的函数对象来表示策略 22.优先考虑静态成员类 1.嵌套类包括以下四种,除了静态成员类,其他的都可以被称为内部类 1.1 静态成员类:可以被外部访问的一种辅助类,访问的时候并不需要实例化外部类, 1.2 非静态成员内部类,这样的类是需要外部类实例化之后才可以使用的,往往是需要用于操作内部的一些字段进行实现的,例如集合当中的迭代器设计. 1.3 匿名内部类,它的
cwl_java
2019/10/26
3860
Effective Java 2.0_中英文对照_Item 1
THIS chapter concerns creating and destroying objects: when and how to create them, when and how to avoid creating them, how to ensure they are destroyed in a timely manner, and how to manage any cleanup actions that must precede their destruction.
Tyan
2022/05/09
2720
Effective Java笔记(不含反序列化、并发、注解和枚举)
最近把Effective Java复习了一遍,其中有比较多的java最佳实践可以在平时编程中用到。反序列化、并发、注解和枚举这四章没看,并发这本书里讲的比较简单,推荐java并发编程实战这本书。注解和枚举与 Thinking in java中讲的差不多。反序列化用的不多就没看了,以后用到了再复习一下。放上一些书籍笔记的源码,肯定会有纰漏大家可以选择性看看Thinking in Java、算法导论、Effective Java笔记源码 1.创建和销毁对象 1.考虑用静态工厂方法代替构造器: 优势:
何时夕
2018/05/02
9720
Effective.Java 读书笔记(9)关于HashCode
有很多程序会错误的原因之一,就是当你重写一个类的equals方法的时候忘记重写它的hashCode了
Mezereon
2018/09/13
2970
相关推荐
《Effective Java》读书笔记(一)之创建和销毁对象
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验