第七章 复用类

组合和继承是复用类的两种主要方式。

7.1 组合语法
组合只需将对象引用置于新类中即可。这些引用的初始化方式有四种:
1) 在定义对象的地方进行初始化,即在创建新类对象前就被初始化;
2) 在新类的构造器中初始化;
3) 在使用这些引用之前再初始化,这被称之为惰性初始化,推荐这一方式;
4) 使用新类对象进行初始化。

7.2 继承语法
当创建一个类时,总是在继承,不是明确从其他类继承就是隐式地继承Java标准根类Object。
1) 子类继承父类的所有成员变量和成员方法;
2) 子类可以添加父类所不具备的新方法;
3) 子类可以重写与父类同名的方法。

7.2.1 初始化基类
当创建子类对象时,该对象会包含一个父类的子对象。针对该父类子对象的初始化,Java会在子类的构造器中首先自动调用父类的构造器,来对父类子对象进行初始化。

public class Cartoon extends Drawing {public Cartoon(){System.out.println("Cartoon constructor");}public static void main(String[] args){Cartoon x = new Cartoon();}
}class Art{Art(){System.out.println("Art constructor");}
}class Drawing extends Art{Drawing(){System.out.println("Drawing constructor");}
}

在上述代码中,当创建Cartoon对象时,在Cartoon的构造器中,会首先调用Art类、Drawing类的构造器。

以上是无参构造器的情况。针对有参的的情况,在父类没有默认的无参构造器或者想调用父类的有参构造器的情况下,必须在子类的构造器中第一句显式地使用super关键字调用父类相应的构造器。

public class Chess extends BoardGame {Chess(){super(11);System.out.println("Chess constructor");}public static void main(String[] args){Chess x = new Chess();}
}class Game{Game(int i){System.out.println("Game constructor");}
}class BoardGame extends Game{BoardGame(int i){super(i);System.out.println("BoardGame constructor");}
}

在父类没有默认的无参构造器的情况下,必须在子类的构造器的第一行明确调用父类的有参构造器。

7.4 代理
代理也是复用类的一种方式,只是Java并没有提供对它的直接支持。与组合类似,也是作为新类的引用,只是新类具有相同的方法,并在每一方法调用引用的相应方法。

public class SpaceShipControls {void up(int velocity){}void down(int velocity){}void left(int velocity){}void right(int velocity){}void forward(int velocity){}void back(int velocity){}void turboBoost(){}
}
public class SpaceShipDelegation {private String name;private SpaceShipControls controls = new SpaceShipControls();public SpaceShipDelegation(String name){this.name = name;}public void back(int velocity){controls.back(velocity);}public void down(int velocity){controls.down(velocity);}public void forward(int velocity){controls.forward(velocity);}public void left(int velocity){controls.left(velocity);}public void right(int velocity){controls.right(velocity);}public void up(int velocity){controls.up(velocity);}public void turboBoost(){controls.turboBoost();}public static void main(String[] args){SpaceShipDelegation protector = new SpaceShipDelegation("NESA Protector");protector.forward(100);}
}

7.4 结合使用组合和继承
可以同时使用组合和继承。

7.4.1 确保正确清理
子对象的清理顺序与其初始化顺序最好正好相反,以防出现一个子对象依赖于另一个子对象的情形发生。

public class CADSystem extends Shape{private Circle c;private Triangle t;private Line[] lines = new Line[3];public CADSystem(int i){super(i + 1);for(int j = 0; j < lines.length; j++){lines[j] = new Line(j, j * j);}c = new Circle(1);t = new Triangle(1);System.out.println("Combined constructor");}/*** 清理顺序与初始化顺序相反,以防出现某个子对象依赖于另一个子对象情形发生*/public void dispose(){System.out.println("CADSystem.dispose()");t.dispose();c.dispose();for(int i = lines.length - 1; i >= 0; i--){lines[i].dispose();}super.dispose();}public static void main(String[] args){CADSystem x = new CADSystem(47);try{}finally {x.dispose();}}
}class Shape{Shape(int i){System.out.println("Shape constructor");}void dispose(){System.out.println("Shape dispose");}
}class Circle extends Shape{Circle(int i){super(i);System.out.println("Drawing Circle");}void dispose(){System.out.println("Erasing Circle");super.dispose();}
}class Triangle extends Shape{Triangle(int i){super(i);System.out.println("Drawing triangle");}void dispose(){System.out.println("Erasing Triangle");super.dispose();}
}class Line extends Shape{private int start, end;Line(int start, int end){super(start);this.start = start;this.end = end;System.out.println("Drawing Line: " + start + ", " + end);}void dispose(){System.out.println("Erasing Line: " + start + ", " + end);}
}

7.4.2 名称屏蔽
方法重载的不同版本可以分别位于父类和子类。

public class Hide {public static void main(String[] args){Bart b = new Bart();b.doh(1);b.doh('x');b.doh(1.0f);b.doh(new Milhouse());}
}class Homer{char doh(char c){System.out.println("doh(char)");return 'd';}float doh(float f){System.out.println("doh(float)");return 1.0f;}
}class Milhouse{}class Bart extends Homer{void doh(Milhouse m){System.out.println("doh(Milhouse)");}
}

