# Effective Java 读书笔记(一)

news2024/7/6 18:03:13

Effective Java 读书笔记(一)

构造方法参数过多使用Builder模式

  • Car类是不可变的,所有的参数默认值都在一个地方。buildersetter 方法返回builder 本身,这样就可以进行链式调用,从而生成一个流畅的 API
public class Car {

    /**
     * id
     */
    private String id;
    /**
     * 名称
     */
    private String name;
    /**
     * 速度
     */
    private float speed;

    private Car(Builder builder) {
        id = builder.id;
        name = builder.name;
        speed = builder.speed;
    }

    public static class Builder {

        /**
         * id
         */
        private String id;
        /**
         * 名称
         */
        private String name;
        /**
         * 速度
         */
        private float speed;

        public Builder() {
        }

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

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

        public Builder speed(float speed) {
            this.speed = speed;
            return this;
        }

        public Car build() {
            return new Car(this);
        }
    }
    // 省略 getset()
}

优点

  • Builder 模式非常灵活。 单个 builder 可以重复使用来构建多个对象。
  • 当设计类的构造方法或静态工厂的参数超过几个时,Builder 模式是一个不错的选择,特别是许多参数是可选的或相同类型的。
  • builder 模式客户端代码比使用伸缩构造方法(telescopingconstructors)更容易读写,并且 builder 模式比 JavaBeans 更安全。

缺点

  • 为了创建对象,首先必须创建它的 builder。虽然创建这个 builder 的成本在实践中不太可能被注意到,但在看中性能的场合下这可能就是一个问题。而且,builder 模式比伸缩构造方法模式更冗长,因此只有在有足够的参数时才值得使用它,比如四个或更多。

工具类添加私有构造方法

  • 工具类不是设计用来被实例化的,因为实例化对它没有任何意义。然而,在没有显式构造器的情况下,编译器提供了一个公共的、无参的默认构造器。对于用户来说,该构造器与其他构造器没有什么区别。

  • 可以通过包含一个私有构造器来实现类的非实例化:

public class UtilityClass {
	
    // 该类不能被实例化
    private UtilityClass() {
    	throw new AssertionError();
    }
}

避免创建不必要的对象

  • 每次需要时重用一个对象而不是创建一个新的相同功能对象通常是恰当的。重用可以更快更流行。

  • 反例

// 语句每次执行时都会创建一个新的 String 实例,而这些对象的创建都不是必需的
String s = new String("hello");
// 使用单个 String 实例,而不是每次执行时创建一个新实例。
String s = "hello";
  • 工厂方法Boolean.valueOf(String) 比构造方法 Boolean(String) 更可取,

Split编译告警

  • 'split()' could be replaced with compiled 'java.util.regex.Pattern' construct

  • 方法内部为正则表达式创建一个 Pattern 实例,并且只使用它一次,之后它就有资格进行垃圾收集。 创建 Pattern 实例是昂贵的

// 反例
@Test
public void test2(){
    String str = "Hello world";
    String[] split = str.split("");
}

// 正例
private static final Pattern BLANK_PATTERN = Pattern.compile("");
@Test
public void test2(){
    String str = "Hello world";
    String[] split = BLANK_PATTERN.split(str);
}

自动装箱

  • 优先使用基本类型而不是装箱的基本类型,也要注意无意识的自动装箱
private static long sum() {
    // 可以使用 long 基本类型
    Long sum = 0L;
    for (long i = 0; i <= Integer.MAX_VALUE; i++)
    sum += i;
    return sum;
}

使用 try-with-resources 语句替代 try-finally语句

  • 常用写法
@Test
public void test1() throws IOException {
    String path = "./test.txt";
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        String s = br.readLine();
        logger.info(s);
    } finally {
        // 有可能会出错
        br.close();
    }
}
  • try-with-resources
@Test
public void test1() throws IOException {
    String path = "./test.txt";
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        String s = br.readLine();
        logger.info(s);
    }
}
  • 要使用这个构造,资源必须实现 AutoCloseable 接口,该接口由一个返回为 voidclose 组成。Java

    类库和第三方类库中的许多类和接口现在都实现或继承了 AutoCloseable 接口。如果你编写的类表示

    必须关闭的资源,那么这个类也应该实现 AutoCloseable 接口。

public abstract class Reader implements Readable, Closeable{}

