读Java编程思想随笔の初始化与清理
初始化和清理正式涉及安全的两个问题。
如果用户不知道如何初始化库的构件(或者用户必须初始化的其他东西),更是如此。清理也是一个特殊问题,当使用完一个元素时,它对你也就不会有什么影响,所以很容易把它忘记。这样一来,这个元素占用的资源就会一直得不到释放,结果是资源用尽。
用构造器确保初始化
在Java中,通过提供构造器,类的设计者可确保每个对象都会得到初始化。创建对象时,如果其类具有构造器,Java就会在用户有能力操作对象之前自动调用相应的构造器,从而保证了初始化的进行。
现在创建对象时:new Rock();
将会为对象分配存储空间,并调用相应构造器。这就确保了在你能操作对象之前,它已经被恰当地初始化了。
不接受任何参数的构造器叫做默认构造器,Java文档中的术语是无参构造器,但是默认构造器在Java出现之前已经使用许多年了,所以我仍旧倾向于使用它。但是和其他方法一样,构造器也能带有形式参数,以便指定如何创建对象。
默认构造器
1 public class NoSynthesis { 2 3 public static void main(String[] args) { 4 // TODO Auto-generated method stub 5 Bird2 b1 = new Bird2(1); 6 Bird2 b2 = new Bird2(2.8); 7 Bird2 b1 = new Bird2();//no default 8 } 9 } 10 class Bird2{ 11 Bird2 (int i) {} 12 Bird2 (double d) {} 13 }
要是你这样写:
new Bird2()
编译器就会报错:没有找到匹配的构造器。这就好比,要是你没有提供任何构造器,编译器会认为“你需要一个构造器,让我给你制造一个吧”;但假如你已经写了一个构造器,编译器则会认为“啊,你已经写了一个构造器,所以你知道你在做什么;你是可以省略了默认构造器”。
this关键字
1 public class Leaf { 2 int i = 0; 3 Leaf increment (){ 4 i++; 5 return this; 6 } 7 void print(){ 8 System.out.println("i="+i); 9 } 10 public static void main(String[] args) { 11 // TODO Auto-generated method stub 12 Leaf x = new Leaf(); 13 x.increment().increment().increment().print(); 14 } 15 // i=3 16 17 }
由于increment()通过this关键字返回了对当前对象的引用,所以很容易在一条语句里对同一个对象执行多次操作。
this关键字对于当前对象传递给其他方法也很有用。
1 public class PassingThis { 2 public static void main(String[] args) { 3 // TODO Auto-generated method stub 4 new Person().eat(new Apple()); 5 } 6 } 7 class Person { 8 public void eat(Apple apple){ 9 Apple peeled = apple.getPeeled(); 10 System.out.println("Yummy"); 11 } 12 } 13 class Peeler{ 14 static Apple peel (Apple apple){ 15 return apple; 16 } 17 } 18 class Apple { 19 Apple getPeeled(){ 20 return Peeler.peel(this); 21 } 22 } 23 //Yummy
Apple需要调用Peeler.peel()方法,他是一个外部的工具方法,将执行由于某种原因而必须放在apple外部的操作(也许是因为该外部方法要应用于许多不同的类,而你却不想重复这些代码)。为了将自身传递给外部方法,apple必须使用this关键字。
static
static方法是没有this的方法。在static方法的内部不能调用非静态方法,反过来倒是可以。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static的用途,它很像全局方法。Java中禁止使用全局方法,但你在类中置于static方法就可以访问其他static方法和static域。
清理 终结处理和垃圾回收
Java有垃圾回收器负责回收无用对象占据的内存资源。但也有特殊情况,假定你的对象并非使用new获得一块特殊的内存区域,由于垃圾回收器只知道回收那些使用new分配的内存,所以它不知道该如何释放对象这块“特殊”的内存。为了应对这种情况,Java允许在类中定义一个名为finalize()方法。它的工作原理“假定”是这样的,一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用finalize(),并且下一次垃圾回收动作发生时,才能真正释放内存。
垃圾回收本身就有内存开销
垃圾回收期如何工作
垃圾回收器对于提高对象的创建速度,却具有明显的效果。听起来很奇怪,存储空间的释放竟然会影响存储空间的分配,但这确实是某些Java虚拟机的工作方式。
当它工作时,将一面回收空间,一面使堆中的对象紧凑排列,这样堆指针就可以很容易移动到更靠近传送带的开始处,也就尽量避免了页面错误。通过垃圾回收器对对象的重新排列,实现了一种高速的,有无限空间可供分配的堆模型。
静态数据的初始化
无论创建多少个对象,静态数据都只占用一份存储区域。static关键字不能用于局部变量,因此它只能作用于域。如果一个域是静态的基本类型域,且也没有对它进行初始化,那么它就会获得基本类型的标准初值;如果它是一个对象的引用,那么它的默认初始化值就是null。

1 public class StaticInitialization { 2 3 public static void main(String[] args) { 4 // TODO Auto-generated method stub 5 System.out.println("create new Cupboard () in main"); 6 new Cupboard (); 7 System.out.println("create new Cupboard () in main"); 8 new Cupboard (); 9 table.f2(1); 10 cupboard.f3(1); 11 12 } 13 static Table table = new Table (); 14 static Cupboard cupboard = new Cupboard (); 15 16 } 17 class Bowl { 18 Bowl (int marker) { 19 System.out.println("Bowl("+marker+")"); 20 } 21 void f1 (int marker) { 22 System.out.println("f1("+marker+")"); 23 } 24 } 25 class Table { 26 static Bowl bowl1 = new Bowl(1); 27 Table () { 28 System.out.println("Table ()"); 29 bowl2.f1(1); 30 } 31 void f2 (int marker) { 32 System.out.println("f2("+marker+")"); 33 } 34 static Bowl bowl2 = new Bowl(2); 35 } 36 class Cupboard { 37 Bowl bowl3 = new Bowl(3); 38 static Bowl bowl4 = new Bowl(4); 39 Cupboard () { 40 System.out.println("Cupboard ()"); 41 bowl4.f1(2); 42 } 43 void f3 (int marker) { 44 System.out.println("f3("+marker+")"); 45 } 46 static Bowl bowl5 = new Bowl(5); 47 } 48 49 50 /* 51 * Bowl(1) 52 * Bowl(2) 53 * Table () 54 * f1(1) 55 * Bowl(4) 56 * Bowl(5) 57 * Bowl(3) 58 * Cupboard () 59 * f1(2) 60 * create new Cupboard () in main 61 * Bowl(3) 62 * Cupboard () 63 * f1(2) 64 * create new Cupboard () in main 65 * Bowl(3) 66 * Cupboard () 67 * f1(2) 68 * f2(1) 69 * f3(1) 70 * */ 71 /*1、普通变量初始化优先于静态变量初始化*/ 72 /*2、静态变量初始化只会被执行一次*/
显式的静态初始化
1 public class ExplicitStatic { 2 3 public static void main(String[] args) { 4 // TODO Auto-generated method stub 5 System.out.println("inside main()"); 6 Cups.cup1.f(99); 7 } 8 9 } 10 class Cup { 11 Cup(int marker){ 12 System.out.println("Cup("+marker+")"); 13 } 14 void f (int marker){ 15 System.out.println("f("+marker+")"); 16 } 17 } 18 class Cups { 19 static Cup cup1; 20 static Cup cup2; 21 static { 22 cup1 = new Cup(1); 23 cup2 = new Cup(2); 24 } 25 Cups(){ 26 System.out.println("Cups()"); 27 } 28 } 29 //inside main() 30 //Cup(1) 31 //Cup(2) 32 //f(99)
数组初始化
int [] a1 = {1,2,3,4,5}
那么,为什么还要在没有数组的时候定义一个数组引用呢?
int [] a2
在Java中可以将一个数组赋值给另一个数组,所以可以这样:
a2 = a1
其实真正做的只是复制了一个引用
1 public class ArraysOfPrimitives { 2 3 public static void main(String[] args) { 4 // TODO Auto-generated method stub 5 int [] a1 = {1,2,3,4,5}; 6 int [] a2 ; 7 a2 = a1; 8 for (int i = 0; i < a2.length; i++) { 9 a2 [i] +=1; 10 } 11 for (int i = 0; i < a1.length; i++) { 12 System.out.println("a1["+i+"]="+a1[i]); 13 } 14 } 15 /** 16 * a1[0]=2 17 * a1[1]=3 18 * a1[2]=4 19 * a1[3]=5 20 * a1[4]=6 21 */ 22 }
可变参数列表
Object... args
String... args
Character... args
int... args
...
...
可传多个参数,也可不传参数
1 public class OptionalTrailingArguments { 2 3 public static void main(String[] args) { 4 // TODO Auto-generated method stub 5 //f(1,"one"); 6 //f(1,"one","two"); 7 //f(0); 8 g(0); 9 g(1,"one"); 10 f(2,"one","two"); 11 } 12 static void f (int required,String... trailing) { 13 System.out.println("required"+required+"\t"); 14 for (int i = 0; i < trailing.length; i++) { 15 System.out.println(trailing[i]+ ""); 16 } 17 System.out.println(); 18 } 19 static void g (Object... trailing){ 20 for (int i = 0; i < trailing.length; i++) { 21 System.out.println(trailing[i]+ ""); 22 } 23 } 24 25 } 26 //0 27 //1 28 //one 29 //required2 30 //one 31 //two
枚举类型
1 package com.hhl.initialization.enums; 2 public enum Spiciness { 3 NOT,MILD,MEDIUM,HOT,FLAMING 4 } 5 6 package com.hhl.initialization.enums; 7 public class SimpleEnumUse { 8 9 public static void main(String[] args) { 10 // TODO Auto-generated method stub 11 Spiciness s = Spiciness.HOT; 12 System.out.println(s);//HOT 13 } 14 15 }
ordinal()方法可返回某个特定enum常量的声明顺序,以及static values()方法返回由这些常量值构成的数组:
1 public class EnumOrder { 2 3 public static void main(String[] args) { 4 // TODO Auto-generated method stub 5 for (Spiciness s : Spiciness.values()) { 6 System.out.println(s+",ordinal:"+s.ordinal()); 7 } 8 } 9 // NOT,ordinal:0 10 // MILD,ordinal:1 11 // MEDIUM,ordinal:2 12 // HOT,ordinal:3 13 // FLAMING,ordinal:4 14 15 }
enum有一个特别使用的特性,即它可以和switch语句有完美的配合:
public class Burrito { Spiciness s; public static void main(String[] args) { // TODO Auto-generated method stub Burrito plain = new Burrito(Spiciness.HOT), greenChile = new Burrito(Spiciness.MEDIUM), jalapeno = new Burrito(Spiciness.HOT); plain.describe (); greenChile.describe (); jalapeno.describe (); } public Burrito (Spiciness s) { this.s = s; } void describe () { System.out.print("this is burrito"); switch (s) { 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"); } } // this is burrito maybe too hot // this is burrito a little hot // this is burrito maybe too hot }