ThinkingInJava 学习 之 0000004 初始化与清理

  1. 用构造器确保初始化。

    不接受任何参数的构造器叫做默认构造器。

            Tree tree = new Tree(12);        

    如果Tree(int)时Tree类的唯一的构造器,那么编译器将不会允许你以其他任何方式创建Tree对象。

  2. 方法重载

    方法名相同而形式参数不同

    1. 区分重载方法

      每个重载的方法都必须有一个独一无二的参数类型列表。

      1. 参数个数

      2. 参数类型

      3. 参数顺序

    public void println(int decimal){
        System.out.println("int decimal : " + decimal + " String str : " + "Oracle");
    }

    public void println(String str){
        System.out.println("int decimal : " + 12 + " String str : " + str);
    }

    public void println(int decimal,String str){
        System.out.println("int decimal : " + decimal + " String str : " + str);
    }
    public void println(String str,int decimal){
        System.out.println("int decimal : " + decimal + " String str : " + str);
    }

    2. 涉及基本类型的重载

      如果实参数据类型小于形参数据类型,实参数据类型就会被提升。char型实参会提升为int型。

      如果实参数据类型大于形参数据类型,通过类型转换执行窄化转换。

    3. 以返回值区分重载方法

      NO WAY

  3. 默认构造器

    如果类中无构造器,编译器会自动创建默认构造器。

  4. this关键字

    1. 在构造器中调用构造器

      在构造器中,如果为this添加了参数列表,将产生对符合此参数列表的某个构造器的明确调用。

    public Flower() {
//        System.out.println("this is this Constructor");//编译错误:必须将构造器调用置于最起始处
        this(1,"lo");
//        this(2,"ve");//编译错误:不能调用两个
    }

    public Flower(int flower, String flowerName) {
        this.flower = flower;
        this.flowerName = flowerName;
    }

    2. static 的含义

  5. 清理: 终结处理和垃圾回收

    Java 有垃圾回收器负责回收无用对象占据的内存资源。但也有特殊情况:假定对象(并非使用new)获得了一块“特殊”的内存区域,由于垃圾回收器只知道shifang那些经由new分配的内存,所以它不知道该如何释放该对象的这块“特殊”内存。

    为了应对这种情况,Java允许在类中定义一个名为finalize()的方法。

    工作原理“假定”:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。所以要是打算用finalize(),就能在垃圾回收时刻做一些重要的清理工作。

    ⊙ C++中的对象一定会使用析构函数销毁

    ⊙ Java中的对象并非总是被垃圾回收

    ⊙ 对象可能不被垃圾回收

    ⊙ 垃圾回收并不等于“析构”

    Class : Potato

package lime.thinkingInJava._005001.ins;

public class Potato {
    private static int idBuilder = 0;
    private int id;

    public Potato() {
        id = ++idBuilder;
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("call Finalize() And id = " + id);
        System.exit(0);
    }
}

    Class : Main

package lime.thinkingInJava._005001.ins;

public class _InsMain {
    public static void main(String[] args) {
        {
            for(int i = 0;i < Integer.MAX_VALUE;i++){
                new Potato();
            }
        }
    }
}

    Console : 

