前言

“复用代码是Java众多引人注目的功能之一。但要想成为极具革命性的语言,仅仅能够复制代码并对之加以改变是不够的,它必须还能够做更多的事情。” Java解决问题都围绕类展开的,对于复用代码,可以创建新的类来复用,也可以使用别人已经开发并调试好的类。方法的关键在于使用类而不破坏现有程序代码。有两种方式达成此方法的目的:组合和继承。下面将介绍这两种代码重用机制。

组合和继承的实现

在新的类中产生现有类的对象,即做组合。该方法只是复用了现有程序代码的功能,而不是它的形式。按照现有类的类型创建新类,无需改变现有类的形式,采用现有类的形式并在其中添加新的代码,此方式就是继承。

组合的实现

只需要将对象的引用置于新类中即可。

class A{
...
}
class B{
private A;
...
}

在Java中,类中域为基本类型时能够被自动初始化为零,对象引用会被初始化为null。编译器并不是简单地为每一个引用都创建默认对象。若是想初始化对象引用,可以在代码中的下列位置进行:

  • 在定义对象的地方。这也意味着它们会在构造器被调用之前被初始化。
  • 在类的构造器中。
  • 在临近使用这些对象之前,再初始化,这种方式就叫做惰性初始化
  • 使用实例初始化。

使用《Java编程思想》上面的实例说明:

class Soap{
private String s;
public Soap() {
System.out.println("Soap()无参构造器");
s = "Constructed"; //在类构造器中初始化
} @Override
public String toString() { return s;}
} public class Bath {
private String s1 = "Happy";//在定义对象的地方初始化
private Soap castille = new Soap();
private String s2;
private int i; public Bath() { System.out.println("Bath() 无参构造器");}
//实例初始化
{
i = 31;
System.out.println("初始化i为31");
} @Override
public String toString() {
if(s2 == null) { //惰性初始化
s2 ="Java";
}
return s1 + "\t"+ s2 + "\t" + i + "\t" + castille;
} public static void main(String[] args) {
System.out.println(new Bath());
}
}
/*
output:
Soap()无参构造器
初始化i为31
Bath() 无参构造器
Happy Java 31 Constructed
*/

继承的实现

Java中的继承是使用关键字extends实现的,使用继承子类将会得到父类中所有的域和方法。父类中的私有域和方法将被隐式继承,非私有方法和域将被显式继承。隐式继承也就说可以被间接访问到。

下面将以一个例子说明继承中出现的一些问题。

class Person {
private String name; //私有域
public Person() {
System.out.println("Person() 无参构造器");
name = "noName";
}
public Person(String name) {
System.out.println("Person(String name) 带参构造器");
this.name = name;
} public String getName() { return name;} //让子类可以间接访问到私有域name public String toString() { return "Person: "+name; }
} class Student extends Person{
public Student() {
//默认调用 super(); 表示调用父类的无参构造器
System.out.println("Student() 无参构造器");
} public Student(String name) {
super(name); //调用父类的带参构造器
System.out.println("Student(String name) 带参构造器");
} public String toString() { return "Student: " + getName();}
} public class ExtendsExample {
public static void main(String[] args) {
System.out.println(new Student());
System.out.println(new Student("sakura"));
}
}
/*
output:
Person() 无参构造器
Student() 无参构造器
Student: noName
Person(String name) 带参构造器
Student(String name) 带参构造器
Student: sakura
*/

子类和父类的构造器调用问题

关于构造器的调用顺序在前面构造方法和匿名对象提过,这里再着重介绍下子类和父类的构造器调用问题。
有一个常识问题就是肯定是先有父再有子,那么在程序中也是,应该是现有父类然后才有子类。Java中使用super关键字表示超类也就是父类。若是子类中没有显式调用父类的构造器,那么编译器就会默认调用super(),即父类的默认构造器;若是父类没有默认构造器,则子类中就必须显式调用父类的带参构造器,并且调用必须放在构造器中的第一行!

super()和this()可以同时存在吗?

通过代码可以得知,super()和this()是不能同时出现在构造器中。需要知道,不管怎么样,在子类构造器调用之前一定会先执行父类的构造器。

组合和继承之间的区别

组合和继承都允许在新的类中防止子对象,组合是显式这样做,而继承则是隐式这样做。组合技术通常是用于想在新类中使用现有类的功能而非其接口的情况。即,在新类中嵌入某个对象,让其实现所需要的功能;新类的用户看到的只是为新类所定义的接口,而非所嵌入类的接口。继承是使用某个现有类并开发其特殊版本,会拥有现有类的接口并且可以为新类开发新的接口。所以,继承关系为“is-a”(是一个),组合关系为“has-a”(有一个)。

protected关键字

在实际的项目中,经常会想要将某些事物尽可能对这个世界隐藏起来,但是仍然允许导出类的成员访问它们。protected关键字就是实现这个作用。它表明:对于任何继承于此类的导出类或者任何与此类在同一个包中的类来说,被protected修饰的方法和域是可以访问的,但是对于除这两种类的其他类则是不可访问的。

继承的局限

Java中继承的局限便是不允许类似C++那样的多继承,至于为什么没有多继承最直接的原因就为简单编程,不用去考虑父类的父类是谁、父类的父类的父类是谁、以及自己包含的方法继承自谁的。比如B和C继承自A,D又继承B、C,当D调用A中的一个方法时,是继承自B的还是继承自C的呢?

