Java——代码复用(组合和继承)
前言
“复用代码是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——代码复用(组合和继承)的更多相关文章
- java代码复用(继承,组合以及代理)
作为一门面向对象开发的语言,代码复用是java引人注意的功能之一.java代码的复用有继承,组合以及代理三种具体的表现形式,下面一一道来. 第一种方式是通过按照现有的类的类型创建新类的方式实现代码的复 ...
- 深入理解Java中的组合和继承
Java是一个面向对象的语言.每一个学习过Java的人都知道,封装.继承.多态是面向对象的三个特征.每个人在刚刚学习继承的时候都会或多或少的有这样一个印象:继承可以帮助我实现类的复用.所以,很多开发人 ...
- Java代码复用的三种常用方式:继承、组合和代理
复用代码是Java众多引人注目的功能之一.这句话很通顺,没什么问题,但问题在于很多人并不清楚“复用”是什么.就好像我说“沉默王二是一个不止会写代码的程序员”,唉,沉默王二是谁? 我们需要来给“复用”下 ...
- Java 代码复用 —— 泛型
public interface Comparable<T> { public int compareTo(T o); } 1. 接口(Comparable:可比较接口) public s ...
- Rust 中的继承与代码复用
在学习Rust过程中突然想到怎么实现继承,特别是用于代码复用的继承,于是在网上查了查,发现不是那么简单的. C++的继承 首先看看c++中是如何做的. 例如要做一个场景结点的Node类和一个Sprit ...
- 初涉JavaScript模式 (13) : 代码复用 【上】
引子 博客断了一段时间,不是不写,一是没时间,二是觉得自己沉淀不够,经过一段时间的学习和实战,今天来总结下一个老生常谈的东西: 代码复用. 为何复用 JS门槛低,故很多人以为写几个特效就会JS,其实真 ...
- javascript 模式(1)——代码复用
程序的开发离不开代码的复用,通过代码复用可以减少开发和维护成本,在谈及代码复用的时候,会首先想到继承性,但继承并不是解决代码复用的唯一方式,还有其他的复用模式比如对象组合.本节将会讲解多种继承模式以实 ...
- Java面向对象理解_代码块_继承_多态_抽象_接口
面线对象: /* 成员变量和局部变量的区别? A:在类中的位置不同 成员变量:在类中方法外 局部变量:在方法定义中或者方法声明上 B:在内存中的位置不同 成员变量:在堆内存 局部变量:在栈内存 C:生 ...
- 编写高质量代码改善C#程序的157个建议——建议103:区分组合和继承的应用场合
建议103:区分组合和继承的应用场合 继承所带来的多态性虽然是面向对象的一个重要特性,但这种特性不能在所有的场合中滥用.继承应该被当做设计架构的有用补充,而不是全部. 组合不能用于多态,但组合使用的频 ...
随机推荐
- python 错误记录
class Func: d = dict() def __setitem__(self, key, value): # xxx object does not support item assignm ...
- 微信小程序----没有 DOM 对象,一切基于组件化 ---- mpvue
封装好用的 类库 和 组件,复用且灵活度高 抽取相同的部分放在函数内部(组件内部) 抽取不同的部分放在形参(组件 props 传参,或者插槽) new Promise 运行时 初始化实例对象的状态为 ...
- ssm知识点整理
第1章 resultType和resultMap的区别是什么? MyBatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap,resultType ...
- C盘突然爆满
C盘突然爆满!幸好还开的机!~~ 因为是突然就爆满了,想着应该是虚拟内存的原因!于是就开始了探索.... 1.文件夹选项中把所有文件都显示出来. 2.在C盘你就会看到一个“pagefile.sys”的 ...
- 【RL-TCPnet网络教程】第24章 RL-TCPnet之网络控制报文协议ICMP
第24章 RL-TCPnet之网络控制报文协议ICMP 本章节为大家讲解ICMP(Internet Control Message Protocol,网络控制报文协议),通过前面章节对TCP和 ...
- Websocket实现即时通讯
前言 关于我和WebSocket的缘:我从大二在计算机网络课上听老师讲过之后,第一次使用就到了毕业之后的第一份工作.直到最近换了工作,到了一家是含有IM社交聊天功能的app的时候,我觉得我现在可以谈谈 ...
- Java面试题中常考的容易混淆的知识点区别
以下是我收集的Java编程里各种区别,供Java学习爱好者参考,这些区别都是每次Java面试中常考的,大家好好掌握,如有失误请留言指出.想要获取Java详细全套学习资料请到上海尚学堂官网获取. 1.H ...
- MySQL 数据库字段类型使用说明
简介 MySQL支持大量的列类型,它可以被分为3类:数字类型.日期和时间类型以及字符串(字符)类型. 数值类型 下列用于描述的代码字母中: M表示最大的显示尺寸.最大的合法的显示尺寸是 255 .(注 ...
- [Swift]LeetCode238. 除自身以外数组的乘积 | Product of Array Except Self
Given an array nums of n integers where n > 1, return an array outputsuch that output[i] is equa ...
- [Swift]LeetCode326. 3的幂 | Power of Three
Given an integer, write a function to determine if it is a power of three. Example 1: Input: 27 Outp ...