call Finalize() And id = 447009

    1. finalize()的用途

      finalize()方法不能作为通用的清理方法。

      ⊙ 垃圾回收只与内存有关

      使用垃圾回收器的唯一原因是为了回收程序不再使用的内存。所以对于与垃圾回收有关的任何行为来说(尤其时finalize()方法),它们必须同内存及其回收有关。

      但这是否以为着要是对象中含有其他对象,finalize()就应该明确释放那些对象呢?不,无论对象时如何创建的,垃圾回收器都会负责释放对象占据的所有内存。这就将对finalize()的需求限制到一种特殊情况,即通过某种创建对象方式以外的方式为对象分配了存储空间。不过,Java中一切皆为对象,那这种特殊情况时怎么回事呢?

      之所以要有finalize(),是由于在分配内存时可能采用了类似C语言中的做法,而非Java中的通常做法。这种情况主要发生在使用“本地方法”的情况下,本地方法是一种在Java中调用非Java代码的方式。

      本地方法目前只支持C和C++,但它们可以调用其他语言写的代码,所以实际上可以调用任何代码。

      在非Java代码中,也许会调用C的malloc()函数鞋类来分配存储空间,而且除非调用了free()函数,否则存储空间将得不到释放,从而造成内存泄露。当然,free()时C和C++中的函数,所以需要在finalize()中用本地方法调用它。

      至此,已经明白了不要过多地使用finalize()的道理了。它确实不是进行普通的清理工作的合适场所。那么,普通的清理工作应该在哪里执行呢?

    2. 你必须实施清理

    3. 终结条件

    4. 垃圾回收器如何工作

  6. 成员初始化

    对于方法的局部变量,Java 以编译时错误的形式来提示程序员进行变量的初始化。

    对于类变量,Java 会默认初始化值。基本类型数据成员默认初始值,对象引用默认初始化为null。

    1. 指定初始化

      Class : InitialValues

package lime.thinkingInJava._005001.exercise;

import java.text.SimpleDateFormat;
import java.util.Date;

public class InitialValues {
    //    基本数据类型 默认初始化
    int genderCode;
    //    对象引用 默认初始化
    Date date;
    //    基本数据类型指定初始化
    boolean bool = true;
    char ch = 'x';
    byte b = 47;
    short s = 0xff;
    int i = 999;
    long lng = 1;
    float f = 3.14f;
    double d = 3.14159;
    //    对象引用指定初始化
    Depth depth = new Depth();
    //    调用方法(无参)
    String birthday = getBirthdayDate();
    //    调用方法(有参:须初始化)
    String gender = getSelfGender(genderCode);

    private String getSelfGender(int genderCode) {
        switch (genderCode) {
            case 0:
                return "男";
            case 1:
                return "女";
            default:
                return "ladyboy";
        }
    }

    public String getBirthdayDate() {
        if (null == date) {
            return "1992-03-01";
        } else {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
        }
    }

    @Override
    public String toString() {
        return "InitialValues{" + "\n" +
                "genderCode=" + genderCode +
                ", date=" + date + "\n" +
                ", bool=" + bool +
                ", ch=" + ch +
                ", b=" + b +
                ", s=" + s +
                ", i=" + i +
                ", lng=" + lng +
                ", f=" + f +
                ", d=" + d + "\n" +
                ", depth=" + depth + "\n" +
                ", birthday='" + birthday + '\'' + "\n" +
                ", gender='" + gender + '\'' + "\n" +
                '}';
    }
}    

  7. 构造器初始化

    在运行时刻,可以方法或执行某些动作来确定初值,但是却无法阻止自动初始化的进行,自动初始化将在构造器被调用之前发生。

package lime.thinkingInJava._005001.exercise;

public class Counter {
//    自动初始化发生在构造函数调用之前
    int i;
    public Counter() {
        System.out.println("i = " + i);
        i = 131;
    }

    @Override
    public String toString() {
        return "Counter{" +
                "i=" + i +
                '}';
    }
}

    1. 初始化顺序

      在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。

package lime.thinkingInJava._005001.exercise;
class Window{
    public Window(int marker) {
        System.out.println("Window(" + marker + ")");
    }
}
public class House {
//    构造器之前 定义变量
    Window w1 = new Window(1);

    public House() {
        System.out.println("House()");
        w3 = new Window(33);
    }
//    构造器之后 定义变量
    Window w2 = new Window(2);
    void f(){
        System.out.println("f()");
    }
//    方法之后 定义变量
    Window w3 = new Window(3);
}

    2. 静态数据的初始化

      Class : StaticInitialization

package lime.thinkingInJava._005001.exercise;

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("Creating new Cupboard() in main");
        new Cupboard();
        System.out.println("Creating new Cupboard() in main");
        new Cupboard();
        table.f2(1);
        cupboard.f3(1);
    }
    static Table table = new Table();
    static Cupboard cupboard = new Cupboard();


}

      Console : 

