第五章 初始化与清理(下)

时间:2022-08-07 19:42:01
                              第五章 初始化与清理
         现在总结的东西很多都需要用代码来帮助理解了,所以会有大量的测试代码,不过这中方式非常有用,如果认真敲过一遍之后,并且将这些代码弄清楚了,我相信你一定会对书中描述的内容有一个更清楚的认识。我是在eclipse工具上进行测试的,这里边的代码可以之间粘贴运行,不过对于程序员来说还是自己敲一遍吧,效果会更好。
5.5  清理 : 终结处理和垃圾回收       程序员都了解初始化的重要性,但常常会忘记同样也重要的清理工作。Java有垃圾回收器负责回收无用对象占据的内存资源。但是,垃圾回收器只知道释放那些经过new分配的内存,如果你的对象(并非使用new)获得了一块“特殊”的内存区域,则垃圾回收器就不知道该如何释放这块“特殊”内存。为了应对这种情况,Java允许在类中定义一个名为finalize()的方法。这个方法的工作原理:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。所以要是你打算用finalize(),就能在垃圾回收时刻做一些重要的清理工作。       注意:在Java中                   1、对象可能不被垃圾回收。                2、垃圾回收并不等于“析构”。(析构是在c++里出现的一个概念,就是在c++中要销毁对象必须使用析构函数) 5.5.1 finalize()的用途何在         这里需要记住第三点:         3、垃圾回收只与内存有关           也就是说,使用垃圾回收器的唯一原因是为了回收程序不再使用的内存。所以对于与垃圾回收相关的任何行为来说(尤其是finalize()方法),它们也必须同内存及其回收有关。 5.5.2  你必须实施清理         要清理一个对象,用户必须在需要清理的时刻调用执行清理动作的方法。无论是“垃圾回收”还是“终结”,都不保证一定会发生。如果Java虚拟机(JVM)并未面临内存耗尽的情形,它是不会浪费时间去执行垃圾回收以恢复内存的。 5.5.3  终结条件         通常,不能指望finalize(),必须创建其他的“清理”方法,并且明确地调用他们。      class Book{      boolean checkedOut = false;
     Book(boolean checkOut){
          checkedOut=checkOut;
     }
     void checkIn(){
          checkedOut=false;
     }
     protected void finalize(){
          if(checkedOut){
               System.out.println("error : checked out");
               try {
                    super.finalize();
               } catch (Throwable e) {
                    System.out.println("Throwable");
                    e.printStackTrace();
               }
          }
     }
}
public class TerminationCondition {
     public static void main(String[] args) {

          Book novel = new Book(true);
          novel.checkIn();
          new Book(true);
          System.gc();
     }

} 运行结果:error : checked out 因为在main()方法中,由于程序员错误,有一本书未被签入。要是没有finalize()来验证终结条件,将很难发现这种缺陷。 5.5.4  垃圾回收器如何工作
5.6  成员初始化      Java尽力保证:所有变量在使用前都能得到恰当的初始化。对于方法的局部变量,Java以编译时错误的形式来贯彻这种保证。      例如:我们定义成员变量的时候并没有给它们赋值,但是编译器会给它们初始化默认值,比如 int i;在编译的时候初始值就是0。但是在方法中就不行了:  void f(){              int i;              i++;     //错误,i没有被初始化 } 5.6.1  指定初始化        如果想为某个变量赋初值,在基本数据类型中可以直接赋值。例如:int i=1;  char ch='x' ; boolean bool=true;         如果是其他类型的对象则可以创建一个对象来初始化:Depth d=new Depth();        当然也可以通过调用方法来初始化:int i=f();
5.7  构造器初始化       public class Counter {
     int i;
     public Counter() {
          i=7;
     } 该程序中i首先会被置零,然后变成7。 5.7.1  初始化顺序       在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散步与方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。 5.7.2  静态数据的初始化        无论创建多少个对象,静态数据都只占用一份存储区域。static关键字不能应用于局部变量,因此它只能作用于域。如果一个域是静态的基本类型域,且也没有对它进行初始化,那么它就会获得基本类型的标准初值;如果它是一个对象的引用,那么它的默认初始化值就是null。 class Bowl{
     Bowl(int marker){
          System.out.println("Bow1("+marker+")");
     }
     void f1(int marker){
          System.out.println("f1("+marker+")");
     }
}
class Table{
     static Bowl bowl1= new Bowl(1);
     Table(){
          System.out.println("Table()");
          bowl2.f1(1);
     }
     void f2(int marker){
          System.out.println("f2("+marker+")");
     }
     static Bowl bowl2=new Bowl(2);
}
class Cupboard{
     Bowl bowl3=new Bowl(3);
     static Bowl bowl4=new Bowl(4);
     Cupboard(){
          System.out.println("Cupboard");
          bowl4.f1(2);
     }
     void f3(int marker){
          System.out.println("f3("+marker+")");
     }
     static Bowl bowl5=new Bowl(5);
}
public class StaticInitialization {

     public static void main(String[] args) {
          System.out.println("createing new cupboard in main");
          new Cupboard();
          System.out.println("createing new cupboard in main");
          new Cupboard();
          table.f2(1);
          cupboard.f3(1);
     }
     static Table table=new Table();
     static Cupboard cupboard=new Cupboard();

} 运行结果: Bow1(1) Bow1(2)
Table()
f1(1)
Bow1(4)
Bow1(5)
Bow1(3)
Cupboard
f1(2)
createing new cupboard in main
Bow1(3)
Cupboard
f1(2)
createing new cupboard in main
Bow1(3)
Cupboard
f1(2)
f2(1)
f3(1)
由运行结果可以发现:初始化的顺序是先对静态对象(前提是得有对象被初始化时才会进行,就是说静态初始化只有在必要时刻才会进行),而后是“非静态”对象,然后才是构造器。 5.7.3  显式的静态初始化        Java允许将多个静态初始化动作组织成一个特殊的“静态子句”(有时也叫做“静态块”)。      class Cup{
     Cup(int marker){
          System.out.println("cup("+marker+")");
     }
     void f(int marker){
          System.out.println("f("+marker+")");
     }
}
class Cups{
     static Cup cup1;
     static Cup cup2;
     static{
          cup1=new Cup(1);
          cup2=new Cup(2);
     }
     Cups(){
          System.out.println("cups ()");
     }
}
public class Explicitstatic {

     public static void main(String[] args) {
          System.out.println("inside main");
          Cups.cup1.f(99);
     }
//     static Cups cups1=new Cups();
} 运行结果: inside main
cup(1)
cup(2)
f(99)
      静态块跟静态初始化一样也只执行一次,当首次生成这个类的一个对象时,或者首次访问属于那个类的静态数据成员(即便从未生成过那个类的对象)。就会被执行。 5.7.4  非静态实例初始化       Java中也有被称为实例初始化的类似语法,用来初始化每一个对象的非静态变量。例如:       class Mug{
     Mug(int marker){
          System.out.println("Mug("+marker+")");
     }
     void f(int marker){
          System.out.println("f("+marker+")");
     }
}
public class Mugs {
     Mug mug1;
     Mug mug2;
     {
          mug1=new Mug(1);
          mug2=new Mug(2);
          System.out.println("mug1 & mug2 initialized");
     }
     Mugs(){
          System.out.println("Mugs()");
     }
     Mugs(int i){
          System.out.println("Mugs(int)");
     }

     public static void main(String[] args) {
          System.out.println("inside main()");
          new Mugs();
          System.out.println("new Mugs() completed");
          new Mugs(1);
          System.out.println("new Mugs(1) completed");

     }

} 运行结果: 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
从以上代码可以看出少了static关键字,所以会重复执行。
5.8  数组的初始化      数组的初始化有几种方法:      第一种:int [] a={a,b,c}   这种方式是你明确知道要初始化多少个数据,这种形式很有用,但缺点也很明显,就是更加受限。      第二种 : int [] a = new a [n]   这种形式可以解决你不知道具体要多少数据的难题,使用时可以根据情况而定。        5.9  枚举类型       在Java SE5中添加了一个看似很小的特性,即enum关键字,它使得我们在需要群组并使用枚举类型集时,可以很方便的处理。在很大程度上,可以将enum当作其他任何类来处理,事实上enum确实是类,并且具有自己的方法。enum有一个特别实用的特性,即ta3可以在switch语句内使用;      enum Spiciness{
     NOT, MILD, MEDIUM, HOT, FLAMING
}
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");
               break;
          }
     }
     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();
                }

} 运行结果: this burrito is not spicy at all
this burrito is A little hot
this burrito is maybe too hot