初始化与清理(二)

6. 构造器初始化

可以使用构造器初始化,即在运行时刻,可以调用方法或执行某些动作来确定初值。但要牢记:无法阻止自动初始化的进行,它将在构造器调用之前发生。如下代码,首先i会先被置0,然后变为7:

1 public class Counter {
2     int i;
3     Counter() {
4         i = 7;
5     }
6 }

6.1 初始化顺序

在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。例如:

在House类中,故意把几个Window对象定义散布到各处,以证明它们全都会在调用构造器或其他方法之前得到初始化

 1 /**
 2  * 初始化的顺序
 3  */
 4 
 5 class Window {
 6     Window(int marker) {
 7         System.out.println("Window(" + marker + ")");
 8     }
 9 }
10 class House { 11 Window w1 = new Window(1); // 构造器之前 12  House() { 13 System.out.println("House"); 14 w3 = new Window(33); // 重新初始化 15  } 16 Window w2 = new Window(2); // 构造器之后 17 void f() { 18 System.out.println("f()"); 19  } 20 Window w3 = new Window(3); 21 } 22 public class OrderOfInit { 23 public static void main(String[] args) { 24 House h = new House(); 25  h.f(); 26  } 27 } 28 29 输出: 30 Window(1) 31 Window(2) 32 Window(3) 33 House 34 Window(33) 35 f()

6.2 静态数据初始化       

无论创建多少个对象,静态数据都只占用一份存储区域。static关键字不能用于局部变量。如果一个域是静态的基本类型域,且没有初始化,那么它就会获得基本类型的标准初值;如果它是一个对象引用,那么它的默认初值就是null。如果定义时初始化,和非静态数据没有区别。

 1 /**
 2  * 静态数据初始化
 3  */
 4 class Bowl {
 5     Bowl(int marker) {
 6         System.out.println("Bowl(" + marker + ")");
 7     }
 8     void f1(int marker) {
 9         System.out.println("f1(" + marker +")"); 10  } 11 } 12 13 class Table { 14 Bowl bowl3 = new Bowl(3); 15 static Bowl bowl1 = new Bowl(1); 16  Table() { 17 System.out.println("Table()"); 18 bowl2.f1(1); 19  } 20 static void s() { 21 System.out.println("Table static method s()"); 22  } 23 void f2(int marker) { 24 System.out.println("f2(" + marker +")"); 25  } 26 static Bowl bowl2 = new Bowl(2); 27 } 28 //public class StaticInit { 29 // public static void main(String[] args) { 30 // System.out.println("Creating new Cupboard() in main"); 31 // Bowl bowl1 = Table.bowl1; 32 // } 33 //} 34 //输出: 35 //Creating new Cupboard() in main 36 //Bowl(1) 37 //Bowl(2) 38 39 //public class StaticInit { 40 // public static void main(String[] args) { 41 // System.out.println("Creating new Cupboard() in main"); 42 // Table.s(); 43 // } 44 //} 45 //输出: 46 //Creating new Cupboard() in main 47 //Bowl(1) 48 //Bowl(2) 49 //Table static method s() 50 public class StaticInit { 51 public static void main(String[] args) { 52 System.out.println("Creating new Cupboard() in main"); 53 table.f2(1); 54  } 55 static Table table = new Table(); 56 } 57 58 输出: 59 Bowl(1) 60 Bowl(2) 61 Bowl(3) 62 Table() 63 f1(1) 64 Creating new Cupboard() in main 65 f2(1)

由输出可见静态初始化只有在必要时刻才会进行,只有在一个Table对象被创建(或者第一次访问静态数据或静态方法,见注释代码部分)的时候,它们才会被初始化。此后静态变量不会初始化。初始化的顺序是先静态对象,后非静态对象。

对象创建的过程,假设有个名为Dog的类:

  1. 即使没有显式的使用static关键字,构造器实际上也是静态方法。因此当首次创建为Dog的对象时,或者Dog类的静态方法/静态域(静态成员变量)首次被访问时,Java解释器必须查找类的路径,以定位Dog.class文件
  2. 然后载入Dog.class(后面会学到),有关静态初始化的动作都会执行。因此静态初始化只在Class对象首次加载的时候进行一次
  3. 当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间
  4. 这块存储空间会被清零,这就自动将Dog对象中的所有基本类型数据设置成了默认值,而引用则被设置成了null
  5. 执行所有出现于字段定义出的初始化动作
  6. 执行构造器

6.3 显示的静态初始化

Java允许将多个静态初始化组成一个特殊的“静态初始化子句”(也叫“静态块”),例如:

1 class Cups {
2     static Cup cup1;
3     static Cup cup2;
4     // 静态子句(静态块)
5     static {
6         cup1 = new Cup(1);
7         cup2 = new Cup(2); 8  } 9 }

 与其他静态初始化一样,静态子句仅执行一次:当首次生成这个类的一个对象时,或者首次访问属于这个类的静态数据成员时。例如:

 1 /**
 2  * 显式的静态初始化
 3  */
 4 class Cup {
 5     Cup(int marker) {
 6         System.out.println("Cup(" + marker +")");
 7     }
 8     void f(int marker) {
 9         System.out.println("f(" + marker +")"); 10  } 11 } 12 13 class Cups { 14 static Cup cup1; 15 static Cup cup2; 16 // 静态子句(静态块) 17 static { 18 cup1 = new Cup(1); 19 cup2 = new Cup(2); 20  } 21  Cups(){ 22 System.out.println("Cups()"); 23  } 24 } 25 26 public class ExplicitStatic { 27 public static void main(String[] args) { 28 System.out.println("Inside main()"); 29 Cups.cup1.f(99); 30  } 31 // static Cups cups1 = new Cups(); 32 } 33 34 输出: 35 Inside main() 36 Cup(1) 37 Cup(2) 38 f(99)

6.4 非静态实例初始化

Java中也有“实例初始化子句”,用来初始化每一个对象的非静态变量,例如:

 1 /**
 2  * 非静态实例初始化
 3  */
 4 /**
 5  * 非静态实例初始化
 6  */
 7 class Mug {
 8     Mug(int marker) {
 9         System.out.println("Mug(" + marker +")");
10     }
11 }
12 public class Mugs { 13  Mug mug1; 14  Mug mug2; 15 // 实例初始化子句 16  { 17 mug1 = new Mug(1); 18 mug2 = new Mug(2); 19 System.out.println("mug1 & mug2 init"); 20  } 21  Mugs() { 22 System.out.println("Mugs()"); 23  } 24 25 public static void main(String[] args) { 26 new Mugs(); 27 new Mugs(); 28  } 29 } 30 输出: 31 Mug(1) 32 Mug(2) 33 mug1 & mug2 init 34 Mugs() 35 Mug(1) 36 Mug(2) 37 mug1 & mug2 init 38 Mugs()

“实例初始化子句”相比“静态初始化子句”只少了static关键字,从输出可以看出实例初始化自己是在构造器之前执行的。

7. 数组初始化       

定义:类型名+[],例如:

int[] a1; 

方括号也可以放在标识符后面:

int a1[]; 

两种格式是一样的,推荐使用前一种,能明确表明定义的是“一个int类型的数组”,还有就是如果使用第二种定义有时与自己预期不符,比如想定义两个int数组,实际上只有a3是int数组,a4是整形变量:int a3[], a4;

编译器不允许指定数组的大小。因为创建的只是对数组的一个引用,并且没有给对象本身分配空间。数组初始化的几种方式:

  1. 定义的时候初始化: int[] a1 = {1, 2, 3}; // 这种方式只能在定义的时候使用
  2. 使用new创建,不赋初值: int[] a3 = new int[5]; // 这种方式可以先定义a3,在其他任何地方都可以初始化。因为没有赋初值,默认数组所有元素都为0
  3. 使用new创建,并赋初值: int[] a3 = new int[]{1, 2, 3, 4,5,}; // 这种方式同样可以先定义a3,在任何地方都可以初始化,其中初始化列表中的最后一个逗号可选(维护长列表方便,增加也方便)

所有数组都有length的成员,可以用来获取数组元素的个数: a.length

 1 public class ListTest {
 2 
 3     static String listToString(int[] a) {
 4         String l = "";
 5         for (int i = 0; i < a.length; i++) {
 6             l += a[i] + " ";
 7  } 8 return l; 9  } 10 public static void main(String[] args) { 11 int[] a1 = {1, 2, 3, 4, 5}; 12 // int[] a1 = {1, 2, 3, 4, 5,}; //增加逗号也可以 13 int[] a2; 14 // a2 = {1, 2, 3, 4, 5}; // 编译报错,这种方式只能在定义时初始化 15 int[] a3 = new int[5]; // 使用new创建数组 16 int[] a4; // 先定义数组,在其他地方使用new初始化 17 int[] a5; // 先定义数组,在其他地方使用new初始化,并指定初值 18 a4 = new int[5]; 19 a5 = new int[]{1, 2, 3, 4,5,}; 20 System.out.println("a1 = " + listToString(a1)); 21 System.out.println("a3 = " + listToString(a3)); 22 System.out.println("a4 = " + listToString(a4)); 23 System.out.println("a5 = " + listToString(a5)); 24  } 25 } 26 输出: 27 a1 = 1 2 3 4 5 28 a3 = 0 0 0 0 0 29 a4 = 0 0 0 0 0 30 a5 = 1 2 3 4 5

7.1 可变参数

printArray函数可以接受0-n个任意对象, f函数至少接收一个整数,接收0-n个字符串:

 1 public class NewArgs {
 2     static void printArray(Object... args) {
 3         for (Object obj : args) {
 4             System.out.print(obj + " ");
 5         }
 6  System.out.println(); 7  } 8 static void f(int required, String... strs) { 9 System.out.print("requied: " + required + " "); 10 for (String s : strs) { 11 System.out.print(s + " "); 12  } 13  System.out.println(); 14  } 15 public static void main(String[] args) { 16 printArray(new Integer(47), new Float(3.14), new Double(11.11)); 17 printArray(47, 3.14f, 11.11); 18 printArray("one", "two", "three"); 19 printArray(); // 传空也可以 20 // f(); // 编译报错,至少传递一个参数 21 f(0); 22 f(1, "one"); 23 f(2, "one", "two"); 24  } 25 } 26 输出: 27 47 3.14 11.11 28 47 3.14 11.11 29 one two three 30 31 requied: 0 32 requied: 1 one 33 requied: 2 one two

8. 枚举类型

创建枚举类型Grade,具有A、B、C、D 4个值,枚举都是常量,使用大写字母表示(如果一个名字中有多个单词,用下划线分隔):

1 public enum Grade {
2     A, B, C, D
3 }

使用enum,需要创建一个该类型的引用,并将其赋值给某个实例:

1 Grade grade = Grade.A

创建enum时,编译器会自动增加一些有用的特性。例如,创建toString()方法,可以方便显示enum的实例名字;ordinal()方法,用来表示某个特定的enum常量的生命顺序;static values()方法,用enum常量的生命顺序,产生由这些常量构成的数组:

 1 public class Ranking {
 2     public static void main(String[] args) {
 3         for (Grade grade : Grade.values()) {
 4             System.out.println(grade + " ordinal " + grade.ordinal());
 5         }
 6  } 7 } 8 输出: 9 A ordinal 0 10 B ordinal 1 11 C ordinal 2 12 D ordinal 3

还可用于switch语句:

1 // Grade.java
2 public enum Grade {
3     A, B, C, D
4 }
 1 //Ranking.java
 2 public class Ranking {
 3     Grade grade;
 4     Ranking(Grade grade) {
 5         this.grade = grade;
 6     }
 7     public void getRankingDes() { 8 switch (grade) { 9 case A: 10 System.out.println("优秀"); 11 break; 12 case B: 13 System.out.println("良好"); 14 break; 15 case C: 16 System.out.println("及格"); 17 break; 18 case D: 19 System.out.println("不及格"); 20 break; 21 default: 22 System.out.println("错误输入"); 23  } 24  } 25 public static void main(String[] args) { 26 Ranking rank1 = new Ranking(Grade.A); 27 Ranking rank2 = new Ranking(Grade.D); 28  rank1.getRankingDes(); 29  rank2.getRankingDes(); 30  } 31 } 32 33 输出: 34 优秀 35 不及格

猜你喜欢

转载自www.cnblogs.com/songchj-bear/p/10381613.html