• 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包装类实例了。

  • 相关阅读:
    3164 质因数分解
    codevs3249搭积木
    codevs 2964公共素数因数
    爱改名的小融1
    单链表基础练习
    并查集(union-find sets)
    string类中字符的大小写转换
    蒜头君学英语--set()练习
    打印锯齿矩阵
    堆积木
  • 原文地址:https://www.cnblogs.com/ttylinux/p/4356418.html
Copyright © 2020-2023  润新知