Thinking In Java 学习笔记 - 初始化与清理

为了保证正确的初始化与清理。Java提供构造器、垃圾回收器。

对象在创建时,系统会自动调用构造器,可以在构造器中完成初始化工作。对不再使用的内存资源,垃圾回收器会自动将其释放。

一、用构造器保证初始化

构造器的基本特征如下图所示:

示例代码:

public class Exercise1_constructor {
	String str1 = "str1";
	String str2;

	public Exercise1_constructor() {
		// TODO Auto-generated constructor stub
		str2 = "str2";
	}

	public static void main(String[] args) {
		Exercise1_constructor constructor = new Exercise1_constructor();
		System.out.println(constructor.str1);
		System.out.println(constructor.str2);
	}
}
// "str1"
// "str2"

二、方法重载

1、在JAVA中,拥有相同名称的方法,可以通过参数类型,个数,顺序来对方法加以区分。
2、不能通过返回值来对方法进行重载。因为编译器无法识别。
3、基本类型的重载:基本类型能从一个“较小”的类型自动提升为一个“较大”的类型。
      基本类型的提升顺序为:char<bety<short<int<long<float<double。
     char类型是一个例外,如果没能找到恰好接收char类型的参数,会自动转换为int。

     当从”较大“的类型转向”较小“的类型时,必须用强制类型转换来执行窄化转换,否则编译器会报错。

三、默认构造器

默认构造器,又称无参数构造器,如果类中没有定义构造器,则编译器会自动创建一个默认的构造器。如果已定义构造器,则编译器不会再创建默认构造器。
下面的代码展示了构造器、重载。

public class Exercise3_Dog {
	public Exercise3_Dog() {
		// TODO Auto-generated constructor stub
		System.out.println("constructor dog");
	}

	public Exercise3_Dog(String str) {
		System.out.println(str + "constructor with paramter");
	}

	void bark(String str) {
		System.out.println("barking " + str);
	}

	void bark(int i) {
		System.out.println("howling " + i);
	}

	public static void main(String[] args) {
		Exercise3_Dog dog = new Exercise3_Dog();
		Exercise3_Dog dog2 = new Exercise3_Dog("haha ");
		dog2.bark(2);
		dog2.bark("mark");
	}
}

// constructor dog
// haha constructor with paramter
// howling 2
// barking mark

四、this关键字

this关键字只能在方法内部使用,表示“对调用此方法的那个对象的引用”。需要注意的是,如果在方法内调用同一个类的其他方法,则不必加上this引用,虽然语法上是可行的。

package com.tij.ch5;

public class Exercise8 {

	void function1(int i) {
		System.out.println("function 1 be called : " + i);
	}

	void function2() {
		this.function1(2);
		function1(6);
	}

	public static void main(String[] args) {
		Exercise8 exercise = new Exercise8();
		exercise.function2();
	}

}
// function 1 be called : 2
// function 1 be called : 6

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

在构造器中,如果对this添加了参数列表,则视为对符合此参数列表的构造器的调用。

a、尽管可以使用this在构造器中调用构造器,但是只能调用一次,并且将调用置于起始处。

b、除构造器外,编译器禁止其他方法调用构造器。

示例代码

public class Exercise8_constructor {
	String name;
	int age;

	Exercise8_constructor(String name) {
		// 使用this.name来消除岐义
		this.name = name;
	}

	Exercise8_constructor(int age) {
		this.age = age;
	}

	public Exercise8_constructor(String name, int age) {
		// 构造器中,使用this带参数,可以调用另外一个构造器
		this(name);
		// ERROR: Constructor call must be the first statement in a constructor
		// this(age);
	}

}
2、static方法

static方法就是没有this的方法,可以在不创建对象的情况下调用static方法,因此在static方法内部无法调用非static方法。

调用static方法,一般用classname.method(),而不用对象。否则编译器会有warnning。

public class Exercise9_static {
	
