【第4条】避免创建不必要的对象

2024-04-17 22:48
文章标签 创建 对象 避免 不必要

本文主要是介绍【第4条】避免创建不必要的对象,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

此条在中文版第二版中被译为了“避免创建不必要的对象”,用此更加严谨了。

 

此条认为重复使用同一对象,比每次需要时都创建一个功能上相等价的新对象更好。如果对象是非可变的(见【第13条】),那么他总是可以被重用的。

 

一例子是:

String s1 = "Hello World !";
........  // 其他一些列代码
String s2= "Hello World !";

 虽然看起来s2是新创建的一个新对象,但是由于String是非可变的,所以 String s2= "Hello World !";
 等同于 String s2 = s1; 实际内存中并没有新开辟一块空间,而是将s2的地址指针指向s1的空间地址。

 

但是如果写成  String s2 = new String(s1); 则系统会为s2新开辟一块内存空间,这是我们所不希望看到的。

进而举个更为极端的反例: String s = new String("Hello !");

由于实参"Hello !"本身就是一个String的实例,这时候实际上已经创建了一个String的实例了,再用之作为new String()的参数,就再次创建了一个String实例给s,于是内存的消耗就Double了!

 

    对于提供了静态工厂方法(见【第1条】)和构造函数的非可变类,你通常可以利用静态工厂方法而不是构造函数,以避免重复创建对象。例如,Boolean.valueOf(String)几乎总是优先于构造函数Boolean(String)的。因为构造函数每次被调用都会创建一个新的对象,而静态工厂方法从来不要求这样做。

 

    再有就是那些已知不会被修改的对象,它们一旦被计算出来就不再变化,可以随时使用,而不必每次使用前再次计算。例如,有一个SystemInfo类,里面有保存和取得系统信息的域和方法。为了简单我们认为只有一个用于获取和保持CPUID的方法和域。如果这个类写的不好的话,可能在每次调用 getCPUID()时,都要重新创建SystemInfo中的cpuId,重新获取,甚至重新创建一个SystemInfo类;而好的代码,是在第一次调用时获取,并保持起来,之后再次调用时只是返回就可以了。

以下是我的一段真实代码中的截取:

public class SystemInfo {private static final SystemInfo INSTANCE = new SystemInfo();private static List<String> NULL_STRING_LIST;   // 应该是一个final常量,但由于这里的执行顺序在构造函数之后,所以要在被构造函数调用的方法中使用这个常量就不可以了,所以才这样写// 是用List是因为CPU可能不止一个private List<String> cpuIds;/*** 这是一个私有的构造函数 目的是禁止此类被外部实例化,因为这是一个单例模式*/private SystemInfo() {NULL_STRING_LIST = new ArrayList<String>();this.cpuIds = getCPUIDs();}public static SystemInfo getInstance() {return INSTANCE;}public List<String> getCPUID() {return this.cpuIds;}private List<String> getCPUIDs() {try {return Arrays.asList(...);} catch (Exception e) {return NULL_STRING_LIST; // 见【第27条】返回零长度的数组而不是null,推想一下ArrayList也是同理}}
}

   

使用方法: String myCpu = SystemInfo.getInstance().getCPUID();

这个例子综合了【前4条】的知识,并且还包括了【第27条】,是我能想出的比较好的例子了。

【第1条】它提供了静态工厂方法,用于代替公有构造函数

【第2条】它使用私有构造函数来强化它是个单例模式

【第3条】它使用私有构造函数来强化不可实例化的能力(但与第3条不完全相同,它允许内部实例化一次)

【第27条】异常时返回零长度的数组而不是null,避免使用者需要过多的保护性代码

 

 

 进而这段代码的一个细微变形版本如下,不再是单例模式,而是不可实例化的工具类了。(是【第2条】的进一步,完全满足【第3条】)

public class SystemInfo {private static final SystemInfo INSTANCE = new SystemInfo();private static final List<String> NULL_STRING_LIST = new ArrayList<String>();// 是用List是因为CPU可能不止一个private static List<String> cpuIds;/*** 这是一个空的私有的构造函数 目的是禁止此类被实例化,因为这是一个工具类  */private SystemInfo() {}static{cpuIds = getCPUIDs();}public static SystemInfo getInstance() {return INSTANCE;}public List<String> getCPUID() {return cpuIds;}private static List<String> getCPUIDs() {try {return Arrays.asList(...);} catch (Exception e) {return NULL_STRING_LIST; // 见【第27条】返回零长度的数组而不是null,推想一下ArrayList也是同理}}
}

 

