java-第五章 初始化与清理

时间:2022-08-07 19:42:13

    初始化和清理正是设计安全的两个问题。比如C程序忘记初始化变量,当使用完一个元素时,它也很容易忘记清理。

C++引入构造器(constructor)的概念,这是一个创建对象时会被自动调用的特殊方法。Java中也采用了构造器,并额外提供了“垃圾回收器”。对于不在使用的内存资源,垃圾回收器能自动将其释放。

1.用构造器确保初始化

(助于理解)可以假想为编写的每个类都定义一个initialize()方法。该方法的名称提醒你在使用其对象之前,应首先调用initialize()。然而,这同时意味着用户必须记得自己去调用此方法。在Java中,通过提供构造器,类的设计者可以确保每个对象都会得到初始化。创建对象时,如果其类具有构造器,Java就会在用户有能力操作对象之前自动调用相应的构造器,从而保证了初始化的进行。

构造器命名,两个问题1.名字冲突,2.编译器怎么认识这个是构造器。Java采用和C++一样的方案:即构造器采用与类相同的名称。

一下就是一个带有构造器的简单类:

class Rock {

  Rock(){

system.out.println("Rock");

}

}

现在main函数 new Rock();         

请注意,由于构造器的名称必须与类名完全相同,所以“每个方法首字母小写”的编码风格并不适用与构造器。

不接受任何参数的构造器叫做默认构造器,Java文档中通常使用术语无参构造器。构造器也能带有形式参数。

class Rock {

  Rock(i){

system.out.println("Rock" +i);

}

}

构造器是一种特殊类型的方法,因为他没有返回值。这与返回值为空(void)明显不同。对于空返回值,尽管方法本身不会啊自动返回什么,但仍可选择让他返回别的东西。构造器则不会返回任何东西,你别无选择(new表达式确实返回了对新建对象的引用,但构造器本身并没有返回值)。假如构造器具有返回值,并且允许人们自行选择返回类型,那么势必得让编译器知道该如何处理此返回值。


方法重载,方法名相同参数不同,构造器也可以重载。甚至参数顺序不同也可以区分两个方法。不过一般情况下别这么做,因为这会使代码难以维护。

根据返回值来区分重载方法是行不通的。

默认构造器

如果你写的类中没有构造器,则编译器会自动帮你创建一个默认构造器。但是,如果已经定义了一个构造器(无论是否有参数),编译器就不会帮你创建默认构造器。

This关键字

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

----有些人执意将this放在每一个方法调用和字段引用前,认为这样“更清楚更明确”。但是,千万别这么做。我们使用高级语言的原因之一就是它能帮我们做一些事情。要是你把this放在一些没有必要的地方,就会使读你程序的人不知所措,因为别人写的代码不会到处使用this。人们期望只在必要处使用this。遵循一种一致而直观的风格能节省时间和金钱。

this还有一种用法,由于参数S的名称和数据成员S的名字相同,使用this.S来代表数据成员就能解决这个问题。


static的含义

在static方法内部不能调用非静态方法,反过来倒是可以的。而且可以再没有创建任何对象的前提下,仅仅通过本身来调用static方法。这实际上正是static方法的主要用途。他很像全局方法。java中禁止使用全局方法,但你再类中置入static方法就可以访问其他static方法和statci域。

----这不是完全不可能。如果你传递一个对象的引用到静态方法里(静态方法可以创建其自身对象),然后通过这个引用(和this效果相同),你就可以调用非静态方法和访问非静态数据成员了。但通常要达到这样的效果,你只需要写一个非静态方法即可。

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

Java允许在类中定义一个名为fialize()的方法。1.对象可能不被垃圾回收 2.垃圾回收并不等于“构析”。

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

finalize()的用途何在

垃圾回收只与内存有关

在非java代码中,也许会调用C的malloc()函数系列来分配存储空间,而且除非调用了free()函数否则存储空间将得不到释放,从而造成内存泄露。----本地方法调用。

你必须实施清理

如果希望进行除释放存储空间之外的清理工作,还是得明确调用某个恰当的java方法。这就等同于使用析构函数了,只是没有它方便。记住,无论是“垃圾回收”还是“终结”,都不保证一定会发生。如果Java虚拟机(JVM)并未面临内存耗尽的情形,他是不会浪费时间去执行垃圾回收以恢复内存的。

终结条件

finalize()验证终结条件。

package test_cqh;


public class Cqh_test {
public static void main(String[] args) {

Book book = new Book(true);
book.checkIn();
new Book(true);
System.gc();
}

}

package test_cqh;


public class Book {
boolean checkedOut = false;
public Book(boolean checkOut) {
checkedOut = checkOut;
}
void checkIn(){
checkedOut = false;
}
protected void finalize(){
if(checkedOut){
System.out.println("Error :checked out");
}
}

}

垃圾回收器如何工作

想要更好的理解java中垃圾回收,先了解其他系统中的垃圾回收机制将会很有帮助。引用计数是一种简单但速度很慢的垃圾回收技术。

自适应的、分带的、停止--复制、标记--清扫“式垃圾回收器。

Java虚拟机中有许多附加技术用以提升速度。尤其是与类加载有关的,被称为”及时“。(Just -In-Time,JIT)编译器的技术。这种技术可以把程序全部或部分翻译成本地机器码。

另一种做法惰性评估,意思是及时编译器只在必要时编译代码。

成员初始化

构造器初始化

静态数据的初始化

先静态对象(如果他们尚未因前面的对象创建过程而被初始化),而后是非静态对象。

显示静态初始化

非静态实例初始化

数组初始化


Integer [] a = {

new Integer (1);

new Integer (2);

3

,};

Integer [] b = new Integer [] {

new Integer (1);

new Integer (2);

3

};

尽管第一种形式很有用,但是他也更加受限,因为他只能用于数组被定义之处。你可以在任何地方使用第二种形式,比如String对象数组,传递给另一个main方法。

可变参数列表

如果没有定义toString()方法的话,就是打印类的名字和对象的数组。

有了可变参数,就再也不用显示的编写数组语法了,当你指定参数时,编译器实际上会为你去填充数组。


总结

构造器,这种精巧的初始化机制,应该给了读者很强的暗示:初始化在Java中占有至关重要的地位。C++的发明人Bjarne Stroustrup在设计C++期间,在针对C语言的生产效率所进行的最初调查中发现,大量编程错误都源于不正确的初始化。这种错误很难发现,并且不恰当的清理也会导致类似问题。构造器能保证正确的初始化和清理(没有正确的构造器调用,编译器就不允许创建对象),所以有了完全的控制,也很安全。

随着时间的推移,Java在性能方面已经取得了长足的进步,但速度问题任然是它涉足某些特定编程领域的障碍。

由于要保证所有对象都被创建,构造器要比这里讨论的更复杂。特别当通过组合和继承生成新类的时候,这种保证任然成立,并且需要一些附加的语法提供支持。在后续的章节中读者将学习到有关组合、继承以及他们对构造器造成的影响等方面的知识。