《JAVA编程思想》日志(五)--------初始化与清理

本章主要讲解java的构造器(构造方法)和“垃圾处理器”。

5.1 用构造器确保初始化

首先,构造器的方法名与类名相同,保证了构造器名称不会与类中成员命名冲突,同时编译器在初始化期间能够自动明确调用该构造器。在java中,“初始化”与“创建”捆绑在一起,不能分离。例如,在创建对象时,new Rock();将会为对象分配存储空间,并调用相应的构造器,以确保在你能操作对象前,它已经被恰当的初始化。(***)

其次,不接受任何参数的构造器叫做默认构造器,也是无参构造器。当然,也可以带有形式参数,例如,

package init;

public class Construstor {
	public static void main(String[] args){
		for(int i=0;i<8;i++){ 
			new Rock(i);             //因为构造方法是带有形式参数的,所以在初始化对象时就提供实际参数
		}
	}
}

class Rock{
	Rock(int i){                             //构造方法含参
		System.out.println("Rock"+i+"");
	}
}

构造器是一种特殊类型的方法,因为没有返回值,不会返回任何东西,不同于void。

package init;

public class UseThis {
	String s;
	void print(){
		System.out.println(s);
	}

	public static void main(String[] args) {
		UseThis u=new UseThis(); //静态方法必须通过对象的引用才能调用非静态方法
		u.print();               //用于测试未初始化的string变量的默认值 ,结果输出null     
	}

}

5.2方法重载

为了让方法名相同而形式参数不同的构造器同时存在,必须用到方法重载。每个重载的方法都必须有一个独一无二的参数形式列表。

5.2.1涉及基本类型的重载

基本类型能从一个“较小”的类型自动提升为一个“较大”的类型。

如果传入的数据类型(实际参数类型)小于方法中声明的形式参数类型,实际数据类型会被提升;

char类型不同,如果无法找到恰好接收char参数的方法,就会把char直接提升为int类型;

如果传入的实际参数大于重载方法声明的形式参数,会发生窄化转换,如果不这样做,编译器会报错。

5.2.2不能用返回值来区分重载方法

5.3默认构造器

它的作用是创建一个“默认对象”。

public class Construstor {
	public static void main(String[] args){
<span style="white-space:pre">		</span>//Rock r=new Rock();           //(1)
		Rock r=new Rock(1);
	}
}
//class{}                                     //如果你写的类中没有构造器,编译器会自动创建一个默认构造器,(1)可以运行
class Rock{
	Rock(int i){                          //如果已经定义了构造器,无论是否有参数,编译器就不会帮你创建默认构造器,此时(1)报错
		System.out.println("Rock"+i+"");
	}
}
5.4 this 关键字

this关键字只能在方法内部使用,表示对“调用方法的那个对象的引用”。this的用法和其他对象引用并无不同。但要注意,如果在方法内部调用同一个类的另一个方法,不需要使用this,直接调用即可。当前方法的this引用会自动应用于同一类的其他方法。

只有当需要明确指出对当前对象的引用时,才需要使用this关键字。例如,当需要返回对当前对象的引用时,return this;

this关键字对于将当前对象传递给其他方法很有用。(????书上例子没看懂)

package init;

public class UseThis {
	
	static String s="hello";            //在静态的main函数输出s,必须是静态的数据成员,不能调用非静态方法和非静态数据成员,除非在静态方法里创建对象的
	UseThis(){                          //引用,用这个引用去调用非静态的数据成员和成员方法
		this("hi",47);
	}
	UseThis(String s,int i){
		//this.s=s;                //注释这行后,输出hi,hello;
		System.out.println(s);     
	}

	public static void main(String[] args) {
		UseThis u=new UseThis();
		System.out.println(s);
	}

}          //但是,非静态方法可以访问静态数据成员和成员方法。
this.s=s;表示形式参数的复制给了数据成员的引用,因为这二者指向了同一个值。所以,程序中加上这句话后,输出结果相同,都是“hi”。因为参数s的名称与数据成员名字相同,所以使用this.s代替数据成员,this 代表的当前对象的引用。

5.4.1在构造器中调用构造器

可以为一个类写多个构造器,在一个构造器中调用另一个构造器(只能调用一个,且调用必须置于最起始处),以避免重复代码。在构造器中,如果为this添加了参数列表,将产生符合此参数列表的某个构造器的调用。

除构造器外,编译器禁止在其他任何地方调用构造器。

5.5 清理:终结处理和垃圾回收(此部分内容会在以后 单独提出,整理成文************************************)

(1)对象可能不被垃圾回收

(2)垃圾回收并不等于“析构”

