ThinkingInJava 学习 之 0000004 初始化与清理
1. 用构造器确保初始化。
不接受任何参数的构造器叫做默认构造器。
Tree tree = new Tree(12);
如果Tree(int)时Tree类的唯一的构造器,那么编译器将不会允许你以其他任何方式创建Tree对象。
2. 方法重载
方法名相同而形式参数不同
1. 区分重载方法
每个重载的方法都必须有一个独一无二的参数类型列表。
1. 参数个数
2. 参数类型
3. 参数顺序
public void println(int decimal){ System.out.println("int decimal : " + decimal + " String str : " + "Oracle"); } public void println(String str){ System.out.println("int decimal : " + 12 + " String str : " + str); } public void println(int decimal,String str){ System.out.println("int decimal : " + decimal + " String str : " + str); } public void println(String str,int decimal){ System.out.println("int decimal : " + decimal + " String str : " + str); }
2. 涉及基本类型的重载
如果实参数据类型小于形参数据类型,实参数据类型就会被提升。char型实参会提升为int型。
如果实参数据类型大于形参数据类型,通过类型转换执行窄化转换。
3. 以返回值区分重载方法
NO WAY
3. 默认构造器
如果类中无构造器,编译器会自动创建默认构造器。
4. this关键字
1. 在构造器中调用构造器
在构造器中,如果为this添加了参数列表,将产生对符合此参数列表的某个构造器的明确调用。
public Flower() { // System.out.println("this is this Constructor");//编译错误:必须将构造器调用置于最起始处 this(1,"lo"); // this(2,"ve");//编译错误:不能调用两个 } public Flower(int flower, String flowerName) { this.flower = flower; this.flowerName = flowerName; }
2. static 的含义
Java 有垃圾回收器负责回收无用对象占据的内存资源。但也有特殊情况:假定对象(并非使用new)获得了一块“特殊”的内存区域,由于垃圾回收器只知道shifang那些经由new分配的内存,所以它不知道该如何释放该对象的这块“特殊”内存。
为了应对这种情况,Java允许在类中定义一个名为finalize()的方法。
工作原理“假定”:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。所以要是打算用finalize(),就能在垃圾回收时刻做一些重要的清理工作。
⊙ C++中的对象一定会使用析构函数销毁
⊙ Java中的对象并非总是被垃圾回收
⊙ 对象可能不被垃圾回收
⊙ 垃圾回收并不等于“析构”
Class : Potato
package lime.thinkingInJava._005001.ins; public class Potato { private static int idBuilder = 0; private int id; public Potato() { id = ++idBuilder; } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("call Finalize() And id = " + id); System.exit(0); } }
Class : Main
package lime.thinkingInJava._005001.ins; public class _InsMain { public static void main(String[] args) { { for(int i = 0;i < Integer.MAX_VALUE;i++){ new Potato(); } } } }
Console :
call Finalize() And id = 447009
1. finalize()的用途
finalize()方法不能作为通用的清理方法。
⊙ 垃圾回收只与内存有关
使用垃圾回收器的唯一原因是为了回收程序不再使用的内存。所以对于与垃圾回收有关的任何行为来说(尤其时finalize()方法),它们必须同内存及其回收有关。
但这是否以为着要是对象中含有其他对象,finalize()就应该明确释放那些对象呢?不,无论对象时如何创建的,垃圾回收器都会负责释放对象占据的所有内存。这就将对finalize()的需求限制到一种特殊情况,即通过某种创建对象方式以外的方式为对象分配了存储空间。不过,Java中一切皆为对象,那这种特殊情况时怎么回事呢?
之所以要有finalize(),是由于在分配内存时可能采用了类似C语言中的做法,而非Java中的通常做法。这种情况主要发生在使用“本地方法”的情况下,本地方法是一种在Java中调用非Java代码的方式。
本地方法目前只支持C和C++,但它们可以调用其他语言写的代码,所以实际上可以调用任何代码。
在非Java代码中,也许会调用C的malloc()函数鞋类来分配存储空间,而且除非调用了free()函数,否则存储空间将得不到释放,从而造成内存泄露。当然,free()时C和C++中的函数,所以需要在finalize()中用本地方法调用它。
至此,已经明白了不要过多地使用finalize()的道理了。它确实不是进行普通的清理工作的合适场所。那么,普通的清理工作应该在哪里执行呢?
2. 你必须实施清理
3. 终结条件
4. 垃圾回收器如何工作
6. 成员初始化
对于方法的局部变量,Java 以编译时错误的形式来提示程序员进行变量的初始化。
对于类变量,Java 会默认初始化值。基本类型数据成员默认初始值,对象引用默认初始化为null。
1. 指定初始化
Class : InitialValues
package lime.thinkingInJava._005001.exercise; import java.text.SimpleDateFormat; import java.util.Date; public class InitialValues { // 基本数据类型 默认初始化 int genderCode; // 对象引用 默认初始化 Date date; // 基本数据类型指定初始化 boolean bool = true; char ch = 'x'; byte b = 47; short s = 0xff; int i = 999; long lng = 1; float f = 3.14f; double d = 3.14159; // 对象引用指定初始化 Depth depth = new Depth(); // 调用方法(无参) String birthday = getBirthdayDate(); // 调用方法(有参:须初始化) String gender = getSelfGender(genderCode); private String getSelfGender(int genderCode) { switch (genderCode) { case 0: return "男"; case 1: return "女"; default: return "ladyboy"; } } public String getBirthdayDate() { if (null == date) { return "1992-03-01"; } else { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date); } } @Override public String toString() { return "InitialValues{" + "\n" + "genderCode=" + genderCode + ", date=" + date + "\n" + ", bool=" + bool + ", ch=" + ch + ", b=" + b + ", s=" + s + ", i=" + i + ", lng=" + lng + ", f=" + f + ", d=" + d + "\n" + ", depth=" + depth + "\n" + ", birthday='" + birthday + '\'' + "\n" + ", gender='" + gender + '\'' + "\n" + '}'; } }
7. 构造器初始化
在运行时刻,可以方法或执行某些动作来确定初值,但是却无法阻止自动初始化的进行,自动初始化将在构造器被调用之前发生。
package lime.thinkingInJava._005001.exercise; public class Counter { // 自动初始化发生在构造函数调用之前 int i; public Counter() { System.out.println("i = " + i); i = 131; } @Override public String toString() { return "Counter{" + "i=" + i + '}'; } }
1. 初始化顺序
在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。
package lime.thinkingInJava._005001.exercise; class Window{ public Window(int marker) { System.out.println("Window(" + marker + ")"); } } public class House { // 构造器之前 定义变量 Window w1 = new Window(1); public House() { System.out.println("House()"); w3 = new Window(33); } // 构造器之后 定义变量 Window w2 = new Window(2); void f(){ System.out.println("f()"); } // 方法之后 定义变量 Window w3 = new Window(3); }
2. 静态数据的初始化
Class : StaticInitialization
package lime.thinkingInJava._005001.exercise; class Bowl{ Bowl(int marker){ System.out.println("Bowl(" + marker + ")"); } void f1(int marker){ System.out.println("f1(" + marker + ")"); } } class Table { static Bowl bowl1 = new Bowl(1); Table(){ System.out.println("Table()"); bowl2.f1(1); } void f2(int marker){ System.out.println("f2(" + marker + ")"); } static Bowl bowl2 = new Bowl(2); } class Cupboard { Bowl bowl3 = new Bowl(3); static Bowl bowl4 = new Bowl(4); Cupboard(){ System.out.println("Cupboard()"); bowl4.f1(2); } void f3(int marker){ System.out.println("f3(" + marker + ")"); } static Bowl bowl5 = new Bowl(5); } public class StaticInitialization { public static void main(String[] args){ System.out.println("Creating new Cupboard() in main"); new Cupboard(); System.out.println("Creating new Cupboard() in main"); new Cupboard(); table.f2(1); cupboard.f3(1); } static Table table = new Table(); static Cupboard cupboard = new Cupboard(); }
Console :
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)
静态数据成员 初始化时刻:只有在第一个Table对象创建(或者第一次访问静态数据)的时候,静态数据成员才会被初始化,此后 静态数据成员 不会再次被初始化。
初始化的顺序:先静态对象(静态数据成员),而后时“非静态”对象。
对象的创建过程:(Dog)
1. 即使没有显示地使用static关键字,构造器实际上也是静态方法。因此,当首次创建类型为Dog的对象时(构造器可以看成静态方法),或者Dog类的静态方法/静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件。
2. 然后载入Dog.class(这将创建一个Class对象),有关静态初始化的所有动作都会执行。因此,静态初始化只在Class对象首次加载的时候进行一次。
3. 当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间。
4. 这块存储空间会被清零,这就自动地将Dog对象中的所有基本类型数据都设置成了默认值(对数字来说就是0,对布尔类型和字符型也相同),而引用则被设置成了null。
5. 执行所有出现于字段定义出的初始化动作。
6. 执行构造器。
3. 显式的静态初始化(静态代码块)
Java 允许将多个静态初始化动作组织成一个特殊的“静态子句”,即 “静态块”。
当首次生成这个类的一个对象时,或者首次访问属于那个类的静态数据成员时(即便从未生成过那个类的对象),静态块 才执行。
Class : ExplicitStatic
package lime.thinkingInJava._005001.exercise; public class ExplicitStatic { static { j = 110; } static int j; static { i = 10; } static int i; static { i = 11; } public static void main(String[] args){ System.out.println("j = " + j); System.out.println("i = " + i); } }
Console :
j = 110
i = 11
4. 非静态实例初始化(代码块)
非静态代码块和非静态成员变量 与 静态代码块和静态成员变量 类比:
1. 初始化顺序:静态 --> 非静态;成员变量的定义(声明及默认初始化) --> 成员变量的指定初始化
2. 归属: 静态static 属性(静态代码块和静态成员变量)属于类,存储在 方法区;非静态属性(非静态代码块和非静态成员变量)属于对象,存储在 堆空间;
3. 执行时刻: 静态static 属性 在类加载时执行(仅执行一次);非静态属性 在类被实例化时执行(每有一个对象实例化就执行一次)。
Class : Mugs
package lime.thinkingInJava._005001.exercise; class Mug{ Mug(int marker){ System.out.println("Mug(" + marker + ")"); } void f(int marker){ System.out.println("f(" + marker + ")"); } } public class Mugs { Mug mug1; Mug mug2; { mug1 = new Mug(1); mug2 = new Mug(2); System.out.println("mug1 & mug2 initialized"); } Mugs(){ System.out.println("Mugs()"); } Mugs(int i){ System.out.println("Mugs(int)"); } public static void main(String[] args){ System.out.println("inside main()"); new Mugs(); System.out.println("new Mugs() completed"); new Mugs(1); System.out.println("new Mugs(1) completed"); } }
Console :
inside main() Mug(1) Mug(2) mug1 & mug2 initialized Mugs() new Mugs() completed Mug(1) Mug(2) mug1 & mug2 initialized Mugs(int) new Mugs(1) completed
8. 数组初始化
1. 可变参数列表
9. 枚举类型
Enum : Spiciness
package lime.thinkingInJava._005009000.exercise; public enum Spiciness { NOT,MILD,MEDIUM,HOT,FLAMING }
Main :SimpleEnumUse
package lime.thinkingInJava._005009000.exercise; public class SimpleEnumUse { public static void main(String[] args){ Spiciness howHot = Spiciness.MEDIUM; System.out.println(howHot); } }
编译器自动添加一些特性:
1. toString()方法,以便显示某个enum实例的名字。
2. ordinal()方法,用来标识某个特定enum常量的声明顺序。
3. static values()方法,用来按照enum常量的声明顺序,产生由这些常量值构成的数组。
package lime.thinkingInJava._005009000.exercise; public class EnumOrder { public static void main(String[] args){ for(Spiciness s : Spiciness.values()){ System.out.println(s + ", ordinal " + s.ordinal()); } } }
enum 可以在switch语句内使用。
啦啦啦
啦啦啦
.
啦啦啦