// BufferedReader 实现 close 接口
public void close() throws IOException {
    synchronized (lock) {
        if (in == null)
            return;
        try {
            in.close();
        } finally {
            in = null;
            cb = null;
        }
    }
}
  • 可以在 try-with-resources 语句中添加 catch 子句,就像在常规的 try-finally 语句中一样。这允许你

    处理异常,而不会在另一层嵌套中污染代码。

  • 使用 try-with-resources 语句替代 try-finally 语句。 生成的代码更简洁,更清晰,并且生成的异常更有用。 try-with-resources 语句在编写必须关闭资源的代码时会更容易,也不会出错,

重写 equals 方法时遵守通用约定

通用约定

  • 重写 equals 方法时,必须遵守它的通用约定。Object 的规范如下:

equals 方法实现了一个等价关系,它有以下这些属性:

自反性: 对于任何非空引用 xx.equals(x) 必须返回 true

对称性: 对于任何非空引用 xy,如果且仅当 y.equals(x) 返回 truex.equals(y) 必须

返回 true

传递性: 对于任何非空引用 xyz,如果 x.equals(y) 返回 truey.equals(z) 返回 true

x.equals(z) 必须返回 true

一致性: 对于任何非空引用 xy,如果在 equals 比较中使用的信息没有修改,则

x.equals(y) 的多次调用必须始终返回 true 或始终返回 false

对于任何非空引用 xx.equals(null) 必须返回 false

违反约定

自反性

  • 将对象添加到集合中,使用contains方法,如果违反了自反性会找不到这个对象
boolean contains(Object o);

public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

public boolean equals(Object obj) {
    return (this == obj);
}

对称性

import java.util.Objects;
public final class CaseInsensitiveString {
	private final String s;
	public CaseInsensitiveString(String s) {
		this.s = Objects.requireNonNull(s);
	}
 
    @Override
    public boolean equals(Object o) {
    	if (o instanceof CaseInsensitiveString)
    		return s.equalsIgnoreCase(((CaseInsensitiveString) o).s);
        // 操作 String 字符串
    	if (o instanceof String) 
   			 return s.equalsIgnoreCase((String) o);
    	return false;
    }
}

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
String s = "polish";
System.out.println(cis.equals(s)); // true
System.out.println(s.equals(cis)); // false
  • CaseInsensitiveString 类中的 equals 方法知道正常字符串,但 String 类中的 equals 方法却忽略了不区分大小写的字符串。 因此,s.equals(cis) 返回 false,明显违反对称性。

重写 equals 方法时同时也要重写hashcode 方法

// Object equals 比较 两个对象是否具有相同的引用
public boolean equals(Object obj) {
    return (this == obj);
}

public native int hashCode();
  • hashCodeequals 两个方法是用来协同判断两个对象是否相等的,采用这种方式的原因是可以提高程序插入和查询的速度,如果在重写 equals 时,不重写 hashCode,就会导致在某些场景下,例如将两个相等的自定义对象存储在 Set 集合时,就会出现程序执行的异常,为了保证程序的正常执行,所以我们就需要在重写 equals 时,也一并重写 hashCode 方法才行。

始终重写 toString 方法

  • toString 通用约定「建议所有的子类重写这个方法」。