(3)垃圾回收只与内存有关:垃圾回收器的唯一目的是回收程序不再使用的内存,无论对象时怎样创建的。

  java的垃圾回收机制并不是万能的。假定你的对象获得了一块“特殊”的内存区域,并非使用new,由于垃圾回收器只知道释放那些经由new分配的内存,所以它不知道该如何释放这块“特殊”内存。为了应对这种情况,java允许在类中定义finalize()方法,就能在垃圾回收时做一些重要的清理工作。

5.6成员初始化

package init;

public class UseThis {
	String s;
	UseThis ut;                  
	void print(){
		System.out.println(s);
		System.out.println(ut);
	}

	public static void main(String[] args) {
		UseThis u=new UseThis(); //静态方法必须通过对象的引用才能调用非静态方法
		u.print();               //用于测试为初始化的string变量的默认值      
	}

}
在类里定义了一个对象引用,如果不将其初始化,此引用就会获得一个特殊值null。在上述程序中,对象引用指的是ut,还是u????

5.6.1指定初始化

对于基本或非基本类型的对象,都可以在定义时初始化,int i=99;

也可以创建对象时初始化,

        UseThis u=new UseThis(1);     //如果没有为u指定初始值就尝试使用它,就会出现运行时错误。

    也可以通过某个方法初始化,int i=f( a);但 a 必须已经初始化才可以使用.

5.7 构造器初始化

牢记:无法阻止自动初始化的执行,它将在构造器被调用之前发生。

5.7.1初始化顺序

package init;

class Window{
	Window(int i){
		System.out.println("window"+i);
	}
}	

class House{
	Window w1=new Window(1);
	House(){
		System.out.println("house()");
		w3=new Window(33);  //w3会被初始化两次,第一次在构造器前,即(1)处,第二次在构造器内,也就是这里。第一次引用的对象被丢弃,并作为垃圾回收
	}
	Window w2=new Window(2);
	void f(){
		System.out.println("f()");//
	}
	Window w3=new Window(3);   //该定义也先于方法被调用前(1)
	
}
public class OrderOfInit {
	
	public static void main(String[] args) {
		House h=new House();    
		h.f();             //调用house的f方法,但是house类中的变量会先于方法调用之前被定义并初始化
	}

}/*输出顺序:window1
window2
window3
house()
window33
f()*/
5.7.2静态数据初始化

无论创建多少个对象,静态数据的都只占用一份存储区域。

static关键字不能应用于局部变量,只能作用于域。

如果一个域是基本类型,在没有初始化前,会获得默认初值,如果是对象引用,默认初始值是null。

static int i=47;

House h2=new House(2);

   static House h1=new House(1);     //静态成员初始化与非静态没有什么区别,但是要先于上面的非静态初始化(*****)

同时,静态初始化只在class对象首次加载时执行一次。而非静态初始化在每次class加载时都会执行。

5.7.3显式的静态初始化

“静态程序块”:

class Spoon{

static int i;

static {           //表示这段代码只会被执行一次:当首次生成这个类的对象时,或者首次访问属于这个类的静态数据成员时。

i=47;          

}

}

5.7.4非静态实例的初始化

package init;

class Mug{
	Mug(int i){
		System.out.println("Mug"+i);
	}
}
public class Mugs {
	Mug mug1;
	Mug mug2;
	{
		mug1=new Mug(1);
		mug2=new Mug(2);         //实例初始化子句,相对于
	}
//	static Mug mug1;             //如果程序改成这样,输出也会改变,因为静态代码段,也会先于构造器执行,但是只执行一次
//	static Mug mug2;             /*Mug1
//	static{                        Mug2
//		mug1=new Mug(1);           Mugs
//		mug2=new Mug(2);           Mugs(int)
//	}
	Mugs(){
		System.out.println("Mugs");
	}
	Mugs(int i){
		System.out.println("Mugs(int)");
	}
	public static void main(String[] args) {
		new Mugs();
		new Mugs(1);

	}

}/*Mug1
Mug2
Mugs
Mug1
Mug2
Mugs()*///由输出可以看出,无论调用那个显式构造器,实例初始化子句都会先于构造器被调用
5.8数组初始化

int [] a;与int a[];含义相同,但是前一种 更合理,表明类型是“一个int型数组”。

package init;

public class ArrayOfPrimitives {
	
