Effective Java笔记(6)避免创建不必要的对象

这篇具有很好参考价值的文章主要介绍了Effective Java笔记(6)避免创建不必要的对象。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        一般来说,最好能重用单个对象,而不是在每次需要 的时候就创建一个相同功能的新对象 。 重用方式既快速,又流行 。 如果对象是不可变的( immutable ) (详见第 17 条),它就始终可以被重用 。

        作为一个极端的反面例子,看看下面的语句 :

String s = new String("bikini"); // DON'T DO THIS!

        该语句每次被执行的时候都创建一个新的 String 实例,但是这些创建对象的动作全都是不必要的。 传递给 String构造器的参数(bikini)本身就是一个 String 实例,功能方面等同于构造器创建的所有对象 。 如果这种用法是在一个循环中,或者是在一个被频繁调用的方法中,就会创建出成千上万不必要的 String 实例 。

        改进后的版本如下所示 :

String s = "bikini";

        这个版本只用了 一个 String 实例,而不是每次执行的 时候都创建一个新的实例 。 而且,它可以保证,对于所有在同一台虚拟机中运行的代码,只要它们包含相同的字符串字面常量,该对象就会被重用。

        对于同时提供了静态 工厂方法( static factory method) 和构造器的不可变类,通常优先使用静态工厂方法而不是构造器,以避免创建不必要的对象 。 例如,静态工厂方法Boolean.valueOf(String )几乎总是优先于构造器 Boolean(String),注意构造器 Boolean(String)在 Java 9 中已经被废弃了 。 构造器在每次被调用的时候都会创建一个新的对象,而静态工厂方法则从来不要求这样做,实际上也不会这样做 。 除了重用不可变的对象之外,也可以重用那些已知不会被修改的可变对象。

        有些对象创建的成本比其他对象要高得多 。 如果重复地需要这类“昂贵的对象”,建议将它缓存下来重用 。 遗憾的是,在创建这种对象的时候,并非总是那么显而易见 。 假设想要编写一个方法,用它确定一个字符串是否为一个有效的罗马数字 。 下面介绍一种最容易的方法,使用一个正则表达式 :

Effective Java笔记(6)避免创建不必要的对象,Effective Java,java,开发语言,后端

        这个实现的问题在于它依赖 String.matches 方法 。虽然String.matches 方法最易于查看一个字符串是否与正则表达式相匹配 , 但并不适合在注重性能的情形中重复使用 。 问题在于,它在内部为正则表达式创建了一个 Pattern 实例,却只用了一次,之后就可以进行垃圾回收了 。 创建Pattern实例的成本很高 ,因为需要将正则表达式编译成一个有限状态机( finite state machine)。

        为了提升性能,应该显式地将正则表达式编译成一个 Pattern实例(不可变),让它成为类初始化的一部分,并将它缓存起来,每当调用 isRomanNumeral 方法的时候就重用同一个实例:

Effective Java笔记(6)避免创建不必要的对象,Effective Java,java,开发语言,后端

         改进后的 isRomanNumeral 方法如果被频繁地调用,会显示出明显的性能优势。

        如果一个对象是不变的,那么它显然能够被安全地重用,但其他有些情形则并不总是这么明显 。 考虑适配器( adapter )的情形,有时也叫作视图( view ) 。 适配器是指这样一个对象 :它把功能委托给一个后备对象( backing object ),从而为后备对象提供一个可以替代的接口 。 由于适配器除了后备对象之外, 没有其他的状态信息,所以针对某个给定对象的特定适配器而言,它不需要创建多个适配器实例 。

        例如 ,Map 接口 的 keySet 方法返回该 Map 对象的 Set 视图,其 中包含该 Map 中所有的键(key) 。 乍看之下 ,好像每次调用 keySet 都应该创建一个新 的 Set 实例,但是,对于一个给定的 Map 对象,实际上每次调用 keySet 都返回同样的 Set 实例 。 虽然被返回的 Set 实例一般是可改变的,但是所有返回的对象在功能上是等同的 :当其中一个返回对象发生变化的时候,所有其他的返回对象也要发生变化,因为它们是由 同一个 Map 实例支撑的 。 虽然创建 key Set 视图对象的多个实例并无害处, 却是没有必要,也没有好处的 。

        另一种创建多余对象的方法,称作自动装箱( autoboxing ),它允许程序员将基本类型和装箱基本类型( Boxed Primitive Type )混用,按需要自动装箱和拆箱 。自动装箱使得基本类型和装箱基本类型之间的差别变得模糊起来, 但是并没有完全消除 。 它们在语义上还有着微妙的差别,在性能上也有着比较明显的差别 。 请看下面的程序,它计算所有int正整数值的总和 。 为此,程序必须使用 long 算法,因为 int 不够大,无法容纳所有 int 正整数值的总和:

