Item 5 避免创建不必要的对象

场景一:

这个是经常出现的问题,因为我们经常误用String。

public class Test {

  public static void main(String[] args)
  {

    //参数"terrible"会创建一个对象
    //然后,new String(),这个语句会创建一个对象
    //所以,这个语句执行过程中,会创建两个对象
    String s = new String("terrible.");

    //只创建一个对象,该对象的引用被赋值到变量 ok
    String ok = "This is ok" ;
  }
}

根据《Effective Java》一书的说法,对于 String ok = "This is ok" ,它可以保证,对于所有在同一台虚拟机中运行的代码,只要它们包含相同的字符串字面常量,该对象就会被重用。意思就是,我们在这里,创建了一个对象,它包含字符床字面值"This is ok",然后,如果在其它地方,再次出现字面值"This is ok",系统会使用重用当前已经创建完毕的,不会再创建一个新的对象。

这是重用不可变对象。包含字面值"This is ok"的对象,是不可变的。

场景二:

除了可以重用那些不可变对象之外,还可以重用那些在首次初始化之后,就不会改变的对象。这里的关键是要确定,该对象是否在首次初始化之后就不会被改变。

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class Person {
  private final Date birthDate ;

  public Person(Date birthDate) {
    // Defensive copy - see Item 39
    this. birthDate = new Date(birthDate.getTime());
  }

  // Other fields, methods omitted

  // DON‘T DO THIS!
  public boolean isBabyBoomer() {
    // Unnecessary allocation of expensive object
    Calendar gmtCal = Calendar. getInstance(TimeZone.getTimeZone("GMT" ));
    gmtCal.set(1946, Calendar. JANUARY, 1, 0, 0, 0);
    Date boomStart = gmtCal.getTime();
    gmtCal.set(1965, Calendar. JANUARY, 1, 0, 0, 0);
    Date boomEnd = gmtCal.getTime();
    return birthDate.compareTo(boomStart) >= 0 && birthDate.compareTo(boomEnd) < 0;
  }
}

实例化Person之后,每次查询isBabyBoomer()方法,都会执行如下操作:1.创建一个Calendar实例。2.创建一个TimeZone实例。

3.创建两个Date实例。

而用户每次调用的时候,都是执行相同的操作,创建的对象也都是含义相同的,不会变化的。

所以,对于这种情况,可以将这些创建的对象定义为一个静态的类实例。修改为:

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

class Person {
  private final Date birthDate ;

  public Person(Date birthDate) {
    // Defensive copy - see Item 39
    this. birthDate = new Date(birthDate.getTime());
  }

  // Other fields, methods

  /**
   * The starting and ending dates of the baby boom.
   */
  private static final Date BOOM_START;
  private static final Date BOOM_END;

  static {
    Calendar gmtCal = Calendar. getInstance(TimeZone.getTimeZone("GMT" ));
    gmtCal.set(1946, Calendar. JANUARY, 1, 0, 0, 0);
    BOOM_START = gmtCal.getTime();
    gmtCal.set(1965, Calendar. JANUARY, 1, 0, 0, 0);
    BOOM_END = gmtCal.getTime();
  }

  public boolean isBabyBoomer() {
    return birthDate.compareTo(BOOM_START) >= 0 && birthDate.compareTo( BOOM_END) < 0;
  }
}

首次访问类Person时,会执行其中的静态语句块,创建一个Calendar实例,一个TimeZone实例,两个Date实例。

然后,客户端每次通过Person实例访问isBabyBoomer方法时,都是使用相同的Calendar实例,TimeZone实例,Date实例,而不用每次都创建一个局部实例。如此,相对于一个经常被访问的方法isBabyBoomer,程序的性能是提高了的。

场景三:

Java支持自动装箱技术(和对应的自动拆箱技术),装箱,就是把基本数据类型转换为对应的包装类实例。所以,会在无意间创建多个实例,比如:

敲错了一个字符,将l输入为大写的L,又由于自动装箱技术的存在,于是,就出现了下述的问题。

//一段运行缓慢的代码

public class Sum {
  // Hideously slow program! Can you spot the object creation?
  public static void main(String[] args) {
    Long sum = 0L;
    for ( long i = 0; i < Integer.MAX_VALUE; i++) {
      sum += i;
    }
    System.out.println(sum);
  }
}

实际运行的时候,java编译器,根据装箱技术,会将上述代码转换为:

  for ( long j = 0; j < Integer. MAX_VALUE; j++) {
      sum += Long. valueOf(j);
    }
    System. out.println(sum);

这样会创建Integer.MAX_VALUE个Long个包装类的实例,从而影响了性能。

正确的代码是:

  long sum = 0L;
    for ( long i = 0; i < Integer. MAX_VALUE; i++) {
      sum += i;
    }
    System. out.println(sum);

这样就可以避免创建Integer.MAX_VALUE个Long包装类实例了。

