OCA/OCP Java Note (3): Java Building Blocks (3)

8. Understanding Variable Scope

  在方法中定义的变量称为本地变量,本地变量包括方法的参数。本地变量的作用域仅限于方法内部,并可以在方法内部有更小的作用域。如下面的例子:

public void eatIfHungry(boolean hungry) {
    if (hungry) {
        int bitesOfCheese = 1;
    } // bitesOfCheese goes out of scope here
    System.out.println(bitesOfCheese);// DOES NOT COMPILE
}

其中的参数hungry  的作用域在整个方法,而bitesOfCheese 的作用域则较小,由于它是在if内定义的,其作用域也仅限于if内部。每当在代码中遇见一对大括号{ } ,就意味着进入了一个新的代码块(code block),每个代码块都有其自己的作用域。上面的例子中,尝试在if外面打印bitesOfCheese,已超过了bitesOfCheese 的作用域,编译器给出“bitesOfCheese cannot be resolved to a variable”错误。

  各种变量的作用域总结如下:

  • 本地变量——从定义的位置开始,到代码块结束的位置为止
  • 实例变量——从定义的位置开始,到对象被垃圾回收为止
  • 类变量——从定义的位置开始,到程序结束为止

9. Ordering Elements in a Class

  除了注释可以出现在代码中的任何位置,类中其他元素需要遵守表9-1的规则。

表9-1
Element Example Required? Where does it go?
Package declaration package abc; No First line in the file
Import statements import java.util.*; No Immediately after the package
Class declaration public class C Yes Immediately after the import
Field declarations int value; No Anywhere inside a class
Method declarations void method() No Anywhere inside a class

  下面是一个正确的例子,包含了表9-1中的各个元素:

package structure; // package must be first non-comment 
import java.util.*; // import must come after package 
public class Meerkat { // then comes the class
    double weight; // fields and methods can go in either order 
    public double getWeight() {
        return weight; 
    }
    double height; // another field – they don't need to be together
}

  违反表9-1中的规则会导致编译错误:
import java.util.*;
package structure; // DOES NOT COMPILE 
String name; // DOES NOT COMPILE
public class Meerkat { }

  另外值得一提的是,在一个源代码文件中,允许有多个类,但只能有一个类是public的,且这个public类的名称要与源代码文件名一致。一个源代码文件中也可以没有public类。

10. Destroying Objects

  Java提供了垃圾回收(Garbage Collection)机制,用于回收不再使用的对象。Java的所有对象都存储在内存的堆(heap)中,堆是分配给Java应用程序的一块未用空间,这块空间通常很大,但并不是无限的。如果不停地在堆上实例化对象,最终会导致内存不足。

10.1. Garbage Collection

  垃圾回收指的是通过删除程序无法访问的对象,来自动释放堆上的内存的进程。垃圾回收有很多不同的算法,考试中不会涉及,但需要了解的是,System.gc() 不保证一定会运行,并能够指出一个对象在什么时候可以被垃圾回收。

  Java提供了System.gc() 方法,从名称上看,它似乎会让Java进行垃圾回事,但实际上,这个方法仅仅是建议现在可以进行垃圾回收,Java可以选择忽视这个建议。

  一个对象所占用的内存可以被回收的条件是,这个对象不能再被获取,即以下两种情况:

  • 没有引用指向这个对象
  • 这个对象的所有引用都已经超出了作用域

  以下面的代码段为例:

public class Scope {
    public static void main(String[] args) {
        String one, two;
        one = new String("a"); 
        two = new String("b"); 
        one = two;
        String three = one; 
        one = null;
    }
}

在第4和第5行,引用one 和two 非别指向了String对象“a”和“b”,如图10-1所示:

图1

图10-1

在第6行,one 指向了two ,此时没有引用指向String对象“a”,String对象“a”不能再被程序获取,成为了可被垃圾回收的对象。在第7行通过one 将赋给three ,使得three 也指向了String对象“b”,如图10-2所示:

2

图10-2

在第8行,one 被赋值为null,但由于还可以通过two 和three 来获取到String对象“b”,所以String对象“b”还不会被垃圾回收。

10.2. finalize()

  Java允许对象实现一个名为finalize() 的方法,这个方法会在垃圾回收器尝试回收对象的时候被调用。如果没有进行垃圾回收,这个方法就不会被调用;如果垃圾回收失败,在下一次进行垃圾回收时,这个方法就不会被再次调用。也就是说,一个对象的finalize() 方法最多只会被调用1次,如果一次回收成功,finalize() 执行一次后,对象不再存在,对象的finalize() 方法当然也不会被再次调用;如果第一次回收失败,finalize() 只会在第一次回收时执行一次,之后的回收不会再调用finalize() 。

  下面的程序不会有任何输出,因为在程序结束的的时候,没有进行垃圾回收的必要:

public class Finalizer { 
    protected void finalize() {
        System.out.println("Calling finalize"); 
    }
    public static void main(String[] args) { 
        Finalizer f = new Finalizer();
    }
}

  finalize() 方法只有在对象被垃圾回收的时候才会被调用,如10.1中所述,对象被垃圾回收的条件是,没有引用指向这个对象,或者这个对象的所有引用都已经超出了作用域。下面的例子在finalize() 中重新添加了对对象的引用,这使得这个对象不再适合被垃圾回收,Java注意到此情况后,会终止回收该对象。如果在之后,objects  被设为null,这个对象再次被来及回收,则此时对象的finalize() 就不再会被调用,对象将被成功回收。
public class Finalizer {
    private static List objects = new ArrayList(); 
    protected void finalize() {
        objects.add(this); // Don't do this 
    }
}

11. Benefits of Java

  考试需要了解Java的一些优势:

  • 面向对象:Java是一门面向对象的语言,所有代码都定义在类中,大多数类都可以实例化为对象。
  • 封装:Java通过访问修饰符(access modifiers)来保护数据免于意外的访问和修改。
  • 平台独立:Java是解释性语言,只需要编译一次生成字节码(byte code),不需要对不同的操作系统进行再次编译,即“write once, run everywhere”。
  • 健壮:Java通过自动的垃圾回收机制来管理内存,避免内存泄露。
  • 简单:Java的设计意图是比C++简单,消灭了指针和操作符重载。
  • 安全:Java程序运行在JVM上,很难对计算机造成恶意伤害。