	void sayNonStatic() {
		//OK
		sayStatic();
		System.out.println("non static!");
	}
	
	static void sayStatic() {
		// ERROR :Cannot make a static reference to the non-static method
		// sayNonStatic() from the type Exercise9_static
		// sayNonStatic();
		System.out.println("static!");
	}
	
	public static void main(String[] args){
		//static方法调用直接用class name,否则编译会有报警
		Exercise9_static.sayStatic();
		
		Exercise9_static exercise = new Exercise9_static();
		exercise.sayNonStatic();
	}
}
//static!
//static!
//non static!
//为什么会有2个"static!"打印呢,后面再讨论

五、清理:垃圾回收与清理

垃圾回收器在回收对象所占用的内存时,会调用finalize()方法。
垃圾回收并不等于“析构”,对象可能不会被回收,因此在finalize()里执行的清理动作不一定会被调用,finalize是不可预料的。
1,finalize()用途
垃圾回收器会回收所有通过new 分配的内存,对于一些用非java方法分配的内存,可以在finalize()中进行清理。
比如java用本地方法调用了一段c的代码,在c代码中用malloc在堆中分配了空间,则可以在finalize()中调用c的方法进行free()。
finalize不是常规的清理内存的方法,我们一般不应该调用它。
2,必须实施清理的场景
如果JVM并未面临内存耗尽的情形,它不会浪费时间去回收垃圾。
因此,必须要清理一个对象时,需要在清理它时,调用相应的方法,类似于C++中的析构。
3,终结条件
由于finalize()的调用无法预料,因此比较危险,建议不要使用。
不过有一种用法:假定垃圾回收前,对象应该处于某种状态,就可以在finalize()中进行验证。

关于finalize()相关的示例代码如下:

public class Exersice10_Tank{
    private String name;
    private String status;

    protected void finalize(){
        System.out.println("clean with finalize : " + this.name);
        // 终结条件,tank在被释放前,必须为空的,否则会提示错误信息
        if(this.status.equals("full")){
            System.out.println("Tank will not be full, must be empty");
        }
    }

    Exersice10_Tank(String name, String status){
        this.name = name;
        this.status = status;
        System.out.println("constructor : " + this.name);
    }

    public static void main(String[] args){
        // System.gc(),不会被回收,引用有效
        Exersice10_Tank test = new Exersice10_Tank("has name", "full");
        // 没有引用接收对象,会被回收
        new Exersice10_Tank("no name", "full");
        System.gc();
    }
}
// constructor : has name
// constructor : no name
// clean with finalize : no name
// Tank will not be full, must be empty

4,垃圾回收器如何工作
a,“暂停-复制”模式,stop-and-copy:暂停正在运行的程序,把活动对象从当前堆复制到另外一个堆。复制到新堆中的内存是连续分部的,复制完成后,恢复程序,清理旧的堆。这样会有两个问题:一是需要的空间会增大(用于复制),再者如果只产生了少量需要清理的内存,也会把所有活动对象复制一次,效率很低。

b,“标记-清扫”,mark-and-sweep:这种方法会遍历所有的引用,标记出活动对象。标记完成后,开始执行清理工作。把没标记的对象清理,因此这种方式产生的内存是不完整的。

c,自适应:JAVA虚拟机会进行监视,如果只产生了少量需要清理的内存,就会把回收机制转化到“标记-清扫”模式,如果产生的碎片较多,就会切换回“停止-复制”模式。

六、成员初始化

对于方法内的局部变量,Java用编译器来保证其必须被初始化,如果操作未被初始化的量,编译器会报错。
自动初始化:对于类的数据成员,如果没有显式的初始化,则系统会赋默认值。

七、构造器初始化

1、初始化顺序:

类成员自动初始化 - 构造器中的初始化 - 方法调用中的变量初始化。见下例:

class window{

    window(int index){
        System.out.println("window index : " + index);
    }
}

public class Exercise11_Order{
    window w1 = new window(1);

