第5条 避免创建不必要的对象

本文探讨了在Java编程中通过重用不可变对象、使用静态工厂方法及优化对象创建过程来提高代码性能的方法。具体展示了如何避免不必要的String、Date对象创建,以及自动装箱带来的性能损耗。通过案例分析,强调了正确使用基本类型的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

首先举一个反面例子

String result=new String("oschina");

该语句每执行一次就会创建一个String实例,如果这在一个循环中,将会有很多String实例被创建,有可能会创建成千上万的实例,显然这是不必要的。上面的代码可以这样修改

String result="oschina";

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

其次,对于同时提供了静态工厂方法和构造器的不可变类,通常可以使用静态工厂方法而不是构造器,以避免创建不必要的对象。例如,静态工厂方法Boolean.valueOf(String)几乎总是优先于构造器Boolean(String)。构造器在每次被调用的时候都会创建一个新的对象,而静态工厂方法则从来不要求这样做,实际上也不会这样做。

除了重用不可变的对象之外,也可以重用那些已经不会被修改的可变对象。

反面例子

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;
   }

这样的写法,isBabyBoomer方法每次被调用的时候,都会创建一个Calendar、TimeZone、Date实例,这显然是不必要的。

对于上面的代码可以这样修改

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;
   }

这样把每次需要调用的变量放到static静态块里面,这样就保证了避免频繁被调用的次数。从而使性能得到提高。

在Java 1.5 的发行版本中,有一种创建多余对象的新方法,称作自动装箱(autoboxing),它允许程序员将基本类型和装箱基本类型混用,按需自动装箱和拆箱。

public class Sum {
   // Hideously slow program! Can you spot the object creation?
   public static void main(String[] args) {
      LongTimes();
      longTimes();
   }

   public static void LongTimes(){
      long beginTime = System.currentTimeMillis();
      Long sum = 0L;
      for (long i = 0; i < Integer.MAX_VALUE; i++) {
         sum += i;
      }
      long endTime = System.currentTimeMillis();
      System.out.println("Long====总数:"+sum);
      System.out.println("Long====耗时:"+(endTime-beginTime)/1000);
   }


   public static void longTimes(){
      long beginTime = System.currentTimeMillis();
      long sum = 0L;
      for (long i = 0; i < Integer.MAX_VALUE; i++) {
         sum += i;
      }
      long endTime = System.currentTimeMillis();
      System.out.println("long====总数:"+sum);
      System.out.println("long====耗时:"+(endTime-beginTime)/1000);
   }

这种Long和long计算的结果如下,只因为将变量sum声明为Long而不是long,从而多计算了2*31次方,所以结论很明显,要优先使用基本类型而不是自动封装类型,要当心无意识的自动封装。

175325_YUAK_1034176.jpg


转载于:https://my.oschina.net/u/1034176/blog/648687

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值