关于初始化和清理
1. this者谁?
// housekeeping/PassingThis.java class Person { public void eat(Apple apple) { Apple peeled = apple.getPeeled(); System.out.println("Yummy"); } } public class Peeler { static Apple peel(Apple apple) { // ... remove peel return apple; // Peeled } } public class Apple { Apple getPeeled() { return Peeler.peel(this); } } public class PassingThis { public static void main(String[] args) { new Person().eat(new Apple()); } }
this简单来讲即使谁调代表谁,上面的代码就是this的一种用法,将自身引用传递出去。
this的另一个常用地方就是this.super()等这种构造器中调用构造器,可以大量减少重复代码。
// housekeeping/Flower.java // Calling constructors with "this" public class Flower { int petalCount = 0; String s = "initial value"; Flower(int petals) { petalCount = petals; System.out.println("Constructor w/ int arg only, petalCount = " + petalCount); } Flower(String ss) { System.out.println("Constructor w/ string arg only, s = " + ss); s = ss; } Flower(String s, int petals) { this(petals); //- this(s); // Can't call two! this.s = s; // Another use of "this" System.out.println("String & int args"); } Flower() { this("hi", 47); System.out.println("no-arg constructor"); } void printPetalCount() { //- this(11); // Not inside constructor! System.out.println("petalCount = " + petalCount + " s = " + s); } public static void main(String[] args) { Flower x = new Flower(); x.printPetalCount(); } }
这里的构造调用构造有两点要求,1是必须在开头调用,2是只能调用一个构造器(否则它就违背了第一点)。
2. static者谁?
它是为类创建的不是为对象创建的,听起来很别扭,但事实如此,因为它是不需要对象的支撑来调用的,这点和必须要有对象支撑的this截然相反,所以static中不能有this。它有点全局的语义,但是java并没有全局,所以它也是饱受人质疑的一点,它是否破坏了面向对象这个概念,我个人觉着是有一些的。
3. 垃圾回收
Java是有自己GC的,finalize()方法一般是不建议主动用的,我们需要关注的点应该是对象的引用上,比如链表,我们用数组实现的话,remove掉一个node,我们需要将该node的指针置为null。下面有一个手动gc的例子。
// housekeeping/TerminationCondition.java // Using finalize() to detect a object that // hasn't been properly cleaned up import onjava.*; class Book { boolean checkedOut = false; Book(boolean checkOut) { checkedOut = checkOut; } void checkIn() { checkedOut = false; } @Override protected void finalize() throws Throwable { if (checkedOut) { System.out.println("Error: checked out"); } // Normally, you'll also do this: // super.finalize(); // Call the base-class version } } public class TerminationCondition { public static void main(String[] args) { Book novel = new Book(true); // Proper cleanup: novel.checkIn(); // Drop the reference, forget to clean up: new Book(true); // Force garbage collection & finalization: System.gc(); new Nap(1); // One second delay } }
4. 初始化,这块很基础了,就直接post代码了
// housekeeping/OrderOfInitialization.java // Demonstrates initialization order // When the constructor is called to create a // Window object, you'll see a message: class Window { Window(int marker) { System.out.println("Window(" + marker + ")"); } } class House { Window w1 = new Window(1); // Before constructor House() { // Show that we're in the constructor: System.out.println("House()"); w3 = new Window(33); // Reinitialize w3 } Window w2 = new Window(2); // After constructor void f() { System.out.println("f()"); } Window w3 = new Window(3); // At end } public class OrderOfInitialization { public static void main(String[] args) { House h = new House(); h.f(); // Shows that construction is done } }
输出
Window(1) Window(2) Window(3) House() Window(33) f()
静态数据初始化
// housekeeping/StaticInitialization.java // Specifying initial values in a class definition class Bowl { Bowl(int marker) { System.out.println("Bowl(" + 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("main creating new Cupboard()"); new Cupboard(); System.out.println("main creating new Cupboard()"); new Cupboard(); table.f2(1); cupboard.f3(1); } static Table table = new Table(); static Cupboard cupboard = new Cupboard(); }
输出
Bowl(1) Bowl(2) Table() f1(1) Bowl(4) Bowl(5) Bowl(3) Cupboard() f1(2) main creating new Cupboard() Bowl(3) Cupboard() f1(2) main creating new Cupboard() Bowl(3) Cupboard() f1(2) f2(1) f3(1)
显示的静态初始化
// housekeeping/ExplicitStatic.java // Explicit static initialization with "static" clause 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); // [1] Cups.cup1.f(99); // [1] } // static Cups cups1 = new Cups(); // [2] // static Cups cups2 = new Cups(); // [2] }
输出
Inside main() Cup(1) Cup(2) f(99) f(80)
非静态的初始化
// housekeeping/Mugs.java // Instance initialization class Mug { Mug(int marker) { System.out.println("Mug(" + marker + ")"); } } public class Mugs { Mug mug1; Mug mug2; { // [1] 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
5. 对于可变参数的重载
// housekeeping/OverloadingVarargs2.java // {WillNotCompile} public class OverloadingVarargs2 { static void f(float i, Character... args) { System.out.println("first"); } static void f(Character... args) { System.out.println("second"); } public static void main(String[] args) { f(1, 'a'); f('a', 'b'); } }
这段代码是编译不过的,因为不知道该调用哪个,这也是重载的一个常犯的错误,不过好处是编译就会发现这个错误不会产生事故。
// housekeeping/OverloadingVarargs3 public class OverloadingVarargs3 { static void f(float i, Character... args) { System.out.println("first"); } static void f(char c, Character... args) { System.out.println("second"); } public static void main(String[] args) { f(1, 'a'); f('a', 'b'); } }
这样处理就可以了。
6. 枚举类型,其实枚举的用处和性能很强,应该多用,比如替代掉比较传统的常量接口,后者对代码太具有污染性了
// housekeeping/Spiciness.java public enum Spiciness { NOT, MILD, MEDIUM, HOT, FLAMING }
// housekeeping/SimpleEnumUse.java public class SimpleEnumUse { public static void main(String[] args) { Spiciness howHot = Spiciness.MEDIUM; System.out.println(howHot); } }
MEDIUM
// housekeeping/EnumOrder.java public class EnumOrder { public static void main(String[] args) { for (Spiciness s: Spiciness.values()) { System.out.println(s + ", ordinal " + s.ordinal()); } } } NOT, ordinal 0 MILD, ordinal 1 MEDIUM, ordinal 2 HOT, ordinal 3 FLAMING, ordinal 4
enum还与switch绝配
// housekeeping/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(); } }
一个没有高级趣味的人。
email:hushui502@gmail.com