对象的初始化与清理

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

正确的初始化和清理过程对于程序的安全性和性能至关重要,不同的编程语言对此具有不同的处理机制。C++中利用构造器完成初始化,当对象不再被使用时调用析构函数完成销毁,程序员需要手动完成内存释放管理过程;Java也采用了构造器完成初始化,同时提供了垃圾会收器对不再使用的内存资源进行自动管理,减轻了程序员管理手动管理内存的编程负担。

1 重载

Java(和C++)中,构造器是强制重载方法名的原因之一。因为构造器常用于对类中的域完成初始化,当类中存在自定义参数列表的构造器时,系统将调用对应的构造器完成初始化;而当类中不存在自定义参数列表的构造器时,将自动调用类的默认无参构造器,而构造器必须保持与类名相同,所以为了调用的灵活性,需要将构造器重载。

重载可以视为程序实现多态的一种方式,运行时根据参数列表的不同最终确定调用哪个重载函数。重载既可以是构造函数也可以是普通的成员函数【注:编译器区分重载方法时,只能以类名和方法的形参列表(参数个数和类型的不同)作为标准,仅有返回类型不同的两个函数不是重载函数。】

  对象初始化时,常根据不同的重载函数完成域的初始化,因此理解重载是正确理解初始化过程的前提与基础。下面的例子同时示范了重载构造器和重载方法。

/**
 * Created by One on 2016/11/15.
 */
public class Tree {
    int height;//树高
    int age;//树龄

    Tree() {//无参构造器
        System.out.println("调用无参构造器" + "\t" + this.height + "\t" + this.age);
    }

    Tree(int height, int age) {
        this.height = height;
        this.age = age;
        System.out.println("调用自定义构造器" + "\t" + this.height + "\t" + this.age);
    }

    void info() {
        System.out.println("调用无参重载方法1");
    }

    void info(String message) {
        System.out.println("调用含参重载方法2" + "\t" + message);
    }
}
public class OverloadTest {    public static void main(String[] args) {        Tree t1=new Tree();        t1.info();        t1.info("对象t1调用");        Tree t2=new Tree(20,3);        t2.info();        t2.info("对象t1调用");    }}

输出结果:

调用无参构造器 0 0
调用无参重载方法1
调用含参重载方法2 对象t1调用
调用自定义构造器 20 3
调用无参重载方法1
调用含参重载方法2 对象t1调用

该例子表明,可利用构造器和成员函数的重载完成对象域初始化,大大提高程序的灵活性。每一个类均存在一个默认的构造器,当类定义中无显示构造器时系统会调用该默认无参构造器完成初始化工作,其中域会被自动赋予默认值:数字为0,布尔值而false,对象引用为null,不建议程序员开发人员非明确的对域进行初始化,否则可能会影响程序代码的可读性。

2 this关键字

Java中并不存在指针的概念,它摈弃了C/C++指针难以控制的噩梦,转而以引用代替,对象的引用可以理解为对象在内存中的地址,其中this表示对当前对象的引用。当明确需要指明当前对象的引用时才需用到this关键字,利用this关键字可以区分参数名称和域名称的混淆,同时this关键字在将当前对象传递给其他方法时很有用。下面的例子展示了this将当前对象传递给其他方法的过程。

/**
 * Created by One on 2016/11/15.
 */
public class Person {
    public void eat(Apple apple) {
        Apple peeled = apple.getPeeled();
        System.out.println("苹果很好吃!");
    }
}
public class Apple {
    Apple getPeeled() {
        return Peeler.peel(this);//将当前对象传递给其他方法
    }
}
public class Peeler {
    static Apple peel(Apple apple) {
        return apple;
    }
}
public class Test {
    public static void main(String[] args) {
       new Person().eat(new Apple());
    }
}

输出:

苹果很好吃!

类的初始化过程中常会出现构造器调用构造器的现象,若为this添加上了参数列表,那么就有了不同的含义,这将产生对符合次参数列表的某个构造器的明确调用。这是this关键字的又一重要应用。下面的例子表明了构造器调用构造器的过程。

/**
 * Created by One on 2016/11/15.
 */
public class Flower {
    int petalCount = 0;//花瓣数
    String s = "An initial flower";

    Flower(int petalCount) {
        this.petalCount = petalCount;
        System.out.println("构造器只含整型参数,花瓣数为:\t" + this.petalCount);
    }

    Flower(String str) {
        this.s = str;
        System.out.println("构造器只含字符串型参数,参数为:\t" + this.s);
    }

    Flower(String s, int petals) {
        this(s);//调用构造器
        //this(petals);//仅能调用一次
        this.petalCount = petals;
        System.out.println("字符串和整型参数");
    }

    Flower() {
        this("test", 100);
        System.out.println("默认无惨构造函数!");
    }