	public static void main(String[] args) {
		int[] a1={1,2,3,4,5};   //直接在定义时初始化,此时存储空间的分配将由编译器负责
		int[] a2;           //编译器不允许指定数组大小,现在拥有的只是对数组的引用,而且已经为该引用分配了足够多的内存空间,而且也没有给数组对象本身分配任何空间
		a2=a1;                  //此时传递的是引用,a1,a2指向了同一个数组
		for(int i=0;i<a2.length;i++){   //数组的初始化动作可以在代码任何地方
			a2[i]=a2[i]+1;      //a2指向的值改变了,对应的a1指向的值也会改变  
		}
		for(int i=0;i<a1.length;i++){      //所有数组,无论是对象还是基本类型,都有一个固定成员length
			System.out.print(a1[i]+" ");
		}
	}

}/*output:2 3 4 5 6*/
分别创建基本类型的数组和非基本类型的数组:

package init;

import java.util.*;

public class ArrayOfPrimitives {
	
	public static void main(String[] args) {
		Random rand=new Random(47);
		int[] a=new int[rand.nextInt(20)];     //定义随机大小的int型数组
		                                       //应该尽量在数组定义时将其初始化
		Integer[] b=new Integer[rand.nextInt(20)];//定义随机大小的非基本类型数组,此时还没有初始化,还是引用数组
		Integer[] c={
				new Integer(1),  //也可以用花括号括起来的列表初始化对象数组
				new Integer(2),
				3,               //逗号是可选的
		};
		System.out.println(Arrays.toString(a));//打印未初始化的a数组
		//System.out.println(Arrays.toString(b));//运行异常,因为b数组未初始化,是空引用
		System.out.println(Arrays.toString(c));
		for(int i=0;i<b.length;i++){
			b[i]=rand.nextInt(50);//只有当把对象赋值给引用后,初始化进程才算结束
		}
		
		System.out.println(Arrays.toString(b));//此时可以打印出b数组
	}
}/*output:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
		[1, 2, 3]
		[43, 11, 11, 29, 18, 0, 22, 7, 38, 28, 1, 39, 9, 28, 48]*/
5.8.1可变参数列表

应用于参数个数或类型位置未知的场合

以Object数组为参数的方法:

package init;
class A{}
public class VarArgs {
	static void printArray1(Object[] args){ //定义Object数组,与下面的方法进行比较
		for(Object obj:args){              //用foreach语法
			System.out.print(obj+" ");
		}
		System.out.println();
	}
	static void printArray2(Object... args){ //定义Object,可变参数列表,参数个数和类型不限
		for(Object obj:args){              //用foreach语法
			System.out.print(obj+" ");
		}
		System.out.println();
	}
	static void printArray3(int i,Object... args){ //也可以在可变参数列表中指定一个object之外的类型
		for(Object obj:args){              //用foreach语法
			System.out.print(obj+" ");
		}
		System.out.println();
	}
//	static void printArray3(Object... args){ //与上面的参数列表不同,方法名相同,但是这样做是不安全的
//		for(Object obj:args){                //会导致编译器运行错误,要用参数列表严格分开
//			System.out.print(obj+" ");
//		}
//		System.out.println();
//	}
	static void printArray3(char i,Object... args){ //将上面参数列表的int改为char就可以避免错误
		for(Object obj:args){              
			System.out.print("four");
		}
		System.out.println();
	}
	
	public static void main(String[] args) {
		printArray1(new Object[]           //对于第一种方法,需要创建object对象引用
				{new A(),new Integer(47),"one",1});//创建了四种不同的对象,其中new A输出的是类的名字和对象的地址
		//printArray1(1,2,3,4);  运行错误,必须创建object对象
		printArray1(new Object[]{new Integer(47),new Float(3.14),new Double(11.11)});
		printArray2(1,2,3,4);        //可以是任何类型,可以混合使用
		printArray2(new A(),new Integer(48),"two",2);
		printArray3(3,"three");      //参数列表中指定了一个int型
		printArray3('a',"three");    //调用的是第四个方法,因为a是char型的
	}
}/*output:init.A@139a55 47 one 1 
47 3.14 11.11 
1 2 3 4 
init.A@1db9742 48 two 2 
three 
four*/
5.9 枚举类型-------- enum关键字

package init;

public enum Week {
	MON,TEU,WED,THU,FRI; //创建枚举类型集,因为枚举类型的实例是整型常量,要求都大写
}
package init;

public class EnumUse {

	public static void main(String[] args) {
		Week w1=Week.MON;   //给枚举类型实例化,引用w1指向某个实例
		System.out.println(w1);
		for(Week w:Week.values()){  //产生Week中常量值
			System.out.println(w+",ordinal "+w.ordinal());//表示enum常量的声明顺序
		}
		switch(w1){                          //枚举可应用于switch语句中
		case MON:System.out.println("周一");
				break;
		default:System.out.println("hello");
		}
	}
}/*output:MON
MON,ordinal 0
TEU,ordinal 1
WED,ordinal 2
THU,ordinal 3
FRI,ordinal 4
周一*/



评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值