Thinking in Java 3rd Edition

Size: px
Start display at page:

Download "Thinking in Java 3rd Edition"

Transcription

1 致读者 : 我从 2002 年 7 月开始翻译这本书, 当时还是第二版 但是翻完前言和介绍部分后,chinapub 就登出广告, 说要出版侯捷的译本 于是我中止了翻译, 等着侯先生的作品 我是第一时间买的这本书, 但是我失望了 比起第一版, 我终于能看懂这本书了, 但是相比我的预期, 它还是差一点 所以当 Bruce Eckel 在他的网站上公开本书的第三版的时候, 我决定把它翻译出来 说说容易, 做做难 一本 1000 多页的书不是那么容易翻的 期间我也曾打过退堂鼓, 但最终还是全部翻译出来了 从今年的两月初起, 到 7 月底, 我几乎放弃了所有的业余时间, 全身心地投入本书的翻译之中 应该说, 这项工作的难度超出了我的想像 首先, 读一本书和翻译一本书完全是两码事 英语与中文是两种不同的语言, 用英语说得很畅的句子, 翻成中文之后就完全破了相 有时我得花好几分钟, 用中文重述一句我能用几秒钟读懂的句子 更何况作为读者, 一两句话没搞懂, 并不影响你理解整本书, 但对译者来说, 这就不一样了 其次, 这是一本讲英语的人写给讲英语的人的书, 所以同很多要照顾非英语读者的技术文档不同, 它在用词, 句式方面非常随意 英语读者会很欣赏这一点, 但是对外国读者来说, 这就是负担了 再有,Bruce Eckel 这样的大牛人, 写了 1000 多页, 如果都让你读懂, 他岂不是太没面子? 所以, 书里还有一些很有 禅意 的句子 比如那句著名的 The genesis of the computer revolution was in a machine. The genesis of our programming languages thus tends to look like that machine. 我就一直没吃准该怎么翻译 我想大概没人能吃准, 说不定 Bruce 要的就是这个效果 这是一本公认的名著, 作者在技术上的造诣无可挑剔 而作为译者, 我的编程能力差了很多 再加上上面讲的这些原因, 使得我不得不格外的谨慎 当我重读初稿的时候, 我发现需要修改的地方实在太多了 因此, 我不能现在就公开全部译稿, 我只能公开已经修改过的部分 不过这不是最终的版本, 我还会继续修订的 本来, 我准备到 10 月份, 等我修改完前 7 章之后再公开 但是, 我发现我又有点要放弃了, 因此我决定给自己一点压力, 现在就公开 以后, 我将修改完一章就公开一章, 请关注 如果你觉得好, 请给告诉我, 你的鼓励是我工作的动力 ; 如果你觉得不好, 那就更应该告诉我了, 我会参考你的意见作修改的 我希望能通过这种方法, 译出一本配得上原著的书 shhgs 2003 年 9 月 8 日 第 1 页共 29 页

2 Chapter 7: Polymorphism 7: 多态性 多态性是继数据抽象和继承之后的, 面向对象的编程语言的第三个基本特性 它提供了另一个层面的接口与实现的分离, 也就是说把做什么和怎么做分开来 多态性不但能改善代码的结构, 提高其可读性, 而且能让你创建可扩展的 (extensible) 程序 所谓 可扩展 是指, 程序不仅在项目最初的开发阶段能 成长, 而且还可以在需要添加新特性的时候 成长 封装 通过将数据的特征与行为结合在一起, 创建了一种新的数据类型 隐藏实现 通过将细节设成 private, 完成了接口与实现的分离 之所以要采取这种比较呆板的顺序来讲解, 是要照顾那些过程语言的程序员们 但是, 多态性是站在 类 的角度来处理这种逻辑上的分离的 在上一章中, 你看到了, 继承 是怎样允许你将对象当作它自己的, 或者它的基类的类型来处理的 这是一个很重要的功能, 因为它能让你把多个类 ( 派生自同一个基类的 ) 当作一个类来处理, 这样一段代码就能作用于很多不同的类型了 多态方法调用 (polymorphic method call) 能让类表现出各自所独有的特点, 只要这些类都是从同一个基类里派生出来的就行了 当你通过基类的 reference 调用方法的时候, 这些不同就会通过行为表现出来 本章会从基础开始, 通过一些简单的, 只涉及多态行为的程序, 来讲解多态性 ( 也被称为动态绑定 dynamic binding 后绑定 late binding 或运行时绑定 run-time bingding ) 再访上传 (upcasting) 你已经在第 6 章看到, 怎样把对象当作它自己的或是它的基类的对象来使用 把对象的 reference 当作基类的 reference 来用, 被称为上传 (upcasting) 因为在继承关系图中, 基类总是在上面的 但是问题也来了 下面我们用乐器来举例 由于乐器要演奏 Note( 音符 ), 所以我们在 package 里单独创建一个 Note 类 : //: c07:music:note.java // Notes to play on musical instruments. package c07.music; import com.bruceeckel.simpletest.*; public class Note { private String notename; private Note(String notename) { this.notename = notename; public String tostring() { return notename; 第 2 页共 29 页

3 public static final Note MIDDLE_C = new Note("Middle C"), C_SHARP = new Note("C Sharp"), B_FLAT = new Note("B Flat"); // Etc. ///:~ 这是一个 枚举 (enumeration) 类, 它创建了几个固定对象以供选择 你不能再创建其它对象了, 因为构造函数是 private 的 在接下去的程序中,Wind 作为一种乐器继承了 Instrument: //: c07:music:music.java // Inheritance & upcasting. package c07.music; import com.bruceeckel.simpletest.*; public class Music { private static Test monitor = new Test(); public static void tune(instrument i) { //... i.play(note.middle_c); public static void main(string[] args) { Wind flute = new Wind(); tune(flute); // Upcasting monitor.expect(new String[] { "Wind.play() Middle C" ); ///:~ //: c07:music:wind.java package c07.music; // Wind objects are instruments // because they have the same interface: public class Wind extends Instrument { // Redefine interface method: public void play(note n) { System.out.println("Wind.play() " + n); ///:~ Music.tune( ) 需要一个 Instrument 的 reference 作参数, 但是它也可以接受任何由 Instrument 派生出来的 reference 当 main( ) 未经转换就把 Wind 的 reference 传给 tune( ) 的时候, 这一切就发生了 这是完全可以可行的 因为 Wind 继承了 Instrument, 因此它必须实现 Instrument 的接口 从 Wind 上传到 Instrument 的时 第 3 页共 29 页

4 Chapter 7: Polymorphism 候, 接口可能会 变窄, 但是再小也不会比 Instrument 的接口更小 把对象的类型忘掉 可能你会觉得 Music.java 有些奇怪 为什么会有人要故意 忘掉 对象的类型呢? 上传就是在做这件事情 但是, 让 tune( ) 直接拿 Wind 的 reference 作参数好像更简单一些 但是这会有一个问题 : 如果采用这种方法, 你就得为系统里的每个 Instrument 都写一个新的 tune( ) 方法 假设我们顺着这个思路, 再加一个 Stringed ( 弦乐器 ) 和一个 Brass ( 管乐器 ): //: c07:music:music2.java // Overloading instead of upcasting. package c07.music; import com.bruceeckel.simpletest.*; class Stringed extends Instrument { public void play(note n) { System.out.println("Stringed.play() " + n); class Brass extends Instrument { public void play(note n) { System.out.println("Brass.play() " + n); public class Music2 { private static Test monitor = new Test(); public static void tune(wind i) { i.play(note.middle_c); public static void tune(stringed i) { i.play(note.middle_c); public static void tune(brass i) { i.play(note.middle_c); public static void main(string[] args) { Wind flute = new Wind(); Stringed violin = new Stringed(); Brass frenchhorn = new Brass(); tune(flute); // No upcasting tune(violin); tune(frenchhorn); monitor.expect(new String[] { "Wind.play() Middle C", "Stringed.play() Middle C", "Brass.play() Middle C" ); ///:~ 第 4 页共 29 页

