Thinking In Java -- Chapter 5 -- 初始化与清理

方法重载:


  1. 区分重载方法的规则很简单,每个重载的方法都必须有一个独一无二的参数类型列表。参数顺序的不同,也足以区分两个方法,但是一般情况下别这么做,这样会使代码难以维护。
  2. 关于参数是基本类型(char,byte,short,int,long,float,double)的重载,如果传入的数据类型小于方法中声明的形式参数类型,实际数据类型就会被提升。char略有不同,如果无法找到恰好接受char参数的方法,就会把char直接提升到int型。如果传入的实际参数大于重载方法声明的形式参数,就得通过类型转化来执行窄化转换,如果不这样做,编译器就会报错。

在构造器中调用构造器:


  1. 尽管可以用this调用一个构造器,但却不能调用两个
  2. 必须将构造器调用置于最起始处,否则编译器会报错
  3. 除构造器外,编译器禁止在其他任何方法中调用构造器。

static的含义:


在static方法内不能调用非静态方法(这不是完全不可能,如果传递一个对象的引用到静态方法里(静态方法可以创建其自身的对象),然后通过这个引用,你就可以调用非静态方法和访问非静态数据成员了,但通常要达到这样的效果,你只需写一个非静态方法即可),反过来倒是可以的。

终结处理和垃圾回收:


  1. Java允许在类中定义一个名为finalize()的方法。它的工作原理“假定”是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。所以要是你打算用finalize(),就能在垃圾回收时刻做一些重要的清理工作。
  2. 对象可能不被垃圾回收;垃圾回收并不等于“析构”(c++中销毁对象必须用到这个函数);垃圾回收只与内存有关。
  3. 如果程序执行结束,并且垃圾回收器一直都没有释放你创建的任何对象的存储空间,则随着程序的退出,那些资源也会全部交还给操作系统。
  4. 为什么要使用finalize()方法呢?使用垃圾回收器的唯一原因就是回收程序不再使用的内存,对于与垃圾回收有关的任何行为来说(尤其是finalize()方法),它们也必须同内存及其回收有关。也就是说finalize()方法也是与内存及其回收有关。而无论对象是怎么创建的,垃圾回收器都会负责释放对象占据的所有内存。而java中一切皆为对象,那也就是说垃圾回收器可以释放所有被占据的内存。那为什么还需要finalize方法来释放呢?那就只有是某些特殊的情况,就是当通过某种创建对象方式以外的方式为对象分配了存储空间时,这时分配的内存是垃圾回收器无法释放的,所以需要finalize来进行释放(具体的例子在书本88页最上面有讲)。
  5. 无论是“垃圾回收”还是终结,都不保证一定会发生。如果Java虚拟机(JVM)并未面临内存耗尽的情景,它是不会浪费时间去执行垃圾回收以及恢复内存的。
  6. 对象的交互自引用(这种情况是怎么个引用法我还不是很明白)。

成员初始化:


Java尽力保证:所有变量在使用前都能得到恰当的初始化。但是对于局部变量,Java以编译时错误的形式来贯彻这种保证。

构造器初始化:


  1. 初始化顺序:在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布与方法定义之间,它们仍然会在任何方法(包括构造器)被调用之前得到初始化。如下例:
  2. 静态数据的初始化:无论创建多少个对象,静态数据都只占用一份存储区域。static静态关键字不能应用于局部变量,因此它只能作用于域。如果一个域是静态的基本类型域,且没有对它进行初始化,那么它就会获得基本类型的标准初值;如果它是一个对象引用,那么它的默认初始化值就是null。含有静态数据的类的初始化顺序是:先静态对象,而后是非静态对象。
  3. 非静态实例初始化:Java中也有被称为实例初始化的类似语法,用来初始化每一个对象的非静态变量,如下例:
    class Mug {
      Mug(int marker) {
        print("Mug(" + marker + ")");
      }
      void f(int marker) {
        print("f(" + marker + ")");
      }
    }
    
    public class Mugs {
      Mug mug1;
      Mug mug2;
      {
        mug1 = new Mug(1);
        mug2 = new Mug(2);
        print("mug1 & mug2 initialized");
      }
      Mugs() {
        print("Mugs()");
      }
      Mugs(int i) {
        print("Mugs(int)");
      }
      public static void main(String[] args) {
        print("Inside main()");
        new Mugs();
        print("new Mugs() completed");
        new Mugs(1);
        print("new Mugs(1) completed");
      }
    } /* Output:
    Inside main()
    Mug(1)
    Mug(2)
    mug1 & mug2 initialized
    Mugs()
    new Mugs() completed
    Mug(1)
    Mug(2)
    mug1 & mug2 initialized
    Mugs(int)
    new Mugs(1) completed
    *///:~
    

    看起来与静态初始化子句一样,只不过少了静态关键字。这种语法对于支持“匿名内部类”的初始化是必须得,但它也使得你可以保证无论调用了哪个显示构造器,某些操作都会发生。从输出来看,实例初始化子句是在两个构造器之前执行的。