7.5 在继承和组合之间选择
组合和继承都允许在新类中放置子对象,只是前者是显式放置,后者是隐式放置。

组合技术通常用于在新类中使用现有类的功能而非现有类的接口。即,在新类中嵌入某个对象,让其实现所需功能,但新类的用户看到的只是新类的接口,而非嵌入对象的接口,此时该嵌入对象可以设定为private。如果该嵌入对象自身隐藏了具体的实现,也可以将该嵌入对象设置 为public。

继承是使用某个现有类,并开发它的一个特殊版本。

7.6 protected关键字
protected关键字表示只有本类以及其衍生的子类能访问的成员,包括成员变量和成员方法,但是一般成员变量都声明为private。

7.7 向上转型
子类对象的类型既是子类类型,也是父类类型,也就是子类对象同时也是父类对象。在引用上就表现为,子类引用可以转变为父类引用,这称之为向上转型(upcasting)。

注意与多态相联系。

7.7.1 为什么称之为向上转型
在继承图上,父类在上,子类在下,有箭头从子类指向父类,表示继承。

7.7.2 再论组合与继承
多用组合,慎用继承。
判断使用组合还是继承的标准是,是否需要从子类向父类进行向上转型。

7.8 final关键字
final关键字表示只能初始化一次,一旦被初始化后,就不能被再次赋值。其可以作用在数据(成员变量)、方法(成员方法)和类上。

7.8.1 final 数据
这种情况主要有两种应用场景:
1) 永不改变的编译时常量,可以是基本数据类型,也可以是引用类型;
2) 一旦被初始化后,程序员不希望被改变。
一个既是static又是final的数据(成员变量)只占据一段不能改变的存储空间,这种数据在类被加载进入内存时,一旦初始化后(基本数据类型赋值,引用类型创建对象),无论利用该类创建多少对象,这种数据都不变(基本数据类型值不变,引用数据类型地址不变,但是其相应的属性值可以改变)。

空白final
被声明为final但又没有给定初值的成员变量被称为空白final。它具有较大的灵活性,可以在使用时再初始化。
注意,空白final必须被初始化,否则编译报错。

final参数
Java允许在方法的参数列表中将参数声明为final,表示在方法内,无法更改参数引用指向的对象或者对应的基本数据类型的值。

final方法
当一个方法被final修饰时,则继承类无法重写该方法。
实际上,当方法声明为private时,就已经隐式地被声明为final了,显然子类不能继承该方法,所以也就无所谓重写的问题了,但是仍然可以在子类中声明同名的方法,但是这两个方法不存在重写关系。

public class FinalOverridingIllusion {public static void main(String[] args){OverridingPrivate2 op2 = new OverridingPrivate2();op2.f();op2.g();//向上转型OverridingPrivate op = op2;//显然无法调用f()和g()//op.f();//op.g();WithFinals wf = op2;//同上//wf.f();//wf.g();}
}class WithFinals{private final void f(){System.out.println("WithFinals.f()");}private void g(){System.out.println("WithFinals.g()");}
}class OverridingPrivate extends WithFinals{private final void f(){System.out.println("OverridingPrivate.f()");}private void g(){System.out.println("OverridingPrivate.g()");}
}class OverridingPrivate2 extends OverridingPrivate{public final void f(){System.out.println("OverridingPrivate2.f()");}public void g(){System.out.println("OverridingPrivate2.g()");}
}

7.8.3 final类
被final修饰的类无法被继承,即没有子类。

7.8.4 有关final的忠告

7.9 初始化及类的加载
一般来说,类的代码只有在初次使用时才加载。初次使用是指使用该类创建第一个对象,或者是第一次使用该类访问器static成员变量或者成员方法。
7.9.1 继承与初始化

public class Beetle extends Insect{private int k = printInit("Beetle.k initialized");public Beetle(){System.out.println("k = " + k);System.out.println("j = " + j);}private static int x2 = printInit("static Beetle.x2 initialized");public static void main(String[] args){System.out.println("Beetle constructor");Beetle b = new Beetle();Beetle b2 = new Beetle();}
}class Insect{private int i = 9;protected int j;Insect(){System.out.println("i = " + i + ", j = " + j);j = 39;}private static int x1 = printInit("static Insect.x1 initialized");static int printInit(String s){System.out.println(s);return 47;}
}

在这个例子中,在Beetle上运行Java时,第一件事就是访问Beetle.main()方法,于是加载器开始启动并找出Beetle.class,在对其进行加载的过程中,发现其由父类Insect,继而寻找并加载Insect.class,如果还有父类,则继续寻找并加载相应父类的.class文件。

此后是初始化的过程,先对父类的static成员变量进行初始化,然后再是子类的static成员变量进行初始化,这样做是因为子类的static成员变量可能会依赖于父类的static成员变量能否被正确初始化(在这个例子中就是如此)。

至此,类加载完毕,开始创建对象。与前面相同,先执行父类的构造器,再执行子类的构造器。

Java编程思想读书笔记——复用类相关推荐

  1. JAVA编程思想读书笔记(三)--RTTI

    接上篇JAVA编程思想读书笔记(二) 第十一章 运行期类型判定 No1: 对于作为程序一部分的每个类,它们都有一个Class对象.换言之,每次写一个新类时,同时也会创建一个Class对象(更恰当的说, ...

  2. Java编程思想读书笔记(七)

    点击蓝字 关注我们 今天是端午节,在这里祝大家端午节安康.过节的同时也不要忘记知识储备,今天我 为大家带来了Java编程思想第七节多形性读书笔记.请大家一起品鉴,如果发现里面有啥写的不对的地方,请大家 ...

  3. Java编程思想读书笔记一:并发

    1. Thread.yield( )方法 当调用yield()时,即在建议具有相同优先级的其他线程可以运行了,但是注意的是,仅仅是建议,没有任何机制保证你这个建议会被采纳 .一般情况下,对于任何重要的 ...

  4. Java编程思想读书笔记——第七章:复用类

    第七章 复用类 使用类而不破坏现有程序代码,有两种达到这一目的的方法: 在新的类中产生现有类的对象,也就是说new一个对象,这种方法称为组合 按照现有类的类型来创建新类,不改变现有类的形式,在其基础上 ...

  5. Java编程思想读书笔记(02)

    第十三章字符串 字符串是不可变的:是final类固不能继承它:也不能通过指向它的引用来修改它的内容. StringBuilder是Java SE5引用的,在这之前用的是StringBuffer.后者是 ...

  6. java现有一个泛型类 提供数组排序功能,java编程思想读书笔记 第十六章 数组

    数组,你可以创建并组装它们,通过使用整型索引值访问它们的元素,并且它们的尺寸不能改变. 1.数组为什么特殊 数组与其他种类的容器之间的区别有三方面:效率.类型和保存基本类型的能力.在Java中数组是一 ...

  7. java编程思想读书笔记汇总

    第七章:复用类(上) http://blog.csdn.net/u014115673/article/details/53350009 第七章:复用类(下)http://blog.csdn.net/u ...

  8. JAVA编程思想——读书笔记 类再生

    文章目录 类再生 1. 组合的语法 对象句柄的初始化 2. 继承的语法 3. 初始化父类 默认构造器 有参构造器 4.合成和继承的结合 5.选择组合还是继承 谨慎使用继承 6.向上转型 7.什么是向上 ...

  9. Java 编程思想 读书笔记

    第一章 对象导论 一.抽象过程 所有编程语言都提供抽象机制,人们所能解决的问题的复杂性直接取决于抽象的类型和质量. 1.1 只针对问题来进行建模 早期:基于约束条件编程的语言和通过图形符号操作来实现的 ...

最新文章

  1. [置顶] Activity启动流程
  2. web安全编程——权限的分配和控制
  3. JAVA无法加载此类文件,Java 7错误:无法加载本机库:ld.so.1:java:致命:libscf.so.1:打开失败:没有此类文件或目录...
  4. [基础题] 3、设计一个交通工具抽象类,提供抽象方法--驾驶
  5. 【牛客 - 696C】小w的禁忌与小G的长诗(dp 或 推公式容斥)
  6. python path模块_python pathlib模块详解
  7. 【spring】【转】Spring 框架的设计理念与设计模式分析
  8. linux线程切换回调函数,linux C线程退出回调函数
  9. opencv裁剪图片_前端智能化实践——从图片识别UI样式
  10. 剑指offer——面试题60:把二叉树打印成多行
  11. 虚拟机里ubuntu扩容主分区/dev/sda1
  12. java 字符串 数字个数_JAVA 统计字符串中中文,英文,数字,空格的个数
  13. 校门外的树和memset
  14. 解决Ubuntu ssh 登录慢问题
  15. qt TCP 网络编程
  16. 无痛学习ISAC(一)
  17. DRSL: Deep Relational Similarity Learning for Cross-modal Retrieval-多模态学习总结
  18. HTML和CSS的知识点
  19. word-break 换行
  20. (数据科学学习手札32)Python中re模块的详细介绍

热门文章

  1. android 点赞源码,android仿即刻点赞文字部分的自定义View的示例代码
  2. shiro自定义filter,anon不生效
  3. Gabor滤波器 pytorch实现
  4. 项目启动后sqlSessionFactory获取不到创建数据库连接失败
  5. python3使用staf问题_转载—越来越强大的SAFS/STAF/STAX自动化测试框架
  6. 郑州财经学院第54次全国计算机,听爷爷讲故事
  7. 计算机学院的入场词,大学校运会入场词
  8. AIDE手机编程初级教程(零基础向) 1.2 初识界面编程
  9. 基于物理信息深度学习的交通状态估计:以LWR和CTM模型为例
  10. 跑得最快的10种动物