private static long sum() {
    Long sum = OL;
    for (long i = 0; i <= Integer.MAX_VALUE; i++)
        sum += i;
    return sum;
}

        这段程序算出的答案是正确的,但是比实际情况要更慢一些,只因为打错了一个字符 。 变量 sum 被声明成 Long 而不是 long ,意味着程序构造了大约 231 个多余的 Long 实例(大约每次往 Long sum 中增加 long时构造一个实例) 。 将 sum 的声明从 Long 改成long ,在我的机器上使运行时间从 6.3 秒减少到了 0 . 59 秒 。 结论很明显 :要优先使用 基本类型而不是装箱基本类型,要当心无意识的自动装箱

        不要错误地认为本条目所介绍的内容暗示着“创建对象的代价非常昂贵,我们应该要尽可能地避免创建对象” 。 相反,由于小对象的构造器只做很少量的显式工作,所以小对象的创建和回收动作是非常廉价的,特别是在现代的 JVM 实现上更是如此 。 通过创建附加的对象,提升程序的清晰性、简洁性和功能性,这通常是件好事 。

        反之,通过维护自己的对象池( object pool )来避免创建对象并不是一种好的做法,除非池中的对象是非常重量级的 。 正确使用对象池的典型对象示例就是数据库连接池 。 建立数据库连接的代价是非常昂贵的,因此重用这些对象非常有意义 。 而且,数据库的许可可能限制你只能使用一定数量的连接 。 但是,一般而言,维护自己的对象池必定会把代码弄得很乱,同时增加内存占用( footprint ),并且还会损害性能 。 现代的 JVM 实现具有高度优化的垃圾回收器,其性能很容易就会超过轻量级对象池的性能 。文章来源地址https://www.toymoban.com/news/detail-558781.html

到了这里,关于Effective Java笔记(6)避免创建不必要的对象的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 设计模式第九讲:常见重构技巧 - 去除不必要的!=

    项目中会存在大量判空代码,多么丑陋繁冗!如何避免这种情况?我们是否滥用了判空呢?本文是设计模式第九讲,讲解常见重构技巧:去除不必要的!= 通常是这样的 初步的,使用Apache Commons,Guvava,Hutool等 StringUtils 考虑用Assert断言 逐级判断空,还是抛出自定义异常,还是

    2024年02月11日
    浏览(40)
  • 在应用程序中发现不必要的 Http 响应头

      响应头中多了: Server: nginx/1.24.0   在服务器块下的nginx.conf中添加以下参数 保存nginx.conf文件, 然后重新启动Nginx以查看结果。

    2024年02月12日
    浏览(32)
  • 漏洞修复:在应用程序中发现不必要的 Http 响应头

    blablabla描述,一般是在返回的响应表头中出现了Server键值对,那我们要做的就是移除它,解决方案中提供了nginx的解决方案 第一种解决方案 当前解决方案会隐藏nginx的版本号,但还是会返回nginx字样,如果想再彻底点,参考第二种解决方案 or 第二种解决方案 当前方法需要安装

    2024年02月09日
    浏览(38)
  • nginx解决不必要的 Http 响应头漏洞(自定义server信息及隐藏版本号)

    1.自定义server信息 修改nginx解压目录下的/src/core/nginx.h文件     修改nginx解压目录下的/src/http/ngx_http_header_filter_module.c文件 修改 nginx解压目录下的/src/http/ngx_http_special_response.c文件  全部修改完成后,执行./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_sub_module --w

    2024年02月14日
    浏览(35)
  • idea提交代码到git或svn上时,怎么忽略.class、.iml文件和文件夹等不必要的文件

    在Setings– Editor -- File Types --Ignore files and folders中添加需要忽略的文件和文件夹: 注意事项: 千万不要忽略.class文件, 千万不要忽略.class文件, 千万不要忽略.class文件, 重要的事说三遍,因为如果把class文件也忽略的话,就会导致java自带的类大批量报错,如果要忽略class文

    2024年03月24日
    浏览(73)
  • Effective Java笔记(29)优先考虑泛型

            一般来说 ,将集合声 明参数化,以及使用 JDK 所提供的泛型方法,这些都不太困难 。编写自己的泛型会比较困难一些,但是值得花些时间去学习如何编写 。         以简单的(玩具)堆校实现为例 :         这个类应该先被参数化,但是它没有,我们可

    2024年02月13日
    浏览(31)
  • Effective Java笔记(20)接口优于抽象类

            Java提供了两种机制,可以用来定义允许多个实现的类型:接口和抽象类。自从Java 8为继承引入了 缺省方法 ( default method),这两种机制都允许为某些实例方法提供实现。主要的区别在于,为了实现由抽象类定义的类型,类必须成为抽象类的一个子类。因为Java只允许

    2024年02月14日
    浏览(43)
  • Effective Java笔记(30)优先考虑泛型方法

            正如类可以从泛型中受益一般 ,方法也一样。静态工具方法尤其适合于泛型化 。 Collections 中的所有“算法”方法(例如 binarySearch 和 sort )都泛型化了 。         编写泛型方法与编写泛型类型相类似 。 例如下面这个方法,它返回两个集合的联合 :      

    2024年02月13日
    浏览(31)
  • Effective Java笔记(5)优先考虑依赖注入来引用资源

            有许多类会依赖一个或多个底层的资源 。 例如,拼写检查器需要依赖词典 。 因此,像下面这样把类实现为静态工具类的做法并不少见(详见第 4 条):         同样地,将这些类实现为 Singleton 的做法也并不少见(详见第 3 条) :         以上两种方法

    2024年02月15日
    浏览(46)
  • Effective Java笔记(11)覆盖 equals 时总要覆盖 hashCode

             在每个 覆盖了 equals 方法的类中,都 必须 覆盖 hashCode 方法 。 如果不这样做的话,就会违反 hashCode 的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作,这类集合包括 HashMap 和 HashSet 。 下面是约定的内容,摘自 Object 规范: 1、在应用程序的执

    2024年02月15日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包