    void displayPetalCount() {
        System.out.println("花瓣数为:" + this.petalCount + "\t参数为:" + this.s);
    }

    public static void main(String[] args) {
        Flower flower = new Flower();
        flower.displayPetalCount();
    }
}

输出:

构造器只含字符串型参数,参数为: test
字符串和整型参数
默认无惨构造函数!
花瓣数为:100 参数为:test

程序调用顺序为Flower()->Flower(String s,int petals)->Flower(String s),有输出结果可看出当this添加上了参数列表,将产生对符合次参数列表的某个构造器的明确调用。这是this关键字的又一重要用法,对于减少代码重复,提高重构性具有重要作用。

 3 初始化顺序

   Java尽量保证所有的变量在使用前都能够得到恰当的初始化。类的每个基本类型数据成员都会有一个默认的初始值,但我们一般不建议这样做,开发时更多会指定值进行初始化。例如:

public class IntialValues{

//初始化基本类型
   boolean bool=true;
   char ch=‘A‘;
   byte b=100;
   short s=0xff;
   int i=3;
   long lng=1;
   float f=3.14f
   double d=3.14159

//初始化非基本类型(对象等)
   Person person=new Person();
} 

当然,我们也可以利用构造器进行初始化。

对象的初始化包括单个类的初始化和含有父类、子类继承层次的初始化。充分理解类内部的成员、静态数据域和数据块以及非静态数据的初始化顺序是理解含有继承关系的父类、子类初始化顺序的基础。所以,接下来先分别介绍初始化过程。

(1)类内部成员初始化

类内部域的初始化总是早于其他方法甚至是构造器,下面这个例子说明了这个机制。

/**
 * Created by One on 2016/11/8.
 */
public class Window {
    Window(int marker) {
        System.out.println("Window(" + marker + ")");
    }
}

public class House {
    Window w1 = new Window(1);
    Window w2 = new Window(2);
    Window w3 = new Window(3);

    House() {
        w3 = new Window(3);
        System.out.println("House() is invoking......");
    }

    void f1() {
        System.out.println("f1() is invoking......");
    }

}
public class OrderOfInitializaiton {
    public static void main(String[] args) {
        House h=new House();
        h.f1();
    }
}

输出:

Window(1)
Window(2)
Window(3)
Window(3)
House() is invoking......
f1() is invoking......

由以上代码可知,成员函数总是在调用构造器和其他方法前初始化,w3并调用2次,第一次在调用House()之前,第二次在House中被调用。

(2)静态数据的初始化

 【注:无论创建了多少个对象,静态数据都只占用一份存储区域。static关键字不能用于修饰局部变量,只能作用于域。

下面的代码例子很好的说明了静态存储区域的初始化过程,其中初始化顺序总是先静态对象然后非静态对象。

/**
 * Created by One on 2016/11/8.
 */
public class Bowl {
    Bowl(int marker) {
        System.out.println("Bow1(" + marker + ")");
    }

    void f1(int marker) {
        System.out.println("f1(" + marker + ")");
    }
}

public class Table {
    static Bowl bowl = new Bowl(1);
    static Bowl bow2 = new Bowl(2);

    Table() {
        System.out.println("Table()");
        bow2.f1(1);
    }

    void f2(int marker) {
        System.out.println("f2(" + marker + ")");
    }

}

public class Cupboard {
    static Bowl bowl4 = new Bowl(4);
    static Bowl bowl5 = new Bowl(5);
    Bowl bowl3 = new Bowl(3);

    Cupboard() {
        System.out.println("Cupboard()");
        bowl4.f1(2);
    }

    void f3(int maker) {
        System.out.println("f3(" + maker + ")");
    }
}
public class StaticIntializaiton {    static Table table = new Table();    static Cupboard cupboard = new Cupboard();

public static void main(String[] args) {        System.out.println("Creating new Cupbord in main");        new Cupboard();        System.out.println("Creating new Cupbord in main");        new Cupboard();        table.f2(1);        cupboard.f3(1);    }}
 

输出:

Bow1(1)
Bow1(2)
Table()
f1(1)
Bow1(4)
Bow1(5)
Bow1(3)
Cupboard()
f1(2)
Creating new Cupbord in main
Bow1(3)
Cupboard()
f1(2)
Creating new Cupbord in main
Bow1(3)
Cupboard()
f1(2)
f2(1)
f3(1)

 

时间: 2024-10-23 02:10:32

对象的初始化与清理的相关文章

五、初始化与清理

一.创建对象时,如果其类有构造器,Java就会在用户有能力操作对象之前自动调用相应的构造器,从而保证了初始化的进行. 二.每个重载方法都必须有独一无二的参数类型列表 如果传入的数据类型小于方法中声明的形式参数类型,实际数据类型就会被提升. 如果传入的数据类型较大,就得通过类型转换来执行窄化转换. 三.如果写的类中没有构造器,编译器就会自动帮你创建一个默认构造器. 四.this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用 this关键字也可以用于将当前对象传递给其他方法 使用thi

