初始化与清理(一)

随着计算机革命的发展,“不安全”的编程方式已逐渐成为编程代价高昂的主因之一。

初始化和清理正是涉及安全的两个问题。

1. 用构造器确保初始化

在Java中,通过构造器,类的设计者可以确保每个对象都会得到初始化。创建对象时,如果类具有构造器,Java就会在用户操作对象之前自动调用相应的构造器,从而保证了初始化的进行。

构造器的特点

  1. 构造器名称和类名完全相同
  2. 不接受任何参数的构造器叫做“默认构造器”或者“无参构造器”
  3. 如果没有创建任何构造器,编译器会默认创建一个“无参构造器”
  4. 可以自己创建构造器(有参无参都可以),但是这样编译器就不会创建“无参构造器”,如果需要使用,则必须自己创建

2. 方法重载

Java中构造器的名称必须和类名一致,也就是说只能有一个构造器名称,那么如果想用多种方式创建一个对象应该怎么办? 这就要用到方法重载。同时,尽管方法重载是构造器所必需的,但它也可用于其他方法。例如:

 1 /**
 2  * 重载
 3  */
 4 public class Tree {
 5     Tree() {
 6         System.out.println("Tree");
 7     }
 8     Tree(int height) {
 9         System.out.println("Tree height = " + height);
10     }
11 
12     void f() {
13         System.out.println("f()");
14     }
15 
16     void f(String s) {
17         System.out.println("f(String)");
18     }
19 
20     public static void main(String[] args) {
21         Tree t1 = new Tree();
22         Tree t2 = new Tree(1);
23         t1.f();
24         t1.f("test");
25 
26     }
27 }
28 输出:
29 Tree
30 Tree height = 1
31 f()
32 f(String)

2.1 区分重载的方法

通过参数列表:每个重载的方法都一个独一无二的参数类型列表,甚至不同的参数顺序也可以区分(这个要求所有的参数类型不能完全相同,尽量不要这么弄,会使代码难以维护)

疑问:是否可以通过返回值区分重载方法呢?

答案:不可以

如下面的重载函数,如果直接调用f(),编译器如何知道调用哪个方法。所以通过返回值区分重载方法是行不通的。

1 void f() { }
2 int f() {return 1;}

2.2 涉及基本类型的重载

如果传入的数据类型小于方法中声明的参数类型,实际数据类型会被提升。通过下面的代码可以看出基本类型向上转型的顺序:char->short->int->long->float->double;byte->short->int->long->float->double

  1 /**
  2  * 基本类型的重载
  3  */
  4 public class PrimitiveOverloading {
  5 
  6     void f1(char x) { System.out.print("f1(char) "); }
  7     void f1(byte x) { System.out.print("f1(byte) "); }
  8     void f1(short x) { System.out.print("f1(short) "); }
  9     void f1(int x) { System.out.print("f1(int) "); }
 10     void f1(long x) { System.out.print("f1(long) "); }
 11     void f1(float x) { System.out.print("f1(float) "); }
 12     void f1(double x) { System.out.print("f1(double) "); }
 13 
 14     void f2(byte x) { System.out.print("f2(byte) "); }
 15     void f2(short x) { System.out.print("f2(short) "); }
 16     void f2(int x) { System.out.print("f2(int) "); }
 17     void f2(long x) { System.out.print("f2(long) "); }
 18     void f2(float x) { System.out.print("f2(float) "); }
 19     void f2(double x) { System.out.print("f2(double) "); }
 20 
 21     void f3(short x) { System.out.print("f3(short) "); }
 22     void f3(int x) { System.out.print("f3(int) "); }
 23     void f3(long x) { System.out.print("f3(long) "); }
 24     void f3(float x) { System.out.print("f3(float) "); }
 25     void f3(double x) { System.out.print("f3(double) "); }
 26 
 27     void f4(int x) { System.out.print("f4(int) "); }
 28     void f4(long x) { System.out.print("f4(long) "); }
 29     void f4(float x) { System.out.print("f4(float) "); }
 30     void f4(double x) { System.out.print("f4(double) "); }
 31 
 32     void f5(long x) { System.out.print("f5(long) "); }
 33     void f5(float x) { System.out.print("f5(float) "); }
 34     void f5(double x) { System.out.print("f5(double) "); }
 35 
 36     void f6(float x) { System.out.print("f6(float) "); }
 37     void f6(double x) { System.out.print("f6(double) "); }
 38 
 39     void f7(double x) { System.out.print("f7(double) "); }
 40 
 41     void testChar() {
 42         System.out.print("char: ");
 43         char x = 'x';
 44         f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);
 45         System.out.println();
 46     }
 47     void testByte() {
 48         System.out.print("byte: ");
 49         byte x = 0;
 50         f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);
 51         System.out.println();
 52     }
 53     void testShort() {
 54         System.out.print("short: ");
 55         short x = 0;
 56         f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);
 57         System.out.println();
 58     }
 59     void testInt() {
 60         System.out.print("int: ");
 61         int x = 0;
 62         f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);
 63         System.out.println();
 64     }
 65     void testLong() {
 66         System.out.print("long: ");
 67         long x = 0;
 68         f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);
 69         System.out.println();
 70     }
 71     void testfloat() {
 72         System.out.print("float: ");
 73         float x = 0;
 74         f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);
 75         System.out.println();
 76     }
 77     void testDouble() {
 78         System.out.print("double: ");
 79         double x = 0;
 80         f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);
 81         System.out.println();
 82     }
 83     public static void main(String[] args) {
 84         PrimitiveOverloading p = new PrimitiveOverloading();
 85         p.testChar();
 86         p.testByte();
 87         p.testShort();
 88         p.testInt();
 89         p.testLong();
 90         p.testfloat();
 91         p.testDouble();
 92     }
 93 }
 94 
 95 输出:
 96 char: f1(char) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double) 
 97 byte: f1(byte) f2(byte) f3(short) f4(int) f5(long) f6(float) f7(double) 
 98 short: f1(short) f2(short) f3(short) f4(int) f5(long) f6(float) f7(double) 
 99 int: f1(int) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double) 
