JAVA编程思想学习第五篇の初始化与清理
2014-05-21 15:06
405 查看
随着计算机革命的发展,“不安全”的编程方式已逐渐成为编程代价高昂的主因之一。
命名方式和类的名称相同。访问权限与定义类的访问权限一致(即并非一定是public)。构造器是一种特殊类型方法,因为它没有返回值。这与返回值为空(void)明显不同。对于空返回值,尽管方法本身不会自动返回什么,但是仍可选择让它返回别的东西。
区分重载方法:方法名相同返回值相同,参数不同。
*设计基本类型的重载:
基本类型能从一个“较小”的类型自动提升至一个“较大”的类型,此过程一旦牵涉重载,可能会造成一些混淆。见下例:
如果传入的实际参数大于重载方法声明的形式参数,会出现什么情况呢?如果传入的实际参数较大,就得通过类型转换来执行窄化转换。如果不这样,编译器就会报错。但是这会丢失精度。
如果只有一个peel()方法,它如何知道是被a还是b调用的呢?
编译器做了一些事情。它暗自把“所操作对象的引用”作为第一个参数传给了peel()。所以上面调用变成了Banana.peel(a,1);Banana.peel(b,2);
这是内部表示形式,我们并不能这样写。
this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。
Apple需要调用Peeler.peel()方法,它是一个外部的工具方法,将执行由于某种原因而必须放在Apple外部的操作,为了将其自身传给外部方法,Apple必须使用this关键字。
在构造器中调用构造器也可以用this关键字。需要注意:1.不能调用两个。2.必须将构造器调用置于最起始处,否则编译器会报错。
static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来可以。
也许你会发现,只要程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得不到释放。如果程序执行结束,并且垃圾回收器一直都没有释放你创建的任何对象的存储空间,则随着程序的退出,那些资源也会全部交还给操作系统。这个策略是恰当的,因为垃圾回收本身也是有开销的,要是不使用它,那就不用支付这部分开销了。
那finalize()的用途是什么呢?由于在分配内存时可能采用了类似C语言的做法,而非Java中的通常做法。这种情况主要发生在使用“本地方法”的情况下,本地方法是一种在Java中调用非Java代码的方式。本地方法目前只支持C和C++,在非Java代码中也许会调用C的malloc函数,所以需要在finalize()中用本地方法调用free()。
垃圾回收器如何工作呢?Java的垃圾回收器依据的思想是:对于任何“活”的对象,一定能追溯到其存活的堆栈或静态存储区之中的引用。这个引用链条可能会穿过数个对象层次。由此,如果从堆栈和静态存储区开始,遍历所有的引用,就能找到所有“活”的对象。对于发现的每个引用,必须追踪它所引用的对象,然后是此对象包含的所有引用,如此反复进行,直到“根源于堆栈和静态存储区的引用”所形成的网络全部被访问为止。你所访问过的对象必须是活的。
在这种方式下,Java虚拟机将采用一种自适应的垃圾回收技术。至于如何找到的存活对象,取决于不同的java虚拟机实现。有一种做法为停止-复制。显然这意味着,先暂停程序的运行(它不属于后台回收模式),然后将所有存活的对象从当前堆复制到另一个堆,没有被复制的都是垃圾。当对象被复制到新堆时,他们是一个挨着一个的,所以以新堆保持紧凑排列,然后就可以按前述方法简单、直接分配新空间了。
把对象从一处搬到另一处时,所有指向他们的引用都必须修正。位于堆或静态存储区的引用可以直接被修正,但可能还有其他指向他们的对象引用,他们在遍历的过程中才能被找到。
这种“复制式回收器”效率比较低,有2个原因。1.得有2个堆,然后得在这两个堆来回倒腾。从而得维护比实际需要多一倍的时间。某些Java虚拟机对次处理方式是,按需从堆中分配几块较大的内存,复制动作发生在这些大块内存之间。
第2个问题在于复制。程序进入稳定状态之后,可能只产生很少垃圾,甚至没有垃圾。如果还用内存复制方式,这很浪费。为了避免这种情况,一些java虚拟机进行检查;要是没有新垃圾产生,就会转换成另一种工作模式。这种模式叫“标记-清扫”。这种模式也必须在程序暂停情况下才能进行。
“标记-清扫”所依据的思想同样是从堆栈和静态存储区出发,遍历所有的引用,进而找出所有存活的对象。每当它找到一个存活对象,就会给对象一个标记,这个过程中不会回收任何对象。只有全部标记工作完成的时候,清理工作才会开始。在清理过程中,没有标记的对象将被释放,不会发生任何复制动作。所以剩下的堆就on关键是不连续的,垃圾回收器要是希望得到连续空间的话,就得重新整理剩下的对象。
在java虚拟机中,内存分配以较大的“块”为单位。如果对象较大,它会占用单独的块,严格来说,“停止-复制”要求在释放旧有对象之前,必须先把所有存活对象从旧堆复制到新堆,这将导致大量内存复制行为。有了块以后,垃圾回收器在回收的时候就可以往飞起的块里拷贝对象了。每个块都用相应的代数来记录它是否还存活。通常,如果块在某处被引用,其代数会增加;垃圾回收器会定期进行完整的清理动作----大型对象仍然不会被复制(只是代数增加),内含的小型对象的那些快则被复制并整理。Java虚拟机会进行监视,如果所有对象都很稳定,垃圾回收器的效率降低的话,就会切换到“标记-清扫”方式;同样,java虚拟机会跟踪“标记-清扫”的效果,要是堆空间出现很多碎片,就会切换到“停止-复制”方式。这就是“自适应”技术。
在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。
1.用构造器确保初始化
在JAVA中,通过提供构造器,类的设计者可确保每个对象都会得到初始化。创建对象时,如果其类具有构造器,java就会在用户有能力操作对象之前自动调用相应的构造器,从而保证了初始化的进行。命名方式和类的名称相同。访问权限与定义类的访问权限一致(即并非一定是public)。构造器是一种特殊类型方法,因为它没有返回值。这与返回值为空(void)明显不同。对于空返回值,尽管方法本身不会自动返回什么,但是仍可选择让它返回别的东西。
2.方法重载
为了让方法名相同而形式参数不同的构造器同时存在,必须用到方法重载。同样也适用于其他方法。class Tree{ int height; Tree(){ height=0; } Tree(int i){ height=i; } void info(){ } void info(String s){ } }
区分重载方法:方法名相同返回值相同,参数不同。
*设计基本类型的重载:
基本类型能从一个“较小”的类型自动提升至一个“较大”的类型,此过程一旦牵涉重载,可能会造成一些混淆。见下例:
public class test{ void f1(int i){print("f1,int");} void f1(long i){print("f1,long");} void f1(double i){print("f1,double");} void f2(long i){print("f2,long");} void f2(double i){print("f2,double");} void f3(double i){print("f3,double");} public static void main(String[] args){ test t1=new test(); t1.f1(5); t1.f2(5); t1.f3(5); }
}/*Output: f1,int f2,long f3,double *///:~
如果传入的实际参数大于重载方法声明的形式参数,会出现什么情况呢?如果传入的实际参数较大,就得通过类型转换来执行窄化转换。如果不这样,编译器就会报错。但是这会丢失精度。
3.默认构造器
如果你写的类中没有构造器,则编译器会自动帮你创建一个默认构造器。如果已经定义了一个构造器(无论是否有参数),编译器就不会帮你自动创建默认构造器。4.this关键字
class Banana{ void peel(int i){}} public class BananaPeel{ public static void main(String[] args){ Banana a=new Banana(); Banana b=new Banana(); a.peel(1); b.peel(2); } }
如果只有一个peel()方法,它如何知道是被a还是b调用的呢?
编译器做了一些事情。它暗自把“所操作对象的引用”作为第一个参数传给了peel()。所以上面调用变成了Banana.peel(a,1);Banana.peel(b,2);
这是内部表示形式,我们并不能这样写。
this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。
class Person{ public void eat(Apple apple){ Apple peeled=apple.getPeeled(); } } class Peeler{ static Apple peel(Apple apple){ return apple; } } class Apple{ Apple getPeeled(){ return Peeler.peel(this);} } public class PassingThis{ public static void main(String[] args){ new Person().eat(new Apple()); } }
Apple需要调用Peeler.peel()方法,它是一个外部的工具方法,将执行由于某种原因而必须放在Apple外部的操作,为了将其自身传给外部方法,Apple必须使用this关键字。
在构造器中调用构造器也可以用this关键字。需要注意:1.不能调用两个。2.必须将构造器调用置于最起始处,否则编译器会报错。
static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来可以。
5.清理:终结处理和垃圾回收
Java有垃圾回收器负责回收无用对象占据的内存资源。但也有特殊情况:假定你的对象(并非使用new)获得了一块“特殊”的内存区域,由于垃圾回收器只知道释放那些经由new分配的内存,所以它不知道该如何释放该对象的这块“特殊”内存。为了应对这种情况,Java允许在类中定义一个名为finalize()的方法。它的工作原理“假定”是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。为什么会在下一次垃圾回收动作发生时,才会真正回收对象占用的内存?也许你会发现,只要程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得不到释放。如果程序执行结束,并且垃圾回收器一直都没有释放你创建的任何对象的存储空间,则随着程序的退出,那些资源也会全部交还给操作系统。这个策略是恰当的,因为垃圾回收本身也是有开销的,要是不使用它,那就不用支付这部分开销了。
那finalize()的用途是什么呢?由于在分配内存时可能采用了类似C语言的做法,而非Java中的通常做法。这种情况主要发生在使用“本地方法”的情况下,本地方法是一种在Java中调用非Java代码的方式。本地方法目前只支持C和C++,在非Java代码中也许会调用C的malloc函数,所以需要在finalize()中用本地方法调用free()。
垃圾回收器如何工作呢?Java的垃圾回收器依据的思想是:对于任何“活”的对象,一定能追溯到其存活的堆栈或静态存储区之中的引用。这个引用链条可能会穿过数个对象层次。由此,如果从堆栈和静态存储区开始,遍历所有的引用,就能找到所有“活”的对象。对于发现的每个引用,必须追踪它所引用的对象,然后是此对象包含的所有引用,如此反复进行,直到“根源于堆栈和静态存储区的引用”所形成的网络全部被访问为止。你所访问过的对象必须是活的。
在这种方式下,Java虚拟机将采用一种自适应的垃圾回收技术。至于如何找到的存活对象,取决于不同的java虚拟机实现。有一种做法为停止-复制。显然这意味着,先暂停程序的运行(它不属于后台回收模式),然后将所有存活的对象从当前堆复制到另一个堆,没有被复制的都是垃圾。当对象被复制到新堆时,他们是一个挨着一个的,所以以新堆保持紧凑排列,然后就可以按前述方法简单、直接分配新空间了。
把对象从一处搬到另一处时,所有指向他们的引用都必须修正。位于堆或静态存储区的引用可以直接被修正,但可能还有其他指向他们的对象引用,他们在遍历的过程中才能被找到。
这种“复制式回收器”效率比较低,有2个原因。1.得有2个堆,然后得在这两个堆来回倒腾。从而得维护比实际需要多一倍的时间。某些Java虚拟机对次处理方式是,按需从堆中分配几块较大的内存,复制动作发生在这些大块内存之间。
第2个问题在于复制。程序进入稳定状态之后,可能只产生很少垃圾,甚至没有垃圾。如果还用内存复制方式,这很浪费。为了避免这种情况,一些java虚拟机进行检查;要是没有新垃圾产生,就会转换成另一种工作模式。这种模式叫“标记-清扫”。这种模式也必须在程序暂停情况下才能进行。
“标记-清扫”所依据的思想同样是从堆栈和静态存储区出发,遍历所有的引用,进而找出所有存活的对象。每当它找到一个存活对象,就会给对象一个标记,这个过程中不会回收任何对象。只有全部标记工作完成的时候,清理工作才会开始。在清理过程中,没有标记的对象将被释放,不会发生任何复制动作。所以剩下的堆就on关键是不连续的,垃圾回收器要是希望得到连续空间的话,就得重新整理剩下的对象。
在java虚拟机中,内存分配以较大的“块”为单位。如果对象较大,它会占用单独的块,严格来说,“停止-复制”要求在释放旧有对象之前,必须先把所有存活对象从旧堆复制到新堆,这将导致大量内存复制行为。有了块以后,垃圾回收器在回收的时候就可以往飞起的块里拷贝对象了。每个块都用相应的代数来记录它是否还存活。通常,如果块在某处被引用,其代数会增加;垃圾回收器会定期进行完整的清理动作----大型对象仍然不会被复制(只是代数增加),内含的小型对象的那些快则被复制并整理。Java虚拟机会进行监视,如果所有对象都很稳定,垃圾回收器的效率降低的话,就会切换到“标记-清扫”方式;同样,java虚拟机会跟踪“标记-清扫”的效果,要是堆空间出现很多碎片,就会切换到“停止-复制”方式。这就是“自适应”技术。
6.成员初始化
java方法内的局部变量必须初始化,否则会报错。类中的数据成员是基本类型的话,Java就会保证他们初始化,要是类中定义一个对象引用时,如果不将其初始化,此引用就会获得一个特殊的null值。7.构造器初始化
初始化顺序
在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。class window{ window(int i){ print("window("+i+")"); } } class House{ window w1=new window(1); House(){ print("House()"); w3=new window(33); } window w2=new window(2); window w3=new window(3); void f(){print("f()")}; } public class test{ public static void main(String[] args){ House h=new House(); h.f(); } }/*Output: window(1) window(2) window(3) House() window(33) f() *///:~
2.静态数据的初始化
静态数据都只占用一份存数区域。如果一个域是静态的基本类型域,且也没有对它进行初始化,那么它就会获得基本类型的标准初值;如果它是一个对象引用,那么它的默认初始化就是null。static数据会在类中其它成员初始化以前初始化,且只会被初始化一次。<span style="font-size:14px;">class Bowl{ Bowl(int marker){ print("Bowl("+marker+")"); } void f1(int marker){ print("f1("+makrker+")"); } } class Table{ static Bowl bowl1=new Bowl(1); Table(){ print("Table()"); bowl2.f1(1); } void f2(int marker){ print("f2("+marker+")"); } static Bowl bowl2=new Bowl(2); } class Cupboard{ Bowl bowl3=new Bowl(3); static Bowl bowl4=new Bowl(4); Cupboard(){ print("Cupboard"); bowl4.f1(2); } void f3(int marker){ print("f3("+marker+")"); } static Bowl bowl5=new Bowl(5); } public class staticTest{ public static void main(String[] args){ print("Creating new Cupboard() in main"); new Cupboard(); print("Creating new Cupboard() in main"); new Cupboard(); table.f2(1); cupboard.f3(1); } static Table table=new Table(); static Cupboard cupboard=new Cupboard(); }/*Output: 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) *///:~</span>
8.数组初始化
定义方式:int[] a1或者int a1[],编译器不允许指定数组的大小。9.枚举类型
public enum Spiciness{ NOT,MILD,MEDIUM,HOT,FLAMING } public class SimpleEnum{ public static void main(String[] args){ Spiciness howHot=Spiciness.MEDIUM; System.out.println(howHot); } }/*Output: MEDIUM *///:~
相关文章推荐
- JAVA编程思想学习总结:第五章——初始化与清理
- Java 编程思想(第四版)学习笔记(5)初始化与清理
- [学习笔记][Java编程思想]第5章:初始化与清理
- java 编程思想的学习笔记 - 第五章 初始化与清理
- java编程思想学习日志——初始化与清理
- 《Java 编程思想》--第四章:控制执行流程,第五章:初始化与清理
- java 编程思想 第五章 初始化与清理
- java 编程思想--初始化与清理
- java编程思想-第五章-初始化与清理
- 【笔记】Java编程思想—一初始化与清理
- Java编程思想_初始化与清理
- Java编程思想,读书笔记三(第5章 初始化与清理)
- Java编程思想 4th 第5章 初始化与清理
- Java编程思想之初始化与清理
- java编程思想之初始化与清理
- 控制执行流程,初始化与清理-java编程思想-第4、5章
- Java编程思想 第5章 初始化与清理
- 5初始化与清理-Java编程思想
- 【Java编程思想】(1)初始化与清理
- java编程思想 第五章初始化与清理