Java基础

Java编程思想笔记5.初始化与清理

2018-11-09  本文已影响0人  卢卡斯哔哔哔

点击进入我的博客

初始化和清理是编程安全的两个问题。Java延续了C++中的构造器确保初始化,并引入了“垃圾回收器”管理和释放内存。

5.1 用构造方法确保初始化

5.2 方法重载

基本类型的重载
    public static void print(char c) {
        System.out.println("char: " + c);
    }

    public static void print(byte b) {
        System.out.println("byte: " + b);
    }

    public static void print(short s) {
        System.out.println("short: " + s);
    }

    public static void print(int i) {
        System.out.println("int: " + i);
    }

    public static void print(long l) {
        System.out.println("long: " + l);
    }

    public static void print(float f) {
        System.out.println("float: " + f);
    }

    public static void print(double d) {
        System.out.println("double: " + d);
    }
返回值不同无法区分两个方法
void f() {};
boolean f() {
    return true;
};
// 只调用f()无法区分是哪个方法

5.3 默认构造器

5.4 this关键字

下述代码中,有两个对象a1a2,按照面向过程的函数形式,在执行func()函数的时候,怎么知道是被a1a2调用呢?为了能用面向对象的语法来编写代码,编译器做了一些幕后工作。它暗自把“所操作的对象”作为第一个参数传递给func()函数,即func(a1)。这是内部的表示形式,我们并不能这样写代码。

public class Test {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new A();
        a1.func();
        a2.func();
    }
}

class A {
    void func() {}
}

this关键字只能在方法内部使用,表示对“调用方法的那个对象的引用。”

5.4.1 在构造器中调用构造器

    public Test(int i) {
        System.out.println("Test " + i);
    }

    public Test(String str) {
        System.out.println("Test " + str);
    }

    // (1)
    public Test() {
        this(1);
        // this("imbug");
        System.out.println("Test");
    }

    public static void main(String[] args) {
        Test test = new Test();
    }

5.4.2 static方法

5.5 清理:终结处理和垃圾回收

5.5.1 finalize()方法

  1. 一旦GC准备释放对象的存储空间,首先调用该方法;并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。即调用该方法但时候,对象还没有被回收。
  2. finalize()方法不是C++中的析够方法
  3. 在C++中对象一定会被销毁(代码无Bug),但是在Java里的对象并非总是被垃圾回收。
  4. 垃圾回收只与内存相关,也就是说使用GC的唯一原因是为了回收程序不再使用的内存。
  5. 上述讨论了,对象可能会获得一块“特殊”的内存区域,这主要发生在JNI本地方法的情况下,本地方法是在Java中使用非Java代码的方式。非Java代码可能会调用C的malloc()来分配存储空间,而且除了free()方法否则其存储空间将得不到释放,从而造成内存泄漏。此时就可以在finalize()中调用free()方法,清理本地对象。
  6. 不建议用finalize方法完成“非内存资源”的清理工作,但也可以作为确保某些非内存资源(如Socket、文件等)释放的一个补充。
  7. System.gc()与System.runFinalization()方法增加了finalize方法执行的机会,但不保证一定会执行。
  8. 用户可以手动调用对象的finalize方法,但并不影响GC对finalize的行为,即没有卵用~
  9. finalize()执行流程

5.5.2 你必须实施清理

5.5.3 终结条件

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        // if(文件未安全关闭)
        System.out.println("error");
    }

    public static void main(String[] args) {
        func();
        System.gc();
    }

    public static void func() {
        Test t1 = new Test();
        Test t2 = new Test();
    }

5.5.4 GC如何工作

更详细内容请看JVM工作原理!!!

引用计数
根搜索算法
停止-复制算法
标记-清扫算法
分代算法
自适应、分代的、停止复制、标记清扫方式

JVM会进行监视,如果所有对象都很稳定,垃圾回收器的效率降低的话,就切换到标记-清扫模式;同样,JVM会跟踪标记-清扫的效果,要是堆空间出现很多碎片,就会切换回停止-复制模式。

其他附加技术

