1. 构造器
- 构造器的一个重要的作用: 保证对象被使用之前初始化了.
- 构造器是一种特殊类型的方法,因为他没有返回值。这与返回值为空(void)明显不同。对于空返回值,尽管方法本身不会自动返回什么, 但仍可选择让他返回别的东西。
2. 方法重载
- 方法重载的条件
- 名字相同
- 参数列表类型或个数不同
- 基本类型重载的问题
- 基本类型能从一个“较小”的类型自动提升至一个“较大“的类型. 此过程一旦涉及到重载, 可能会出现混淆。看下面这个例子
package net.mindview.initialization; import static net.mindview.util.Print.*; public class PrimitiveOverloading { void f1(char x) {print("f1(char)");} byte x) {print(f1(byte)short x) {print(f1(short)int x) {print(f1(int)long x) {print(f1(long)float x) {print(f1(float)double x) {print(f1(double));} void f2(f2(byte)f2(short)f2(int)f2(long)f2(float)f2(double)void f3(f3(short)f3(int)f3(long)f3(float)f3(double)void f4(f4(int)f4(long)f4(float)f4(double)void f5(f5(long)f5(float)f5(double));} void f6(f6(float)f6(double)void f7(f7(double)/**测试常量*/ void testConstVal(){ print(5:); f1(5);f2(5);f3(5);f4(5);f5(5);f6(5);f7(5);println(""); } *测试char testChar(){ char x = 'x'; print(char:); f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);println(*测试byte testByte(){ byte x = 0byte:*测试Short testShort(){ short x = short:*测试Int testInt(){ int x = int:*测试long testLong(){ long x = long:*测试double testDouble(){ double x = double:static main(String[] args) { PrimitiveOverloading p = new PrimitiveOverloading(); p.testConstVal(); p.testChar(); p.testByte(); p.testShort(); p.testInt(); p.testLong(); p.testDouble(); } }
执行结果:
5:f1(int)f2(int)f3(int)f4(int)f5(long)f6(float)f7(double) char:f1(char)f2(byte:f1(byte)f2(byte)f3(short)f4(short:f1(short)f2(short)f3(int:f1(long:f1(long)f2(long)f3(long)f4(long)f5(double:f1(double)f2(double)f3(double)f4(double)f5(double)f6(double)f7(double)
结论: 有int, 则执行对应的int方法, 如果没有, 则向上转型,执行最近的方法, 其他char,byte,short,int,long,double都是如此. 其中char有所不同,如果无法找到恰好是接收char参数的方法,就会把char提升至int值.
- 如果参数是一个较大的类型,就需要通过强转为一个较小的类型后才能使用,否则,编译报错.
- 基本类型能从一个“较小”的类型自动提升至一个“较大“的类型. 此过程一旦涉及到重载, 可能会出现混淆。看下面这个例子
- 为何不能以返回值来区分方法的重载
4. this关键字
- this关键字只能在方法内部使用,表示对"调用方法的那个对象"的引用.
- 只有当需要明确指出对当前对象的引用时,才需要使用this关键字. 例如,当需要返回堆当前对象的引用时,就常常在return语句中这样写:
Leaf { int i = ; Leaf increment() { i++; return this; } }
注意: this不是指当前类,而是当前类的对象.
- 使用this调用构造器
- static的含义
5. 垃圾回收器
- 垃圾回收器负责回收对象占用的内存资源。
- 垃圾回收器也有不能回收的“特殊的”内存资源。假定你的对象获得了一个“特殊”的内存资源, 由于垃圾回收器只知道负责释放那些经由new分配的内存, 所以他不知道该如何释放该对象的这块“特殊”内存。为了应对这种情况,java允许在类中定义一个名为finalize()的方法。
- finalize()方法的工作原理"假定"是这样:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用 finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。所以你要打算调用finalize(),就能在垃圾回收时做一些重要的清理工作。
- 垃圾回收只与内存有关:
- 普通的清理工作不适合finalize()
- finalize()的用武之地:对象终结条件的验证。
- 对象终结条件是怎么回事呢? 当一个对象没有用的时候,也就是他应该被清理的时候。这个对象应该处于某种状态, 让他占用的内存完全被安全地释放。例如:对象代表了打开文件, 当对象被回收前,程序员应该关闭打开的这个文件。那么,如果,一个文件被打开了, 却没有被关闭, 这样就会导致垃圾回收器不能回收这块资源。久而久之就会导致内存的溢出。 这是程序的缺陷。 这时如果在垃圾回收的时候执行了finalized()方法,这个缺陷就可能会被发现,具体看下面的例子。
package net.mindview.initialization; Book{ boolean checkedOut = false; Book(boolean checkedOut){ this.checkedOut = checkedOut; } checkIn(){ checkedOut = ; } protected finzlized() throws Throwable { if(checkedOut){ System.out.println(有书被借走但未归还 ); //基类版本的finalize()也要做一些重要的事,应使用super来调用. super.finalize(); } } } TerminationCondition { main(String[] args) { Book novel = new Book(true); 合理的清理 novel.checkIn(); 书被借出后,没有归还 强制垃圾回收并且执行finalized方法 System.gc(); } }
本例的总结条件,应该是,被签出的图书全部被签入.也就是说借出的书应该全部被归还. 但在main方法中,程序员错误的将图书借出了,但此书未被归还。但程序却没有任何错误。 这种问题一旦存在了, 很难被程序员自己发现。 直到最后内存溢出,调用finalize()方法,我们才知道。这里finalize就是用来验证终结条件的。
- 再解释一下上面的案例: 一个对象能够被清理, 条件是没有地方在使用它了。 如果一个对象是打开文件, 使用了, 就应该关闭文件, 如果没有关系, 这个对象就不能被回收,久而久之必然导致内存的耗尽,jvm就会调用垃圾回收期回收对象。这时如果程序员手动定义了finalize()方法,那么当垃圾回收时,他就会发现内存溢出的原因在哪里了。所以, 在某种情况下定义finalize()方法是一个好的喜欢.
- 对象终结条件是怎么回事呢? 当一个对象没有用的时候,也就是他应该被清理的时候。这个对象应该处于某种状态, 让他占用的内存完全被安全地释放。例如:对象代表了打开文件, 当对象被回收前,程序员应该关闭打开的这个文件。那么,如果,一个文件被打开了, 却没有被关闭, 这样就会导致垃圾回收器不能回收这块资源。久而久之就会导致内存的溢出。 这是程序的缺陷。 这时如果在垃圾回收的时候执行了finalized()方法,这个缺陷就可能会被发现,具体看下面的例子。
6. 成员初始化
- 类成员
- 基本类型的成员变量,如果在定义的时候没有初始化,系统会自动初始化为对应类型的默认值.
- 对象应用类型如果在定义的时候没有被初始化,此引用就会获得一个特殊的值null.
7.构造器初始化
- 静态数据初始化
- 无论创建多少个对象,静态数据都只占用一份存储区域。
- 静态数据何时被初始化? 静态数据所在的类被实例化的时候初始化。
- 静态数据初始化的顺序在非静态数据之前。也就是说,一个类中有静态成员变量和非静态成员变量, 他们初始化的顺序是,先静态成员,在非静态成员
- 显示的静态成员初始化--静态块
Spoon { int i; static { i = 47; } main(String[] args) { TODO Auto-generated method stub } }
- 非静态成员变量初始化
- 和静态成员初始化一模一样,就是去掉static关键字
- 在构造函数执行之前执行
String str3; 静态成员初始化 } 非静态成员初始化 { str3 = aaa; str2 = mmm";也可以给静态成员初始化 } print(){ System.str1 :"+ str1); System.str2 : str2); } main(String[] args) { Test14 t = Test14(); print(); System.str3 : t.str3); } }
这个例子说明如下问题:
8. 可变参数列表
NewVarArgs { printArray(Object ... args){ for(Object arg: args) { System.out.print(arg + ); } System.); } main(String[] args) { printArray(new Integer(47),new Float(3.14),1)">new Double(2.333)); printArray((Object[])new Integer[]{1,2,1)">3,1)">4}); printArray(); } }
- 使用 ...的方式定义可变数组
- 当指定参数时,编译器实际上会自动填充为一个数组,你获取的仍旧是一个数组, 因此可以再方法内部使用foreach循环打印
- 当参数本身就是一个数组时, 编译器直接接受这个数组
- 可变参数必须是方法的最后一个参数
- 可变参数可以和自动包装机制和谐相处。
- 参数是Integer类型, 如果换地过来的是int类型, 可以自动转换
- 可变参数的重载
9. 枚举类型
enum Spiciness { NOT,MILD,MEDIUM,HOT,FALMING } SimpleEnumUse { main(String[] args) { Spiciness falming = Spiciness.FALMING; System.out.println(falming); } }
- 枚举定义使用enum关键字
- 枚举类型的实例是常量,所以命名要求全部大写.
- 在创建enum时,编译器会自动添加一些使用的特性.
- 自动创建toSring()方法,以便可以很方便的显示enum实例的名字,这正是上面可以产生其输出答案的原因。
- 自动生成oridinal()方法: 用来表示enum常量声明的顺序
- 自动生成static value()方法:按照enum生成的顺序,产生有这些常量构成的数组.
(Spiciness s: Spiciness.values()){ System.out.println(s + ," + s.ordinal()); } } }
- enum是在有限的集合中进行选择,因此其与switch是绝佳组合.
main(String[] args) { Burrito plain = Burrito(Spiciness.HOT); Burrito greenChile = Burrito(Spiciness.MEDIUM); Burrito jalapeno = Burrito(Spiciness.NOT); plain.describe(); greenChile.describe(); jalapeno.describe(); } } Burrito { Spiciness degree; Burrito(Spiciness degree){ this.degree = degree; } describe(){ switch(degree){ case NOT: System.not); break; MILD: MEDIUM: System.medium HOT: System.hot FALMING: default: System.falming; } } }