为了实现多继承的效果的话可以使用多层继承(A继承B,B继承C),但是最好不要超过三层,否则代码会太乱。后面会介绍Java使用接口声明的多继承。一个类虽然不能从继承多个基类但是可以声明继承多个接口(interface),从而达到多继承效果。

小结

继承和组合都可以从现有类型生成新类型。组合一般是将现有类型作为新类型的底层实现的一部分来加以复用,而继承复用的是接口。在使用继承时,由于导出的类具有基类的接口,因此它可以向上转型至基类,这对后面要介绍的多态是至关重要的。

参考:

《Java编程思想》第四版

Java——代码复用(组合和继承)的更多相关文章

  1. java代码复用(继承,组合以及代理)

    作为一门面向对象开发的语言,代码复用是java引人注意的功能之一.java代码的复用有继承,组合以及代理三种具体的表现形式,下面一一道来. 第一种方式是通过按照现有的类的类型创建新类的方式实现代码的复 ...

  2. 深入理解Java中的组合和继承

    Java是一个面向对象的语言.每一个学习过Java的人都知道,封装.继承.多态是面向对象的三个特征.每个人在刚刚学习继承的时候都会或多或少的有这样一个印象:继承可以帮助我实现类的复用.所以,很多开发人 ...

  3. Java代码复用的三种常用方式:继承、组合和代理

    复用代码是Java众多引人注目的功能之一.这句话很通顺,没什么问题,但问题在于很多人并不清楚“复用”是什么.就好像我说“沉默王二是一个不止会写代码的程序员”,唉,沉默王二是谁? 我们需要来给“复用”下 ...

  4. Java 代码复用 —— 泛型

    public interface Comparable<T> { public int compareTo(T o); } 1. 接口(Comparable:可比较接口) public s ...

  5. Rust 中的继承与代码复用

    在学习Rust过程中突然想到怎么实现继承,特别是用于代码复用的继承,于是在网上查了查,发现不是那么简单的. C++的继承 首先看看c++中是如何做的. 例如要做一个场景结点的Node类和一个Sprit ...

  6. 初涉JavaScript模式 (13) : 代码复用 【上】

    引子 博客断了一段时间,不是不写,一是没时间,二是觉得自己沉淀不够,经过一段时间的学习和实战,今天来总结下一个老生常谈的东西: 代码复用. 为何复用 JS门槛低,故很多人以为写几个特效就会JS,其实真 ...

  7. javascript 模式(1)——代码复用

    程序的开发离不开代码的复用,通过代码复用可以减少开发和维护成本,在谈及代码复用的时候,会首先想到继承性,但继承并不是解决代码复用的唯一方式,还有其他的复用模式比如对象组合.本节将会讲解多种继承模式以实 ...

  8. Java面向对象理解_代码块_继承_多态_抽象_接口

    面线对象: /* 成员变量和局部变量的区别? A:在类中的位置不同 成员变量:在类中方法外 局部变量:在方法定义中或者方法声明上 B:在内存中的位置不同 成员变量:在堆内存 局部变量:在栈内存 C:生 ...

  9. 编写高质量代码改善C#程序的157个建议——建议103:区分组合和继承的应用场合

    建议103:区分组合和继承的应用场合 继承所带来的多态性虽然是面向对象的一个重要特性,但这种特性不能在所有的场合中滥用.继承应该被当做设计架构的有用补充,而不是全部. 组合不能用于多态,但组合使用的频 ...

随机推荐

  1. java中的异常(二)

    异常的分类 在使用上 Error不用管他虚拟机错误 Exception必须要用catch抓 RuntimeExcption可以处理也可以不用处理 说明 Error:称为错误,由java虚拟机生成并抛出 ...

  2. 用generator改写ajax

    function request(url) { // this is where we're hiding the asynchronicity, // away from the main code ...

  3. MVC Ajax上传文件

    前台:首先要导入jQuery <html><head> <meta name="viewport" content="width=devic ...

  4. [LeetCode] Loud and Rich 聒噪与富有

    In a group of N people (labelled 0, 1, 2, ..., N-1), each person has different amounts of money, and ...

  5. Java课堂笔记(一):Java基础

    本篇博客将对Java中的数据类型.操作符,常量与变量和数组进行介绍.这些内容都是Java中最基本的知识,也是初学Java时最开始就需要了解的东西. Java数据类型 Java是一种强类型的语言,这就意 ...

  6. python 用正则处理日志实例

    前提:     了解正则基本语法   import re with open('top10_xiaozhuang_net.log','r') as f1: #读取日志文件 subject=f1.rea ...

  7. 3.ifconfig

    Windows下查看IP地址用ipconfig Linux 下查看IP地址用ifconfig 还有 ip addr      而ipconfig 和ip addr的区别则是与net-tools工具和i ...

  8. Idea集成maven插件

    学习目标 1.正确在idea上安装maven 2.安装后使用的基本操作 3.回顾安装步骤 安装过程 设置安装后自动下载功能 maven一键构建概念 我们的项目,往往都要经历编译. 测试. 运行. 打包 ...

  9. mongodb远程数据库的连接以及备份导入导出数据

    环境win10; 运行cmd cd到目录mongodb的bin目录: 连接远程mongodb: 连接命令:mongo -u username -p pwd host:post/database(数据库 ...

  10. Ubuntu VIM下实现python自动缩进

    1.打开vimrc文件 sudo vi /usr/share/vim/vimrc 2.添加 set filetype=python au BufNewFile,BufRead *.py,*.pyw s ...