Bowl(1)
Bowl(2)
Table()
f1(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
f2(1)
f3(1)

      静态数据成员 初始化时刻:只有在第一个Table对象创建(或者第一次访问静态数据)的时候,静态数据成员才会被初始化,此后 静态数据成员 不会再次被初始化。

      初始化的顺序:先静态对象(静态数据成员),而后时“非静态”对象。

      对象的创建过程:(Dog)

        1. 即使没有显示地使用static关键字,构造器实际上也是静态方法。因此,当首次创建类型为Dog的对象时(构造器可以看成静态方法),或者Dog类的静态方法/静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件。

        2. 然后载入Dog.class(这将创建一个Class对象),有关静态初始化的所有动作都会执行。因此,静态初始化只在Class对象首次加载的时候进行一次。

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

        4. 这块存储空间会被清零,这就自动地将Dog对象中的所有基本类型数据都设置成了默认值(对数字来说就是0,对布尔类型和字符型也相同),而引用则被设置成了null。

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

        6. 执行构造器。

    3. 显式的静态初始化(静态代码块)

      Java 允许将多个静态初始化动作组织成一个特殊的“静态子句”,即 “静态块”。

      当首次生成这个类的一个对象时,或者首次访问属于那个类的静态数据成员时(即便从未生成过那个类的对象),静态块 才执行。

      Class : ExplicitStatic

package lime.thinkingInJava._005001.exercise;

public class ExplicitStatic {
    static {
        j = 110;
    }
    static int j;
    static {
        i = 10;
    }
    static int i;
    static {
        i = 11;
    }
    public static void main(String[] args){
        System.out.println("j = " + j);
        System.out.println("i = " + i);
    }
}

      Console : 

j = 110
i = 11

 

    4. 非静态实例初始化(代码块)

      非静态代码块和非静态成员变量 与 静态代码块和静态成员变量 类比:

        1. 初始化顺序:静态 --> 非静态;成员变量的定义(声明及默认初始化) --> 成员变量的指定初始化

        2. 归属: 静态static 属性(静态代码块和静态成员变量)属于类,存储在 方法区;非静态属性(非静态代码块和非静态成员变量)属于对象,存储在 堆空间;

        3. 执行时刻: 静态static 属性 在类加载时执行(仅执行一次);非静态属性 在类被实例化时执行(每有一个对象实例化就执行一次)。

      Class : Mugs

package lime.thinkingInJava._005001.exercise;

class Mug{
    Mug(int marker){
        System.out.println("Mug(" + marker + ")");
    }
    void f(int marker){
        System.out.println("f(" + marker + ")");
    }
}
public class Mugs {
   Mug mug1;
   Mug mug2;
    {
        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");
    }
}

    Console : 

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

  8. 数组初始化

    1. 可变参数列表

  9. 枚举类型

    Enum : Spiciness

package lime.thinkingInJava._005009000.exercise;

public enum  Spiciness {
    NOT,MILD,MEDIUM,HOT,FLAMING
}

    Main :SimpleEnumUse

package lime.thinkingInJava._005009000.exercise;

public class SimpleEnumUse {
    public static void main(String[] args){
        Spiciness howHot = Spiciness.MEDIUM;
        System.out.println(howHot);
    }
}

    编译器自动添加一些特性:

      1. toString()方法,以便显示某个enum实例的名字。

      2. ordinal()方法,用来标识某个特定enum常量的声明顺序。

      3. static values()方法,用来按照enum常量的声明顺序,产生由这些常量值构成的数组。

package lime.thinkingInJava._005009000.exercise;

public class EnumOrder {
    public static void main(String[] args){
        for(Spiciness s : Spiciness.values()){
            System.out.println(s + ", ordinal " + s.ordinal());
        }
    }
}

    enum 可以在switch语句内使用。

啦啦啦

    

啦啦啦

.

 

啦啦啦

    

posted @ 2017-09-27 21:54  limeOracle  阅读(378)  评论(0编辑  收藏  举报