Chapter5_初始化与清理_用构造器初始化

接下来进入第五章,java中初始化和清理的问题,这是两个涉及安全的重要命题.初始化的功能主要是为库中的构件(或者说类中的域)初始化一些值,清理的功能主要是清除程序中不再被需要的元素,防止资源过分被垃圾占用的出现,为解决这些问题,java中提供了对构造器和垃圾清理器的支持,首先讨论构造器的问题. 为了保证程序的安全性,java中采用了构造器的机制来保证初始化.第一个问题就是命名的问题,java中的构造器规定名称必须和类名相同.这主要是出于如下的考虑,第一如果让编程者自行命名,则可能会产生于其他任何

图解 & 深入浅出Java初始化与清理:构造器必知必会

Writer      :BYSocket(泥沙砖瓦浆木匠) 微         博:BYSocket 豆         瓣:BYSocket FaceBook:BYSocket Twitter    :BYSocket 在面向对象编程中,编程人员应该在意"资源".比如 ? 1 <font color="#000000">String hello = "hello": </font> 在代码中,我们很在意在内存中Stri

Java 编程思想 第五章 ----初始化与清理(1)

从今天开始每天一小时的java 编程思想的阅读和编码,其实就是把书上的代码抄下来. 5.5 清理:终结处理和垃圾回收 初始化和清理工作同等重要,但是清理工作却被常常忘记,但是在使用对象之后,对对象弃之不顾的做法并不是很安全.Java有自己的垃圾回收器负责回收无用的对象占据的内存资源.但也有特殊情况:假定你的内存区域不是用new获得的,这是无法用垃圾回收器释放所以java中允许在类中定义一个名为 finalize()的方法.       工作原理: 一旦垃圾回收器准备好释放对象占用的存储空间,将首

[THINKING IN JAVA]初始化和清理

4.5初始化和清理 5.1 构造器初始化 无参:如果一个类没有构造器,那么会有一个默认的无参构造器,并且只能通过无参构造器初始化对象: 带参:传入参数初始化成员变量或者其它初始化操作需要的参数,如果类重质油带参构造器,那不能通过无参构造器初始化对象. 5.2 方法重载 具有相同的方法名称.不同的返回值或者参数列表(个数.类型.顺序) 基本类型重载:基本类型存在自动向上转型的情况 5.3 垃圾回收 finalize():在对象被销毁时调用 如何回收:引用计数.停止-复制.标记-清扫.分代.JIT

Thinking In Java笔记(第五章 初始化与清理(三))

第五章 初始化与清理 5.6 成员初始化 Java尽力保证:所有变量在使用前都能得到恰当的初始化.对于方法的局部变量,Java以编译错误的形式来保证.如下: void f() { int i; i++; //Error:i not initialized } 会得到一条错误的消息,提示i可能没有初始化.编译器可以给i赋初值,但是并没有这么做,因为没有初始化是程序员的疏忽,为了保证程序的稳定性,能帮助程序员找出程序的缺陷所在. 但如果是类的数据成员是基本类型,类的每个基本类型成员变量都会保证有一个

Java编程思想:第5章 初始化与清理

随着计算机革命的发展,“不安全”的编程方式已经逐渐成为编程代价高昂的主因之一. 初始化和清理,正是涉及安全的俩个问题. 5.1 用构造器确保初始化 构造器名与类名相同,没有返回值 5.2 方法重载 构造器的重载与方法重载 5.2.1 区分重载的方法 参数列表的不同(不同顺序也是重载,但不建议这么做) 5.2.2 涉及基本类型的重载 void print(char c){System.out.println("The character is "+c); }    void print(

Java编程思想(二) —— 初始化与清理

(一)操作符一章的int类型数据溢出的问题在Java7的版本中仍然存在,而且溢出的结果千奇百怪. public class Hello { public static void main(String[] args) { int i = Integer.MAX_VALUE; System.out.println(i); System.out.println(i*4); System.out.println(i*5); } } 结果分别为2147483647,-4,2147483643. 当应用中

Thinking In Java笔记(第五章 初始化与清理(二))

第五章 初始化与清理(二) 5.5 清理:终结处理和垃圾回收 清理的工作常常被忽略,Java有垃圾回收器负责回收无用对象占据的内存资源.但也有特殊情况:假定对象(并非使用new)获得了一块"特殊"的内存区域,由于垃圾回收器只知道释放那些由new分配的内存,所以不知道如何释放特殊内存.Java允许在类中定义一个名为finalize()的方法,工作原理"假定"是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,首先调用其finalize()方法,并且在下一次垃圾回收动