【第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

相关文章

Python打印对象所有属性和值的方法小结

《Python打印对象所有属性和值的方法小结》在Python开发过程中,调试代码时经常需要查看对象的当前状态,也就是对象的所有属性和对应的值,然而,Python并没有像PHP的print_r那样直接提... 目录python中打印对象所有属性和值的方法实现步骤1. 使用vars()和pprint()2. 使

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可

MySQL JSON 查询中的对象与数组技巧及查询示例

《MySQLJSON查询中的对象与数组技巧及查询示例》MySQL中JSON对象和JSON数组查询的详细介绍及带有WHERE条件的查询示例,本文给大家介绍的非常详细,mysqljson查询示例相关知... 目录jsON 对象查询1. JSON_CONTAINS2. JSON_EXTRACT3. JSON_TA

Java中实现线程的创建和启动的方法

《Java中实现线程的创建和启动的方法》在Java中,实现线程的创建和启动是两个不同但紧密相关的概念,理解为什么要启动线程(调用start()方法)而非直接调用run()方法,是掌握多线程编程的关键,... 目录1. 线程的生命周期2. start() vs run() 的本质区别3. 为什么必须通过 st

C#之List集合去重复对象的实现方法

《C#之List集合去重复对象的实现方法》:本文主要介绍C#之List集合去重复对象的实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C# List集合去重复对象方法1、测试数据2、测试数据3、知识点补充总结C# List集合去重复对象方法1、测试数据

Macos创建python虚拟环境的详细步骤教学

《Macos创建python虚拟环境的详细步骤教学》在macOS上创建Python虚拟环境主要通过Python内置的venv模块实现,也可使用第三方工具如virtualenv,下面小编来和大家简单聊聊... 目录一、使用 python 内置 venv 模块(推荐)二、使用 virtualenv(兼容旧版 P

Spring中管理bean对象的方式(专业级说明)

《Spring中管理bean对象的方式(专业级说明)》在Spring框架中,Bean的管理是核心功能,主要通过IoC(控制反转)容器实现,下面给大家介绍Spring中管理bean对象的方式,感兴趣的朋... 目录1.Bean的声明与注册1.1 基于XML配置1.2 基于注解(主流方式)1.3 基于Java

C++/类与对象/默认成员函数@构造函数的用法

《C++/类与对象/默认成员函数@构造函数的用法》:本文主要介绍C++/类与对象/默认成员函数@构造函数的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录名词概念默认成员函数构造函数概念函数特征显示构造函数隐式构造函数总结名词概念默认构造函数:不用传参就可以

C++类和对象之默认成员函数的使用解读

《C++类和对象之默认成员函数的使用解读》:本文主要介绍C++类和对象之默认成员函数的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、默认成员函数有哪些二、各默认成员函数详解默认构造函数析构函数拷贝构造函数拷贝赋值运算符三、默认成员函数的注意事项总结一

Linux lvm实例之如何创建一个专用于MySQL数据存储的LVM卷组

《Linuxlvm实例之如何创建一个专用于MySQL数据存储的LVM卷组》:本文主要介绍使用Linux创建一个专用于MySQL数据存储的LVM卷组的实例,具有很好的参考价值,希望对大家有所帮助,... 目录在Centos 7上创建卷China编程组并配置mysql数据目录1. 检查现有磁盘2. 创建物理卷3. 创