5 这种做法不是不可以, 但是有个重大缺陷 : 每次加入新的 Instrument 的时候, 你都必须专门为这个类写一个方法 这就意味着, 不但定义类的时候要多写代码, 而且添加 tune( ) 之类的方法, 或者添加新的 Instrument 的时候, 还会多出很多事情 此外, 如果你忘了重载某个方法, 编译器是不会报错的, 于是类型处理工作就完全乱套了 如果你可以写只一个用基类, 而不是具体的派生类作参数的方法, 那会不会更好一些呢? 也就是, 如果你可以忘掉它们都是派生类, 只写同基类打交道的代码, 那会不会更好呢? 这就是多态性要解决的问题 然而绝大多数从过程语言转过来的程序员们, 在理解多态性的运行机制的时候, 都会些问题 问题的关键 Music.java 让人觉得费解的地方就是, 运行的时候, 真正产生输出的是 Wind.play( ) 诚然, 这正是我们所希望, 但是它却没有告诉我们它为什么要这样运行 看看 tune( ) 方法 : public static void tune(instrument i) { //... i.play(note.middle_c); 它接受一个 Instrument 的 reference 做参数 那么编译器怎么会知道这个 Instrument 的 reference 就指向一个 Wind, 而不是 Brass 或 Stringed 呢? 编译器不可能知道 为了能深入的理解这个问题, 我们先来看看什么是 绑定 (binding) 方法调用的绑定 将方法的调用连到方法本身被称为 绑定 (binding) 当绑定发生在程序运行之前时 ( 如果有的话, 就是由编译器或连接器负责 ) 被称作 前绑定 (early binding) 可能你从没听说过这个术语, 因为面向过程的语言根本就没有这个概念 C 的编译器只允许一种方法调用, 那就是前绑定 上述例程之所以令人费解都是源于前绑定, 因为当编译器只有一个 Instrument 的 reference 的时候, 它是不知道该连到哪个方法的 解决方案就是 后绑定 (late binding), 它的意思是要在程序运行的时候, 根据对象的类型来决定该绑定哪个方法 后绑定也被称为 动态绑定 (dynamic binding) 或 运行时绑定 (run-time binding) 如果语言实现了后绑定, 那它就必须要有能在运行时判断对象类型, 并且调用其 第 5 页共 29 页

6 Chapter 7: Polymorphism 合适的方法的机制 也就是说, 编译器还是不知道对象的类型, 但是方法的调用机制会找出, 并且调用正确的方法 后绑定机制会随语言的不同而不同, 但是你可以设想, 对象里面必定存有 它属于哪种类型 的信息 除了 static 和 final 方法 (private 方法隐含有 final 的意思 ),Java 的所有的方法都采用后绑定 也就是说, 通常情况下你不必考虑是不是应该采用后绑定 它是自动的 为什么要声明 final 方法? 我们在上一章指出, 这样可以禁止别人覆写那个方法 不过, 更重要的可能还是要 关闭 它的动态绑定, 或者更确切的说, 告诉编译器这里不需要使用后绑定 这样编译器就能为 final 方法生成稍微高效一些的调用代码 然而在绝大多数情况下, 这种做法并不会对程序的总体性能产生什么影响, 因此最好还是只把 final 当作一种设计手段来用, 而不要去考虑用它来提高性能 产生正确的行为 一旦知道 Java 通过后绑定实现了多态的方法调用, 你就可以只编写同基类打交道的代码了 因为你知道所有的派生类也能正确地使用这些代码 或者换一个说法, 你 向对象发一个消息, 让它自己判断该做些什么 形状 就是讲解 OOP 的一个经典的例子 它看起来直观, 因此被广泛使用, 但是不幸的是, 这会让新手误以为 OOP 只是用来处理图像编程的, 这显然不对 在这个例子中, 基类被称作 Shape, 它有好几个派生类 :Circle, Square,Triangle, 等等 这个例子之所以好, 是因为我们能很自然地说 圆形是一种形状, 而听的人也能明白 下面的继承关系图体现了这种关系 : 第 6 页共 29 页

7 下面这句就是在 上传 : Shape s = new Circle(); 这里先创建了一个 Circle 对象, 接着马上把它的 reference 赋给了 Shape 看上去这像是一个错误 ( 一种类型怎么能赋给另一种 ); 但是由于 Circle 是由 Shape 派生出来的,Circle 就是一种 Shape, 因此这种做法非常正确 所以编译器会毫不含糊地接受这条语句, 什么错都不报 假设你调用了一个基类方法 ( 派生类已经覆写这个方法 ): s.draw(); 可能你会认为, 这次应该总调用 Shape 的 draw( ) 了吧, 因为毕竟这是 Shape 的 reference 编译器又怎么会知道还要做其它事情呢? 但是由于实现了后绑定 ( 多态性 ), 实际上它会调用 Circle.draw( ) 下面的例程稍微作了一些变化 : //: c07:shapes.java // Polymorphism in Java. import com.bruceeckel.simpletest.*; import java.util.*; class Shape { void draw() { void erase() { class Circle extends Shape { void draw() { System.out.println("Circle.draw()"); void erase() { System.out.println("Circle.erase()"); class Square extends Shape { void draw() { System.out.println("Square.draw()"); void erase() { System.out.println("Square.erase()"); class Triangle extends Shape { void draw() { 第 7 页共 29 页

8 Chapter 7: Polymorphism System.out.println("Triangle.draw()"); void erase() { System.out.println("Triangle.erase()"); // A "factory" that randomly creates shapes: class RandomShapeGenerator { private Random rand = new Random(); public Shape next() { switch(rand.nextint(3)) { default: case 0: return new Circle(); case 1: return new Square(); case 2: return new Triangle(); public class Shapes { private static Test monitor = new Test(); private static RandomShapeGenerator gen = new RandomShapeGenerator(); public static void main(string[] args) { Shape[] s = new Shape[9]; // Fill up the array with shapes: for(int i = 0; i < s.length; i++) s[i] = gen.next(); // Make polymorphic method calls: for(int i = 0; i < s.length; i++) s[i].draw(); monitor.expect(new Object[] { new TestExpression("%% (Circle Square Triangle)" + "\\.draw\\(\\)", s.length) ); ///:~ 基类 Shape 为继承类定义了一个共用的接口 也就是说, 所有的 Shape 都有 draw( ) 和 erase( ) 这两个方法 派生类会覆写这两个方法, 以提供各自所独有的行为 RandomShapeGenerator 是一个 工厂, 每次调用它的 next( ) 方法的时候, 它都会随机选取一个 Shape 对象, 然后返回这个对象的 reference 要注意, 上传就发生在 return 语句, 它拿到的都是 Circle,Square 或是 Triangle, 而它返回的都是 Shape 因此每次调用 next( ) 的时候, 你都没法知道返回值的具体类型, 因为传给你的永远是普通的 Shape reference main( ) 创建了一个 Shape reference 的数组, 然后用 RandomShapeGenerator.next( ) 把它填满 这时, 你只知道它们 第 8 页共 29 页

9 都是 Shape, 至于更具体的, 就不得而知了 ( 编译器也一样 ) 但是一旦程序运行, 当你遍历数组, 逐个地调用它们的 draw( ) 方法的时候, 你就会发现, draw( ) 的行为魔法般地变成了各个具体类型的正确行为了 之所以要随机选择形状, 是想做得道地一点, 让你相信编译器在编译的时候也不知道该选用哪个方法 所有的 draw( ) 都必须使用后绑定 可扩展性 现在, 我们再回到乐器的例子 由于有了多态性, 你就可以根据需要, 往系统里添加任意多个新类型, 而不用担心还要修改 tune( ) 方法了 在一个设计良好的 OOP 程序中, 绝大多数方法都会和 tune( ) 一样, 只跟基类接口打交道 这种程序是可扩展的, 因为你可以通过 让新的数据类型继承通用的基类 的方法, 来添加新的功能 而那些与基类接口打交道的方法, 根本不需要作修改就能适应新的类 就拿乐器为例, 想想该怎样往基类里加新的方法, 并且通过继承产生一些新的类 下面就是关系图 : 第 9 页共 29 页

10 Chapter 7: Polymorphism 这些新的类都能同原先未作修改的 tune( ) 方法协同工作 即便是在 tune( ) 保存在另一个文件, 而 Instrument 的接口已经加入了新方法的情况下, 它也能无须重新编译而正常工作 下面就是这个关系图的实现 : //: c07:music3:music3.java // An extensible program. package c07.music3; import com.bruceeckel.simpletest.*; import c07.music.note; class Instrument { void play(note n) { System.out.println("Instrument.play() " + n); String what() { return "Instrument"; void adjust() { class Wind extends Instrument { void play(note n) { System.out.println("Wind.play() " + n); String what() { return "Wind"; void adjust() { class Percussion extends Instrument { void play(note n) { System.out.println("Percussion.play() " + n); String what() { return "Percussion"; void adjust() { class Stringed extends Instrument { void play(note n) { System.out.println("Stringed.play() " + n); String what() { return "Stringed"; void adjust() { class Brass extends Wind { void play(note n) { System.out.println("Brass.play() " + n); void adjust() { System.out.println("Brass.adjust()"); class Woodwind extends Wind { void play(note n) { System.out.println("Woodwind.play() " + n); String what() { return "Woodwind"; 第 10 页共 29 页

11 public class Music3 { private static Test monitor = new Test(); // Doesn't care about type, so new types // added to the system still work right: public static void tune(instrument i) { //... i.play(note.middle_c); public static void tuneall(instrument[] e) { for(int i = 0; i < e.length; i++) tune(e[i]); public static void main(string[] args) { // Upcasting during addition to the array: Instrument[] orchestra = { new Wind(), new Percussion(), new Stringed(), new Brass(), new Woodwind() ; tuneall(orchestra); monitor.expect(new String[] { "Wind.play() Middle C", "Percussion.play() Middle C", "Stringed.play() Middle C", "Brass.play() Middle C", "Woodwind.play() Middle C" ); ///:~ 新的方法是 what( ) 和 adjust( ) 前者会返回一个描述这个类的 String; 而后者则会为每件乐器调音 当你向 main( ) 的 orchestra 数组放东西的时候, 它们都会被自动地上传到 Instrument 可以看到,tune( ) 方法对周遭所发生的变化一无所知, 但是却能正常工作 这正是我们所希望的, 多态性应该提供的功能 程序的变动不会影响到它不应该影响的部分 换言之, 对程序员来说, 多态性是一项非常重要的技术, 它能让你 将会变和不会变的东西分隔开来 错误 : 覆写 private 的方法 你可能会很无辜地尝试去作下面这类事情 : //: c07:privateoverride.java // Abstract classes and methods. import com.bruceeckel.simpletest.*; public class PrivateOverride { 第 11 页共 29 页

12 Chapter 7: Polymorphism private static Test monitor = new Test(); private void f() { System.out.println("private f()"); public static void main(string[] args) { PrivateOverride po = new Derived(); po.f(); monitor.expect(new String[] { "private f()" ); class Derived extends PrivateOverride { public void f() { System.out.println("public f()"); ///:~ 可能你预想的输出会是 public f( ), 但是 private 方法自动就是 final 的, 而且会对派生类隐藏 因此这里 Derived 的 f( ) 是一个全新的方法 ; 甚至连重载都不算, 因为 Derived 根本看不到基类的 f( ) 结论就是, 只有非 private 的方法才能被覆写 但是你得留意那些看上去像是在覆写 private 方法的程序, 它们不会产生编译错误, 但是很可能会不按你的计划运行 说得再彻底一些, 别用基类的 private 方法的名字去命名派生类的方法 抽象类和抽象方法 在这些乐器例子中,Instrument 基类的方法都是些 样子货 如果这些方法真的被调用了, 那程序就有问题了 实际上 Instrument 的意图是要为所有由它派生出来的类创建一个公共的接口 要创建这种公共接口的唯一原因就是, 各个子类要用它自己的方式来实现这个接口 它定义了一个基本的形式, 你可以说这是所有的派生类所共有的 还有一种说法, 就是 Instrument 是一个 抽象的基类 (abstract base class 或者简化为抽象类 abstract class) 当你想要通过一个公共的接口来操控一组类的时候, 就可以使用抽象类了 通过动态绑定机制, 那些符合方法特征的派生类方法将会得到调用 ( 正如你在上一节看到的, 如果方法名同基类的相同, 而参数列表不同, 那就变成重载了, 这大概不是你想要的结果吧 ) 如果你有一个像 Instrument 这样的 abstract class, 那么这种类的对象是没什么意思的 也就是说 Instrument 只是用来定义接口, 而不是具体实现, 因此创建 Instrument 对象没有意义, 更何况你还可能要禁止用户这么作 要做到这点, 可以让 Instrument 的方法打印错误信 第 12 页共 29 页

13 息, 但是这样一来就把问题留到运行时了, 因此用户端需要进行详尽的测试 更好的办法还是在编译时发现这个问题 Java 提供了一种被称为 抽象方法 (abstract method) 的机制来解决这个问题 [32] 这是一种尚未完成的方法 ; 这种方法只有声明, 没有正文 下面就是抽象方法的声明 : abstract void f(); 包含抽象方法的类被称为 抽象类 (abstract class) 如果类包含一个或多个抽象方法, 那么这个类就必须被定义成 abstract 的 ( 否则编译器就会报错了 ) 既然抽象类是尚未完成的类, 那么如果有人想要创建抽象类的对象的话, 编译器又打算怎么做呢? 编译器没法安全地创建抽象类对象, 所以它会报错 由此, 编译器保证了抽象类的纯粹性, 而你也不用担心它会被误用了 如果你继承了抽象类, 而且还打算创建这个新类的对象, 那你就必须实现基类所定义的全部抽象方法 如果你不这么做 ( 你确实可以选择不这么做 ), 那么这个继承下来的类就也成了抽象类了, 编译器会强制你用 abstract 关键词来声明这个类的 创建一个不包含 abstract 方法的 abstract 类, 是完全可以的 这种技巧可以用于 不必创建 abstract 的方法, 但是又要禁止别人创建这个类的对象 的场合 Instrument 类可以很容易的被修改成 abstract 类 由于抽象类并不要求它的所有方法都是抽象的, 因此只要把几个方法定义成 abstract 的就行了 下面就是设计方案 : 第 13 页共 29 页

14 Chapter 7: Polymorphism 就是用 abstract 类和 abstract 方法来修改 orchestra ( 管弦乐团 ) 的例子 : //: c07:music4:music4.java // Abstract classes and methods. package c07.music4; import com.bruceeckel.simpletest.*; import java.util.*; import c07.music.note; abstract class Instrument { private int i; // Storage allocated for each public abstract void play(note n); public String what() { return "Instrument"; public abstract void adjust(); class Wind extends Instrument { public void play(note n) { System.out.println("Wind.play() " + n); public String what() { return "Wind"; public void adjust() { class Percussion extends Instrument { 第 14 页共 29 页

15 public void play(note n) { System.out.println("Percussion.play() " + n); public String what() { return "Percussion"; public void adjust() { class Stringed extends Instrument { public void play(note n) { System.out.println("Stringed.play() " + n); public String what() { return "Stringed"; public void adjust() { class Brass extends Wind { public void play(note n) { System.out.println("Brass.play() " + n); public void adjust() { System.out.println("Brass.adjust()"); class Woodwind extends Wind { public void play(note n) { System.out.println("Woodwind.play() " + n); public String what() { return "Woodwind"; public class Music4 { private static Test monitor = new Test(); // Doesn't care about type, so new types // added to the system still work right: static void tune(instrument i) { //... i.play(note.middle_c); static void tuneall(instrument[] e) { for(int i = 0; i < e.length; i++) tune(e[i]); public static void main(string[] args) { // Upcasting during addition to the array: Instrument[] orchestra = { new Wind(), new Percussion(), new Stringed(), new Brass(), new Woodwind() ; tuneall(orchestra); monitor.expect(new String[] { "Wind.play() Middle C", "Percussion.play() Middle C", "Stringed.play() Middle C", "Brass.play() Middle C", "Woodwind.play() Middle C" ); 第 15 页共 29 页

16 Chapter 7: Polymorphism ///:~ 可以看到, 除了基类之外, 别的什么都没改 由于它明确了类的抽象性, 并且告诉用户和编译器该如何使用, 因此 abstract 的类和方法能帮你解决很多问题 构造函数与多态性 跟往常一样, 构造函数总是与众不同, 牵涉到多态性的时候也不例外 尽管构造函数不是多态的 ( 实际上它们都是 static 方法, 只是声明的时候没有直说 ), 但是能理解 它在复杂的类系和多态的环境下是如何工作的 仍然十分重要 一旦理解了这个问题, 你就能避开很多让人不舒服的纠缠 构造函数的调用顺序 我们先是在第 4 章简要介绍了构造函数的调用顺序, 后来又在第 6 章作了进一步的讲解, 但是那时我们还没有介绍多态性 在创建派生类对象的过程中, 基类的构造函数总是先得到调用, 这样一级一级的追溯上去, 每个基类的构造函数都会被调用 这种做法是很合乎情理的, 因为构造函数有一个特殊的任务 : 它要知道对象是不是被正确地创建了 派生类只能访问它自己的成员, 它看不到基类的成员 ( 因为它们通常都是 private 的 ) 只有基类的构造函数才知道怎样初始化它的成员, 同时也只有它才有权限进行初始化 因此 把所有的构造函数都调用一遍 就变得非常重要了, 否则对象就没法创建了 这就是为什么编译器会强制每个派生类都要调用其基类的构造函数的原因了 如果你不在派生类的构造函数里明确地调用基类的构造函数, 那编译器就会悄悄的调用那个默认的构造函数 如果没有默认构造函数, 编译器就会报错 ( 要是类没有构造函数, 编译器会自动为你准备一个默认构造函数 ) 我们来看看下面这个例子, 通过它我们可以了解合成, 继承, 以及多态性对于对象创建的影响 : //: c07:sandwich.java // Order of constructor calls. package c07; import com.bruceeckel.simpletest.*; class Meal { Meal() { System.out.println("Meal()"); 第 16 页共 29 页

17 class Bread { Bread() { System.out.println("Bread()"); class Cheese { Cheese() { System.out.println("Cheese()"); class Lettuce { Lettuce() { System.out.println("Lettuce()"); class Lunch extends Meal { Lunch() { System.out.println("Lunch()"); class PortableLunch extends Lunch { PortableLunch() { System.out.println("PortableLunch()"); public class Sandwich extends PortableLunch { private static Test monitor = new Test(); private Bread b = new Bread(); private Cheese c = new Cheese(); private Lettuce l = new Lettuce(); public Sandwich() { System.out.println("Sandwich()"); public static void main(string[] args) { new Sandwich(); monitor.expect(new String[] { "Meal()", "Lunch()", "PortableLunch()", "Bread()", "Cheese()", "Lettuce()", "Sandwich()" ); ///:~ 这段程序用很多类创建了一个复杂的类, 这些类都有构造函数 Sandwich 是最重要的类, 它有三层继承关系 ( 如果你把隐含的继承 Object 也算上的话, 实际上是四层 ), 还有三个成员对象 你可以通过 main( ) 的输出观察到 Sandwich 对象的创建过程 也就是说复杂对象的构造函数的调用顺序是这样的 : 1. 调用基类的构造函数 这是一个递归过程, 因此会先创建继承体系的根, 然后是下一级派生类, 以此类推, 直到最后一个继承类的构造函数 2. 成员对象按照其声明的顺序进行初始化 3. 执行继承类的构造函数的正文 第 17 页共 29 页

18 Chapter 7: Polymorphism 构造函数的调用顺序是非常重要的 继承的前提就是你能看到基类, 并且还能访问它的 public 和 protected 成员 也就是说, 轮到处理派生类的时候, 所有的基类成员都应该已经准备好了 对于普通方法, 构造过程已经结束了, 因此对象已经完全建好了 但是对构造函数来说, 这还只是假设 为了确保这一点, 只能让构造函数先调用基类的构造函数 这样, 当你执行派生类构造函数的时候, 基类成员就都已经初始化完毕了, 可以供你访问了 能用构造函数访问数据成员 也是 只要有可能, 就在定义类的成员对象 ( 也就是用合成的方式放进去的对象 ) 的时候就进行初始化 的原因 ( 比如上述程序中的 b,c, 以及 l) 如果你遵循了这种做法, 那么当前对象的所有 基类成员 (base class members), 以及它自己的成员对象的初始化就有保证了 但不幸的是, 这不是一条万能法则 下一节就会讲到 继承与清理 即便新类既有合成又有继承, 绝大多数情况下, 你都无须担心清理的问题 ; 子对象通常都可以交由垃圾回收器去处理 如果真的需要进行清理, 那就只能辛苦一点, 为新类创建一个 dispose( ) 方法了 ( 我特地选了这个名字, 不过你也可以选一个你觉得更好的名字 ) 而且在继承情况下, 如果垃圾回收过程中还要作一些特殊的处理, 那你还必须在派生类里覆写基类的 dispose( ) 当你编写派生类的 dispose( ) 的时候, 要记住第一件事就是调用基类的 dispose( ), 这点非常重要 因为不这样做的话, 基类就不会得到清理 下面的例程演示了这点 : //: c07:frog.java // Cleanup and inheritance. import com.bruceeckel.simpletest.*; class Characteristic { private String s; Characteristic(String s) { this.s = s; System.out.println("Creating Characteristic " + s); protected void dispose() { System.out.println("finalizing Characteristic " + s); class Description { private String s; Description(String s) { this.s = s; System.out.println("Creating Description " + s); protected void dispose() { System.out.println("finalizing Description " + s); 第 18 页共 29 页

19 class LivingCreature { private Characteristic p = new Characteristic("is alive"); private Description t = new Description("Basic Living Creature"); LivingCreature() { System.out.println("LivingCreature()"); protected void dispose() { System.out.println("LivingCreature dispose"); t.dispose(); p.dispose(); class Animal extends LivingCreature { private Characteristic p= new Characteristic("has heart"); private Description t = new Description("Animal not Vegetable"); Animal() { System.out.println("Animal()"); protected void dispose() { System.out.println("Animal dispose"); t.dispose(); p.dispose(); super.dispose(); class Amphibian extends Animal { private Characteristic p = new Characteristic("can live in water"); private Description t = new Description("Both water and land"); Amphibian() { System.out.println("Amphibian()"); protected void dispose() { System.out.println("Amphibian dispose"); t.dispose(); p.dispose(); super.dispose(); public class Frog extends Amphibian { private static Test monitor = new Test(); private Characteristic p = new Characteristic("Croaks"); private Description t = new Description("Eats Bugs"); public Frog() { System.out.println("Frog()"); protected void dispose() { System.out.println("Frog dispose"); t.dispose(); 第 19 页共 29 页

20 Chapter 7: Polymorphism p.dispose(); super.dispose(); public static void main(string[] args) { Frog frog = new Frog(); System.out.println("Bye!"); frog.dispose(); monitor.expect(new String[] { "Creating Characteristic is alive", "Creating Description Basic Living Creature", "LivingCreature()", "Creating Characteristic has heart", "Creating Description Animal not Vegetable", "Animal()", "Creating Characteristic can live in water", "Creating Description Both water and land", "Amphibian()", "Creating Characteristic Croaks", "Creating Description Eats Bugs", "Frog()", "Bye!", "Frog dispose", "finalizing Description Eats Bugs", "finalizing Characteristic Croaks", "Amphibian dispose", "finalizing Description Both water and land", "finalizing Characteristic can live in water", "Animal dispose", "finalizing Description Animal not Vegetable", "finalizing Characteristic has heart", "LivingCreature dispose", "finalizing Description Basic Living Creature", "finalizing Characteristic is alive" ); ///:~ 这个继承体系中的每个类都有一个 Characteristic 和一个 Description 类型的成员对象 它们也应该得到清理 对象与对象之间有可能会有依赖关系, 因此清理的顺序应该与初始化的顺序相反 对数据成员而言, 这就是说它们的清理顺序应该与声明的顺序相反 ( 因为数据的初始化是按照声明的顺序进行的 ) 对基类而言 ( 它采用了 C++ 拆构函数的形式 ), 你应该先进行派生类的清理, 再进行基类的清理 这是因为派生类的清理可能需要调用某些基类的方法, 也就是说要留着基类, 因此它不能过早地被清除掉 你可以从程序的输出看出,Frog 对象各部分的清理顺序, 正好与它们创建的顺序相反 这个例程表明, 尽管你不会老是进行清理, 但是真的要做的时候, 还是要非常小心的 多态方法在构造函数中的行为 第 20 页共 29 页

21 构造函数的调用顺序也带来了一个有趣的难题 如果构造函数调用了一个动态绑定的方法, 而这个方法又属于那个正在创建中的对象, 那它会产生什么样的效果呢? 如果是普通方法, 你猜也可以猜到它会怎么做 : 由于不知道这个对象应该算是基类还是派生类的, 因此动态绑定会在运行时进行解析 出于一致性的考虑, 你可能会认为构造函数也应该这么作 事实并非完全如此 如果你在构造函数里面调用了动态绑定的方法, 那么它会使用那个覆写后的版本 但是这个 效果 会有些出人意料, 因此会成为一些不易排查的 bug 的藏身之处 从理论上讲, 构造函数的任务就是创建对象 ( 这可不是什么轻而易举的事 ) 从构造函数的角度来看, 对象可能只创建了一半 你只知道基类对象已经初始化了, 但是你还不知道它会派生出什么类 但是动态绑定的方法调用, 会从 外面 把手伸进 类系 (inheritance hierarchy) 它调用的是派生类的方法 如果你在构造函数里面这么做的话, 你就可能调用了一个 会访问尚未初始化的成员 的方法了 这注定会出问题 你可以从下面这个例子理解这个问题 : //: c07:polyconstructors.java // Constructors and polymorphism // don't produce what you might expect. import com.bruceeckel.simpletest.*; abstract class Glyph { abstract void draw(); Glyph() { System.out.println("Glyph() before draw()"); draw(); System.out.println("Glyph() after draw()"); class RoundGlyph extends Glyph { private int radius = 1; RoundGlyph(int r) { radius = r; System.out.println( "RoundGlyph.RoundGlyph(), radius = " + radius); void draw() { System.out.println( "RoundGlyph.draw(), radius = " + radius); public class PolyConstructors { private static Test monitor = new Test(); public static void main(string[] args) { new RoundGlyph(5); monitor.expect(new String[] { "Glyph() before draw()", "RoundGlyph.draw(), radius = 0", "Glyph() after draw()", 第 21 页共 29 页

22 Chapter 7: Polymorphism "RoundGlyph.RoundGlyph(), radius = 5" ); ///:~ Glyph 的 draw( ) 方法是 abstract 的, 所以这种设计就是要叫别人来覆写的 实际上, 你必须在 RoundGlyph 里覆写 draw( ) Glyph 的构造函数调用了 draw( ), 而这个调用最后落到了 RoundGlyph.draw( ), 看来一切都正常 但是当你查看输出的时候, 你就会发现,Glyph 的构造函数调用 draw( ) 的时候,radius 的值还没有被设成缺省的初始值 1 它还是 0 如果是真的编程的话, 这可能会是屏幕上画的一个点, 甚至是什么都不画, 而你呢, 会在那里瞪大了眼睛, 费力地排查到底那里出了错 在上面章节介绍的初始化顺序并不完整, 而缺失的部分才是这个谜团的关键 真正的初始化过程是这样的 : 1. 在进行其它工作之前, 分配给这个对象的内存会先被初始化为两进制的零 2. 正如前面一直在所说的, 先调用基类的构造函数 这时会调用被覆写的 draw( ) 方法 ( 是的, 在调用 RoundGlyph 的构造函数之前调用 ), 这时它发现, 由于受第一步的影响,radius 的值还是零 3. 数据成员按照它们声明的顺序进行初始化 4. 调用派生类的构造函数的正文 这样做有个好处, 就是它不会留一堆垃圾, 最起码把所有的东西都初始化为零了 ( 无论是哪种类型的, 都是零 ) 这其中也包括用合成嵌进去的对象, 这些 reference 都被设成了 null 所以如果你忘了对 reference 进行初始化, 运行的时候就会抛出异常 所有的东西都是零, 这样查看输出的时候多少会有点线索 但是另一方面, 你可能会对程序运行的结果大吃一惊 你的设计完美无缺, 但是程序运行的结果就是那样错得离谱, 而且编译器还不报错 ( 在这种情况下,C++ 的编译器会有一些比较理性的反映 ) 这类 bug 很容易会被忽略, 而且要花很长的时间发现 结论就是, 一个好的构造函数应该, 用最少的工作量把对象的状态设置好, 而且要尽可能地避免去调用方法 构造函数唯一能安全调用的方法, 就是基类的 final 方法 ( 这一条也适用 private 方法, 因为它自动就是 final 的 ) 它们不会被覆写, 因此也不会产生这种意外的行为 用继承来进行设计 第 22 页共 29 页

23 一旦理解了多态性, 你就会觉得所有东西应该都是继承下来的, 因为多态性实在是太聪明了 但是这样做会加重设计的负担 ; 实际上, 如果你一遇到 要用已有的类来创建新类 的情况就想到要用继承的话, 事情就会毫无必要地变得复杂起来了 较好的办法还是先考虑合成, 特别是当你不知道该继承哪个类的时候 合成并不强求你把设计搞成一个类系 此外它还更灵活, 因为使用合成的时候, 你可以动态地选择成员的类型 ( 以及它们的行为 ), 而使用继承的话, 就得在编译时指明对象的确切类型 下面这段程序就演示了这一点 : //: c07:transmogrify.java // Dynamically changing the behavior of an object // via composition (the "State" design pattern). import com.bruceeckel.simpletest.*; abstract class Actor { public abstract void act(); class HappyActor extends Actor { public void act() { System.out.println("HappyActor"); class SadActor extends Actor { public void act() { System.out.println("SadActor"); class Stage { private Actor actor = new HappyActor(); public void change() { actor = new SadActor(); public void performplay() { actor.act(); public class Transmogrify { private static Test monitor = new Test(); public static void main(string[] args) { Stage stage = new Stage(); stage.performplay(); stage.change(); stage.performplay(); monitor.expect(new String[] { "HappyActor", "SadActor" ); ///:~ Stage 对象包含了一个 Actor 的 reference, 而这个 reference 又被初始化为 HappyActor 也就是说 performplay( ) 会有一些特殊的 第 23 页共 29 页

24 Chapter 7: Polymorphism 行为 但是程序运行时 Actor 的 reference 可以连到另一个对象上, 因此可以用 SadActor 对象来替换它, 于是 performplay( ) 的行为就发生了变化 这样你就在运行时获得了高度的灵活性 ( 这也被称为 状态模式 (State Pattern) 参见 所刊载的 Thinking in Pattern (Java 版 )) 反观继承, 它不能让你在运行时继承不同的类 ; 这个问题在编译的时候就已经定下来了 有一条一般准则 使用继承来表示行为的不同, 而用成员数据来表示不同的状态 上述例程同时体现这两者 ; 两个派生类用来表示 act( ) 方法的不同, 而 Stage 则使用合成来表示状态的变化 在这种情况下, 状态的不同会导致行为的不同 纯继承与扩展 看来, 研究继承的最好方式, 还是用 纯继承 的方式创建一个类系 也就是说, 派生类仅覆写基类或 interface 里有的方法 就像下面这张图 : 由于类是由其接口所决定的, 因此这种关系被称为纯的 是 关系 继承保证了所有派生类都至少拥有基类的接口 而如果你采纳了这张图, 那么继承类的接口就不会比基类的更大 可以把这想像成 完全替代 (pure substitution), 因为完全可以用派生类的对象来替换基类的对象, 而且你这样做的时候根本不需要任何子类的信息 : 第 24 页共 29 页

25 也就是说, 由于有着相同的接口, 基类可以接受任何发送给派生类的消息 你所要做的, 只是将派生类的对象上传, 然后就不再需要知道这个对象是什么类型的了 所有的东西都交由多态性去处理 看到这里, 你会觉得纯的 是 关系才是唯一合理的方案, 而其它方案都不过是一些思路混乱, 并且前后矛盾的东西 这也是一个误区 如果你用这种思路考虑问题的话, 很快就会发现事实并非如此, 对某些问题而言, 扩展接口 ( 看到了吗,extends 关键词就是在鼓励你去这么做 ) 是一个完美的解决方案 这种关系可以用 像是 (is-like-a) 这个术语来表示, 因为派生类 像 基类 它有着相同的基本接口 但是它还有一些其它特性, 需要实现一些额外的方法 : 虽然这也是一种有用并且合理的方式 ( 根据情况 ), 但是它有一个缺点 不能通过基类访问派生类的扩展接口, 所以上传之后, 你就无法调用新的方法了 : 如果你不使用上传, 那么什么问题都没有 但是通常情况下, 你都会碰到 要重新发现这个对象的确切类型 的情况, 这样你才能使用那种类型的扩展方法 下面一节会告诉你该怎么做 下传与运行时的类型鉴别 第 25 页共 29 页

26 Chapter 7: Polymorphism 由于 上传 upcast ( 沿着继承关系向上移 ) 之后, 类的具体信息丢了, 因此 用 下传 (downcast) 也就是沿着继承关系重新向下移 来提取类型的信息 就成了顺理成章的事了 你知道上传总是安全的 ; 基类的接口不可能比派生类的更大 因此它肯定能收到那些通过基类接口发送的消息 但是碰到下传的时候, 你就不能肯定 这个形状是不是圆了 ( 只是举个例子 ) 它可以是一个三角型, 一个矩形或是其它什么形状 要想解决这个问题, 必须要有办法能够确保下传是正确的, 这样你就不会把对象误传给另一个类型了, 于是也不会向它发送什么它不能接受的消息了 这是相当不安全的 某些语言 ( 像 C++) 需要经过使用特别处理, 才能安全地进行下传 但是 Java 类型传递都要经过检查! 所以, 尽管看上去只是用一对括号作了些普通的的类型转换, 但是运行的时候, 系统会对这些转换作检查, 以确保它确实是你想要转换的类型 如果不是, 你就会得到一个 ClassCastException 这种运行时的类型检查被称为 运行时的类型鉴别 (run-time type identification 缩写为 RTTI) 下面的例程演示了 RTTI 的行为 : //: c07:rtti.java // Downcasting & Run-Time Type Identification (RTTI). // {ThrowsException class Useful { public void f() { public void g() { class MoreUseful extends Useful { public void f() { public void g() { public void u() { public void v() { public void w() { 第 26 页共 29 页

27 public class RTTI { public static void main(string[] args) { Useful[] x = { new Useful(), new MoreUseful() ; x[0].f(); x[1].g(); // Compile time: method not found in Useful: //! x[1].u(); ((MoreUseful)x[1]).u(); // Downcast/RTTI ((MoreUseful)x[0]).u(); // Exception thrown ///:~ 正如图表所示,MoreUseful 扩展了 Useful 的接口 但是由于它是继承的, 因此可以将它上传给 Useful 可以看到, 当 main( ) 对数组 x 进行初始化的时候, 就进行了上传 由于数组中的两个对象都是 Useful 的, 因此你可以向它们发送 f( ) 和 g( ) 消息, 但是如果你调用了 u( )( 只有 MoreUseful 才有 ), 编译的时候就会报错 如果你想访问 MoreUseful 对象的扩展接口, 你就得先下传 如果类型正确, 这个操作就会成功 否则, 你就会得到 ClassCastException 你不必为这个异常编写什么特殊的代码, 因为它表示这是程序员犯的错误, 而这种错误可能发生在程序的任意地方 RTTI 要比直接转换更复杂 举例来说, 你可以在下传之前先看一看 这个对象是哪种类型的 我们会在第 10 章, 用整章的篇幅研究 Java 的运行时类型鉴别 总结 多态性的意思是 不同的形式 在面向对象的编程中, 你会有 一张相同的脸 ( 基类的公共接口 ) 和很多 不同的使用这张脸的方式 : 各个版本的动态绑定方法 你在本章也看到了, 如果不理解数据抽象和继承的话, 是根本不可能理解多态性的, 更不用说创建多态性的例子了 多态性是一种不能孤立的看待的特性 ( 不像 switch 语句 ), 相反只有放在类关系的 大背景 下, 它才有用武之地 人们通常会把它同 Java 的那些非面向对象的特性相混淆, 比如方法的重载, 它常常会被当作面向对象的特性介绍给大家 千万别上当 : 不是后绑定的, 就不是多态性 第 27 页共 29 页

28 Chapter 7: Polymorphism 要想在编程中有效地使用多态性, 以及面向对象的技术, 那你就必须扩展你的编程视野, 不能只关注单个类的数据成员和消息, 而是要去理解类与类之间的共同性, 以及它们之间的关系 虽然这个要求很高, 但是这种努力是值得的, 因为它能加速程序的开发, 改善代码的逻辑组织, 使得程序更易于扩展, 同时维护代码也变得更方便了 练习 只要付很小一笔费用就能从 下载名为 The Thinking in Java Annotated Solution Guide 的电子文档, 这上面有一些习题的答案 1. 为 Shapes.java 的基类加一个能打印消息的方法, 但是别在派生类里覆写这个方法 预测一下会有什么结果 现在, 再在一个派生类里覆写这个类, 但是别在其它派生类里覆写, 看看结果会怎样 最后, 在所有的派生类里覆写这个方法 2. 向 Shape.java 添加一个新的类, 然后用 main( ) 检查一下, 看看是不是像和旧类一样, 多态性在新类上也一样能正常工作 3. 修改 Music3.java, 让 what( ) 成为 Object 的 tostring( ) 方法 试着用 System.out.println( ) 打印 Instrument 对象 ( 不要用类型转换 ) 4. 往 Music3.java 里面加一种新的 Instrument, 看看多态性是不是也会对新类型正常工作 5. 修改 Music3.java, 让它以 Shapes.java 的方式随机创建 Instrument 对象 6. 创建一个 Rodent( 啮齿动物 ) 类系 :Mouse,Gerbil,Hamster, 等等 在基类里定义所有 Rodent 所共有的方法, 然后在派生类里根据 Rodent 的具体类型覆写这些方法, 以提供不同的行为 创建一个 Rodent 的数组, 用各种具体的 Rodent 填满这个数组, 然后调用基类的方法, 看看程序运行的结果 7. 修改练习 6, 将 Rodent 改写成 abstract 的类 只要有可能, 就把 Rodent 的方法作成抽象方法 8. 创建一个不含任何 abstract 方法的 abstract 类, 然后验证一下, 你是不是不能创建这个类的实例 9. 往 Sandwich.java 里面添加 Pickle( 酸黄瓜 ) 类 10. 修改练习 6, 用它来演示基类与派生类的初始化顺序 再往基类和派生类里同时加入成员对象, 然后看看它们在创建过程中的初始化顺序 11. 创建一个有两个方法的基类 用第一个方法调用第二个方法 继承这个类, 并覆写第二个方法 创建第二个类的对象, 并上传到其基类, 然后调用第一个方法 说说看, 会有什么结果 12. 创建一个带 abstract print( ) 方法的基类, 然后在派生类里覆写这个方法 覆写后的方法要能打印在派生类里定义的 int 变量的值 定义这个变量的时候, 赋给它一个非零的值 在基类的构造函数里, 调用这 第 28 页共 29 页

29 个方法 用 main( ) 创建一个派生类的对象, 然后调用 print( ) 方法 解释一下为什么会有这个结果 13. 根据 Transmogrify.java 创建一个 Starship 类, 这个类里要包含一个 AlertStatus 的 reference, 而这个 AlertStatus 要能表示三种不同的状态 写一个切换状态的方法 14. 创建一个不带方法的 abstract 类 继承这个类, 并且添加一个方法 再创建一个会将基类的 reference 下传到派生类, 并且调用这个方法的 static 方法 用 main( ) 检验一下, 看看是不是能正常工作 然后在基类里把这个方法声明成 abstract 的, 这样就不需要下传了 [32] 对于 C++ 的程序员来说, 这就是 C++ 的 纯虚函数 (pure virtual function) 第 29 页共 29 页

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 复习 Protected 可以被子类 / 同一包中的类访问, 不能被其他类访问 弱化的 private 同时赋予 package access class MyType { public int i; public double d; public char

More information

OOP with Java 通知 Project 4: 推迟至 4 月 25 日晚 9 点

OOP with Java 通知 Project 4: 推迟至 4 月 25 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 推迟至 4 月 25 日晚 9 点 复习 Protected 可以被子类 / 同一包中的类访问, 不能被其他类访问 弱化的 private 同时赋予 package access class MyType { public int i; public double d; public

More information

OOP with Java 通知 Project 4: 5 月 2 日晚 9 点

OOP with Java 通知 Project 4: 5 月 2 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 5 月 2 日晚 9 点 复习 Protected 可以被子类 / 同一包中的类访问, 不能被其他类访问 弱化的 private 同时赋予 package access class MyType { public int i; public double d; public char

More information

OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数

OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数 复习 类的复用 组合 (composition): has-a 关系 class MyType { public int i; public double d; public char c; public void set(double

More information

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 复习 类的复用 组合 (composition): has-a 关系 class MyType { public int i; public double d; public char c; public void set(double x) { d

More information

エスポラージュ株式会社 住所 : 東京都江東区大島 東急ドエルアルス大島 HP: ******************* * 关于 Java 测试试题 ******

エスポラージュ株式会社 住所 : 東京都江東区大島 東急ドエルアルス大島 HP:  ******************* * 关于 Java 测试试题 ****** ******************* * 关于 Java 测试试题 ******************* 問 1 运行下面的程序, 选出一个正确的运行结果 public class Sample { public static void main(string[] args) { int[] test = { 1, 2, 3, 4, 5 ; for(int i = 1 ; i System.out.print(test[i]);

More information

OOP with Java 通知 Project 3: 4 月 19 日晚 9 点

OOP with Java 通知 Project 3: 4 月 19 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 3: 4 月 19 日晚 9 点 复习 Upcasting 同一基类的不同子类可以被视为同一类型 ( 基类 ) 放宽类型一致性 简化接口 class A{ class B{ A a = new A(); B b = new B(); // A a = new B(); compile

More information

OOP with Java 通知 Project 4: 5 月 2 日晚 9 点

OOP with Java 通知 Project 4: 5 月 2 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 5 月 2 日晚 9 点 复习 类的复用 组合 (composition): has-a 关系 class MyType { public int i; public double d; public char c; public void set(double x) { d =

More information

OOP with Java 通知 Project 3: 3 月 29 日晚 9 点 4 月 1 日上课

OOP with Java 通知 Project 3: 3 月 29 日晚 9 点 4 月 1 日上课 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 3: 3 月 29 日晚 9 点 4 月 1 日上课 复习 Java 包 创建包 : package 语句, 包结构与目录结构一致 使用包 : import restaurant/ - people/ - Cook.class - Waiter.class - tools/ - Fork.class

More information

软件工程文档编制

软件工程文档编制 实训抽象类 一 实训目标 掌握抽象类的定义 使用 掌握运行时多态 二 知识点 抽象类的语法格式如下 : public abstract class ClassName abstract void 方法名称 ( 参数 ); // 非抽象方法的实现代码 在使用抽象类时需要注意如下几点 : 1 抽象类不能被实例化, 实例化的工作应该交由它的子类来完成 2 抽象方法必须由子类来进行重写 3 只要包含一个抽象方法的抽象类,

More information

无类继承.key

无类继承.key 无类继承 JavaScript 面向对象的根基 周爱 民 / aimingoo aiming@gmail.com https://aimingoo.github.io https://github.com/aimingoo rand = new Person("Rand McKinnon",... https://docs.oracle.com/cd/e19957-01/816-6408-10/object.htm#1193255

More information

슬라이드 1

슬라이드 1 2018-2019 年度第二学期 00106501 计算机图形学 童伟华管理科研楼 1205 室 E-mail: tongwh@ustc.edu.cn 中国科学技术大学数学科学学院 http://math.ustc.edu.cn/ 附讲三 C/C++ 编程 ( 二 ) 2 C++ 特点 C++ 是面对对象 (object oriented) 编程语言 纯面向对象语言 : 指不管什么东西, 都应该存在于对象之中,

More information

OOP with Java 通知 Project 3 提交时间 3 月 29 日晚 9 点 Piazza Project 2 投票

OOP with Java 通知 Project 3 提交时间 3 月 29 日晚 9 点 Piazza Project 2 投票 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 3 提交时间 3 月 29 日晚 9 点 Piazza Project 2 投票 复习 创建对象 构造函数 函数重载 : 函数 = 函数名 + 参数列表 public class MyType { int i; double d; char c; void set(double x)

More information

新・解きながら学ぶJava

新・解きながら学ぶJava 481! 41, 74!= 40, 270 " 4 % 23, 25 %% 121 %c 425 %d 121 %o 121 %x 121 & 199 && 48 ' 81, 425 ( ) 14, 17 ( ) 128 ( ) 183 * 23 */ 3, 390 ++ 79 ++ 80 += 93 + 22 + 23 + 279 + 14 + 124 + 7, 148, 16 -- 79 --

More information

OOP with Java 通知 Project 4: 5 月 2 日晚 9 点

OOP with Java 通知 Project 4: 5 月 2 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 5 月 2 日晚 9 点 复习 Java 包 创建包 : package 语句, 包结构与目录结构一致 使用包 : import restaurant/ - people/ - Cook.class - Waiter.class - tools/ - Fork.class - Table.class

More information

Microsoft PowerPoint - CPP-Ch Print.ppt [兼容模式]

Microsoft PowerPoint - CPP-Ch Print.ppt [兼容模式] Chapter 13 Object-Oriented Programming: Polymorphism http://jssec.seu.edu.cn 杨明 yangming2002@seu.edu.cn OBJECTIVES What polymorphism( 多态 ) is, how it makes programming more convenient, and how it makes

More information

KillTest 质量更高 服务更好 学习资料 半年免费更新服务

KillTest 质量更高 服务更好 学习资料   半年免费更新服务 KillTest 质量更高 服务更好 学习资料 http://www.killtest.cn 半年免费更新服务 Exam : 310-065Big5 Title : Sun Certified Programmer for the Java 2 Platform, SE 6.0 Version : Demo 1 / 14 1. 35. String #name = "Jane Doe"; 36. int

More information

李 琼 评扎迪 史密斯的 白牙 要是他 指艾伯特 加勒比海移民 真的回去 了 那么他将要面临的失败是明摆在那儿的 因为当地并没有发生什么变化 这就是移民的悲剧 他们比他们离弃的故乡变化得更 快 于是他们永远也不可能因回到家乡而感 到幸福 可是 他们在移居的国家也不幸福 因为这不是家乡 瞿世镜

李 琼 评扎迪 史密斯的 白牙 要是他 指艾伯特 加勒比海移民 真的回去 了 那么他将要面临的失败是明摆在那儿的 因为当地并没有发生什么变化 这就是移民的悲剧 他们比他们离弃的故乡变化得更 快 于是他们永远也不可能因回到家乡而感 到幸福 可是 他们在移居的国家也不幸福 因为这不是家乡 瞿世镜 略论英国移民族群认同的发展和走向 李 琼 李 琼 评扎迪 史密斯的 白牙 要是他 指艾伯特 加勒比海移民 真的回去 了 那么他将要面临的失败是明摆在那儿的 因为当地并没有发生什么变化 这就是移民的悲剧 他们比他们离弃的故乡变化得更 快 于是他们永远也不可能因回到家乡而感 到幸福 可是 他们在移居的国家也不幸福 因为这不是家乡 瞿世镜 年 外国文学 第 期 这些天来 我觉得来到这个国家 就像是和魔鬼签了协议

More information

Chapter 9: Objects and Classes

Chapter 9: Objects and Classes Fortran Algol Pascal Modula-2 BCPL C Simula SmallTalk C++ Ada Java C# C Fortran 5.1 message A B 5.2 1 class Vehicle subclass Car object mycar public class Vehicle extends Object{ public int WheelNum

More information

Microsoft Word - 物件導向編程精要.doc

Microsoft Word - 物件導向編程精要.doc Essential Object-Oriented Programming Josh Ko 2007.03.11 object-oriented programming C++ Java OO class object OOP Ruby duck typing complexity abstraction paradigm objects objects model object-oriented

More information

Microsoft Word - ch04三校.doc

Microsoft Word - ch04三校.doc 4-1 4-1-1 (Object) (State) (Behavior) ( ) ( ) ( method) ( properties) ( functions) 4-2 4-1-2 (Message) ( ) ( ) ( ) A B A ( ) ( ) ( YourCar) ( changegear) ( lowergear) 4-1-3 (Class) (Blueprint) 4-3 changegear

More information

Strings

Strings Polymorphism and Virtual Functions Cheng-Chin Chiang Virtual Function Basics 多 型 (Polymorphism) 賦 予 一 個 函 數 多 種 意 涵, 存 在 於 同 一 類 別 之 內 祖 先 類 別 與 後 代 類 別 間 物 件 導 向 程 式 設 計 基 本 原 理 虛 擬 函 數 (Virtual Function)

More information

Guava学习之Resources

Guava学习之Resources Resources 提供提供操作 classpath 路径下所有资源的方法 除非另有说明, 否则类中所有方法的参数都不能为 null 虽然有些方法的参数是 URL 类型的, 但是这些方法实现通常不是以 HTTP 完成的 ; 同时这些资源也非 classpath 路径下的 下面两个函数都是根据资源的名称得到其绝对路径, 从函数里面可以看出,Resources 类中的 getresource 函数都是基于

More information

EJB-Programming-4-cn.doc

EJB-Programming-4-cn.doc EJB (4) : (Entity Bean Value Object ) JBuilder EJB 2.x CMP EJB Relationships JBuilder EJB Test Client EJB EJB Seminar CMP Entity Beans Session Bean J2EE Session Façade Design Pattern Session Bean Session

More information

Microsoft PowerPoint - ch6 [相容模式]

Microsoft PowerPoint - ch6 [相容模式] UiBinder wzyang@asia.edu.tw UiBinder Java GWT UiBinder XML UI i18n (widget) 1 2 UiBinder HelloWidget.ui.xml: UI HelloWidgetBinder HelloWidget.java XML UI Owner class ( Composite ) UI XML UiBinder: Owner

More information

《计算概论》课程 第十九讲 C 程序设计语言应用

《计算概论》课程 第十九讲  C 程序设计语言应用 Java 高级技术 课程第二讲 Java 语言的面向对象特性 (2) 李戈 北京大学信息科学技术学院软件研究所 2010 年 3 月 21 日 课程助教 : 前言 黎育龙 (10817195@pub.ss.pku.edu.cn) Email 标题 :Java 高级技术 + 题目 请不要把作业发到我的信箱 ; 新的课程网站 http://www.ontoedu.pku.edu.cn/index.jsp

More information

Microsoft PowerPoint - 08_OO_CJC.ppt

Microsoft PowerPoint - 08_OO_CJC.ppt C++ 中的 Hello World! C 程序设计语言 第 8 章 OO 与 C++ Java C# 孙志岗 sun@hit.edu.cn http://sunner.cn 兼容 C 语言的 : #include int main() printf("hello,, world!\n"); return 0; 更具 C++ 味道的 : #include int

More information

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 odps-sdk 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基 开放数据处理服务 ODPS SDK SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基础功能的主体接口, 搜索关键词 "odpssdk-core" 一些

More information

IDEO_HCD_0716

IDEO_HCD_0716 IDEO HCD Toolkit Tencent CDC ...? Tencent CDC Tencent CDC Tencent CDC Tencent CDC Tencent CDC Tencent CDC Tencent CDC Tencent CDC Tencent CDC Tencent CDC Tencent CDC Tencent CDC Tencent CDC Tencent CDC

More information

KillTest 质量更高 服务更好 学习资料 半年免费更新服务

KillTest 质量更高 服务更好 学习资料   半年免费更新服务 KillTest 质量更高 服务更好 学习资料 http://www.killtest.cn 半年免费更新服务 Exam : 310-055Big5 Title : Sun Certified Programmer for the Java 2 Platform.SE 5.0 Version : Demo 1 / 22 1. 11. public static void parse(string str)

More information

Microsoft PowerPoint - plan08.ppt

Microsoft PowerPoint - plan08.ppt 程 序 设 计 语 言 原 理 Principle of Programming Languages 裘 宗 燕 北 京 大 学 数 学 学 院 2012.2~2012.6 8. 面 向 对 象 为 什 么 需 要 面 向 对 象? OO 语 言 的 发 展 面 向 对 象 的 基 本 概 念 封 装 和 继 承 初 始 化 和 终 结 处 理 动 态 方 法 约 束 多 重 继 承 总 结 2012

More information

(procedure-oriented)?? 2

(procedure-oriented)?? 2 1 (procedure-oriented)?? 2 (Objected-Oriented) (class)? (method)? 3 : ( 4 ???? 5 OO 1966 Kisten Nygaard Ole-Johan Dahl Simula Simula 爲 6 Smalltalk Alan Kay 1972 PARC Smalltalk Smalltalk 爲 Smalltalk 爲 Smalltalk

More information

詞 彙 表 編 號 詞 彙 描 述 1 預 約 人 資 料 中 文 姓 名 英 文 姓 名 身 份 證 字 號 預 約 人 電 話 性 別 2 付 款 資 料 信 用 卡 別 信 用 卡 號 信 用 卡 有 效 日 期 3 住 房 條 件 入 住 日 期 退 房 日 期 人 數 房 間 數 量 入

詞 彙 表 編 號 詞 彙 描 述 1 預 約 人 資 料 中 文 姓 名 英 文 姓 名 身 份 證 字 號 預 約 人 電 話 性 別 2 付 款 資 料 信 用 卡 別 信 用 卡 號 信 用 卡 有 效 日 期 3 住 房 條 件 入 住 日 期 退 房 日 期 人 數 房 間 數 量 入 100 年 特 種 考 試 地 方 政 府 公 務 人 員 考 試 試 題 等 別 : 三 等 考 試 類 科 : 資 訊 處 理 科 目 : 系 統 分 析 與 設 計 一 請 參 考 下 列 旅 館 管 理 系 統 的 使 用 案 例 圖 (Use Case Diagram) 撰 寫 預 約 房 間 的 使 用 案 例 規 格 書 (Use Case Specification), 繪 出 入

More information

《大话设计模式》第一章

《大话设计模式》第一章 第 1 章 代 码 无 错 就 是 优? 简 单 工 厂 模 式 1.1 面 试 受 挫 小 菜 今 年 计 算 机 专 业 大 四 了, 学 了 不 少 软 件 开 发 方 面 的 东 西, 也 学 着 编 了 些 小 程 序, 踌 躇 满 志, 一 心 要 找 一 个 好 单 位 当 投 递 了 无 数 份 简 历 后, 终 于 收 到 了 一 个 单 位 的 面 试 通 知, 小 菜 欣 喜

More information

EJB-Programming-3.PDF

EJB-Programming-3.PDF :, JBuilder EJB 2.x CMP EJB Relationships JBuilder EJB Test Client EJB EJB Seminar CMP Entity Beans Value Object Design Pattern J2EE Design Patterns Value Object Value Object Factory J2EE EJB Test Client

More information

(TestFailure) JUnit Framework AssertionFailedError JUnit Composite TestSuite Test TestSuite run() run() JUnit

(TestFailure) JUnit Framework AssertionFailedError JUnit Composite TestSuite Test TestSuite run() run() JUnit Tomcat Web JUnit Cactus JUnit Java Cactus JUnit 26.1 JUnit Java JUnit JUnit Java JSP Servlet JUnit Java Erich Gamma Kent Beck xunit JUnit boolean JUnit Java JUnit Java JUnit Java 26.1.1 JUnit JUnit How

More information

1 下列类头定义中, 正确的是 面向对象程序设计网络课程 A class x { } B public x extends y { } C public class x extends y {.} D class x extends y implements y1 { } 2 现有两个类 A B,

1 下列类头定义中, 正确的是 面向对象程序设计网络课程 A class x { } B public x extends y { } C public class x extends y {.} D class x extends y implements y1 { } 2 现有两个类 A B, 1 下列类头定义中, 正确的是 A class x B public x extends y C public class x extends y. D class x extends y implements y1 2 现有两个类 A B, 以下描述中表示 B 继承自 A 的是 (D ) A) class A extends B B) class B implements A C) class A

More information

Microsoft Word - 新1-12.doc

Microsoft Word - 新1-12.doc 实训 5 面向对象编程练习 实训 5 面向对象编程练习 5.1 实训目的 通过编程和上机实验理解 Java 语言是如何体现面向对象编程基本思想 以及如何创建类 和对象 了解成员变量和成员方法的特性 5.2 实训要求 编写一个体现面向对象思想的程序 编写一个创建对象和使用对象的方法的程序 5.3 实训内容 5.3.1 创建对象并使用对象 1 定义一个 Person 类 可以在应用程序中使用该类 成员属性

More information

Microsoft Word - chap10.doc

Microsoft Word - chap10.doc 78 10. Inheritance in C++ 我 們 已 介 紹 了 物 件 導 向 程 式 的 第 一 個 主 要 特 性, 即 程 式 可 模 組 化 成 為 類 別 ( 物 件 ), 類 別 具 有 資 料 封 裝 的 特 性 接 下 來 我 們 要 介 紹 物 件 導 向 程 式 的 另 一 個 主 要 特 性, 那 就 是 類 別 具 有 繼 承 的 功 能 繼 承 就 是 重 複

More information

untitled

untitled 1 Outline 數 料 數 數 列 亂數 練 數 數 數 來 數 數 來 數 料 利 料 來 數 A-Z a-z _ () 不 數 0-9 數 不 數 SCHOOL School school 數 讀 school_name schoolname 易 不 C# my name 7_eleven B&Q new C# (1) public protected private params override

More information

OOP with Java 通知 Project 7, 6 月 14 日晚 9 点 本周四上机课正常进行 答疑 本周三, 周五下午 1:00 3:00, 理科楼 B911 6 月 12 日考试 ( 随堂 ) 下午 1:00-3:00 教书院 218

OOP with Java 通知 Project 7, 6 月 14 日晚 9 点 本周四上机课正常进行 答疑 本周三, 周五下午 1:00 3:00, 理科楼 B911 6 月 12 日考试 ( 随堂 ) 下午 1:00-3:00 教书院 218 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 7, 6 月 14 日晚 9 点 本周四上机课正常进行 答疑 本周三, 周五下午 1:00 3:00, 理科楼 B911 6 月 12 日考试 ( 随堂 ) 下午 1:00-3:00 教书院 218 OOP with Java 题目类型 : 选择 : 30% 问答 : 30% 编程 :

More information

使 用 Java 语 言 模 拟 保 险 箱 容 量 门 板 厚 度 箱 体 厚 度 属 性 锁 具 类 型 开 保 险 箱 关 保 险 箱 动 作 存 取 款

使 用 Java 语 言 模 拟 保 险 箱 容 量 门 板 厚 度 箱 体 厚 度 属 性 锁 具 类 型 开 保 险 箱 关 保 险 箱 动 作 存 取 款 JAVA 程 序 设 计 ( 肆 ) 徐 东 / 数 学 系 使 用 Java 语 言 模 拟 保 险 箱 容 量 门 板 厚 度 箱 体 厚 度 属 性 锁 具 类 型 开 保 险 箱 关 保 险 箱 动 作 存 取 款 使 用 Java class 代 表 保 险 箱 public class SaveBox 类 名 类 类 体 实 现 封 装 性 使 用 class SaveBox 代 表 保

More information

Microsoft PowerPoint - L17_Inheritance_v4.pptx

Microsoft PowerPoint - L17_Inheritance_v4.pptx C++ Programming Lecture 17 Wei Liu ( 刘 威 ) Dept. of Electronics and Information Eng. Huazhong University of Science and Technology May. 2015 Lecture 17 Chapter 20. Object-Oriented Programming: Inheritance

More information

OOP with Java 通知 Project 2 提交时间 : 3 月 14 日晚 9 点 另一名助教 : 王桢 学习使用文本编辑器 学习使用 cmd: Power shell 阅读参考资料

OOP with Java 通知 Project 2 提交时间 : 3 月 14 日晚 9 点 另一名助教 : 王桢   学习使用文本编辑器 学习使用 cmd: Power shell 阅读参考资料 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 2 提交时间 : 3 月 14 日晚 9 点 另一名助教 : 王桢 Email: 51141201063@ecnu.cn 学习使用文本编辑器 学习使用 cmd: Power shell 阅读参考资料 OOP with Java Java 类型 引用 不可变类型 对象存储位置 作用域 OOP

More information

Mac Java import com.apple.mrj.*;... public class MyFirstApp extends JFrame implements ActionListener, MRJAboutHandler, MRJQuitHandler {... public MyFirstApp() {... MRJApplicationUtils.registerAboutHandler(this);

More information

C++ 程序设计 告别 OJ2 - 参考答案 MASTER 2019 年 5 月 3 日 1

C++ 程序设计 告别 OJ2 - 参考答案 MASTER 2019 年 5 月 3 日 1 C++ 程序设计 告别 OJ2 - 参考答案 MASTER 2019 年 5 月 3 日 1 1 TEMPLATE 1 Template 描述 使用模板函数求最大值 使用如下 main 函数对程序进行测试 int main() { double a, b; cin >> a >> b; cout c >> d; cout

More information

Microsoft Word - 第3章.doc

Microsoft Word - 第3章.doc Java C++ Pascal C# C# if if if for while do while foreach while do while C# 3.1.1 ; 3-1 ischeck Test() While ischeck while static bool ischeck = true; public static void Test() while (ischeck) ; ischeck

More information

KillTest 质量更高 服务更好 学习资料 半年免费更新服务

KillTest 质量更高 服务更好 学习资料   半年免费更新服务 KillTest 质量更高 服务更好 学习资料 http://www.killtest.cn 半年免费更新服务 Exam : 1Z0-854 Title : Java Standard Edition 5 Programmer Certified Professional Upgrade Exam Version : Demo 1 / 12 1.Given: 20. public class CreditCard

More information

工程项目进度管理 西北工业大学管理学院 黄柯鑫博士 甘特图 A B C D E F G 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 甘特图的优点 : 直观明了 ( 图形化概要 ); 简单易懂 ( 易于理解 ); 应用广泛 ( 技术通用 ) 甘特图的缺点 : 不能清晰表示活动间的逻辑关系 WBS 责任分配矩阵 ( 负责〇审批

More information

Microsoft PowerPoint - 07 派生数据类型

Microsoft PowerPoint - 07 派生数据类型 能源与动力工程学院 目录 派生类型 陈 斌 固有数据类型 数值型 (numerical) 整型 INTEGER 实型 REAL 复数型 COMPLEX 非数值型 字符型 CHARACTER 逻辑型 ( 布尔型 )LOGICAL 自定义数据类型 ( 派生类型, derived type) 派生类型是指用户利用 Fortran 系统内部类型, 如整型 实型 复数型 逻辑型 字符型等的组合自行创建出一个新的数据类型,

More information

javaexample-02.pdf

javaexample-02.pdf n e w. s t a t i c s t a t i c 3 1 3 2 p u b l i c p r i v a t e p r o t e c t e d j a v a. l a n g. O b j e c t O b j e c t Rect R e c t x 1 y 1 x 2 y 2 R e c t t o S t r i n g ( ) j a v a. l a n g. O

More information

Strings

Strings Inheritance Cheng-Chin Chiang Relationships among Classes A 類 別 使 用 B 類 別 學 生 使 用 手 機 傳 遞 訊 息 公 司 使 用 金 庫 儲 存 重 要 文 件 人 類 使 用 交 通 工 具 旅 行 A 類 別 中 有 B 類 別 汽 車 有 輪 子 三 角 形 有 三 個 頂 點 電 腦 內 有 中 央 處 理 單 元 A

More information

1: public class MyOutputStream implements AutoCloseable { 3: public void close() throws IOException { 4: throw new IOException(); 5: } 6:

1: public class MyOutputStream implements AutoCloseable { 3: public void close() throws IOException { 4: throw new IOException(); 5: } 6: Chapter 15. Suppressed Exception CH14 Finally Block Java SE 7 try-with-resources JVM cleanup try-with-resources JVM cleanup cleanup Java SE 7 Throwable getsuppressed Throwable[] getsuppressed() Suppressed

More information

1 4 1.1 4 1.2..4 2..4 2.1..4 3.4 3.1 Java.5 3.1.1..5 3.1.2 5 3.1.3 6 4.6 4.1 6 4.2.6 5 7 5.1..8 5.1.1 8 5.1.2..8 5.1.3..8 5.1.4..9 5.2..9 6.10 6.1.10

1 4 1.1 4 1.2..4 2..4 2.1..4 3.4 3.1 Java.5 3.1.1..5 3.1.2 5 3.1.3 6 4.6 4.1 6 4.2.6 5 7 5.1..8 5.1.1 8 5.1.2..8 5.1.3..8 5.1.4..9 5.2..9 6.10 6.1.10 Java V1.0.1 2007 4 10 1 4 1.1 4 1.2..4 2..4 2.1..4 3.4 3.1 Java.5 3.1.1..5 3.1.2 5 3.1.3 6 4.6 4.1 6 4.2.6 5 7 5.1..8 5.1.1 8 5.1.2..8 5.1.3..8 5.1.4..9 5.2..9 6.10 6.1.10 6.2.10 6.3..10 6.4 11 7.12 7.1

More information

02

02 Thinking in C++: Volume One: Introduction to Standard C++, Second Edition & Volume Two: Practical Programming C++ C C++ C++ 3 3 C C class C++ C++ C++ C++ string vector 2.1 interpreter compiler 2.1.1 BASIC

More information

PowerPoint 演示文稿

PowerPoint 演示文稿 The BitCoin Scripting Language 交易实例 交易结构 "result": { "txid": "921a dd24", "hash": "921a dd24", "version": 1, "size": 226, "locktime": 0, "vin": [ ], "vout": [ ], "blockhash": "0000000000000000002c510d

More information

内 容 简 介 本 书 是 一 本 关 于 语 言 程 序 设 计 的 教 材, 涵 盖 了 语 言 的 基 本 语 法 和 编 程 技 术, 其 中 包 含 了 作 者 对 语 言 多 年 开 发 经 验 的 总 结, 目 的 是 让 初 学 的 读 者 感 受 到 语 言 的 魅 力, 并 掌

内 容 简 介 本 书 是 一 本 关 于 语 言 程 序 设 计 的 教 材, 涵 盖 了 语 言 的 基 本 语 法 和 编 程 技 术, 其 中 包 含 了 作 者 对 语 言 多 年 开 发 经 验 的 总 结, 目 的 是 让 初 学 的 读 者 感 受 到 语 言 的 魅 力, 并 掌 语 言 程 序 设 计 郑 莉 胡 家 威 编 著 清 华 大 学 逸 夫 图 书 馆 北 京 内 容 简 介 本 书 是 一 本 关 于 语 言 程 序 设 计 的 教 材, 涵 盖 了 语 言 的 基 本 语 法 和 编 程 技 术, 其 中 包 含 了 作 者 对 语 言 多 年 开 发 经 验 的 总 结, 目 的 是 让 初 学 的 读 者 感 受 到 语 言 的 魅 力, 并 掌 握 语

More information

Fun Time (1) What happens in memory? 1 i n t i ; 2 s h o r t j ; 3 double k ; 4 char c = a ; 5 i = 3; j = 2; 6 k = i j ; H.-T. Lin (NTU CSIE) Referenc

Fun Time (1) What happens in memory? 1 i n t i ; 2 s h o r t j ; 3 double k ; 4 char c = a ; 5 i = 3; j = 2; 6 k = i j ; H.-T. Lin (NTU CSIE) Referenc References (Section 5.2) Hsuan-Tien Lin Deptartment of CSIE, NTU OOP Class, March 15-16, 2010 H.-T. Lin (NTU CSIE) References OOP 03/15-16/2010 0 / 22 Fun Time (1) What happens in memory? 1 i n t i ; 2

More information

<4D6963726F736F667420506F776572506F696E74202D20332D322E432B2BC3E6CFF2B6D4CFF3B3CCD0F2C9E8BCC6A1AAD6D8D4D8A1A2BCCCB3D0A1A2B6E0CCACBACDBEDBBACF2E707074>

<4D6963726F736F667420506F776572506F696E74202D20332D322E432B2BC3E6CFF2B6D4CFF3B3CCD0F2C9E8BCC6A1AAD6D8D4D8A1A2BCCCB3D0A1A2B6E0CCACBACDBEDBBACF2E707074> 程 序 设 计 实 习 INFO130048 3-2.C++ 面 向 对 象 程 序 设 计 重 载 继 承 多 态 和 聚 合 复 旦 大 学 计 算 机 科 学 与 工 程 系 彭 鑫 pengxin@fudan.edu.cn 内 容 摘 要 方 法 重 载 类 的 继 承 对 象 引 用 和 拷 贝 构 造 函 数 虚 函 数 和 多 态 性 类 的 聚 集 复 旦 大 学 计 算 机 科 学

More information

模板

模板 制作人 : 张刚 目录 类和对象 面向对象程序设计基本特征 类的声明 构造方法 成员变量和方法 封装 继承 多态 包 访问控制 final static 抽象类和接口 内部类 沈阳工业大学软件学院 Java 课程教研组 Page 2 核心知识点 类 对象 三个基本特征 类的基本结构 成员变量 构造方法 成员方法 类实例 对象创建和操作 沈阳工业大学软件学院 Java 课程教研组 Page 3 1.

More information

(, : )?,,,,, (, : ),,,, (, ;, ;, : ),,, (, : - ),,, (, : ),,,,,,,,,,,,, -,,,, -,,,, -,,,,,,, ( ), ;, ( ) -,,,,,,

(, : )?,,,,, (, : ),,,, (, ;, ;, : ),,, (, : - ),,, (, : ),,,,,,,,,,,,, -,,,, -,,,, -,,,,,,, ( ), ;, ( ) -,,,,,, : 曹正汉 :, '.,,,., -..,.,,,.,, -., -,,,,,,,,,,,,,,, ( ),,,,,,,?,,?,, ( ), :? (. ) (, ),?, (, : )?,,,,, (, : ),,,, (, ;, ;, : ),,, (, : - ),,, (, : ),,,,,,,,,,,,, -,,,, -,,,, -,,,,,,, ( ), ;, ( ) -,,,,,,

More information

Microsoft PowerPoint - 第06讲_继承.ppt [兼容模式]

Microsoft PowerPoint - 第06讲_继承.ppt [兼容模式] 程序设计实习 (I): C++ 程序设计 第六讲继承 上节内容回顾 三种运算符重载的实现方式 重载为普通函数 重载为成员函数 重载为友元 常见的运算符重载 流运算符 (>>

More information

使用MapReduce读取XML文件

使用MapReduce读取XML文件 使用 MapReduce 读取 XML 文件 XML( 可扩展标记语言, 英语 :extensible Markup Language, 简称 : XML) 是一种标记语言, 也是行业标准数据交换交换格式, 它很适合在系统之间进行数据存储和交换 ( 话说 Hadoop H ive 等的配置文件就是 XML 格式的 ) 本文将介绍如何使用 MapReduce 来读取 XML 文件 但是 Had oop

More information

前言 C# C# C# C C# C# C# C# C# microservices C# More Effective C# More Effective C# C# C# C# Effective C# 50 C# C# 7 Effective vii

前言 C# C# C# C C# C# C# C# C# microservices C# More Effective C# More Effective C# C# C# C# Effective C# 50 C# C# 7 Effective vii 前言 C# C# C# C C# C# C# C# C# microservices C# More Effective C# More Effective C# C# C# C# Effective C# 50 C# C# 7 Effective vii C# 7 More Effective C# C# C# C# C# C# Common Language Runtime CLR just-in-time

More information

chp6.ppt

chp6.ppt Java 软 件 设 计 基 础 6. 异 常 处 理 编 程 时 会 遇 到 如 下 三 种 错 误 : 语 法 错 误 (syntax error) 没 有 遵 循 语 言 的 规 则, 出 现 语 法 格 式 上 的 错 误, 可 被 编 译 器 发 现 并 易 于 纠 正 ; 逻 辑 错 误 (logic error) 即 我 们 常 说 的 bug, 意 指 编 写 的 代 码 在 执 行

More information

骨头的故事

骨头的故事 头 1 图 206 33 7 12 5 5 4 12 2 54 10 200-400 3 500 图 类 图 图 动 节 4 5 图 发 图 节 180 Youtube 180 [1] 7 2 7 6 9 270 6 图 树懒 块颈 13-25 14 17 25 7 图 扭头 头鹰 鹅 8 图 红 为 关节 绿 为 关节 9 图 类 10 图 类 11 图 盘 动 类 图 阴 犸 艺 你可能会以为图

More information

res/layout 目录下的 main.xml 源码 : <?xml version="1.0" encoding="utf 8"?> <TabHost android:layout_height="fill_parent" xml

res/layout 目录下的 main.xml 源码 : <?xml version=1.0 encoding=utf 8?> <TabHost android:layout_height=fill_parent xml 拓展训练 1- 界面布局 1. 界面布局的重要性做应用程序, 界面是最基本的 Andorid 的界面, 需要写在 res/layout 的 xml 里面, 一般情况下一个 xml 对应一个界面 Android 界面布局有点像写 html( 连注释代码的方式都一样 ), 要先给 Android 定框架, 然后再在框架里面放控件,Android 提供了几种框架,AbsoluteLayout,LinearLayout,

More information

untitled

untitled 4.1AOP AOP Aspect-oriented programming AOP 來說 AOP 令 理 Cross-cutting concerns Aspect Weave 理 Spring AOP 來 AOP 念 4.1.1 理 AOP AOP 見 例 來 例 錄 Logging 錄 便 來 例 行 留 錄 import java.util.logging.*; public class HelloSpeaker

More information

K301Q-D VRT中英文说明书141009

K301Q-D VRT中英文说明书141009 THE INSTALLING INSTRUCTION FOR CONCEALED TANK Important instuction:.. Please confirm the structure and shape before installing the toilet bowl. Meanwhile measure the exact size H between outfall and infall

More information

Microsoft PowerPoint - 6. 用户定义类型User-defined Datatypes.ppt [兼容模式]

Microsoft PowerPoint - 6. 用户定义类型User-defined Datatypes.ppt [兼容模式] 用户定义类型 User-defined Datatypes classes and structs 几何向量 (Geometry Vector) 二维平面上的向量由起点和终点构成 每个点包含两个坐标 (x, y), 因此一个向量需要四个实数表示 Start= (0.9,1.5) Start= (0.4,0.8) int main() { double xstart = 0.4; double xend

More information

幻灯片 1

幻灯片 1 信息科学技术学院 程序设计实习 郭炜微博 http://weibo.com/guoweiofpku http://blog.sina.com.cn/u/3266490431 刘家瑛微博 http://weibo.com/pkuliujiaying 1 信息科学技术学院 程序设计实习 郭炜刘家瑛 继承和派生 ( 教材 P215) 2 继承和派生的概念 继承 : 在定义一个新的类 B 时, 如果该类与某个已有的类

More information

Microsoft Word - 4danalysis-pt3-p2-9.doc

Microsoft Word - 4danalysis-pt3-p2-9.doc TECHNIQUES & METHODS OF 4D ANALYSIS (Part III) 4-D Master, Charles In Part II, I presented the performance (hit rates) of the Most Frequent, Top-10 4D numbers over the last 15 years, ie., from 1 Jan 1996

More information

untitled

untitled 1 Outline 料 類 說 Tang, Shih-Hsuan 2006/07/26 ~ 2006/09/02 六 PM 7:00 ~ 9:30 聯 ives.net@gmail.com www.csie.ntu.edu.tw/~r93057/aspnet134 度 C# 力 度 C# Web SQL 料 DataGrid DataList 參 ASP.NET 1.0 C# 例 ASP.NET 立

More information

Microsoft PowerPoint - 8. 运算符重载 Operator Overloading.pptx

Microsoft PowerPoint - 8. 运算符重载 Operator Overloading.pptx 运算符重载 Operator Overloading class Point { public: ; double x_, y_; Why Operator Overloading? Point (double x =0, double y = 0):x_(x),y_(y) { int main(){ Point a(1., 2), b(3,4); Point c = a + b; return 0;

More information

第四章 102 图 4唱16 基于图像渲染的理论基础 三张拍摄图像以及它们投影到球面上生成的球面图像 拼图的圆心是相同的 而拼图是由球面图像上的弧线图像组成的 因此我 们称之为同心球拼图 如图 4唱18 所示 这些拼图中半径最大的是圆 Ck 最小的是圆 C0 设圆 Ck 的半径为 r 虚拟相机水平视域为 θ 有 r R sin θ 2 4畅11 由此可见 构造同心球拼图的过程实际上就是对投影图像中的弧线图像

More information

untitled

untitled 1 Outline 類别 欄 (1) 類 類 狀 更 易 類 理 若 類 利 來 利 using 來 namespace 類 ; (2) namespace IBM class Notebook namespace Compaq class Notebook 類别 類 來 類 列 欄 (field) (property) (method) (event) 類 例 立 來 車 類 類 立 車 欄 料

More information

untitled

untitled 20 1 66 1 20 2 66 2 20 3 66 N WAKIN 20 WAKIN 20 20 20 3 20 4 66 4 20 5 66 Dear Wakin, I m a Chinese girl living in Denmark together with my parents. I started to listen to your music at the age of 14 and

More information

JavaIO.PDF

JavaIO.PDF O u t p u t S t ream j a v a. i o. O u t p u t S t r e a m w r i t e () f l u s h () c l o s e () public abstract void write(int b) throws IOException public void write(byte[] data) throws IOException

More information

9, : Java 19., [4 ]. 3 Apla2Java Apla PAR,Apla2Java Apla Java.,Apla,,, 1. 1 Apla Apla A[J ] Get elem (set A) A J A B Intersection(set A,set B) A B A B

9, : Java 19., [4 ]. 3 Apla2Java Apla PAR,Apla2Java Apla Java.,Apla,,, 1. 1 Apla Apla A[J ] Get elem (set A) A J A B Intersection(set A,set B) A B A B 25 9 2008 9 M ICROEL ECTRON ICS & COMPU TER Vol. 25 No. 9 September 2008 J ava 1,2, 1,2, 1,2 (1, 330022 ; 2, 330022) :,. Apla - Java,,.. : PAR ;Apla - Java ; ;CMP ; : TP311 : A : 1000-7180 (2008) 09-0018

More information

Microsoft Word - 01.DOC

Microsoft Word - 01.DOC 第 1 章 JavaScript 简 介 JavaScript 是 NetScape 公 司 为 Navigator 浏 览 器 开 发 的, 是 写 在 HTML 文 件 中 的 一 种 脚 本 语 言, 能 实 现 网 页 内 容 的 交 互 显 示 当 用 户 在 客 户 端 显 示 该 网 页 时, 浏 览 器 就 会 执 行 JavaScript 程 序, 用 户 通 过 交 互 式 的

More information

获取 Access Token access_token 是接口的全局唯一票据, 接入方调用各接口时都需使用 access_token 开发者需要进行妥善保存 access_token 的存储至少要保留 512 个字符空间 access_token 的有效期目前为 2 个小时, 需定时刷新, 重复

获取 Access Token access_token 是接口的全局唯一票据, 接入方调用各接口时都需使用 access_token 开发者需要进行妥善保存 access_token 的存储至少要保留 512 个字符空间 access_token 的有效期目前为 2 个小时, 需定时刷新, 重复 获取 Access Token access_token 是接口的全局唯一票据, 接入方调用各接口时都需使用 access_token 开发者需要进行妥善保存 access_token 的存储至少要保留 512 个字符空间 access_token 的有效期目前为 2 个小时, 需定时刷新, 重复 获取将导致上次获取的 access_token 失效 接入方可以使用 AppID 和 AppSecret

More information

Microsoft PowerPoint - 04-Inheritance.ppt

Microsoft PowerPoint - 04-Inheritance.ppt 继承 派生与多态性 1 本章主要内容 类的继承与派生 类成员的访问控制 简单继承与多重继承 派生类的构造 析构函数 多态性 2 1 类的继承与派生 保持已有类的特性, 并在其基础上产生新的类, 称新类继承了已有类的特征, 或称已有类派生出新类 被继承的已有类称为基类 ( 或父类 ) 派生出的新类称为派生类 派生类将自动继承基类的所有特性 ( 属性和方法 ) 派生类可以定义新的特性 ( 属性和方法 )

More information

運算子多載 Operator Overloading

運算子多載 Operator Overloading 多型 Polymorphism 講師 : 洪安 1 多型 編譯時期多型 ( 靜態多型 ) function overloading 如何正確呼叫同名的函數? 利用參數個數與型態 operator overloading 其實同 function overloading 執行時期多型 ( 或動態多型 ) 如何正確呼叫不同物件的相同名稱的成員函數 利用繼承與多型 2 子類別與父類別物件間的指定 (assignment)

More information

CC213

CC213 : (Ken-Yi Lee), E-mail: feis.tw@gmail.com 9 [P.11] : Dev C++ [P.12] : http://c.feis.tw [P.13] [P.14] [P.15] [P.17] [P.23] Dev C++ [P.24] [P.27] [P.34] C / C++ [P.35] 10 C / C++ C C++ C C++ C++ C ( ) C++

More information

未命名-1

未命名-1 1 2 3 4 5 6 7 8 9 10 11 12 ss a c y e vg 13 14 15 16 17 18 19 H 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 发现生命的螺旋 克里克在提出 中心法则 时曾指出 遗传信息是沿 D N A - R N A - 蛋白质的方向流动的 遗传信息不可能从 R N A 回到 D N

More information

1 Framework.NET Framework Microsoft Windows.NET Framework.NET Framework NOTE.NET NET Framework.NET Framework 2.0 ( 3 ).NET Framework 2.0.NET F

1 Framework.NET Framework Microsoft Windows.NET Framework.NET Framework NOTE.NET NET Framework.NET Framework 2.0 ( 3 ).NET Framework 2.0.NET F 1 Framework.NET Framework Microsoft Windows.NET Framework.NET Framework NOTE.NET 2.0 2.0.NET Framework.NET Framework 2.0 ( 3).NET Framework 2.0.NET Framework ( System ) o o o o o o Boxing UnBoxing() o

More information

C++ 程序设计 OJ2 - 参考答案 MASTER 2019 年 5 月 3 日 1

C++ 程序设计 OJ2 - 参考答案 MASTER 2019 年 5 月 3 日 1 C++ 程序设计 OJ2 - 参考答案 MASTER 2019 年 5 月 3 日 1 1 PERSON 1 Person 题目描述 编写程序, 定义一个基类 Person, 包含 name 和 age 两个数据成员 ; 再由它派生出学生类 Student 和教师类 Teacher, 其中学生类添加学号 no 数据, 教师类添加职称 title 数据 ; 要求每个类均有构造函数 析构函数和显示数据的函数

More information

(6) 要 求 付 款 管 理 员 从 预 订 表 中 查 询 距 预 订 的 会 议 时 间 两 周 内 的 预 定, 根 据 客 户 记 录 给 满 足 条 件 的 客 户 发 送 支 付 余 款 要 求 (7) 支 付 余 款 管 理 员 收 到 客 户 余 款 支 付 的 通 知 后, 检

(6) 要 求 付 款 管 理 员 从 预 订 表 中 查 询 距 预 订 的 会 议 时 间 两 周 内 的 预 定, 根 据 客 户 记 录 给 满 足 条 件 的 客 户 发 送 支 付 余 款 要 求 (7) 支 付 余 款 管 理 员 收 到 客 户 余 款 支 付 的 通 知 后, 检 2016 年 上 半 年 软 件 设 计 师 考 试 真 题 ( 下 午 题 ) 下 午 试 题 试 题 一 ( 共 15 分 ) 阅 读 下 列 说 明 和 图, 回 答 问 题 1 至 问 题 4, 将 解 答 填 入 答 题 纸 的 对 应 栏 内 说 明 某 会 议 中 心 提 供 举 办 会 议 的 场 地 设 施 和 各 种 设 备, 供 公 司 与 各 类 组 织 机 构 租 用 场

More information

劳动保护与医疗保健 第 二 章 止标志共 23 个 劳 动 安 全 技 术 22 2 警告标志 由于三角形引人注目 故用作 警告 标志 警告人们注意可能发生的多种危险 三角的背景使用黄色 三角图形和三角内的图像均用黑色描 绘 黄色是有警告含义的颜色 在对比色黑色的衬托下 绘成的 警告标志 就更引人注目 3 指令标志 在圆形内配上指令含义的颜色 蓝 色 并用白色绘制必须执行的图形符号 构成 指令标志

More information

附录J:Eclipse教程

附录J:Eclipse教程 附 录 J:Eclipse 教 程 By Y.Daniel Liang 该 帮 助 文 档 包 括 以 下 内 容 : Eclipse 入 门 选 择 透 视 图 创 建 项 目 创 建 Java 程 序 编 译 和 运 行 Java 程 序 从 命 令 行 运 行 Java Application 在 Eclipse 中 调 试 提 示 : 在 学 习 完 第 一 章 后 使 用 本 教 程 第

More information

图1 嘉兴市住宅建设规划图 有章可循 的 2 更具专业性 划 城市雕塑规划等等均需要深入 了解与规划内容相关的专业知识 宏观层面上的非法定规划如概 这对规划师的学习能力提出了新的 念规划 城市发展战略规划等需要 要求之外 也更加大了规划编制的 对区域问题和城市问题有深入的探 难点 讨与研究 经济发展规划涉及城市 3 更具动态性 非法定规划可以说是在法定规 题 而微观层面上的非法定规划如 划的基础上考虑我们怎样做才能使

More information

extend

extend (object oriented) Encapsulation Inheritance Polymorphism Dynamic Binding (base class) (derived class) 1 class Base { int I; void X(); void Y(); class Derived: public Base { private: int j; void z(); Derived

More information

D C 93 2

D C 93 2 D9223468 3C 93 2 Java Java -- Java UML Java API UML MVC Eclipse API JavadocUML Omendo PSPPersonal Software Programming [6] 56 8 2587 56% Java 1 epaper(2005 ) Java C C (function) C (reusability) eat(chess1,

More information

Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0,

Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0, http://debut.cis.nctu.edu.tw/~chi Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0, : POSITIVE_INFINITY NEGATIVE_INFINITY

More information

untitled

untitled How to using M-Power Report API M-Power Report API 力 了 M-Power Report -- Java (Library) M-Power Report API 行 Java M-Power Report M-Power Report API ( 30 ) PDF/HTML/CSV/XLS JPEG/PNG/SVG 料 料 OutputStream

More information

Learning Java

Learning Java Java Introduction to Java Programming (Third Edition) Prentice-Hall,Inc. Y.Daniel Liang 2001 Java 2002.2 Java2 2001.10 Java2 Philip Heller & Simon Roberts 1999.4 Java2 2001.3 Java2 21 2002.4 Java UML 2002.10

More information

<4D6963726F736F667420576F7264202D20B5DAC8FDB7BDBE57C9CFD6A7B8B6D6AEB7A8C2C98696EE7DCCBDBEBF2E646F63>

<4D6963726F736F667420576F7264202D20B5DAC8FDB7BDBE57C9CFD6A7B8B6D6AEB7A8C2C98696EE7DCCBDBEBF2E646F63> 題 目 : 第 三 方 網 上 支 付 之 法 律 問 題 探 究 Title:A study on legal issues of the third-party online payment 姓 名 Name 學 號 Student No. 學 院 Faculty 課 程 Program 專 業 Major 指 導 老 師 Supervisor 日 期 Date : 王 子 瑜 : 1209853J-LJ20-0021

More information

拦截器(Interceptor)的学习

拦截器(Interceptor)的学习 二 拦截器 (Interceptor) 的学习 拦截器可以监听程序的一个或所有方法 拦截器对方法调用流提供了细粒度控制 可以在无状态会话 bean 有状态会话 bean 和消息驱动 bean 上使用它们 拦截器可以是同一 bean 类中的方法或是一个外部类 下面介绍如何在 Session Bean 类中使用外部拦截器类 @Interceptors 注释指定一个或多个在外部类中定义的拦截器 下面拦截器

More information

可 愛 的 動 物 小 五 雷 雅 理 第 一 次 小 六 甲 黃 駿 朗 今 年 暑 假 發 生 了 一 件 令 人 非 常 難 忘 的 事 情, 我 第 一 次 參 加 宿 營, 離 開 父 母, 自 己 照 顧 自 己, 出 發 前, 我 的 心 情 十 分 緊 張 當 到 達 目 的 地 後

可 愛 的 動 物 小 五 雷 雅 理 第 一 次 小 六 甲 黃 駿 朗 今 年 暑 假 發 生 了 一 件 令 人 非 常 難 忘 的 事 情, 我 第 一 次 參 加 宿 營, 離 開 父 母, 自 己 照 顧 自 己, 出 發 前, 我 的 心 情 十 分 緊 張 當 到 達 目 的 地 後 郭家朗 許鈞嵐 劉振迪 樊偉賢 林洛鋒 第 36 期 出版日期 28-3-2014 出版日期 28-3-2014 可 愛 的 動 物 小 五 雷 雅 理 第 一 次 小 六 甲 黃 駿 朗 今 年 暑 假 發 生 了 一 件 令 人 非 常 難 忘 的 事 情, 我 第 一 次 參 加 宿 營, 離 開 父 母, 自 己 照 顧 自 己, 出 發 前, 我 的 心 情 十 分 緊 張 當 到 達 目

More information

ESOL-CN-Bleed.pub

ESOL-CN-Bleed.pub NZCB Discover New Zealand BELIEVE YOU CAN www.nzcb.ac.nz ,, 我非常喜欢这里, 我在这里得到了很多的帮助, 谢谢学校为我提供这么好的学习环境和升学机会, 因此我也强烈推荐我的朋友们也来 NZCB 学习国际英文 (IESOL) 课程 - 黎曦 ( 中国 ) 在 NZCB 学习期间我的收获非常大, 不止是英语能力的提升, 还让我更加熟悉新西兰,

More information