时间: 2024-08-08 01:15:04

Item 5 避免创建不必要的对象的相关文章

String s=new String("xyz");创建几个String对象的问题

首先让我们了解几个概念: 栈 :由JVM分配区域,用于保存线程执行的动作和数据引用. 堆 :由JVM分配的,用于存储对象等数据的区域. 常量池constant pool :在堆中分配出来的一块存储区域,用于存储显式 的String,float或者integer.这是一个特殊的共享区域,可以在内存中共享的不经常改变的东西,都可以放在这里. 进入正题: String a = "abc";①String b = "abc";② 使用String a = "abc

Effective Java之避免创建不必要的对象和消除过期的引用

为什么要避免创建不必要的对象?创建对象的过程还是比较耗性能的,根据书上给出的性能测试,重复创建对象的时间消耗是重用对象时间消耗的250倍.下面列出一些可以重用对象的场景. 1.String类型要注意,String s = "as" 和 String s = new String("as")之间有很大的差别,第二种创建String实例的方法其实会产生两个"as"对象. 2.针对已知不会被修改的可变对象,可以只在第一次使用时初始化,而不是在每次使用时

String str=new String(&quot;abc&quot;)到底创建了几个对象

这句代码到底创建了几个对象?研究了好一阵,现在才能说清楚. package com.sun.test; public class Test<T> { T a; /** * @param args */ public static void main(String[] args) { String str=new String("abc"); } } 我们来看下这段简单代码的字节码: <pre name="code" class="java

在lua中创建字段安全的对象

lua萌新,刚刚学习和使用不到一个月.有不对的地方,还望各路大神不吝赐教. lua中可以用table来模拟对象,但table是可以任意增加键值的.在对象模拟中,暂且也叫它为字段(field)吧.如果在面向对象中,你定义了一个对象,可以在对象以外的地方随意改动这个对象的字段,访问不存在的字段,你想象一下这有多恐怖?比如你定义了一个Vector3{float x = 0; float y = 0; float z = 0;}  我在外面某处加一个float t = 1; 当你在创建并引用这对象的时候

避免创建不必要的对象

1,一般来说,做好能重用对象而不是在每次需要的时候就创建一个相同功能的对象,重用方式即快速,又流行.如果对象是不可变的,他就始终可以被重用. 如:String s=new String("stringtee");该语句在每次执行时都会创建一个新的String实例,如果这种用法是在一个循环中,或者是在一个频繁调用的方法中,就会创建出成千上万个不必要的String实例. 改进方法: String s="stringtee";这个版本只用了一个实例而不是每次都创建一个新的

【转载】如何在Android中避免创建不必要的对象

在编程开发中,内存的占用是我们经常要面对的现实,通常的内存调优的方向就是尽量减少内存的占用.这其中避免创建不必要的对象是一项重要的方面. Android设备不像PC那样有着足够大的内存,而且单个App占用的内存实际上是比较小的.所以避免创建不必要的对象对于Android开发尤为重要. 本文会介绍一些常见的避免创建对象的场景和方法,其中有些属于微优化,有的属于编码技巧,当然也有确实能够起到显著效果的方法. 使用单例 单例是我们常用的设计模式,使用这种模式,我们可以只提供一个对象供全局调用.因此单例

写一个类,在任何时候都可以向它查询“你已经创建了多少个对象?”

源代码: package jxl;class sum{ public static int a=0; int c1; public sum(int c2){ c1=c2; a++; } public int get(){ return a; }}public class Test4 { public static void main(String[] args) { // TODO 自动生成的方法存根 //调用 sum one=new sum(2); sum two=new sum(3); //

图形对象的创建(常用图形对象的创建方法及特殊属性)

1.图形窗口对象 MATLAB的一切图形图像的输出都是在图形窗口中完成的. 一.创建 建立图形窗口对象使用figure函数,其调用格式为:句柄变量=figure(属性名1,属性值1,属性名2,属性值2,...) MATLAB通过对属性一的操作来改变图形窗口的形式.也可以使用figure函数按MATLAB默认的属性值建立图形窗口,格式为:figure  或  句柄变量 =figure MATLAB通过figure函数建立窗口之后,还可以调用figure函数来显示该窗口,并将其设定为当前窗口,调用格

创建内部类的实例对象,必须创建外部类的实例对象

Outer类中定义了一个成员内部类Inner,需要在main()方法中创建Inner类实例对象,方法:Outer.Inner in  = new Outer().new Inner(); 内部类可以引用全局变量; 静态方法创建实例内部类对象 内部类的最重要的一个特点就是它可以直接访问它外部类的成员变量.成员变量是 对象身上的.对象创建完成了,才会为成员变量分配空间.能调用成员变量, 意味着一定有了实例对象. main方法是静态的,它执行的时候可以不用创建那个对象.这就矛盾了. main方法运行的