100 long: f1(long) f2(long) f3(long) f4(long) f5(long) f6(float) f7(double) 
101 float: f1(float) f2(float) f3(float) f4(float) f5(float) f6(float) f7(double) 
102 double: f1(double) f2(double) f3(double) f4(double) f5(double) f6(double) f7(double)

如果传入的实际参数**大于**重装方法声明的形式参数,需要通过类型转换,否则编译器会报错:

1 void f8(short x) { System.out.print("f8(short) "); }
2  // p.f8(5); //编译报错
3 p.f8((short)5);

3. this关键字

this表示“调用方法的那个对象”的引用,只能在方法内部使用:

  1. 可以在构造器中使用,调用其他的构造器
  2. 当方法的参数和成员变量名字相同时,可使用this关键字区分成员变量和传入参数

3.1 在构造器中调用构造器

可能为了一个类写了多个构造器,有时想在一个构造器中调用另一个构造器,以避免重复代码,可使用this关键字做到这点。在构造器中,如果为this添加了参数列表(空列表也可以),那么就会对符合此参数列表的某个构造器进行调用。

使用this调用构造器限制条件:

  1. 只能在构造器中使用this调用其他的构造器,其他方法不行
  2. 只能调用一次
  3. 必须是第一个被执行的语句
 1 public class Flower {
 2     int petalCount = 0;
 3     String s = "initial value";
 4 
 5     Flower(int petals) {
 6         petalCount = petals;
 7         System.out.println("Constructor int arg only, petalCount = " + petalCount);
 8     }
 9 
10     Flower(String s, int petals) {
11 
12         this(petals);
13         // this(s); // 编译报错,只能调用一次
14         this.s = s; // 使用this关键字代表数据成员,防止和参数s混淆
15         System.out.println("String & int args");
16     }
17 
18     Flower() {
19         this("hi", 47);
20         System.out.println("default constructor (no args)");
21     }
22 
23     void pringPetalCount() {
24         // this(11); // 编译报错,只能在构造器中使用
25         System.out.println("petalCount = " + petalCount + " s = " + s);
26     }
27 
28     public static void main(String[] args) {
29         Flower x = new Flower();
30         x.pringPetalCount();
31     }
32 }
33 
34 输出:
35 Constructor int arg only, petalCount = 47
36 String & int args
37 default constructor (no args)
38 petalCount = 47 s = hi

3.2 static方法的含义

static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来可以。并且在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。

4. 清理

java中有垃圾回收器负责回收无用对象占据的内存,但是什么时候会执行垃圾清理不确定,有可能你创建的对象在程序运行过程中永远不会被清理:

  1. 对象可能不被垃圾回收
  2. 垃圾回收并不等于析构
  3. 垃圾回收只与内存有关

几种常见的垃圾清理机制:

垃圾清理机制 简要描述 缺陷
引用计数 简单但速度慢。每个对象都有一个引用计数,当有引用连接到对象时,引用计数加1。当引用离开作用域或者被置为null时,引用及时减一。当对象的引用计数为0时,释放其占用的空间。 对象之间存在循环引用,可能出现“对象应该被回收,但是引用计数却不为0”
停止-复制 从堆栈和静态存储区出发,遍历所有对象引用,进而找出所有存活的对象。暂停程序的运行,将所有存活的对象拷贝到另一个堆,当对象被复制到新堆时,它们是一个挨着一个的。 效率低。首先需要两个堆,需要多维护一倍的空间。程序运行过程中可能只产生少量的垃圾,这种拷贝属于浪费
标记-清扫 从堆栈和静态存储区出发,遍历所有对象引用,进而找出所有存活的对象。每当找到一个存活对象会给对象一个标记,这个过程中不会回收任何对象,当全部标记工作方程时,才会清理。没有标记的对象被释放。 清理后剩余的空间是不连续的

5. 成员初始化

方法的局部变量不会给默认值,必须自己进行初始化;类的成员变量,会给默认值:

 1 /**
 2  *
 3  */
 4 class Base {
 5 
 6 }
 7 public class DataInit {
 8     boolean t;
 9     char c;
10     byte b;
11     short s;
12     int i;
13     long l;
14     float f;
15     double d;
16     String ss;
17     DataInit reference;
18 
19     void f() {
20         int j;
21         // System.out.println(j); // 编译报错
22         j = 1;
23         System.out.println("j = " + j);
24     }
25 
26     void printInitValues() {
27         System.out.println("数据类型       默认初始化值");
28         System.out.println("boolean        " + t);
29         System.out.println("char           #" + c + "#"); // 是个空格
30         System.out.println("byte           " + b);
31         System.out.println("short          " + s);
32         System.out.println("int            " + i);
33         System.out.println("long           " + l);
34         System.out.println("float          " + f);
35         System.out.println("double         " + d);
36         System.out.println("String         " + ss);
37         System.out.println("reference      " + reference);
38     }
39     public static void main(String[] args) {
40         DataInit d = new DataInit();
41         d.printInitValues();
42     }
43 }
44 
45 输出:
46 数据类型       默认初始化值
47 boolean        false
48 char           # #
49 byte           0
50 short          0
51 int            0
52 long           0
53 float          0.0
54 double         0.0
55 String         null
56 reference      null

 

猜你喜欢

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