数组的初始化:


  1. 编译器不允许指定数组的大小。如果在编写程序时,并不确定数组里需要多少个元素,可以直接用new在数组里创建元素。看下例:
    //: initialization/ArrayNew.java
    // Creating arrays with new.
    import java.util.*;
    import static net.mindview.util.Print.*;
    
    public class ArrayNew {
      public static void main(String[] args) {
        int[] a;
        Random rand = new Random(47);
        a = new int[rand.nextInt(20)];
        print("length of a = " + a.length);
        print(Arrays.toString(a));
      }
    } /* Output:
    length of a = 18
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    *///:~
    

    当然,在本例中,也可以在定义的同时初始化:

    int[] a =  new int[rand.nextInt(20)];

    如果可能的话,请尽量这么做。

  2. 如果你创建了一个非基础类型的数组,那么你就创建了一个引用数组,而引用数组直到通过创建新的对象,并把对象赋值给引用,初始化进程才算结束。而如果忘记了创建对象,并且试图使用数组中的空引用,就会在运行时产生异常。对于引用数组的创建,可以先创建引用数组,再创建对象赋值给引用,如下例:

    //: initialization/ArrayClassObj.java
    // Creating an array of nonprimitive objects.
    import java.util.*;
    import static net.mindview.util.Print.*;
    
    public class ArrayClassObj {
      public static void main(String[] args) {
        Random rand = new Random(47);
        Integer[] a = new Integer[rand.nextInt(20)];
        print("length of a = " + a.length);
        for(int i = 0; i < a.length; i++)
          a[i] = rand.nextInt(500); // Autoboxing
        print(Arrays.toString(a));
      }
    } /* Output: (Sample)
    length of a = 18
    [55, 193, 361, 461, 429, 368, 200, 22, 207, 288, 128, 51, 89, 309, 278, 498, 361, 20]
    *///:~
    

    先创建引用数组:

     Integer[] a = new Integer[rand.nextInt(20)];

    然后再创建对象赋值给引用:

    a[i] = rand.nextInt(500);

    也可以用花括号括起来的列表来初始化对象数组。有两种形式:

    //: initialization/ArrayInit.java
    // Array initialization.
    import java.util.*;
    
    public class ArrayInit {
      public static void main(String[] args) {
        Integer[] a = {
          new Integer(1),
          new Integer(2),
          3, // Autoboxing
        };
        Integer[] b = new Integer[]{
          new Integer(1),
          new Integer(2),
          3, // Autoboxing
        };
        System.out.println(Arrays.toString(a));
        System.out.println(Arrays.toString(b));
      }
    } /* Output:
    [1, 2, 3]
    [1, 2, 3]
    *///:~
    

    在这两种形式中,初始化列表的最后一个逗号都是可选的。

  3. 可变参数列表:可变参数列表使得重载过程变得复杂了。你应该总是只在重载方法的一个版本上使用可变参数列表,或者压根就不用它。这里放一个可变参数列表的例子,可以看一次可变参数是怎么定义的:

    //: initialization/OverloadingVarargs.java
    
    public class OverloadingVarargs {
      static void f(Character... args) {
        System.out.print("first");
        for(Character c : args)
          System.out.print(" " + c);
        System.out.println();
      }
      static void f(Integer... args) {
        System.out.print("second");
        for(Integer i : args)
          System.out.print(" " + i);
        System.out.println();
      }
      static void f(Long... args) {
        System.out.println("third");
      }
      public static void main(String[] args) {
        f('a', 'b', 'c');
        f(1);
        f(2, 1);
        f(0);
        f(0L);
        //! f(); // Won't compile -- ambiguous
      }
    } /* Output:
    first a b c
    second 1
    second 2 1
    second 0
    third
    *///:~
    

     

枚举类型:


enum关键字,使得我们在需要群组并使用枚举类型集时,可以很方便的处理。enum有一个特别实用的特性,它可以在switch语句内使用。大体上,你可以将enum用作另外一种创建数据类型的方式,然后直接将所得到的类型拿来使用。下面是enum的一个实例:

//: initialization/Spiciness.java

public enum Spiciness {
  NOT, MILD, MEDIUM, HOT, FLAMING
} ///:~
//: initialization/Burrito.java

public class Burrito {
  Spiciness degree;
  public Burrito(Spiciness degree) { this.degree = degree;}
  public void describe() {
    System.out.print("This burrito is ");
    switch(degree) {
      case NOT:    System.out.println("not spicy at all.");
                   break;
      case MILD:
      case MEDIUM: System.out.println("a little hot.");
                   break;
      case HOT:
      case FLAMING:
      default:     System.out.println("maybe too hot.");
    }
  }	
  public static void main(String[] args) {
    Burrito
      plain = new Burrito(Spiciness.NOT),
      greenChile = new Burrito(Spiciness.MEDIUM),
      jalapeno = new Burrito(Spiciness.HOT);
    plain.describe();
    greenChile.describe();
    jalapeno.describe();
  }
} /* Output:
This burrito is not spicy at all.
This burrito is a little hot.
This burrito is maybe too hot.
*///:~

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值