Java编程思想 初始化与清理

初始化与清理

1.构造器
  • 名称:

构造器的名称必须与类名完全相同,所以“每个方法首字母小写”并不适用于构造器。

  • 返回值

构造器是没有返回值,这与返回值为空(void)不同。对于空返回值,尽管方法本设计不会自动返回,但是仍可以选择让它返回别的东西。但是构造器不会返回任何东西。

  • 方法重载

为实现多种方式创建一个对象,就要有多个同名的构造器。构造器是强制重载方法名的一个原因。

基本类型的重载涉及到窄化转换和扩展转换。

区分重载方法:每个重载的方法都必须有一个独一无二的参数列表。不能以返回值的类型区分。

  • 默认构造器

默认构造器是没有形式参数的。如果已经定义了一个构造器,编译器就不会帮忙自动创建默认构造器。

2.this关键字

-在方法内部获得对当前对象的调用

在一条语句里对一个对象执行多次操作

public class leaf {
    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();
        //this传递x到下一个方法中,使得x.increment()连续调用三次
    }
}//输出为i = 3

将自身传递给外部方法

class Peeler {
    static Apple peel(Apple apple){
        return apple;
    }
}
class Apple {
    Apple getPeeled() {
        return Peeler.peel(this);
    }
}
  • 在构造器中调用构造器、防止数据成员与参数混淆

只能用this调用一个构造器,必须将构造器置于最起始处。

public class Flower{
    int petalCount = 0;
    String s = "initial value";
    Flower(int petals) {
        petalCount = petals;
    }
    Flower(String ss) {
        s = ss;
    }
    Flower(String s, int petals){
        this(petals);//调用第一个构造器,不能再放第二个,放在最起始处
        this.s = s;//用this.s访问数据成员,避免与参数混淆
    } 
}
3.垃圾回收机制
  • finalize()方法

工作原理:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。

扫描二维码关注公众号,回复: 2009025 查看本文章

无论对象时如何创建的,垃圾回收器都会负责释放对象占据的所有内存,所以对finalize()的需求限制到,通过某种创建对象以外的方式为对象分配了存储空间。不过这种情况一般发生在使用“本地方法”的情况下,本地方法是一种在Java中调用非Java代码的方式。

终结条件:可以用finalize()发现一些隐藏的缺陷。System.gc()用于强制进行终结动作,不一定真的可以触发finalize()finalize()用于验证终结条件。

  • 无论是垃圾回收还是finalize()方法都是不可控的
  • 垃圾回收器的工作方式

引用计数:每个对象都含有一个引用计数器,当有引用连接至对象时,计数器加一;当引用离开作用域或者被置为NULL时,计数器减一。当发现某个对象的引用计数为0时,就释放其空间。这种方法无法回收存在循环引用的对象。

停止复制:对任何活的对象,都一定能最终追溯到存活在堆栈或静态存储区之中的引用。具体做法是,先暂停程序的运行,将所有存活的对象从当前堆移到另一个堆,没有被复制的都是垃圾。另外所有指向对象的引用必须要修正。(比较浪费内存,不适用于当程序处于稳定状态,只有少量垃圾的情况)

标记清扫:同样从堆栈和静态存储区出发,遍历所有的引用,找出所有存活的对象。但是它会对所有存活的对象进行标记。只有在全部标记工作完成后,清理动作才会开始。没有标记的对象将被释放,剩下的堆空间将是不连续的。垃圾回收期可能需要重新整理剩下的对象以得到连续的空间。

Java虚拟机中,内存以较大的块为单位。每个块由相应的代数(generation count)来记录是否存活。在垃圾回收器进行整理过程中,大型对象不会被复制,只是代数会改变;内含小型对象的块则被复制和整理。

Java虚拟机会跟踪标记清扫的效果,即堆断裂的情况,决定何时切换回停止复制。

4.初始化

类的数据成员中基本类型都会被初始化。局部变量(定义在方法内部的变量)通过编译时出错保证变量初始化。

可以在定义类成员变量的地方为其赋值。(C++不允许)

可以由默认构造器进行初始化,也可以自己定义构造器使其初始化。

也可以通过调用某个方法提供初始值。

public class MethodInit2 {
    int i = f();
    int j = f(i);//上下两句不能互换位置,必须i先被初始化才能给j赋值
    int f() { return 11; }
    int g(int n) { return n * 10; }
}
  • 初始化顺序:
static关键字先初始化,只初始化一次。再是变量初始化。最后调用方法或者构造器。
class Bowl {
  Bowl(int marker) {
    System.out.println("Bowl(" + marker + ")");
  }
  void f(int marker) {
    System.out.println("f(" + marker + ")");
  }
}

class Table {
  static Bowl b1 = new Bowl(1);//第一步,仅一次
  Table() {
    System.out.println("Table()");
    b2.f(1);
  }
  void f2(int marker) {
    System.out.println("f2(" + marker + ")");
  }
  static Bowl b2 = new Bowl(2);//第二步,仅一次
}

class Cupboard {
  Bowl b3 = new Bowl(3);//第三步
  static Bowl b4 = new Bowl(4);//第一步,仅一次
  Cupboard() {
    System.out.println("Cupboard()");
    b4.f(2);
  }
  void f3(int marker) {
    System.out.println("f3(" + marker + ")");
  }
  static Bowl b5 = new Bowl(5);//第二步,仅一次
}

public class StaticInitialization {
  public static void main(String[] args) {
    System.out.println(//三及以后
      "Creating new Cupboard() in main");
    new Cupboard();
    System.out.println(
      "Creating new Cupboard() in main");
    new Cupboard();
    t2.f2(1);
    t3.f3(1);
  }
  static Table t2 = new Table();//第一步
  static Cupboard t3 = new Cupboard();//第二步
} 

输出结果:

Bowl(1)//接口中第一个static,Table()第一次构造
Bowl(2)
Table()
f1(1)
Bowl(4)//接口中第二个static,Cupboard()第一次构造
Bowl(5)
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)//Cupboard()第二次构造
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)//Cupboard()第三次构造
Cupboard()
f1(2)
f2(1)
f3(1)
  • 对象创建过程:

假设有个名为Dog()的类。首次创建Dog()的对象时(构造器可看作静态方法),或者Dog类的静态方法/静态域首次被访问时,Java解释器必须查找类的路径,定位Dog.class文件。

载入Dog.class,完成静态初始化。静态初始化只在Class对象首次加载时进行一次。

new Dog()创建对象时,首先在堆上为Dog对象分配足够的存储空间。

存储空间将被清零,Dog数据成员中所有基本数据类型都设置成默认值,引用被设成NULL。

执行所有出现于字段定义处的初始化动作。

执行构造器。

5.静态数据初始化和非静态实例初始化
  • 静态数据初始化
  static {
    c1 = new Cup(1);
    c2 = new Cup(2);
  }
  • 静态实例初始化
  {
    c1 = new Mug(1);
    c2 = new Mug(2);
    System.out.println("c1 & c2 initialized");
  }
  • 初始化顺序

静态数据、静态数据块按照在类中位置的先后顺序依次进行初始化,未创建对象也会初始化,而且只初始化一次。只有在创建对象后,(在静态数据初始化后)才进行实例块初始化,最后再进行构造器初始化。

6.数组初始化
  • 基本类型初始化
int[] a2;
int a1[] = { 1, 2, 3, 4, 5 };//直接初始化
a2 = a1;//只是复制了引用
int[] a = new int[rand.nextInt(20)];
//用new创建基本类型数组,数组的大小小于20随机决定,数据元素中基本数据类型值会自动初始化为空值
  • 数组的打印方法
import java.util.*;
System.out.println(Arrays.toString(a));//提供a的打印方法
  • 非基本类型初始化
Integer[] a = new Integer[rand.nextInt(20)];//目前只是一个空引用,还没有指向对象
for(int i = 0; i < a.length; i++)
    a[i] = rand.nextInt(69);//直到创建了对象,并将对象赋值给了引用,初始化才算结束

或者 数组的聚集初始化

Integer[] a = {
    new Integer(1),
    new Integer(2),
    3,//该,可有可没有
}
public class VarArgs {
  static void f(Object[] x) {
    for(int i = 0; i < x.length; i++)
      System.out.println(x[i]);
  }
  public static void main(String[] args) {
    f(new Object[] { 
        new Integer(47), new VarArgs(), 
        new Float(3.14), new Double(11.11) });//直接在传输时定义了数组
    f(new Object[] {"one", "two", "three" });
    f(new Object[] {new A(), new A(), new A()});
  }
} 

猜你喜欢

转载自blog.csdn.net/ljfyyj/article/details/80233479