以上两段哪种写法更好呢?可能仁者见仁吧,实际中我使用的是后者,而且是把这个类实现了一个接口,因为考虑到可能会有不同版本的“取得系统信息”的方法(Win32的、Linux的、JNI的...)。

而且当使用Spring的注入后,getInstance()方法 和 INSTANCE 常量也不再是必须的了(Spring的注入不在现在的讨论之内)

 

 

 

【Effective Java 学习笔记】系列连载专题请见:
http://tonylian.iteye.com/categories/64208

 

这篇关于【第4条】避免创建不必要的对象的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/913075

相关文章

CentOS和Ubuntu系统使用shell脚本创建用户和设置密码

《CentOS和Ubuntu系统使用shell脚本创建用户和设置密码》在Linux系统中,你可以使用useradd命令来创建新用户,使用echo和chpasswd命令来设置密码,本文写了一个shell... 在linux系统中,你可以使用useradd命令来创建新用户,使用echo和chpasswd命令来设

使用Python和Pyecharts创建交互式地图

《使用Python和Pyecharts创建交互式地图》在数据可视化领域,创建交互式地图是一种强大的方式,可以使受众能够以引人入胜且信息丰富的方式探索地理数据,下面我们看看如何使用Python和Pyec... 目录简介Pyecharts 简介创建上海地图代码说明运行结果总结简介在数据可视化领域,创建交互式地

Java对象转换的实现方式汇总

《Java对象转换的实现方式汇总》:本文主要介绍Java对象转换的多种实现方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java对象转换的多种实现方式1. 手动映射(Manual Mapping)2. Builder模式3. 工具类辅助映

Python中判断对象是否为空的方法

《Python中判断对象是否为空的方法》在Python开发中,判断对象是否为“空”是高频操作,但看似简单的需求却暗藏玄机,从None到空容器,从零值到自定义对象的“假值”状态,不同场景下的“空”需要精... 目录一、python中的“空”值体系二、精准判定方法对比三、常见误区解析四、进阶处理技巧五、性能优化

Java Optional避免空指针异常的实现

《JavaOptional避免空指针异常的实现》空指针异常一直是困扰开发者的常见问题之一,本文主要介绍了JavaOptional避免空指针异常的实现,帮助开发者编写更健壮、可读性更高的代码,减少因... 目录一、Optional 概述二、Optional 的创建三、Optional 的常用方法四、Optio

在java中如何将inputStream对象转换为File对象(不生成本地文件)

《在java中如何将inputStream对象转换为File对象(不生成本地文件)》:本文主要介绍在java中如何将inputStream对象转换为File对象(不生成本地文件),具有很好的参考价... 目录需求说明问题解决总结需求说明在后端中通过POI生成Excel文件流,将输出流(outputStre

idea中创建新类时自动添加注释的实现

《idea中创建新类时自动添加注释的实现》在每次使用idea创建一个新类时,过了一段时间发现看不懂这个类是用来干嘛的,为了解决这个问题,我们可以设置在创建一个新类时自动添加注释,帮助我们理解这个类的用... 目录前言:详细操作:步骤一:点击上方的 文件(File),点击&nbmyHIgsp;设置(Setti

Spring 中使用反射创建 Bean 实例的几种方式

《Spring中使用反射创建Bean实例的几种方式》文章介绍了在Spring框架中如何使用反射来创建Bean实例,包括使用Class.newInstance()、Constructor.newI... 目录1. 使用 Class.newInstance() (仅限无参构造函数):2. 使用 Construc

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

Java实现将byte[]转换为File对象

《Java实现将byte[]转换为File对象》这篇文章将通过一个简单的例子为大家演示Java如何实现byte[]转换为File对象,并将其上传到外部服务器,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言1. 问题背景2. 环境准备3. 实现步骤3.1 从 URL 获取图片字节数据3.2 将字节数组