import java.util.*; class Tree { int height; Tree() { prt("Planting a seedling"); height = 0; } Tree(int i) { prt("Creating new Tree that is " + i + " feet tall"); height = i; } void info() { prt("Tree is " + height + " feet tall"); } void info(String s) { prt(s + ": Tree is " + height + " feet tall"); } static void prt(String s) { System.out.println(s); } } public class Overloading { public static void main(String[] args) { for(int i = 0; i < 5; i++) { Tree t = new Tree(i); t.info(); t.info("overloaded method"); } // Overloaded constructor: new Tree(); } }
2. this的使用
/** 假定我们在一个方法的内部,并希望获得当前对象的句柄。由于那个句柄是由编译器“秘密”传递的,所以 没有标识符可用。然而,针对这一目的有个专用的关键字:this。this关键字(注意只能在方法内部使用) 可为已调用了其方法的那个对象生成相应的句柄。可象对待其他任何对象句柄一样对待这个句柄。但要注 意,假若准备从自己某个类的另一个方法内部调用一个类方法,就不必使用this。只需简单地调用那个方法 即可。当前的this句柄会自动应用于其他方法。所以我们能使用下面这样的代码: class Apricot { void pick() { /* ... */ } void pit() { pick(); /* ... */ } } 在pit()内部,我们可以说this.pick(),但事实上无此必要。编译器能帮我们自动完成。this关键字只能 用于那些特殊的类——需明确使用当前对象的句柄。例如,假若您希望将句柄返回给当前对象,那么它经常 在return语句中使用。 */ public class Leaf { private int i = 0; Leaf increment() { i++; return this; } void print() { System.out.println("i = " + i); } public static void main(String[] args) { Leaf x = new Leaf(); x.increment().increment().increment().print(); } } 结果为: 3
no1,在构建器里调用构建器
若为一个类写了多个构建器,那么经常都需要在一个构建器里调用另一个构建器,以避免写重复的代码。可
用this关键字做到这一点。
通常,当我们说this的时候,都是指“这个对象”或者“当前对象”。而且它本身会产生当前对象的一个句
柄。在一个构建器中,若为其赋予一个自变量列表,那么this关键字会具有不同的含义:它会对与那个自变
量列表相符的构建器进行明确的调用。这样一来,我们就可通过一条直接的途径来调用其他构建器。
no2,
尽管可用this调用一个构建器,
但不可调用两个。除此以外,构建器调用必须是我们做的第一件事情,否则会收到编译程序的报错信息。
这个例子也向大家展示了this的另一项用途。由于自变量s的名字以及成员数据s的名字是相同的,所以会
出现混淆。为解决这个问题,可用this.s来引用成员数据。经常都会在Java代码里看到这种形式的应用,
本书的大量地方也采用了这种做法。
在print()中,我们发现编译器不让我们从除了一个构建器之外的其他任何方法内部调用一个构建器。
3.static 含义
4.收尾和垃圾收集
对于
库来说,用完后简单地“释放”一个对象并非总是安全的。当然,Java可用垃圾收集器回收由不再使用的对
象占据的内存。现在考虑一种非常特殊且不多见的情况。假定我们的对象分配了一个“特殊”内存区域,没
有使用new。垃圾收集器只知道释放那些由new分配的内存,所以不知道如何释放对象的“特殊”内存。为
解决这个问题,Java提供了一个名为finalize()的方法,可为我们的类定义它。在理想情况下,它的工作原
理应该是这样的:一旦垃圾收集器准备好释放对象占用的存储空间,它首先调用finalize(),而且只有在下
一次垃圾收集过程中,才会真正回收对象的内存。所以如果使用finalize(),就可以在垃圾收集期间进行一
些重要的清除或清扫工作。
public class Garbage { public static void main(String[] args) { if(args.length == 0) { System.err.println("Usage: \n" + "java Garbage before\n or:\n" + "java Garbage after"); return; } while(!Chair.f) { new Chair(); new String("To take up space"); } System.out.println( "After all Chairs have been created:\n" + "total created = " + Chair.created + ", total finalized = " + Chair.finalized); if(args[0].equals("before")) { System.out.println("gc():"); System.gc(); System.out.println("runFinalization():"); System.runFinalization(); } System.out.println("bye!"); if(args[0].equals("after")) System.runFinalizersOnExit(true); } } ///:~
class Chair { static boolean gcrun = false; static boolean f = false; static int created = 0; static int finalized = 0; int i; Chair() { i = ++created; if(created == 47) System.out.println("Created 47"); } protected void finalize() { if(!gcrun) { gcrun = true; System.out.println( "Beginning to finalize after " + created + " Chairs have been created"); } if(i == 47) { System.out.println( "Finalizing Chair #47, " + "Setting flag to stop Chair creation"); f = true; } finalized++; if(finalized >= created) System.out.println( "All " + finalized + " finalized"); } }
/** 上面这个程序创建了许多Chair对象,而且在垃圾收集器开始运行后的某些时候,程序会停止创建Chair。 由于垃圾收集器可能在任何时间运行,所以我们不能准确知道它在何时启动。因此,程序用一个名为gcrun 的标记来指出垃圾收集器是否已经开始运行。利用第二个标记f,Chair可告诉main()它应停止对象的生 成。这两个标记都是在finalize()内部设置的,它调用于垃圾收集期间。 另两个static变量——created以及finalized——分别用于跟踪已创建的对象数量以及垃圾收集器已进行 完收尾工作的对象数量。最后,每个Chair都有它自己的(非static)int i,所以能跟踪了解它具体的编 号是多少。编号为47的Chair进行完收尾工作后,标记会设为true,最终结束Chair对象的创建过程。 所有这些都在main()的内部进行——在下面这个循环里: while(!Chair.f) { new Chair(); new String("To take up space"); } 大家可能会疑惑这个循环什么时候会停下来,因为内部没有任何改变Chair.f值的语句。然而,finalize() 进程会改变这个值,直至最终对编号47的对象进行收尾处理。 每次循环过程中创建的String对象只是属于额外的垃圾,用于吸引垃圾收集器——一旦垃圾收集器对可用内 存的容量感到“紧张不安”,就会开始关注它。 运行这个程序的时候,提供了一个命令行自变量“before”或者“after”。其中,“before”自变量会调用 System.gc()方法(强制执行垃圾收集器),同时还会调用System.runFinalization()方法,以便进行收尾工作。这些方法都可在Java 1.0中使用,但通过使用“after”自变量而调用的runFinalizersOnExit()方 法却只有Java 1.1及后续版本提供了对它的支持(注释③)。注意可在程序执行的任何时候调用这个方法, 而且收尾程序的执行与垃圾收集器是否运行是无关的。 ③:不幸的是,Java 1.0采用的垃圾收集器方案永远不能正确地调用finalize()。因此,finalize()方法 (特别是那些用于关闭文件的)事实上经常都不会得到调用。现在有些文章声称所有收尾模块都会在程序退 出的时候得到调用——即使到程序中止的时候,垃圾收集器仍未针对那些对象采取行动。这并不是真实的情 况,所以我们根本不能指望finalize()能为所有对象而调用。特别地,finalize()在Java 1.0里几乎毫无 用处。 前面的程序向我们揭示出:在Java 1.1中,收尾模块肯定会运行这一许诺已成为现实——但前提是我们明确 地强制它采取这一操作。若使用一个不是“before”或“after”的自变量(如“none”),那么两个收尾工 作都不会进行,而且我们会得到象下面这样的输出: Created 47 Beginning to finalize after 8694 Chairs have been created Finalizing Chair #47, Setting flag to stop Chair creation After all Chairs have been created: total created = 9834, total finalized = 108 bye! 因此,到程序结束的时候,并非所有收尾模块都会得到调用(注释④)。为强制进行收尾工作,可先调用 System.gc(),再调用System.runFinalization()。这样可清除到目前为止没有使用的所有对象。这样做一 个稍显奇怪的地方是在调用runFinalization()之前调用gc(),这看起来似乎与Sun公司的文档说明有些抵 触,它宣称首先运行收尾模块,再释放存储空间。然而,若在这里首先调用runFinalization(),再调用 gc(),收尾模块根本不会执行。 ④:到你读到本书时,有些Java虚拟机(JVM)可能已开始表现出不同的行为。 针对所有对象,Java 1.1有时之所以会默认为跳过收尾工作,是由于它认为这样做的开销太大。不管用哪种 方法强制进行垃圾收集,都可能注意到比没有额外收尾工作时较长的时间延迟。 */