【java编程思想第五版笔记】第八章复用(更新中)

Letitia ·
更新时间:2024-09-20
· 922 次阅读

【java编程思想第五版笔记】第八章复用(更新中)面向过程和面向对象复用的区别:组合的语法关于上面的程序的解析初始化引用有四种方法:继承语法

更新自2020.04.22

代码复⽤是⾯向对象编程(OOP)最具魅⼒的原因之⼀。

面向过程和面向对象复用的区别:

对于像 C 语⾔等⾯向过程语⾔来说,“复⽤”通常指的就是“”

Java 围绕“类”(Class)来解决问题。我们可以直接使⽤别⼈构建或

调试过的代码,⽽⾮创建新类、重新开始。

前提:
在不污染源代码的前提下使⽤现存代码

两种方法:组合和继承

他们都是基于现有类型构建新的类型

组合的语法

概念:把对象的引⽤(object references)放置在⼀个新的类⾥,这就使⽤了组合。

举例如下:假设你需要⼀个对象,其中内置了⼏个 String 对象,两个基本类

型(primitives)的属性字段,⼀个其他类的对象。

注:

对于⾮基本类型对象,

将引⽤直接放置在新类中,对于基本类型属性字段则仅进⾏声明。

代码如下:

class WaterSource { private String s; WaterSource() { System.out.println("WaterSource()"); s = "Constructed"; } @Override public String toString() { return s; } } public class SprinklerSystem { private String valve1, valve2, valve3, valve4; private WaterSource source = new WaterSource(); private int i; private float f; @Override public String toString() { return "valve1 = " + valve1 + " " + "valve2 = " + valve2 + " " + "valve3 = " + valve3 + " " + "valve4 = " + valve4 + "\n" + "i = " + i + " " + "f = " + f + " " + "source = " + source; // [1] } public static void main(String[] args) { SprinklerSystem sprinklers = new SprinklerSystem(); System.out.println(sprinklers); } } /*输出结果: WaterSource() valve1 = null valve2 = null valve3 = null valve4 = null i = 0 f = 0.0 source = Constructed */ 关于上面的程序的解析

仅限于个人理解

如果没有设置初始值,那么,引用的初始化结果为null,基础类型为int 的初始化结果是0,float类型的初始化结果是0.0

其中最重要的是source = Constructed的运行逻辑是new SprinklerSystem(),又因为在这个初始化块中声明了s的值是"Constructed",所以最后输出source = Constructed

编译器不会为每个引用创建一个默认对象,这是有意义的,因为在许多情况下,这会导致不必要的开销。

初始化引用有四种方法: 当对象被定义时。这意味着它们总是在调用构造函数之前初始化。 在该类的构造函数中。 在实际使用对象之前。这通常称为延迟初始化。在对象创建开销大且不需要每次都创建对象的情况下,它可以减少开销。 使用实例初始化。
代码如下: class Soap { private String s; Soap() { System.out.println("Soap()"); s = "Constructed"; } @Override public String toString() { return s; } } public class Bath { private String // Initializing at point of definition: s1 = "Happy", s2 = "Happy", s3, s4; private Soap castille; private int i; private float toy; public Bath() { System.out.println("Inside Bath()");//1 s3 = "Joy"; toy = 3.14f; castille = new Soap(); } // Instance initialization: { i = 47; } @Override public String toString() { if(s4 == null) // Delayed initialization: s4 = "Joy"; return "s1 = " + s1 + "\n" + "s2 = " + s2 + "\n" + "s3 = " + s3 + "\n" + "s4 = " + s4 + "\n" + "i = " + i + "\n" + "toy = " + toy + "\n" + "castille = " + castille; } public static void main(String[] args) { Bath b = new Bath(); System.out.println(b); } } /* 输出结果: Inside Bath() Soap() s1 = Happy s2 = Happy s3 = Joy s4 = Joy i = 47 toy = 3.14 castille = Constructed */

Bath 构造函数中,有一个代码块在所有初始化发生前就已经执行了。当你不在定义处初始化时,仍然不能保证在向对象引用发送消息之前执行任何初始化——如果你试图对未初始化的引用调用方法,则未初始化的引用将产生运行时异常。

当调用 toString() 时,它将赋值 s4,以便在使用字段的时候所有的属性都已被初始化。

继承语法

继承是面向对象的三大特性之一,每个类都会继承,如果它们不显式继承其他类,那么肯定它们隐式继承(Object)类,也是所有java类的祖类。

组合和继承的区别就是继承使用了一种特殊的语法——使用关键字 extends 后跟基类的名称。

class 子类 extends 父类

之后子类就会拥有父类的所有字段和方法,无需再次声明。

注:java不允许菱形继承

代码示例如下

class Cleanser { private String s = "Cleanser"; public void append(String a) { s += a; } public void dilute() { append(" dilute()"); } public void apply() { append(" apply()"); } public void scrub() { append(" scrub()"); } @Override public String toString() { return s; } public static void main(String[] args) { Cleanser x = new Cleanser(); x.dilute(); x.apply(); x.scrub(); System.out.println(x); } } public class Detergent extends Cleanser { // Change a method: @Override public void scrub() { append(" Detergent.scrub()"); super.scrub(); // Call base-class version } // Add methods to the interface: public void foam() { append(" foam()"); } // Test the new class: public static void main(String[] args) { Detergent x = new Detergent(); x.dilute(); x.apply(); x.scrub(); x.foam(); System.out.println(x); System.out.println("Testing base class:"); Cleanser.main(args); } } /* Output: Cleanser dilute() apply() Detergent.scrub() scrub() foam() Testing base class: Cleanser dilute() apply() scrub() */

这演示了一些特性。首先,在 Cleanserappend() 方法中,使用 += 操作符将字符串连接到 s,这是 Java 设计人员“重载”来处理字符串的操作符之一 ,专门为了处理字符串而进行重载的。

在这里,Detergent.main() 显式地调用 Cleanser.main(),从命令行传递相同的参数(当然,你可以传递任何字符串数组)。

Cleanser 的接口中有一组方法: append()dilute()apply()scrub()toString()。因为 Detergent 是从 Cleanser 派生的(通过 extends 关键字),即使你没有在 Detergent 中看到所有这些方法的显式定义,它也会在其接口中自动获取所有这些方法。

但是如果从父类里面的继承过来的方法不满意的话,可以覆写这个方法,只需要定义一个同名方法即可,不过你最好加上 @Override注解,如 scrub() 方法就进行了重写。

那么,可以把继承看作是复用类。如在 scrub() 中所见,可以使用基类中定义的方法并修改它。在这里,你可以在新类中调用基类的该方法。但是在 scrub() 内部,不能简单地调用 scrub(),因为这会产生递归调用。为了解决这个问题,Java的 super 关键字引用了当前类继承的“超类”(基类)。因此表达式 super.scrub() 调用方法 scrub() 的基类版本。

继承时,你不受限于使用基类的方法。你还可以像向类添加任何方法一样向派生类添加新方法:只需定义它。方法 foam() 就是一个例子。Detergent.main() 中可以看到,对于 Detergent 对象,你可以调用 CleanserDetergent 中可用的所有方法 (如 foam() )。

本笔记大部分基于《on java 8》整理, 另外初学者一枚,大家多多关照,有错误可以在下面说出来 谢谢大家


作者:星痕落雪



java编程思想 java编程 JAVA 更新

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章