public String toString() {
    // 类名后跟一个「at」符号(@)和哈希码的无符号十六进制表示组成
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  • toString 方法应该以一种美观的格式返回对象的简明有用的描述。

使类和成员的可访问性最小化

  • 让每个类或成员尽可能地不可访问。 换句话说,使用尽可能低的访问级别,与你正在编写的软件的对应功能保持一致。
  • 应该尽可能地减少程序元素的可访问性(在合理范围内)

编译告警

  • 这样的字段通常用于存储常量值的数组。尽管如此,它们仍然代表着安全隐患,因为它们的内容可能被修改,即使字段被声明为final
public static final String[] STR_ARR = {"1", "2", "3", "4"};

解决方法

// 方法 1
private static final String[] STR_ARR = { ... };
public static final List<String> VALUES = Collections.unmodifiableList(Arrays.asList(STR_ARR));

// 方法 2
private static final String[] STR_ARR = {"1", "2", "3", "4"};
public static final String[] strArrayValues() {
	return STR_ARR.clone();
}

接口优于抽象类

  • 一个接口通常是定义允许多个实现的类型的最佳方式。 如果你导出一个重要的接口,应该强烈考虑提供一个骨架的实现类。 在可能的情况下,应该通过接口上的默认方法提供骨架实现,以便接口的所有实现者都可以使用它。 也就是说,对接口的限制通常要求骨架实现类采用抽象类的形式。

接口仅用来定义类型

  • 一种失败的接口就是所谓的常量接口,常量接口模式是对接口的糟糕使用

消除非检查警告

  • 尽可能地消除每一个未经检查的警告

  • 每当使用 @SuppressWarnings(“unchecked”) 注解时,请添加注释,说明为什么是安全的。

不要忽略异常

  • 空的 catch块会使异常达不到应有的目的
  • 如果选择忽略异常,catch 块中应该包含一条注释,说明为什么可以这么做,并且变量应该命名为 ignored:
@Test
public void test4() {
    try {
        String a = null;
        method1();
        logger.info(a);
    } catch (RuntimeException ignored) {
        // 捕获异常不做任何处理
    }
    logger.info("ok");
}

始终使用 Override 注解

  • 在每个方法声明中使用 Override 注解,并且认为要重写父类声明,那么编译器可以保护免受很多错误的影响,但有一个例外。 在具体的类中,不需要注解标记你确信可以重写抽象方法声明的方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/443268.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

docker资源管理

目录 docker资源控制 CPU 资源控制 cgroups四大功能 设置CPU使用率上限 进行CPU压力测试 设置CPU资源占用比 设置容器绑定指定的CPU 对内存使用的限制 对磁盘IO配额控制&#xff08;blkio&#xff09;的限制 面试题 docker数据管理 数据卷 数据卷容器 端口映射 容…

C++变量和数据类型进阶

变量和数据类型进阶 有符号整数和无符号整数 short、int、long、long long 类型的变量&#xff0c;可以表示正数&#xff0c;也可以表示负数&#xff0c;称为有符号的整数类型。 unsigned short&#xff0c; unsigned int, unsigned long,unsigned long long类型的变量&…

linux部署Jenkins

随着软件开发需求及复杂度的不断提高&#xff0c;团队开发成员之间如何更好地协同工作以确保 软件开发的质量已经慢慢成为开发过程中不可回避的问题。Jenkins 自动化部署可以解决集成、测试、部署等重复性的工作&#xff0c;工具集成的效率明显高于人工操作&#xff1b;并且持续…

Vue 自定义指令

文章目录 Vue 自定义指令钩子钩子函数钩子函数参数 Vue 自定义指令 除了默认设置的核心指令( v-model 和 v-show ), Vue 也允许注册自定义指令。 下面我们注册一个全局指令 v-focus, 该指令的功能是在页面加载时&#xff0c;元素获得焦点&#xff1a; 实例 <div id"…

C++——入门讲解(2)

作者&#xff1a;几冬雪来 时间&#xff1a;2023年4月18日 内容&#xff1a;C入门内容讲解 目录 前言&#xff1a; 1.函数重载&#xff1a; 2.引用&#xff1a; 3.缺省参数&#xff1a; 结尾&#xff1a; 前言&#xff1a; 在上一篇博客中我们正式的踏入了对C板块的学…

jar包内容修改

准备材料及环境&#xff1a; 1.安装jdk&#xff0c;这里安装了1.8版本&#xff0c;可以自由选择&#xff1b; 2.准备demo.jar 3.复制一份demo.jar该扩展名为demo.zip 4.本次使用的是windows系统 修改jar包内容&#xff1a; 1.解压demo.zip到本文件夹&#xff0c;解压后的…

根文件系统类型

Linux启动时&#xff0c;第一个必须挂载的是根文件系统;若系统不能从指定设备上挂载根文件系统&#xff0c;则系统会出错而退出启动。之后可以自动或手动挂载其他的文件系统。因此&#xff0c;一个系统中可以同时存在不同的文件系统。 不同的文件系统类型有不同的特点&#xff…

浅谈图像生成模型 Diffusion Model 原理

重磅推荐专栏&#xff1a; 《Transformers自然语言处理系列教程》 手把手带你深入实践Transformers&#xff0c;轻松构建属于自己的NLP智能应用&#xff01; 可不可以先 点击下方链接&#xff0c;求赞 点击 like ❥(^_-) 一下我的 Model 和 Space&#xff0c;再看后面的正文~~&…

深度学习基础入门篇[七]:常用归一化算法、层次归一化算法、归一化和标准化区别于联系、应用案例场景分析。

【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化算法、卷积模型、序列模型、预训练模型、对抗神经网络等 专栏详细介绍&#xff1a;【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化…

学习小程序基础内容之页面结构

一个小程序页面由四个文件组成&#xff0c;分别是&#xff1a; 文件类型 必需 作用 js 是 页面逻辑 wxml 是 页面结构 json 否 页面配置 wxss 否 页面样式表wxss 否 页面样式表[TOC] 但是我们创建一个页面&#xff0c;最快的方法是在app.json 里面&#xff0c;命名一个文件&a…

Win10输入法设置,详细方法在这里!

案例&#xff1a;win10输入法怎么设置 【想问问大家知道win10输入法该怎么设置吗&#xff1f;想更改一些默认的设置&#xff0c;不知道该如何操作&#xff0c;感谢&#xff01;】 Win10输入法是在Win10操作系统下使用的一种输入工具&#xff0c;是Win10系统的默认输入法&…

【Vim】 【初始篇】Vim之增删改查(idcf)中的改(c)

前言&#xff1a; 都说上古神器vim&#xff0c;可以提高效率&#xff0c;但是我觉得它更能增加乐趣&#xff0c;还能装B于无形。所以我准备开个新的板块用于记录vim使用中的骚操作。 VS2022 使用Vim 作为一个.net程序员&#xff0c;不可能完全脱离vs而使用专门vim编辑器&#x…

Linux文件系统 文件恢复

inode和block block 用于存储文件数据。 文件是存储在硬盘上的&#xff0c;硬盘的最小存储单位叫做“扇区”&#xff08;sector&#xff09;&#xff0c;每个扇区存储512字节。连续八个扇区组成一个"块"&#xff08;block&#xff09;&#xff0c;一个块是4K大小&…

极豆科技加入飞桨技术伙伴计划,共筑智能网联汽车新生态

近日&#xff0c;极豆科技正式加入百度飞桨技术伙伴计划&#xff0c;双方将共同努力&#xff0c;联合推进人工智能、大数据、云计算等前沿技术在智能网联汽车领域的应用落地&#xff0c;携手推动汽车产业变革&#xff0c;加速车企迈向全面数字化。 上海极豆科技有限公司‍‍‍‍…

Java并发基石_CAS原理实战01_CAS机制入门

快了&#xff0c;快要拿到offer了&#xff01;&#x1f339; 案例引入&#xff1a; 开发一个网站&#xff0c;对访问量进行统计&#xff0c;用户每发送一次请求&#xff0c;访问量就1&#xff0c;模拟有100个人同时访问&#xff0c;并且每个人对网站发起10次请求&#xff0c;所…

Nginx+Tomcat负载均衡、动静分离群集

Tomcat优化 Tomcat是java开发的应用程序&#xff0c;作用&#xff1a;作为web服务器处理html页面&#xff0c;但能力一般&#xff0c;更多的用于jsp、servlet容器处理java开发的jsp动态页面。 组织架构&#xff1a;连接器 、 容器 连接器&#xff1a;暴露端口&#xff0c;接…

云表:无代码“打破”工业软件开发壁垒,数字化只需"画表格"

无代码已成为新兴趋势 近年来&#xff0c;在如火如荼的制造业数字化转型浪潮中&#xff0c;“无代码开发”也因其敏捷、易用的独有特性&#xff0c;助力企业实现数字化应用的快速开发与落地&#xff0c;使得数据业务价值在企业级场景下释放&#xff0c;受到市场广泛关注。据国际…

003 常用组件开发使用

目录 一.基础组件 Blank:填充控件 Button&#xff1a;按钮 ButtonType枚举说明 Text&#xff1a;文本显示 QRCode 二.常用布局 线性布局&#xff08;Row和Column&#xff09; 层叠布局 弹性布局&#xff08;Flex&#xff09; 一.基础组件 Blank:填充控件 这个是鸿蒙…

涨薪5k,100多天从功能测试进阶自动化测试,我整理的超全学习指南

个人简介 学渣一枚&#xff0c;2017年6月某大专学校毕业&#xff0c;从事功能测试已经4年&#xff0c;最初毕业是从事了一份销售的工作&#xff0c;工资当时好像是3k&#xff0c;可能也是我个人的原因不适合销售&#xff0c;后来在朋友的介绍下转行到了测试行业&#xff0c;转…

访问者模式解读

目录 问题引进 访问者模式基本介绍 基本介绍 访问者模式的原理类图 对原理类图的说明 访问者模式应用实例 思路分析和图解(类图) 代码实战 应用案例的小结 访问者模式的注意事项和细节 优点 问题引进 测评系统的需求 1) 将观众分为男人和女人&#xff0c;对歌手进行…