    Exercise11_Order(){
        System.out.println("order constructor");
        this.w1 = new window(10);
    }

    void f(){
        window w3 = new window(3);
    }

    window w2 = new window(2);

    public static void main(String[] args){
        Exercise11_Order ex1 = new Exercise11_Order();
        ex1.f();
    }
}
// window index : 1
// window index : 2
// order constructor
// window index : 10
// window index : 3

2、静态数据初始化:

无论创建多少个静态对象,静态数据都只占用一份存储区域。static关键字不能作用于方法内部的局数变量,只能作用于类的一个域,即成员变量(或成员方法)。 虽然没有显式使用“static"关键字,但是构造器是静态方法。

3、显式的静态初始化:

Java可以将多个静态初始化组织成一个静态块,在块中执行初始化。无论使用哪种初始化,他们都会在使用之前完成初始化动作。见下例:

public class Exercise14_StaticBlock{
    static String str1 = "init str1";
    static String str2;

    // 使用静态块显式初始化
    static{
        str2 = "init str2";
    }

    public static void speak(){
        System.out.println(str1);
        System.out.println(str2);
    }

    public static void main(String[] args){
        Exercise14_StaticBlock.speak();
    }
}
// init str1
// init str2

4、非静态实例初始化:

实例初始化,用来初始化非静态变量。和静态初始化块语法相似,少了static关键字。在匿名内部类中,必须用这种方法来初始化。见下例:

public class Exercise15_NonStaticInit{
    private String name;
    private String addr;

    // 非静态成员的实例初始化
    {
        name = "init name";
        addr = "init addr";
    }

    public static void main(String[] args){
        Exercise15_NonStaticInit exer = new Exercise15_NonStaticInit();
        System.out.println(exer.name);
        System.out.println(exer.addr);
    }
}
// init name
// init addr

八、数组初始化

数组是由同类型的基本数据或者对象组织成的序列。包含的语法知识,见下面的示例代码:
public class Execise_Array{

    public static void main(String[] args){
        // 1,定义数组的语法,'[]'放在变量前,变量后都可以
        int[] iArray, iArray2[];
        Integer[] intArray;

        // 2,语法错误:不能在编译期给定数组大小
        // int iArray3[3];

        // 3,可以在运行期分配大小
        iArray = new int[3];
        intArray = new Integer[3];

        // 4,iArray.length表示数组内元素个数,不可以对其修改
        for(int i = 0; i < iArray.length; i++){
            // 5,基本数据类型会初始化为默认值,int为0
            System.out.println(iArray[i] + ", ");
        }

        for(Integer i : intArray){
            // 6,对象数据必须被初始化,分配空间,否则不能引用其对象,其值为'null'
            System.out.println(i + ", ");
            // 运行时异常:因为i未初始化
            // System.out.println(i.intValue());
        }

        iArray = new int[] { 1, 2, 3 };
        intArray = new Integer[] { new Integer(1), new Integer(2) };

        for(int i = 0; i < iArray.length; i++){
            System.out.println(iArray[i]);
        }
        for(Integer i : intArray){
            System.out.println(i);
            System.out.println(i.intValue());
        }
    }
}
// 0,
// 0,
// 0,
// null,
// null,
// null,
// 1
// 2
// 3
// 1
// 1
// 2
// 2

习题16,创建一个String对象数组,为每个元素赋值一个String对象,并打印出来:
public class Exercise16_StringArray{

    public static void main(String[] args){

        String[] strArray = { "I", "love", "you" };
        String strArray2[] = new String[] { new String("I"), new String("miss"), new String("you"), };

        for(String str : strArray){
            System.out.print(str + " ");
        }

        System.out.println();
        // Arrays.toString方法产生一个一维数组的可打印版本
        System.out.println(Arrays.toString(strArray));

        for(String str : strArray2){
            System.out.print(str + " ");
        }
    }
}
// I love you
// [I, love, you]
// I miss you

