`
onlylau
  • 浏览: 4998 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

《Java编程思想》学习笔记之初始化与清理

阅读更多

初始化

1、区分重载的方法

要是几个方法有相同的名字,Java如何才能知道你指的是哪一个呢?其实规则很简单:每个重载的方法都必须有一个独一无二的参数类型列表。

有些人可能会想:“在区分重载方法的时候,为什么只能以类名和方法的形参列表作为标准呢?能否考虑用方法的返回值来区分呢?”比如下面两个方法,虽然它们有同样的名字和形式参数,但却很容易区分它们:

 

void f(){}
int f(){return 1;}

 只要编译器可以根据语境明确判断出语义中,比如在int x=f()中,那么的确可以据此区分重载方法。不过,有时你并不关心方法的返回值,你想要的是方法调用的其他效果,这时你可能会调用方法而忽略其返回值。所以如果像下面这样调用方法:

 

f();

 此时Java如何才能判断该调用哪一个f()呢?

因此,根据方法的返回值来区分重载方法是行不能的。

 

2、默认构造器

如果某个类已经定义了一个构造器(无论是否有参数),编译器都不会帮你自动创建默认构造器:

 

class Bird2{
      Bird2(int i){}
}

public class NoSynthesis{
      public static void main(String[] args){
            //! Bird2 b = new Bird2();  //没有默认的,此处调用会出错
            Bird2 b2 = new Bird2(1);
      }
}

 

3、this关键字

如果有同一个类型的两个对象,分别是a和b。

 

class Banana{
	void peel(int i){
		/*........*/
	}
}

public class BananaPeel{
	public static void main(String[] args){
		Banana a = new Banana(),
			   b = new Banana();
		a.peel(1);
		b.peel(2);
	}
}

 如果只有一个peel()方法,它如何知道是被a还是被b所调用的呢?

 

为了能用简便、面向对象的语法来编写代码----即“发送消息给对象”,编译器做了一些幕后工作。它暗自把“所操作对象的引用”作为第一个参数传递给peel()方法。所以上述两个方法的调用 就变成了这样:

 

Banana.peel(a,1);
Banana.peel(b,1);

 这是内部的表示形式。我们并不能这样书写代码,并试图通过编译。

this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。this的用法与其他对象引用并无不同。但要注意,如果在方法内部调用同一个类的另一个方法,就不必使用this,直接调用即可。

 

可能为一个类写了多个构造器,有时可能想在一个构造器中调用另一个构造器,以避免重复代码。可用this关键字做到这一点。在构造器中,如果为this添加了参数列表,那么就有了不同的含义。这将产生对符合此参数列表的某个构造器的明确引用。

 

public class Flower{
	String s = "abc";
	Flower(int petals){
		/*...*/
	}
	Flower(String ss){
		/*...*/
		s = ss;
	}
	Flower(String s,int petals){
		this(petals);
		//this(s);//类似的调用不能调用两次
		this.s = s;
		/*...*/
	}
}

 

 

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

程序员都了解初始化的重要性,但常常会忘记同样也重要的清理工作。毕竟,谁需要清理一个int呢?但在使用程序库时,把一个对象用完后就“弃之不顾”的做法并非总是安全的。当然,Java有垃圾回收器负责回收无用对象占据的内存资源。但也有特殊情况:假定你的对象(并非使用new)获得了一块“特殊”的内存区域,由于垃圾回收器只知道释放那些经由new分配的内存,所以它不知道该如何释放该对象的这块“特殊”内存。为了应对这种情况,Java允许在类中定义一个名为finalize()的方法。它的工作原理“假定”是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并有在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。所以要是你打算用finalize(),就能在垃圾回收时刻做一些重要的清理工作。

这里有一个潜在的编程陷阱,因为有些程序员(特别是c++程序员)刚开始可能会误把finalize()当作c++中的析构函数(c++中销毁对象必须用到这个函数)。所以有必要明确区分一下:在c++中,对象一定会被销毁(如果程序中没有bug的话);而Java里的对象并非总是被垃圾回收。或者换句话说 :

              1.对象可能不被垃圾回收。

              2.垃圾回收并不等于“析构”。

Java并未提供“析构函数”或相似的概念,要做类似的清理工作,必须自己动手创建一个执行清理工作的普通方法。例如,假设某个对象在创建过程中会将自己绘制到屏幕上,如果不是明确地从屏幕上将其擦除,它可能永远得不到清理。如果在finalize()里加入某种擦除功能,当“垃圾回收”发生时(不能保证一定会发生),finalize()得到调用,图像就会被擦除。要是“垃圾回收”没有发生,图像就会一直保留下来。

重点:
只要程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得到不到释放。如果程序执行结束,并且垃圾回收器一直都没有释放你创建的任何对象的存储空间,则随着程序的退出,那些资源也会全部交还给操作系统。这个策略是恰当的,因为垃圾回收本身也有开销,要是不使用它,那就不用支付这部分开销了。

 1、finalize()的用途何在?

                 3.垃圾回收只与内存有关。

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

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

 

finalize()还有一个有趣的用法,它并不依赖于每次都要对finalize()进行调用,这就是对对象终结条件的验证。当对某个对象不再感兴趣----也就是它可以被清理了,这个对象应该处于某种状态,使它占用的内存可以被安全释放。例如,要是对象代表了一个打开的文件,在对象被回收前程序员应该关闭这个文件。只要对象中存在没有被适当清理的部分,程序就存在很隐晦的缺陷。finalize()方法可以用来最终发现这种情况----尽管它并不总是会被调用。

//Using finalize() to detect an object that hasn't been properly cleaned up
class Book{
	boolean checkedOut = false;
	Book(boolean checkOut){
		checkedOut = checkOut;
	}
	
	void checkIn(){
		checkedOut = false;
	}
	
	protected void finalize(){
		if(checkedOut){
			System.out.println("Error:checked out!");
			//Normally,you'll also do this
			//super.finalize();
		}
	}
}

public class TerminationCondition{
	public static void main(String[] args){
		Book novel = new Book(true);
		//Proper cleanup
		novel.checkIn();
		//Drop the reference,forget to clean up
		new Book(true);
		//force garbage collection & finalization
		System.gc();
	}
}
 

 

未完,待续。。。。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics