Thinking in Java 第七章 3-1

Thinking in Java第七章研读3-1总结

问题引入:如何复用代码

1.新的类是由现有类的对象所组成,方法称为组合。(该方法只是复用了现有程序代码的功能,而非他的形式)

2.按照现有类的类型创建新类。方法称为继承。(该方法无需改变现有类的形式,采用现有类的形式并在其中添加新代码)

3.代理proxy

组合Demo(存在问题:对象引用的初始化)

 1 package com.thxy.section.seven;
 2
 3 public class Compo {
 4     public static void main(String[] args) {
 5         Bath bath = new Bath();
 6         System.out.println(bath);
 7
 8     }
 9 }
10
11 class Soap {
12     private String s;
13
14     Soap() {
15         System.out.println("Soap()");
16         s = "Constructed";
17     }
18
19     @Override
20     public String toString() {
21         return s;
22     }
23 }
24
25 class Bath {
26     private String s1 = "Happy";
27     private String s2 = "Happy";
28     private String s3, s4;
29     private Soap castille;
30     private int i;
31     private float toy;
32
33     Bath() {
34         System.out.println("Inside Bath()");
35         s3 = "Joy";
36         toy = 3.14f;
37         castille = new Soap();
38     }
39
40     {
41         i = 47;
42     }
43
44     @Override
45     public String toString() {
46         if (s4 == null) {
47             s4 = "Joy";
48         }
49         return
50                 "s1=" + s1 + "\n" + "s2=" + s2 + "\n" + "s3=" + s3 + "\n" + "s4=" + s4 + "\n" + "toy=" + toy + "\n" + "castille=" + castille;
51     }
52 }

总结:

初始化引用对象可以在代码中的如下位置

1.在定义对象的地方。这意味着它们总能在构造器被调用之前被初始化。

2.在类的构造器中

3.就在正要使用这些对象之前,这种方法称为惰性初始化。

使用断点调试执行过程

1.

由调试结果得知:

1.先初始化s4=Joy

2.初始化s1,s2

3.{i=47}初始化i

2.

由调试结果得知:

1.先初始化s3,toy

2.再初始化Soup引用对象

4.最后初始化s引用对象

问题:为什么会立即初始化s4引用对象?

原著:当toString被调用时,它将填充s4的值,以确保所有的域在使用之时已被妥善初始化。是否这么写就错误了?通过debug调试jvm虚拟机将s4就初始化了。

继承Demo

 1 package com.thxy.section.seven;
 2
 3 public class Inherit {
 4     public static void main(String[] ars) {
 5         Detergent x = new Detergent();
 6         x.dilute();
 7         x.apply();
 8         x.scrub();
 9         x.foam();
10         System.out.print(x);
11     }
12 }
13
14 class Cleanser {
15     private String s = "Cleanser.";
16
17     public void append(String a) {
18         s += a;
19     }
20
21     public void dilute() {
22         append("dilute()");
23     }
24
25     public void apply() {
26         append("apply()");
27     }
28
29     public void scrub() {
30         append("scrub()");
31     }
32
33     @Override
34     public String toString() {
35         return s;
36     }
37 }
38
39 class Detergent extends Cleanser {
40     /*
41     覆盖
42      */
43     @Override
44     public void scrub() {
45         append("Detergent.scrub()");
46     }
47
48     /*
49     新增
50      */
51     public void foam() {
52         append("foam()");
53     }
54 }

总结:

Cleanser.dilute()apply()Detergent.scrub()foam()

1.继承,一般的规则是将所有数据成员都指定为private,将所有方法指定为public(protected成员也可以借助导出来类访问)。

2.由于Detergent是由关键字extends从Cleanser导出,所以它可以在接口中自动获取这些方法,尽管我们不能看到这些方法在Detergent中的显示定义。因此,可以将继承可以看做是对类的复用。

3.对基类中定义的方法对他进行修改是可行的(覆盖/重写)。

4.在继承过程中,不一定非得使用基类的方法。可以在导出类中添加新方法。

5.对于导出来的对象不仅可以使用自己在类中定义的方法,而且还可以使用基类的方法。

基类和导出类构造方法(初始化)Demo

 1 package com.thxy.section.seven;
 2
 3 public class Init {
 4     public static void main(String[] args) {
 5         Cartoon cartoon=new Cartoon();
 6
 7     }
 8 }
 9
10 class Art {
11     Art() {
12 //        super();
13         System.out.println("Art constructor");
14     }
15 }
16
17 class Drawing extends Art {
18     Drawing() {
19 //        super();
20         System.out.println("Drawing constructor");
21     }
22 }
23
24 class Cartoon extends Drawing {
25     Cartoon() {
26 //        super();
27         System.out.println("Cartoon constructor");
28     }
29 }

总结:

1.结果

Art constructor
Drawing constructor
Cartoon constructor

2.构造过程是从基类“向外”扩展的。

3.系统会默认地调用super()方法即使程序员不显示调用。

4.系统会默认为类创建一个默认地构造器即使程序员不显示写构造器。

 1 package com.thxy.section.seven;
 2
 3 public class Init {
 4     public static void main(String[] args) {
 5         Cartoon cartoon = new Cartoon(11);
 6         Cartoon c = new Cartoon();
 7
 8     }
 9 }
10
11 class Art {
12     Art() {
13 //      super();
14         System.out.println("Art constructor");
15     }
16
17     Art(int i) {
18         System.out.println("Art constructor" + i);
19     }
20 }
21
22 class Drawing extends Art {
23     Drawing() {
24         System.out.println("Drawing constructor");
25
26     }
27
28     Drawing(int i) {
29         super(i);
30         System.out.println("Drawing constructor" + i);
31     }
32 }
33
34 class Cartoon extends Drawing {
35     Cartoon() {
36         System.out.println("Cartoon constructor");
37
38     }
39
40     Cartoon(int i) {
41         super(i);
42         System.out.println("Cartoon constructor" + i);
43     }
44 }

总结:

1.结果

Art constructor11
Drawing constructor11
Cartoon constructor11
Art constructor
Drawing constructor
Cartoon constructor

2.如果程序员写了带参数的构造器系统将不会默认创建构造器。

3.如果想调用一个带参数的基类构造器就必须用关键字super显示地编写调用基类构造器的语句。

4.注意:如果基类重写了构造器导出类想调用一个带参数的基类构造器就要显示调用super语句;如果不写则报错因为导出类的构造器会默认调用super()方法。

中国中庸之道之代理Demo(继承和组合的中庸之道)

example:太空船需要一个控制模块

 1 package com.thxy.section.seven;
 2
 3 public class Proxy {
 4     public static void main(String[] args) {
 5         SpaceShip spaceShip = new SpaceShip("NSEA Protector");
 6         spaceShip.forward(100);
 7     }
 8 }
 9
10 class SpaceShipControls {
11     void up(int velocity) {
12     }
13
14     void down(int velocity) {
15     }
16
17     void left(int velocity) {
18     }
19
20     void right(int velocity) {
21     }
22
23     void forward(int velocity) {
24         System.out.println(this+" "+"forward" + " "+velocity);
25     }
26
27     void back(int velocity) {
28     }
29
30     void turboBoost() {
31     }
32 }
33
34 class SpaceShip extends SpaceShipControls {
35     private String name;
36
37     SpaceShip(String name) {
38         this.name = name;
39     }
40
41     @Override
42     public String toString() {
43         return name;
44     }
45 }

总结:

1.结果:NSEA Protector forward 100

2.SpaceShip并非真正的SpaceShipControls类型(按向上转型地说SpaceShip对象是一种类型的SpaceShipControls?)(向上转型时说法)中文翻译很难懂。真正逻辑上SpaceShip中包含了SpaceShipControls;应该用组合才对。这里就不适合用继承因为逻辑乱了。

3.SpaceShipControls所有的方法在SpaceShip中暴露了。

需要解决的问题:1.SpaceShip和SpaceShip的关系 2.隐藏SpaceShipControls具体实现方法(结合实际想象我们国防内部技术实现不能透露,可是他的功能是可以暴露的)

逻辑优化Demo(采用单例模式)

  1 package com.thxy.section.seven;
  2
  3 public class Proxy2 {
  4     public static void main(String[] args) {
  5         SpaceShip2 spaceShip2 = new SpaceShip2();
  6         spaceShip2.up(100);
  7         spaceShip2.down(100);
  8         spaceShip2.forward(100);
  9         spaceShip2.back(100);
 10         spaceShip2.turboBoost();
 11
 12     }
 13 }
 14
 15 class SpaceShipControls2 {
 16
 17     private SpaceShipControls2() {
 18
 19     }
 20
 21     /*
 22     一条太空飞船中只有一个控制器
 23      */
 24     private static SpaceShipControls2 spaceShipControls2 = new SpaceShipControls2();
 25
 26     public static SpaceShipControls2 spaceShipControl() {
 27         return spaceShipControls2;
 28     }
 29
 30     void up(int velocity) {
 31         System.out.println(this + " " + "up" + " " + velocity);
 32     }
 33
 34     void down(int velocity) {
 35         System.out.println(this + " " + "down" + " " + velocity);
 36     }
 37
 38     void left(int velocity) {
 39         System.out.println(this + " " + "left" + " " + velocity);
 40     }
 41
 42     void right(int velocity) {
 43         System.out.println(this + " " + "right" + " " + velocity);
 44     }
 45
 46     void forward(int velocity) {
 47         System.out.println(this + " " + "forward" + " " + velocity);
 48     }
 49
 50     void back(int velocity) {
 51         System.out.println(this + " " + "back" + " " + velocity);
 52     }
 53
 54     void turboBoost() {
 55         System.out.println(this + " " + "turboBoost");
 56     }
 57 }
 58
 59 class SpaceShip2 {
 60     private String name;
 61     private SpaceShipControls2 spaceShipControls2 = SpaceShipControls2.spaceShipControl();
 62
 63     SpaceShip2() {
 64         this.name = name;
 65     }
 66
 67     @Override
 68     public String toString() {
 69         return name;
 70     }
 71
 72     void up(int velocity) {
 73         spaceShipControls2.up(velocity);
 74     }
 75
 76     void down(int velocity) {
 77         spaceShipControls2.down(velocity);
 78     }
 79
 80     void left(int velocity) {
 81         spaceShipControls2.left(velocity);
 82     }
 83
 84     void right(int velocity) {
 85         spaceShipControls2.right(velocity);
 86     }
 87
 88     void forward(int velocity) {
 89         spaceShipControls2.forward(velocity);
 90     }
 91
 92     void back(int velocity) {
 93         spaceShipControls2.up(velocity);
 94     }
 95
 96     void turboBoost() {
 97         spaceShipControls2.turboBoost();
 98     }
 99
100 }

总结:

1.结果:

com.thxy.section.seven.SpaceShipControls2@4554617c up 100
com.thxy.section.seven.SpaceShipControls2@4554617c down 100
com.thxy.section.seven.SpaceShipControls2@4554617c forward 100
com.thxy.section.seven.SpaceShipControls2@4554617c up 100
com.thxy.section.seven.SpaceShipControls2@4554617c turboBoost

2.通过上述的实现中可以很好理清逻辑:广泛的说每条太空船都有其名称和专属的控制器:控制器的具体如何实现都隐藏在控制器中巧妙地解决了逻辑问题。

3.从上述的实现中可以很好理解到向上转型中当选择使用继承还是组合时考虑自己是否真的很需要向上转型吗?现实中很多对象都是包含和被包含的关系这时通常不需要向上转型可以使用组合或许更好的解决逻辑关系。但是包含与被包含关系也有部分要利用继承扩张。毛主席曾经说过具体问题具体分析,实事求是是不无道理的。

神奇的名称屏蔽(类和类之间的重载)

重载机制的条件:

1.以参数区分重载方法

2.以返回值区分重载方法(行不通因为有时并不关心返回值只关心方法是如何实现的)

总的来说构成重载机制方法名一致;参数类型和个数和返回值其中一个不同。

 1 package com.thxy.section.seven;
 2
 3 public class Overloading {
 4     public static void main(String[] args) {
 5         Bart bart = new Bart();
 6         bart.doh(new MilHouse(10));
 7         bart.doh(‘c‘);
 8         bart.doh(0.01f);
 9
10     }
11 }
12
13 class Homer {
14     /*
15     重载
16      */
17     void doh(char c) {
18         System.out.println(c);
19     }
20
21     void doh(float f) {
22         System.out.println(f);
23     }
24 }
25
26 class MilHouse {
27     private int i;
28
29     MilHouse(int i) {
30         this.i = i;
31     }
32
33     @Override
34     public String toString() {
35         return "" + i;
36     }
37 }
38
39 class Bart extends Homer {
40     /*
41     重载
42      */
43     void doh(MilHouse milHouse) {
44         System.out.println(milHouse);
45     }
46 }

总结:

1.结果

10
c
0.01

2.可以看出在基类定义的重载方法doh(xx)方法,在导出类也定义了重载方法doh(xx)方法。在导出类中不仅可以调用自己的重载方法,也可以调用基类的重载方法。

3.由2知虽然Bart引入了一个新的重载方法,但是在Bart中Homer的所有重载方法都是可用的。

4.在程序员不留意重载而并非重写了该方法时使用Java SE5新增的@Override注解可以大大减少阅读的困难性。

5.有大部分人都认为重载和重写的区别是重载一定是发生在同一类中的,重写是发生在不同类中的。其实这种说法是片面的。当导出类继承了基类也可以重载基类的方法

并且可以调用基类的方法。我想他们那些人会考虑导出类继承了基类就相当于导出类是基类的一种类型也相当在一个类中。

备注:本人大二在校生在研读Thinking in Java这本经典书,由于英语水平有限不能读原版的Thinking in Java英文版所以只能读中文版,但是中文翻译读起来也是够呛。如果我对其中知识点理解有误请指正。谢谢。上面有残留一个问题请大神指教。(原著:当toString被调用时,它将填充s4的值,以确保所有的域在使用之时已被妥善初始化。是否这么写就错误了?通过debug调试jvm虚拟机将s4就初始化了。)请将你们答案写在下方留言!!!

原文地址:https://www.cnblogs.com/KYAOYYW/p/10590441.html

时间: 2024-10-09 22:20:23

Thinking in Java 第七章 3-1的相关文章

“全栈2019”Java第七章:IntelliJ IDEA注释快捷键

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第七章:IntelliJ IDEA注释快捷键 下一章 "全栈2019"Java第八章:IntelliJ IDEA设置注释不显示在行首 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,

[Effective Java]第七章 方法

第七章      方法 38.      检查参数的有效性 绝大多数方法和构造器对于传递给它们的参数值都会有某些限制.例如,索引值必须是非负的,对象引用不能为null等,这些都是常见的.你应该在文档中清楚地指明所有这些限制,并且在方法体的开头处检查参数,以强制施加这些限制. 应该在方法和构造器体前进行了参数的有效性检查,并且及时向外抛出适当的异常.如果方法没有检查它的参数,就有可能发生几种情形.该方法可能在处理过程中失败,并且产生令人费解的异常,更有可能,该方法可以正常返回,但是会悄悄地计算出错

Java 第七章 类和对象 笔记

一.对象的特征--类的属性 每个对象的每个属性都有特定的值 对象的操作--类的方法 二.封装:对象同时具有属性和方法两项特性.     对象的属性和方法通常被封装在一起,共同体现事物的特性,     二者相辅相成,不能分割. 三.类和对象的区别:     类:抽象的概念,是一个模子,确定了对象的属性和方法.    对象:能够看得见摸得着的实物.具有静态和动态特征. 四.定义类的步骤:     1.定义类名      2.定义类的属性 访问修饰符 属性类型 属性名称=默认值:     3.定义类的

java第七章:常用类

包装类型:讲8种基本数据类型包装成类,进而使用类中的属性和方法. byte --Byte,short--Short,long--Long,    float--Float,double--Double  ,boolean--Boolean int--Integer,char--Charactor,除了Boolean和Charactor,其他包装类型的父类是java.lang.Number; String 类,不可变的字符序列,每次追加新内容,就会摒弃当前字符串,而创建一个新的字符串,所以说效率很

Thinking In Java笔记(第七章 复用类)

第七章 复用类 复用代码是Java众多引人注目的功能之一,但想要成为极具革命性的语言,仅仅能够复制代码并对之加以改变是不够的,它还必须能够做更多的事情. Java中所有事物都是围绕着类来展开的.通过创建新类来复用代码,不必重新开头编写.此方法的窍门在于使用类而不破坏现有程序代码.本章中有两种代码重用机制来达到这一目的: 只需要在新的类中生成现有类的对象.由于新的类是由现有类的对象所组成的,这种方法通常成为组合.该方法只是复用了现有程序代码功能,而非形式上和之前的类有相似之处. 第二种方法更加细致

Java学习笔记—第七章 类的深入解析

第七章 类的深入解析 1. 继承 1.1 类继承的方法:在Java中,子类对父类的继承是在类的声明中使用extends关键字来指明的.其一    般格式为:[类修饰符] class <子类名> extends <父类名>{ 类体内容 }.一个类只能直接继承一个    父类,一个父类可以有多个子类. 1.2 成员变量的继承和隐藏:基于父类创建子类时,子类可以继承父类的成员变量和成员方法.但是,     如果在父类和子类中同时声明了一个同名变量,则这两个变量在程序运行时同时存在.即:父

“全栈2019”Java第七十一章:外部类访问静态内部类成员详解

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第七十一章:外部类访问静态内部类成员详解 下一章 "全栈2019"Java第七十二章:静态内部类访问外部类成员 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"Jav

“全栈2019”Java第七十五章:内部类持有外部类对象

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第七十五章:内部类持有外部类对象 下一章 "全栈2019"Java第七十六章:静态.非静态内部类访问权限 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"Java学习

“全栈2019”Java第七十六章:静态、非静态内部类访问权限

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第七十六章:静态.非静态内部类访问权限 下一章 "全栈2019"Java第七十七章:抽象内部类与抽象静态内部类详解 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"J