习题17、18:创建一个类,它有一个接受一个String参数的构造器,在构造器中打印出该参数。创建一个该类对象的引用数组,运行程序,看构造器中的打印是否打印出来了,对引用数组中的对象赋值,再次观察。
public class Exercise17_ObjectArray{
    private String name;

    Exercise17_ObjectArray(String name){
        this.name = name;
        System.out.println(this.name);
    }

    public static void main(String[] args){

        System.out.println("no init object Array");
        Exercise17_ObjectArray[] objectArray = new Exercise17_ObjectArray[4];

        System.out.println("init object Array");

        Exercise17_ObjectArray[] objectArray2 = new Exercise17_ObjectArray[] { new Exercise17_ObjectArray("You"),
                new Exercise17_ObjectArray("Get") };
    }
}
// no init object Array
// init object Array
// You
// Get
1、可变参数列表
语法包含在2个习题中。
习题19:写一个类,它接受一个可变参数的String数组,验证你可以向该方法传递一个用逗号分隔的String列表,或者是一个String[]:
package com.study.ch5;

public class Execise19_Variable{
    // 语法:变量名 + "..." + 参数名
    static void printString(String... args){
        // 语法:可以像数组一样用Foreach方法来对可变参数取值
        for(String str : args){
            System.out.print(str + ", ");
        }
        System.out.println();
    }

    public static void main(String[] args){
        printString("I", "miss", "you");
        printString(new String[] { "Hellow", "World", "!" });
    }
}
// I, miss, you,
// Hellow, World, !,

习题20:创建一个使用可变参数列表语法的main(),打印产生的args数据元素:
public class Excise20_VariableMain{
    public static void main(String... args){
        // 调用可变参数语法的main
        test.main("I", "have", "a test");
    }
}

class test{
    public static void main(String... args){
        for(String str : args){
            System.out.println(str);
        }
    }
}
// I
// have
// a test

九、枚举

枚举的语法,public enum Spiciness{NOT, MILD, HOT}
编译器会自动为enum添加一些特性,如:
toString()方法,可以显示某个enum实例的名字;
ordinal()方法,表示某个enum常量的声明顺序;
static values(),按照enum的声明顺序,产生由这些常量构成的数组。
习题:创建一个enum,它包含纸币中最小面值的6种类型,通过values()循环,并打出每一个值与ordinal(); 然后为enum写一个switch语句,在每个case下,输出详细描述。
package com.study.ch5;

public class Excercise21_Money{

    public static void main(String[] args){
        // ?问题 不用import Enum_Money也行啊
        Enum_Money money2 = Enum_Money.FIFTY_YUAN;
        // values():产生由这些常量构成的数组
        for(Enum_Money money : Enum_Money.values()){
            // ordinal(),常量声明顺序
            System.out.println(money + ", ordinal : " + money.ordinal());
            switch(money){
            // 不能用 Enum_Money.ONE_YUAN
                case ONE_YUAN:{
                    System.out.println("this is 1 yuan.");
                    break;
                }
                case FIVE_YUAN:{
                    System.out.println("this is 5 yuan.");
                    break;
                }
                case TEN_YUAN:{
                    System.out.println("this is 10 yuan.");
                    break;
                }
                case FIFTY_YUAN:{
                    System.out.println("this is 50 yuan.");
                    break;
                }
                default:{
                    System.out.println("error");
                }
            }
        }

    }
}
// ONE_YUAN, ordinal : 0
// this is 1 yuan.
// FIVE_YUAN, ordinal : 1
// this is 5 yuan.
// TEN_YUAN, ordinal : 2
// this is 10 yuan.
// FIFTY_YUAN, ordinal : 3
// this is 50 yuan.

Java初始化与清除部分读书笔记完成。后续会记录其他部分。
读书笔记主要是写给自己看的,自己不懂的地方,记得比较琐碎;已经明白了的地方一带而过。所以读者看看就好,以此做参考未必合适。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值