
Java编程思想第四版初始化与清理.ppt
59页第五章第五章 初始化与清理初始化与清理★★★★ 初始化初始化初始化初始化★★★★ 清理清理清理清理★★★★ 数组的初始化和枚举类型数组的初始化和枚举类型数组的初始化和枚举类型数组的初始化和枚举类型一、用构造器确保初始化一、用构造器确保初始化1 1、、、、 为什么要引入为什么要引入为什么要引入为什么要引入构造器(构造器(构造器(构造器(constructorconstructor))))????★★★★ 假设没有构造器,那么我们用户就要假设没有构造器,那么我们用户就要假设没有构造器,那么我们用户就要假设没有构造器,那么我们用户就要 自己去完成自己去完成自己去完成自己去完成初始化工作初始化工作初始化工作初始化工作◆◆◆◆ 但用户可能不知道如何初始化,甚至但用户可能不知道如何初始化,甚至但用户可能不知道如何初始化,甚至但用户可能不知道如何初始化,甚至 会忘记初始化会忘记初始化会忘记初始化会忘记初始化一、用构造器确保初始化一、用构造器确保初始化2 2、、、、 构造器如何取名?构造器如何取名?构造器如何取名?构造器如何取名?★★★★ C++C++语言采用的解决方案看来最简单语言采用的解决方案看来最简单语言采用的解决方案看来最简单语言采用的解决方案看来最简单 而且最符合逻辑,所以在而且最符合逻辑,所以在而且最符合逻辑,所以在而且最符合逻辑,所以在JavaJava中也是中也是中也是中也是 采用了这种方案采用了这种方案采用了这种方案采用了这种方案◆◆◆◆ 构造器采用与类相同的名称构造器采用与类相同的名称构造器采用与类相同的名称构造器采用与类相同的名称一、用构造器确保初始化一、用构造器确保初始化3 3、、、、 构造器的种类构造器的种类构造器的种类构造器的种类★★★★ 无参的构造器(习惯称为无参的构造器(习惯称为无参的构造器(习惯称为无参的构造器(习惯称为默认构造器默认构造器默认构造器默认构造器))))★★★★ 带参的构造器带参的构造器带参的构造器带参的构造器一、用构造器确保初始化一、用构造器确保初始化4 4、、、、 构造器的特点构造器的特点构造器的特点构造器的特点★★★★ 构造器是一种特殊类型的方法,因为构造器是一种特殊类型的方法,因为构造器是一种特殊类型的方法,因为构造器是一种特殊类型的方法,因为 它没有返回值它没有返回值它没有返回值它没有返回值◆◆◆◆ 分析:与返回值为分析:与返回值为分析:与返回值为分析:与返回值为void void (空)的区别,(空)的区别,(空)的区别,(空)的区别, 后者仍有可能以其它形式返回某些值后者仍有可能以其它形式返回某些值后者仍有可能以其它形式返回某些值后者仍有可能以其它形式返回某些值二、方法重载二、方法重载1 1、、、、 方法重载方法重载方法重载方法重载的概念的概念的概念的概念★★★★ 方法名相同,而方法名相同,而方法名相同,而方法名相同,而参数类型列表参数类型列表参数类型列表参数类型列表不同不同不同不同◆◆◆◆ 也就是依靠也就是依靠也就是依靠也就是依靠参数的个数、类型和顺序参数的个数、类型和顺序参数的个数、类型和顺序参数的个数、类型和顺序 的不同加以区分(后者不推荐使用)的不同加以区分(后者不推荐使用)的不同加以区分(后者不推荐使用)的不同加以区分(后者不推荐使用)二、方法重载二、方法重载2 2、、、、 在在在在C++C++和和和和JavaJava中,为什么必须要支持中,为什么必须要支持中,为什么必须要支持中,为什么必须要支持 方法重载?方法重载?方法重载?方法重载?★★★★ 构造器是一个非常重要的原因构造器是一个非常重要的原因构造器是一个非常重要的原因构造器是一个非常重要的原因★★★★ 既然构造器的名字由类名所决定,就只能有既然构造器的名字由类名所决定,就只能有既然构造器的名字由类名所决定,就只能有既然构造器的名字由类名所决定,就只能有 一个构造器名,那么如果想用多种方式创建一个构造器名,那么如果想用多种方式创建一个构造器名,那么如果想用多种方式创建一个构造器名,那么如果想用多种方式创建 一个对象,该怎么办?一个对象,该怎么办?一个对象,该怎么办?一个对象,该怎么办?◆◆◆◆ 典例:典例:典例:典例:TreeTree()()()() // // 无参构造器无参构造器无参构造器无参构造器 TreeTree((((int iint i)))) // // 带参构造器带参构造器带参构造器带参构造器◆◆◆◆ 为了让方法名相同而参数不同的构造器同时为了让方法名相同而参数不同的构造器同时为了让方法名相同而参数不同的构造器同时为了让方法名相同而参数不同的构造器同时 存在,必须用到存在,必须用到存在,必须用到存在,必须用到方法重载方法重载方法重载方法重载三、默认构造器三、默认构造器★★★★ 如果你写的类没有构造器,那么编译器如果你写的类没有构造器,那么编译器如果你写的类没有构造器,那么编译器如果你写的类没有构造器,那么编译器 会会会会自动帮你创建一个默认构造器自动帮你创建一个默认构造器自动帮你创建一个默认构造器自动帮你创建一个默认构造器(无参(无参(无参(无参 构造器)构造器)构造器)构造器)◆◆◆◆ 如果已经定义了一个构造器(无论是否如果已经定义了一个构造器(无论是否如果已经定义了一个构造器(无论是否如果已经定义了一个构造器(无论是否 有参数),编译器就不会帮你自动创建有参数),编译器就不会帮你自动创建有参数),编译器就不会帮你自动创建有参数),编译器就不会帮你自动创建 默认构造器默认构造器默认构造器默认构造器★★★★ 典例典例典例典例1 1::::P83P83代码代码代码代码class Bird {} class Bird {} public class DefaultConstructorpublic class DefaultConstructor{ public static void main{ public static void main((((String[] argsString[] args)))) { Bird b = { Bird b = new Birdnew Bird()()()() // Default! // Default! } } } } ◆◆◆◆ 表达式表达式表达式表达式new Birdnew Bird()将调用默认构造器,即使你()将调用默认构造器,即使你()将调用默认构造器,即使你()将调用默认构造器,即使你 并没有明确定义它并没有明确定义它并没有明确定义它并没有明确定义它★★★★ 典例典例典例典例2 2::::P83P83代码代码代码代码Bird class Bird2 Bird class Bird2 { Bird2{ Bird2((((int iint i)))) {} {} Bird2 Bird2((((double ddouble d)))) {} } {} } public class NoSynthesispublic class NoSynthesis{ public static void main{ public static void main((((String[] argsString[] args)))) { //! Bird2 b = { //! Bird2 b = new Bird2new Bird2();();();();// // 为什么出错?为什么出错?为什么出错?为什么出错? Bird2 b2 = new Bird2Bird2 b2 = new Bird2((((1 1);););); Bird2 b3 = new Bird2Bird2 b3 = new Bird2((((1.01.0););););} } } } 四、四、this关键字关键字1 1、、、、 thisthis关键字的含义关键字的含义关键字的含义关键字的含义★★★★ 概念:概念:概念:概念:指向对象本身的引用指向对象本身的引用指向对象本身的引用指向对象本身的引用◆◆◆◆ 问题:为什么需要问题:为什么需要问题:为什么需要问题:为什么需要thisthis关键字?关键字?关键字?关键字?★★★★ class Banana class Banana { void peel { void peel((((int iint i)))){/*....*/} }{/*....*/} } public class BananaPeel public class BananaPeel { public static void main { public static void main((((String[] argsString[] args)))) { Banana a=new Banana{ Banana a=new Banana();();();(); Banana b=new BananaBanana b=new Banana()()()(); ; a.peela.peel((((1 1))));;;; b.peelb.peel((((2 2))));;;; } }} }★★★★ 仔细分析以上代码在内存中执行的情况仔细分析以上代码在内存中执行的情况仔细分析以上代码在内存中执行的情况仔细分析以上代码在内存中执行的情况◆◆◆◆ 首先,我们知道:编译器只为每个对象的首先,我们知道:编译器只为每个对象的首先,我们知道:编译器只为每个对象的首先,我们知道:编译器只为每个对象的数据数据数据数据 成员成员成员成员分配存储空间,而不为分配存储空间,而不为分配存储空间,而不为分配存储空间,而不为成员函数成员函数成员函数成员函数分配空间,分配空间,分配空间,分配空间, 成员函数将随着整个类的装载而装载成员函数将随着整个类的装载而装载成员函数将随着整个类的装载而装载成员函数将随着整个类的装载而装载 ◆◆◆◆ 为什么?所有对象的成员函数都是一样的,为什么?所有对象的成员函数都是一样的,为什么?所有对象的成员函数都是一样的,为什么?所有对象的成员函数都是一样的, 所以没必要为成员函数分配空间所以没必要为成员函数分配空间所以没必要为成员函数分配空间所以没必要为成员函数分配空间★★★★ 于是产生了一个有意思的问题:于是产生了一个有意思的问题:于是产生了一个有意思的问题:于是产生了一个有意思的问题: 成员函数是怎么知道它究竟被哪个对象调用的?成员函数是怎么知道它究竟被哪个对象调用的?成员函数是怎么知道它究竟被哪个对象调用的?成员函数是怎么知道它究竟被哪个对象调用的?◆◆◆◆ 答案是:通过答案是:通过答案是:通过答案是:通过thisthis关键字(关键字(关键字(关键字(编译器自动完成编译器自动完成编译器自动完成编译器自动完成))))◆◆◆◆ 编译器将编译器将编译器将编译器将a.peela.peel((((1 1)理解为)理解为)理解为)理解为Banana.peelBanana.peel((((a a,,,,1 1)))) // // 即将当前所操作对象的引用作为即将当前所操作对象的引用作为即将当前所操作对象的引用作为即将当前所操作对象的引用作为第一个参数第一个参数第一个参数第一个参数传递传递传递传递 给给给给peelpeel()()()()四、四、this关键字关键字2 2、、、、 我们如何使用我们如何使用我们如何使用我们如何使用thisthis关键字?关键字?关键字?关键字?⑴⑴⑴⑴ 返回当前对象的引用返回当前对象的引用返回当前对象的引用返回当前对象的引用★★★★ 典例分析典例分析典例分析典例分析P84P84代码代码代码代码★★★★ public class Leaf public class Leaf { int i=0 { int i=0;;;; LeafLeaf Increment Increment()()()() { i++{ i++;;;; return thisreturn this;;;; } } void print void print { System.out.println { System.out.println((((“ “i=”+ii=”+i);););); } }★★★★ public static void main public static void main((((String[] argsString[] args)))) { { Leaf x=new Leaf Leaf x=new Leaf();();();(); x.incrementx.increment()()()().increment.increment()()()() .increment.increment()()()().print.print()()()(); ; } } } }四、四、this关键字关键字⑵⑵⑵⑵ 在构造器中调用构造器在构造器中调用构造器在构造器中调用构造器在构造器中调用构造器★★★★ 为避免重复代码,往往使用为避免重复代码,往往使用为避免重复代码,往往使用为避免重复代码,往往使用thisthis关键字关键字关键字关键字◆◆◆◆ 分析分析分析分析P86P86代码代码代码代码★★★★ public class Flower public class Flower { int petalCount=0 { int petalCount=0;;;; String s=“initial value”String s=“initial value”;;;; FlowerFlower((((int petalCountint petalCount)))){ /*....*/ }{ /*....*/ } Flower Flower((((String ssString ss)))){ /*....*/ }{ /*....*/ } Flower Flower((((int petalCountint petalCount,,,,String sString s)))) { { thisthis((((petalCountpetalCount))));;;; this.s=sthis.s=s;;;; // Another use of this// Another use of this ..... } ..... } } }五、清理:终结处理和垃圾回收五、清理:终结处理和垃圾回收1 1、、、、 C++C++的的的的析构函数(析构函数(析构函数(析构函数(destructordestructor))))⑴⑴⑴⑴ 什么时候调用析构函数?什么时候调用析构函数?什么时候调用析构函数?什么时候调用析构函数?★★★★ 当对象脱离其作用域时当对象脱离其作用域时当对象脱离其作用域时当对象脱离其作用域时(如对象所在的(如对象所在的(如对象所在的(如对象所在的 函数已调用完毕),系统自动调用析构函数已调用完毕),系统自动调用析构函数已调用完毕),系统自动调用析构函数已调用完毕),系统自动调用析构 函数函数函数函数五、清理:终结处理和垃圾回收五、清理:终结处理和垃圾回收⑵⑵⑵⑵ 析构函数的作用是什么?析构函数的作用是什么?析构函数的作用是什么?析构函数的作用是什么?★★★★ 析构函数往往用来做析构函数往往用来做析构函数往往用来做析构函数往往用来做清理善后清理善后清理善后清理善后的工作的工作的工作的工作◆◆◆◆ 例如:例如:例如:例如: 在建立对象时用在建立对象时用在建立对象时用在建立对象时用newnew开辟了一片开辟了一片开辟了一片开辟了一片 内存空间,应在退出前在析构函数中用内存空间,应在退出前在析构函数中用内存空间,应在退出前在析构函数中用内存空间,应在退出前在析构函数中用 deletedelete释放释放释放释放五、清理:终结处理和垃圾回收五、清理:终结处理和垃圾回收⑶⑶⑶⑶ 是否每一个类都具有析构函数?是否每一个类都具有析构函数?是否每一个类都具有析构函数?是否每一个类都具有析构函数?★★★★ 如果用户没有编写析构函数,编译系统如果用户没有编写析构函数,编译系统如果用户没有编写析构函数,编译系统如果用户没有编写析构函数,编译系统 会会会会自动生成一个缺省的析构函数自动生成一个缺省的析构函数自动生成一个缺省的析构函数自动生成一个缺省的析构函数,而它,而它,而它,而它 也不进行任何操作也不进行任何操作也不进行任何操作也不进行任何操作 五、清理:终结处理和垃圾回收五、清理:终结处理和垃圾回收2 2、、、、 垃圾回收器垃圾回收器垃圾回收器垃圾回收器的作用的作用的作用的作用★★★★ JavaJava没有析构函数没有析构函数没有析构函数没有析构函数★★★★ 垃圾回收器垃圾回收器垃圾回收器垃圾回收器只知道只知道只知道只知道释放那些释放那些释放那些释放那些经由经由经由经由newnew 关键字分配的内存关键字分配的内存关键字分配的内存关键字分配的内存★★★★ 问题问题问题问题1 1:是否存在:是否存在:是否存在:是否存在不经不经不经不经newnew分配的内存分配的内存分配的内存分配的内存???? ◆◆◆◆ 如果不存在:上面一段话应该改为如果不存在:上面一段话应该改为如果不存在:上面一段话应该改为如果不存在:上面一段话应该改为垃圾回收器垃圾回收器垃圾回收器垃圾回收器 能够回收一切不再需要的内存能够回收一切不再需要的内存能够回收一切不再需要的内存能够回收一切不再需要的内存◆◆◆◆ 如果存在:但如果存在:但如果存在:但如果存在:但在在在在JavaJava中中中中一切都是对象,而对象一切都是对象,而对象一切都是对象,而对象一切都是对象,而对象 必须经由必须经由必须经由必须经由newnew关键字来分配内存关键字来分配内存关键字来分配内存关键字来分配内存◆◆◆◆ 答案:确实存在着不经答案:确实存在着不经答案:确实存在着不经答案:确实存在着不经newnew分配的内存(利用分配的内存(利用分配的内存(利用分配的内存(利用 本地方法本地方法本地方法本地方法调用非调用非调用非调用非JavaJava代码,如代码,如代码,如代码,如C C的的的的mallocmalloc函数)函数)函数)函数)★★★★ 问题问题问题问题2 2:垃圾回收器是否知道如何释放这块:垃圾回收器是否知道如何释放这块:垃圾回收器是否知道如何释放这块:垃圾回收器是否知道如何释放这块特殊特殊特殊特殊 的内存的内存的内存的内存????◆◆◆◆ 答案很显然:不知道答案很显然:不知道答案很显然:不知道答案很显然:不知道◆◆◆◆ 那么这块特殊的内存如何释放呢?那么这块特殊的内存如何释放呢?那么这块特殊的内存如何释放呢?那么这块特殊的内存如何释放呢?JavaJava允许在允许在允许在允许在 类中定义一个类中定义一个类中定义一个类中定义一个finalizefinalize()方法()方法()方法()方法,可以通过调用,可以通过调用,可以通过调用,可以通过调用 该方法来实现该方法来实现该方法来实现该方法来实现★★★★ 问题问题问题问题3 3:垃圾回收器是否等同于析构函数?:垃圾回收器是否等同于析构函数?:垃圾回收器是否等同于析构函数?:垃圾回收器是否等同于析构函数?◆◆◆◆ 不等同(范围不等同)不等同(范围不等同)不等同(范围不等同)不等同(范围不等同)◆◆◆◆ 在在在在C++C++中,中,中,中,每一个对象每一个对象每一个对象每一个对象一定会通过析构函数而一定会通过析构函数而一定会通过析构函数而一定会通过析构函数而 销毁销毁销毁销毁◆◆◆◆ 而垃圾回收器而垃圾回收器而垃圾回收器而垃圾回收器只知道只知道只知道只知道回收经由回收经由回收经由回收经由newnew分配的内存,分配的内存,分配的内存,分配的内存, 而其它的对象(不经而其它的对象(不经而其它的对象(不经而其它的对象(不经newnew分配的)不能被垃圾分配的)不能被垃圾分配的)不能被垃圾分配的)不能被垃圾 回收器回收回收器回收回收器回收回收器回收五、清理:终结处理和垃圾回收五、清理:终结处理和垃圾回收3 3、、、、 垃圾回收器是如何工作的?垃圾回收器是如何工作的?垃圾回收器是如何工作的?垃圾回收器是如何工作的?★★★★ 在堆上分配对象的代价十分高昂在堆上分配对象的代价十分高昂在堆上分配对象的代价十分高昂在堆上分配对象的代价十分高昂◆◆◆◆ 但由于垃圾回收器的存在,但由于垃圾回收器的存在,但由于垃圾回收器的存在,但由于垃圾回收器的存在,JavaJava从堆从堆从堆从堆 分配空间的速度,可以和其它语言从分配空间的速度,可以和其它语言从分配空间的速度,可以和其它语言从分配空间的速度,可以和其它语言从 堆栈分配空间的速度相媲美(了解)堆栈分配空间的速度相媲美(了解)堆栈分配空间的速度相媲美(了解)堆栈分配空间的速度相媲美(了解)六、成员初始化六、成员初始化1 1、、、、 JavaJava编译器将尽力保证:编译器将尽力保证:编译器将尽力保证:编译器将尽力保证:所有变量所有变量所有变量所有变量在在在在 使用前都要进行恰当的初始化使用前都要进行恰当的初始化使用前都要进行恰当的初始化使用前都要进行恰当的初始化★★★★ JavaJava的变量有两种:的变量有两种:的变量有两种:的变量有两种:引用变量引用变量引用变量引用变量和和和和基本基本基本基本 类型变量类型变量类型变量类型变量,后者又分为下面两种情况,后者又分为下面两种情况,后者又分为下面两种情况,后者又分为下面两种情况六、成员初始化六、成员初始化⑴⑴⑴⑴ 非数据成员(方法中局部变量)非数据成员(方法中局部变量)非数据成员(方法中局部变量)非数据成员(方法中局部变量)★★★★ JavaJava以以以以编译时错误编译时错误编译时错误编译时错误的形式来保证其的形式来保证其的形式来保证其的形式来保证其 初始化初始化初始化初始化◆◆◆◆ 典例分析:典例分析:典例分析:典例分析:★★★★ void f void f()()()() { int i{ int i;;;; i++i++;;;; // Error// Error,,,,i not initialized }i not initialized }◆◆◆◆ 强制程序员提供一个初始值强制程序员提供一个初始值强制程序员提供一个初始值强制程序员提供一个初始值◆◆◆◆ 编译器可以为编译器可以为编译器可以为编译器可以为i i赋一个默认值,但是未初始化的赋一个默认值,但是未初始化的赋一个默认值,但是未初始化的赋一个默认值,但是未初始化的 局部变量更有可能是程序员的疏忽,局部变量更有可能是程序员的疏忽,局部变量更有可能是程序员的疏忽,局部变量更有可能是程序员的疏忽, 所以采用所以采用所以采用所以采用 默认值反而会掩盖这种失误默认值反而会掩盖这种失误默认值反而会掩盖这种失误默认值反而会掩盖这种失误六、成员初始化六、成员初始化⑵⑵⑵⑵ 数据成员为基本类型数据成员为基本类型数据成员为基本类型数据成员为基本类型★★★★ JavaJava将为将为将为将为类的每个基本数据成员提供类的每个基本数据成员提供类的每个基本数据成员提供类的每个基本数据成员提供 自动初始化自动初始化自动初始化自动初始化(保证都会有一个初始值)(保证都会有一个初始值)(保证都会有一个初始值)(保证都会有一个初始值)◆◆◆◆ 典例分析:典例分析:典例分析:典例分析:P92P92代码(自己调试)代码(自己调试)代码(自己调试)代码(自己调试)六、成员初始化六、成员初始化⑶⑶⑶⑶ 引用类型引用类型引用类型引用类型★★★★ 无论是否数据成员,如果不将其初始化,无论是否数据成员,如果不将其初始化,无论是否数据成员,如果不将其初始化,无论是否数据成员,如果不将其初始化, 该该该该对象引用会被对象引用会被对象引用会被对象引用会被JavaJava自动赋值为自动赋值为自动赋值为自动赋值为nullnull六、成员初始化六、成员初始化2 2、、、、 指定初始化指定初始化指定初始化指定初始化★★★★ JavaJava允许:允许:允许:允许:在定义类的数据成员的同时在定义类的数据成员的同时在定义类的数据成员的同时在定义类的数据成员的同时 可以为其赋初值可以为其赋初值可以为其赋初值可以为其赋初值◆◆◆◆ 优点:简单直观优点:简单直观优点:简单直观优点:简单直观 缺点:每个对象都具有相同的初值缺点:每个对象都具有相同的初值缺点:每个对象都具有相同的初值缺点:每个对象都具有相同的初值★★★★ 典例:典例:典例:典例:P93P93代码代码代码代码void void publicpublic class class InitialValues2InitialValues2{ { int int i i ;;;; // // 自动初始化自动初始化自动初始化自动初始化 int int j j = 999 = 999;;;;… // … // 先自动初始化,再指定初始化先自动初始化,再指定初始化先自动初始化,再指定初始化先自动初始化,再指定初始化} } ◆◆◆◆ 注意:注意:注意:注意:C++C++不允许这样做,必须全部通过构造不允许这样做,必须全部通过构造不允许这样做,必须全部通过构造不允许这样做,必须全部通过构造 函数来初始化函数来初始化函数来初始化函数来初始化七、构造器初始化七、构造器初始化1 1、、、、 初始化的第一基本原则初始化的第一基本原则初始化的第一基本原则初始化的第一基本原则★★★★ 可以用构造器来进行初始化可以用构造器来进行初始化可以用构造器来进行初始化可以用构造器来进行初始化◆◆◆◆ 但执行顺序是:但执行顺序是:但执行顺序是:但执行顺序是:先自动初始化,再指定先自动初始化,再指定先自动初始化,再指定先自动初始化,再指定 初始化,最后调用构造器进行初始化初始化,最后调用构造器进行初始化初始化,最后调用构造器进行初始化初始化,最后调用构造器进行初始化★★★★ public class Counter public class Counter { int i=99 { int i=99;;;; CounterCounter()()()(){ i=7{ i=7;;;;} } // ...... // ...... } }◆◆◆◆ 执行顺序执行顺序执行顺序执行顺序::::i i首先被自动初始化为首先被自动初始化为首先被自动初始化为首先被自动初始化为0 0,然后指定,然后指定,然后指定,然后指定 初始化为初始化为初始化为初始化为9999,最后利用构造器初始化为,最后利用构造器初始化为,最后利用构造器初始化为,最后利用构造器初始化为7 7★★★★ 大家肯定会问一个问题:既然这些数据成员的大家肯定会问一个问题:既然这些数据成员的大家肯定会问一个问题:既然这些数据成员的大家肯定会问一个问题:既然这些数据成员的 初始化已经得到保证,那还要构造器做什么?初始化已经得到保证,那还要构造器做什么?初始化已经得到保证,那还要构造器做什么?初始化已经得到保证,那还要构造器做什么?◆◆◆◆ 首先:自动初始化只是保证这些首先:自动初始化只是保证这些首先:自动初始化只是保证这些首先:自动初始化只是保证这些JavaJava类的稳定性类的稳定性类的稳定性类的稳定性 它的值并不是实际情况所需要的值它的值并不是实际情况所需要的值它的值并不是实际情况所需要的值它的值并不是实际情况所需要的值◆◆◆◆ 其次:其次:其次:其次:构造器存在着并没有为某个数据成员进行构造器存在着并没有为某个数据成员进行构造器存在着并没有为某个数据成员进行构造器存在着并没有为某个数据成员进行 初始化的危险初始化的危险初始化的危险初始化的危险,那么如果没有自动初始化,我们,那么如果没有自动初始化,我们,那么如果没有自动初始化,我们,那么如果没有自动初始化,我们 该怎么办?该怎么办?该怎么办?该怎么办?七、构造器初始化七、构造器初始化2 2、、、、 变量的初始化顺序变量的初始化顺序变量的初始化顺序变量的初始化顺序★★★★ 变量定义的先后顺序决定其初始化顺序变量定义的先后顺序决定其初始化顺序变量定义的先后顺序决定其初始化顺序变量定义的先后顺序决定其初始化顺序◆◆◆◆ 不管这些变量的定义位于什么地方,不管这些变量的定义位于什么地方,不管这些变量的定义位于什么地方,不管这些变量的定义位于什么地方, 它们一定会在它们一定会在它们一定会在它们一定会在任何方法任何方法任何方法任何方法(包括构造器)(包括构造器)(包括构造器)(包括构造器) 被调用之前得到初始化被调用之前得到初始化被调用之前得到初始化被调用之前得到初始化七、构造器初始化七、构造器初始化3 3、、、、 静态数据的初始化静态数据的初始化静态数据的初始化静态数据的初始化★★★★ 还有一个棘手的问题没有解决:还有一个棘手的问题没有解决:还有一个棘手的问题没有解决:还有一个棘手的问题没有解决:★★★★ 我们从前面知道:类的数据成员将会先后进行我们从前面知道:类的数据成员将会先后进行我们从前面知道:类的数据成员将会先后进行我们从前面知道:类的数据成员将会先后进行 自动初始化和指定初始化自动初始化和指定初始化自动初始化和指定初始化自动初始化和指定初始化◆◆◆◆ 问题是:类的数据成员又分为问题是:类的数据成员又分为问题是:类的数据成员又分为问题是:类的数据成员又分为静态数据成员静态数据成员静态数据成员静态数据成员和和和和 非静态数据成员非静态数据成员非静态数据成员非静态数据成员,那么在所谓的自动初始化和,那么在所谓的自动初始化和,那么在所谓的自动初始化和,那么在所谓的自动初始化和 指定初始化过程中,是先做静态成员的初始化指定初始化过程中,是先做静态成员的初始化指定初始化过程中,是先做静态成员的初始化指定初始化过程中,是先做静态成员的初始化 还是先做非静态成员的初始化?还是先做非静态成员的初始化?还是先做非静态成员的初始化?还是先做非静态成员的初始化?七、构造器初始化七、构造器初始化★★★★ 初始化的第二基本原则初始化的第二基本原则初始化的第二基本原则初始化的第二基本原则◆◆◆◆ 初始化的顺序是初始化的顺序是初始化的顺序是初始化的顺序是先静态对象先静态对象先静态对象先静态对象,而,而,而,而后是非后是非后是非后是非 静态对象静态对象静态对象静态对象◆◆◆◆ 典例分析:典例分析:典例分析:典例分析:P95P95★★★★ 分析:分析:分析:分析:1 1、、、、publicpublic所修饰的类是所修饰的类是所修饰的类是所修饰的类是主类主类主类主类,里面包含的,里面包含的,里面包含的,里面包含的mainmain方法方法方法方法 称为称为称为称为主方法主方法主方法主方法,是,是,是,是程序的入口点程序的入口点程序的入口点程序的入口点◆◆◆◆ 所谓程序的入口点是指:程序将从这个地方开始所谓程序的入口点是指:程序将从这个地方开始所谓程序的入口点是指:程序将从这个地方开始所谓程序的入口点是指:程序将从这个地方开始 执行,而这将会导致主类的加载执行,而这将会导致主类的加载执行,而这将会导致主类的加载执行,而这将会导致主类的加载★★★★ 分析:分析:分析:分析:2 2、前面提到:不管变量的定义位于什么地方,它们、前面提到:不管变量的定义位于什么地方,它们、前面提到:不管变量的定义位于什么地方,它们、前面提到:不管变量的定义位于什么地方,它们 一定会在任何方法(包括构造器)被调用之前得一定会在任何方法(包括构造器)被调用之前得一定会在任何方法(包括构造器)被调用之前得一定会在任何方法(包括构造器)被调用之前得 到初始化到初始化到初始化到初始化◆◆◆◆ 所以:所以:所以:所以: table table 和和和和cupboard cupboard 两个静态变量将首先被两个静态变量将首先被两个静态变量将首先被两个静态变量将首先被 初始化,而在利用构造器初始化的过程中,将导初始化,而在利用构造器初始化的过程中,将导初始化,而在利用构造器初始化的过程中,将导初始化,而在利用构造器初始化的过程中,将导 致致致致TableTable类和类和类和类和CupboardCupboard类的先后加载类的先后加载类的先后加载类的先后加载 ★★★★ 分析:分析:分析:分析:3 3、重要结论、重要结论、重要结论、重要结论:(后面章节还要仔细分析):(后面章节还要仔细分析):(后面章节还要仔细分析):(后面章节还要仔细分析)◆◆◆◆ 类在必要的时候才会被加载类在必要的时候才会被加载类在必要的时候才会被加载类在必要的时候才会被加载◆◆◆◆ 这样一来,我们很容易知道:该类的静态初始化这样一来,我们很容易知道:该类的静态初始化这样一来,我们很容易知道:该类的静态初始化这样一来,我们很容易知道:该类的静态初始化 也只有在必要的时候才会进行也只有在必要的时候才会进行也只有在必要的时候才会进行也只有在必要的时候才会进行★★★★ 分析:分析:分析:分析:4 4、推论:、推论:、推论:、推论:初始化第二基本原则的重要补充初始化第二基本原则的重要补充初始化第二基本原则的重要补充初始化第二基本原则的重要补充◆◆◆◆ 静态对象的初始化静态对象的初始化静态对象的初始化静态对象的初始化发生在发生在发生在发生在类的加载时期类的加载时期类的加载时期类的加载时期◆◆◆◆ 非静态对象的初始化非静态对象的初始化非静态对象的初始化非静态对象的初始化发生在发生在发生在发生在创建对象时期创建对象时期创建对象时期创建对象时期八、数组初始化八、数组初始化1 1、、、、 有关数组的基本知识有关数组的基本知识有关数组的基本知识有关数组的基本知识⑴⑴⑴⑴ 数组的概念数组的概念数组的概念数组的概念★★★★ 数组只是相同类型的,且用一个标识符数组只是相同类型的,且用一个标识符数组只是相同类型的,且用一个标识符数组只是相同类型的,且用一个标识符 封装到一起的一个封装到一起的一个封装到一起的一个封装到一起的一个对象序列对象序列对象序列对象序列或或或或基本类型基本类型基本类型基本类型 数据序列数据序列数据序列数据序列八、数组初始化八、数组初始化⑵⑵⑵⑵ 数组的声明(数组的声明(数组的声明(数组的声明(P99P99))))★★★★ 如:如:如:如:int[] aint[] a;或者;或者;或者;或者int a[]int a[];;;;★★★★ 仅仅声明了对仅仅声明了对仅仅声明了对仅仅声明了对数组的一个引用数组的一个引用数组的一个引用数组的一个引用,而且,而且,而且,而且 也没有给数组对象本身分配任何空间也没有给数组对象本身分配任何空间也没有给数组对象本身分配任何空间也没有给数组对象本身分配任何空间八、数组初始化八、数组初始化⑶⑶⑶⑶ 数组的定义数组的定义数组的定义数组的定义★★★★ 包括给数组创建相应的存储空间,包括给数组创建相应的存储空间,包括给数组创建相应的存储空间,包括给数组创建相应的存储空间, 以及对数组的初始化以及对数组的初始化以及对数组的初始化以及对数组的初始化八、数组初始化八、数组初始化⑷⑷⑷⑷ 数组的固定成员数组的固定成员数组的固定成员数组的固定成员lengthlength★★★★ 同同同同C/C++C/C++一样,一样,一样,一样,JavaJava数组计数也是从数组计数也是从数组计数也是从数组计数也是从 0 0开始,到开始,到开始,到开始,到length-1length-1结束结束结束结束◆◆◆◆ 一旦越界,则系统自动抛出异常一旦越界,则系统自动抛出异常一旦越界,则系统自动抛出异常一旦越界,则系统自动抛出异常八、数组初始化八、数组初始化⑸⑸⑸⑸ 数组的大小(与数组的大小(与数组的大小(与数组的大小(与C/C++C/C++很大不同)很大不同)很大不同)很大不同)★★★★ 数组的大小可以在运行时刻才决定,数组的大小可以在运行时刻才决定,数组的大小可以在运行时刻才决定,数组的大小可以在运行时刻才决定, P100P100的的的的ArrayNew.javaArrayNew.java可证明这点可证明这点可证明这点可证明这点◆◆◆◆ 但是不推荐这种使用方式但是不推荐这种使用方式但是不推荐这种使用方式但是不推荐这种使用方式八、数组初始化八、数组初始化⑹⑹⑹⑹ 数组元素的类型可以是对象引用,数组元素的类型可以是对象引用,数组元素的类型可以是对象引用,数组元素的类型可以是对象引用, 这种数组称为这种数组称为这种数组称为这种数组称为引用数组引用数组引用数组引用数组★★★★ 注意对引用数组进行初始化,否则在注意对引用数组进行初始化,否则在注意对引用数组进行初始化,否则在注意对引用数组进行初始化,否则在 程序中所使用的是空引用(程序中所使用的是空引用(程序中所使用的是空引用(程序中所使用的是空引用(nullnull))))八、数组初始化八、数组初始化2 2、、、、 可变参数列表可变参数列表可变参数列表可变参数列表★★★★ 应用于参数个数或类型未知的场合应用于参数个数或类型未知的场合应用于参数个数或类型未知的场合应用于参数个数或类型未知的场合◆◆◆◆ 典例分析典例分析典例分析典例分析P102P102★★★★ public class NewVarArgs public class NewVarArgs { // { // 可变参数列表的语法可变参数列表的语法可变参数列表的语法可变参数列表的语法 static void printArraystatic void printArray((((Object...argsObject...args)))) { { // Foreach // Foreach语法语法语法语法 forfor((((Object obj : agrsObject obj : agrs)))) System.out.printlnSystem.out.println((((obj+“”obj+“”);););); System.out.printlnSystem.out.println();();();(); } } ★★★★ public static void main public static void main((((String[] argsString[] args)))) { { printArray printArray((((new Integernew Integer((((4747),),),),new Floatnew Float ((((3.143.14),),),),new Doublenew Double((((11.1111.11));));));)); printArrayprintArray((((4747,,,,3.14F3.14F,,,,11.1111.11);););); printArrayprintArray((((“ “one”one”,,,,“ “two”two”,,,,“ “three”three”);););); printArrayprintArray((((new Anew A(),(),(),(),new Bnew B(),(),(),(), new Cnew C());());());()); ..... }}..... }}习题习题★★★★ P77P77:::: 习题习题习题习题1 1 P86 P86:::: 习题习题习题习题9 9 P101 P101:习题:习题:习题:习题1616。