即使编译器(Just-In-Time JIT):可以把程序全部或部分翻译成机器码来提高运行速度。当需要装载某个类时,编译器会先找到其.class文件,然后将该类的字节码装入内存。此时,有两种方案可供选择:

5.6 成员初始化

Java尽量保证:所有变量使用前一定会初始化
局部变量:不会自动初始化,而是编译错误
类成员变量:类的每个基本类型数据成员都保证会有初始值;引用类型为null

指定初始化
  1. 定义类成员变量的时候给它赋值——(1)
  2. 通过调用某个方法来提供初值——(2)
  3. 注意:(2)、(3)不能颠倒顺序,因为存在向前引用。
  4. 缺点:这种方式所有成员有相同的属性
public class Test {
    // (1)
    int a = 10;
    // (2)
    int i = f();
    // (3)
    int j = g(i);
    int g(int n) {
        return n;
    }
    int f() {
        return 1;
    }

    public static void main(String[] args) {
        Test t = new Test();
    }
}

5.7 构造器初始化

无法阻止自动初始化的进行,它发生在构造器被调用之前!

5.7.1 初始化顺序

遍历定义的先后顺序决定了初始化的顺序。

5.7.2 静态数据的初始化

对象创建过程
  1. 在调用该类的静态方法或者首次new对象(构造器其实也是静态方法)的时候,Java解释器查找类路径定位到该类的.class文件。
  2. 载入该.class文件,静态数据进行初始化,执行静态代码块。
  3. new对象创建对象的时候,首先在堆内存中为此对象分配足够的内存空间。
  4. 把此存储空间清零,即所有非静态基本数据类型置为0,对象类型置为null
  5. 执行非静态数据初始化动作。
  6. 执行构造器。

5.7.3 显式的静态初始化

5.7.4 非静态实例初始化

    // (1)
    {
        System.out.println(1);
    }
    // (2)    
    int i = func();
    int func() {
        System.out.println(2);
        return 2;
    }
    // (3)
    Test() {
        System.out.println(3);
    }

    public static void main(String[] args) {
        new Test(); // output 123
    }

5.8 数组初始化

5.8.1 可变参数列表

void func(String... args) {}

数组的class
        System.out.println(new int[0].getClass()); // class [I
        System.out.println(new Integer[0].getClass()); // class [Ljava.lang.Integer;
        System.out.println(new long[0].getClass()); // class [J
        System.out.println(new double[0].getClass()); // class [D
        System.out.println(new int[0][0].getClass()); // class [[I
        System.out.println(new int[0][0][0].getClass()); // class [[[I
        System.out.println(new String[0].getClass()); // class [Ljava.lang.String;
        System.out.println(new String[0][0].getClass()); // class [[Ljava.lang.String;
可变类型引起的重载问题

此段代码编译失败,因为编译器发现有多个方法可以调用。

    public static void main(String[] args) {
        func(1, 'a');
        func('a', 'b');
    }

    static void func(int i, Character... args) {
        System.out.println("first");
    }

    static void func(Character... args) {
        System.out.println("second");
    }

5.9 枚举类型

public class Test {
    public static void main(String[] args) {
        Color green = Color.GREEN;
        Color red = Color.RED;
        System.out.println(green + "   " + green.ordinal());
        System.out.println(red + "   " + red.ordinal());
    }
}

enum Color {
    RED,
    GREEN;
}
反编译后的Color类
// final class 禁止继承
final class Color extends Enum
{

    public static Color[] values()
    {
        return (Color[])$VALUES.clone();
    }

    public static Color valueOf(String name)
    {
        return (Color)Enum.valueOf(s2/Color, name);
    }
    // 私有构造方法,所以无法用new创建对象
    private Color(String s, int i)
    {
        super(s, i);
    }

    public static final Color RED;
    public static final Color GREEN;
    private static final Color $VALUES[];

    static 
    {
        RED = new Color("RED", 0);
        GREEN = new Color("GREEN", 1);
        $VALUES = (new Color[] {
            RED, GREEN
        });
    }
}
上一篇 下一篇

猜你喜欢

热点阅读