Java提高篇 —— Java三大特性之继承

一、前言

 

       在《Think in java》中有这样一句话:复用代码是Java众多引人注目的功能之一。但要想成为极具革命性的语言,仅仅能够复制代码并对加以改变是不够的,它还必须能够做更多的事情。在这句话中最引人注目的是“复用代码”,尽可能的复用代码使我们程序员一直在追求的,现在我来介绍一种复用代码的方式,也是java三大特性之一——继承。

 

二、继承

 

       在讲解之前我们先看一个例子,该例子是前篇博文(Java提高篇 —— Java三大特性之封装)。

       从这里我们可以看出,Wife、Husband两个类除了各自的husband、wife外其余部分全部相同,作为一个想最大限度实现复用代码的我们是不能够忍受这样的重复代码,如果再来一个小三、小四……我们是不是也要这样写呢?那么我们如何来实现这些类的可复用呢?利用继承!!

       首先我们先离开软件编程的世界,从常识中我们知道丈夫、妻子、小三、小四…,他们都是人,而且都有一些共性,有名字、年龄、性别、头等等,而且他们都能够吃东西、走路、说话等等共同的行为,所以从这里我们可以发现他们都拥有人的属性和行为,同时也是从人那里继承来的这些属性和行为的。

       从上面我们就可以基本了解了继承的概念了,继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码,能够大大的提高开发的效率。

       对于Wife、Husband使用继承后,除了代码量的减少我们还能够非常明显的看到他们的关系。

       继承所描述的是“is-a”的关系,如果有两个对象A和B,若可以描述为“A是B”,则可以表示A继承B,其中B是被继承者称之为父类或者超类,A是继承者称之为子类或者派生类。

       实际上继承者是被继承者的特殊化,它除了拥有被继承者的特性外,还拥有自己独有得特性。例如猫有抓老鼠、爬树等其他动物没有的特性。同时在继承关系中,继承者完全可以替换被继承者,反之则不可以,例如我们可以说猫是动物,但不能说动物是猫就是这个道理,其实对于这个我们将其称之为“向上转型”,下面介绍。

       诚然,继承定义了类如何相互关联,共享特性。对于若干个相同或者相识的类,我们可以抽象出他们共有的行为或者属相并将其定义成一个父类或者超类,然后用这些类继承该父类,他们不仅可以拥有父类的属性、方法还可以定义自己独有的属性或者方法。

同时在使用继承时需要记住三句话:

       1、子类拥有父类非private的属性和方法。

       2、子类可以拥有自己属性和方法,即子类可以对父类进行扩展。

       3、子类可以用自己的方式实现父类的方法。

       综上所述,使用继承确实有许多的优点,除了将所有子类的共同属性放入父类,实现代码共享,避免重复外,还可以使得修改扩展继承而来的实现比较简单。

       诚然,讲到继承一定少不了这三个东西:构造器、protected关键字、向上转型。

 

三、构造器

 

       通过前面我们知道子类可以继承父类的属性和方法,除了那些private的外还有一样是子类继承不了的---构造器。对于构造器而言,它只能够被调用,而不能被继承。 调用父类的构造方法我们使用super()即可。

       对于子类而已,其构造器的正确初始化是非常重要的,而且当且仅当只有一个方法可以保证这点:在构造器中调用父类构造器来完成初始化,而父类构造器具有执行父类初始化所需要的所有知识和能力。

public class Person {protected String name;protected int age;protected String sex;Person(){System.out.println("Person Constrctor...");}
}public class Husband extends Person{private Wife wife;Husband(){System.out.println("Husband Constructor...");}public static void main(String[] args) {Husband husband  = new Husband();}
}Output:
Person Constrctor...
Husband Constructor...

       通过这个示例可以看出,构建过程是从父类“向外”扩散的,也就是从父类开始向子类一级一级地完成构建。而且我们并没有显示的引用父类的构造器,这就是java的聪明之处:编译器会默认给子类调用父类的构造器。

       但是,这个默认调用父类的构造器是有前提的:父类有默认构造器。如果父类没有默认构造器,我们就要必须显示的使用super()来调用父类构造器,否则编译器会报错:无法找到符合父类形式的构造器。

public class Person {protected String name;protected int age;protected String sex;Person(String name){System.out.println("Person Constrctor-----" + name);}
}public class Husband extends Person{private Wife wife;Husband(){super("chenssy");System.out.println("Husband Constructor...");}public static void main(String[] args) {Husband husband  = new Husband();}
}Output:
Person Constrctor-----chenssy
Husband Constructor...

       所以综上所述:对于继承而已,子类会默认调用父类的构造器,但是如果没有默认的父类构造器,子类必须要显示的指定父类的构造器,而且必须是在子类构造器中做的第一件事(第一行代码)。

 

四、protected关键字

 

       private访问修饰符,对于封装而言,是最好的选择,但这个只是基于理想的世界,有时候我们需要这样的需求:我们需要将某些事物尽可能地对这个世界隐藏,但是仍然允许子类的成员来访问它们。这个时候就需要使用到protected。

       对于protected而言,它指明就类用户而言,他是private,但是对于任何继承与此类的子类而言或者其他任何位于同一个包的类而言,他却是可以访问的。

public class Person {private String name;private int age;private String sex;protected String getName() {return name;}protected void setName(String name) {this.name = name;}public String toString(){return "this name is " + name;}/** 省略其他setter、getter方法 **/
}public class Husband extends Person{private Wife wife;public  String toString(){setName("chenssy");    //调用父类的setName();return  super.toString();    //调用父类的toString()方法}public static void main(String[] args) {Husband husband = new Husband();System.out.println(husband.toString());}
}Output:
this name is chenssy

       从上面示例可以看书子类Husband可以明显地调用父类Person的setName()。

       诚然尽管可以使用protected访问修饰符来限制父类属性和方法的访问权限,但是最好的方式还是将属性保持为private(我们应当一致保留更改底层实现),通过protected方法来控制类的继承者的访问权限。

 

五、向上转型

 

       在上面的继承中我们谈到继承是is-a的相互关系,猫继承与动物,所以我们可以说猫是动物,或者说猫是动物的一种。这样将猫看做动物就是向上转型。如下:

public class Person {public void display(){System.out.println("Play Person...");}static void display(Person person){person.display();}
}public class Husband extends Person{public static void main(String[] args) {Husband husband = new Husband();Person.display(husband);      //向上转型}
}

       在这我们通过Person.display(husband)。这句话可以看出husband是person类型。

       将子类转换成父类,在继承关系上面是向上移动的,所以一般称之为向上转型。由于向上转型是从一个叫专用类型向较通用类型转换,所以它总是安全的,唯一发生变化的可能就是属性和方法的丢失。这就是为什么编译器在“未曾明确表示转型”活“未曾指定特殊标记”的情况下,仍然允许向上转型的原因。

 

六、慎用继承

 

       上面讲了继承所带来的诸多好处,那我们是不是就可以大肆地使用继承呢?送你一句话:慎用继承。

首先我们需要明确,继承存在如下缺陷:

       1、父类变,子类就必须变。

       2、继承破坏了封装,对于父类而言,它的实现细节对与子类来说都是透明的。

       3、继承是一种强耦合关系。     

       所以说当我们使用继承的时候,我们需要确信使用继承确实是有效可行的办法。那么到底要不要使用继承呢?《Think in java》中提供了解决办法:问一问自己是否需要从子类向父类进行向上转型。如果必须向上转型,则继承是必要的,但是如果不需要,则应当好好考虑自己是否需要继承。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/446957.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java提高篇 —— Java三大特性之多态

一、前言 面向对象编程有三大特性:封装、继承、多态。 封装:隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。 继…

光盘刻录制作Ubuntu等操作系统的启动盘

前提条件 软媒刻录 空白光盘(至少4.7G)电脑(最好使用外置的光驱)系统镜像(ISO格式) 具体操作 打开软媒魔方选择光盘刻录按照标红的进行选择选择镜像->选择或者拖拽都可以选择刻录机->如果使用外部刻…

Java提高篇 —— 抽象类与接口

一、前言 接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。 抽象类与接口是java语言中对抽象概念进行定义的两种机制,正是由于他们的存在才赋予java强大的面向对象的能力。他们两者之间对抽象概念的支持有很大的相似,甚至可以互换&…

C++ const相关内容学习

const 作用 修饰变量,说明变量不可以被修改修饰指针,分为指向常量的指针(pointer to const)和自身是常量的指针(常量指针,const pointer)修饰引用,指向常量的引用(refe…

Java提高篇 —— Java浅拷贝和深拷贝

一、前言 我们知道在Java中存在这个接口Cloneable,实现该接口的类都会具备被拷贝的能力,同时拷贝是在内存中进行,在性能方面比我们直接通过new生成对象来的快,特别是在大对象的生成上,使得性能的提升非常明显。然而我们…

openssl里面AES算法主要函数的参数的介绍

注意事项 使用API的时候,需要特别小心数据长度,一般没有指定长度的参数,默认都是16(AES_BLOCK_SIZE)个字节。输出数据的长度一般都是16字节的倍数,否则会出现数组越界访问。以下API中,encrypt表…

Java提高篇 —— Java内部类详解

一、简介 内部类是一个非常有用的特性但又比较难理解使用的特性。 内部类我们从外面看是非常容易理解的,无非就是在一个类的内部在定义一个类。 public class OuterClass {private String name ;private int age;public String getName() {return name;}public voi…

Ubuntu修改界面的大小

命令 xrandr 就会显示ubuntu支持的屏幕比例使用命令 xrandr --size 1680x1050 切换屏幕大小

Java提高篇 —— String缓冲池

一、String缓冲池 首先我们要明确,String并不是基本数据类型,而是一个对象,并且是不可变的对象。查看源码就会发现String类为final型的(当然也不可被继承),而且通过查看JDK文档会发现几乎每一个修改String对…

C++最新使用开源openssl实现输入是文件,输出是文件的AES加解密的代码

AES.h头文件 #include <cstring> #include <fstream> #include <iostream> #include <openssl/aes.h>//AES文件加密函数 int aes_encrypt_file(const std::string &original_backup_file_path,const std::string &encrypted_file_path,const …

Java基础 —— JVM内存模型与垃圾回收

目录一、概述二、运行时数据区方法区运行时常量池堆栈本地方法栈程序计数器三、对象访问四、垃圾回收如何定义垃圾1、引用计数法2、可达性分析垃圾回收方法1、Mark-Sweep标记-清除算法2、Copying复制算法3、Mark-Compact标记-整理算法4、Generational Collection 分代收集垃圾收…

Report Design

ERP_ENT_STD-CSDN博客

规范化流程化提交自己代码到远程gitlab服务器

流程 进入到build目录使用make命令进行编译 make -j 6&#xff0c;前提是cmake命令已经执行../format.py rungit add .. 添加文件git checkout -b xxx 创建xxx分支&#xff0c;其中xxx是分支名字git branch 查看分支git commit -m "[chy/backup] modify parameters for…

Java提高篇 ——Java注解

目录一、注解注解的定义注解的应用元注解RetentionDocumentedTargetInheritedRepeatable注解的属性Java 预置的注解DeprecatedOverrideSuppressWarningsSafeVarargsFunctionalInterface二、注解的提取三、注解与反射四、注解的使用场景五、亲手自定义注解完成某个目的六、注解应…

linux使用openssl查看文件的md5数值

代码 #include <stdio.h> #include <openssl/md5.h>std::string get_file_md5(const char *path){unsigned char digest [MD5_DIGEST_LENGTH];std::ifstream file(path, std::ios::in | std::ios::binary); //打开文件MD5_CTX md5_ctx;MD5_Init(&md5_ctx);cha…

Android 性能优化四个方面总结

目录一、四个方面二、卡顿优化1、Android系统显示原理2、卡顿根本原因3、性能分析工具&#xff08;1&#xff09;Profile GPU Rendering&#xff08;2&#xff09;TraceView&#xff08;3&#xff09;Systrace UI 性能分析4、优化建议&#xff08;1&#xff09;布局优化&#x…

pycharm/clion/idea等产品多含代码左移右移操作

左移 选中多行代码后&#xff0c;按下Tab键&#xff0c;一次缩进四个字符 右移 鼠标选中多行代码后&#xff0c;同时按住shiftTab键&#xff0c;一次左移四个字符

Android 开源框架选择

目录一、前言二、APP的整体架构三、技术选型的考量点四、日志记录能力五、JSON解析能力1、gson2、jackson3、Fastjson4、LoganSquare六、数据库操作能力1、ActiveAndroid2、ormlite3、greenDAO4、Realm七、网络通信能力1、android-async-http2、OkHttp3、Volley4、Retrofit八、…

使用opensll的md5对于string进行加密

代码 #include <openssl/md5.h>#include <sstream> #include <iomanip> #include <iostream>void get_string_md5(const std::string& await_md5_string) {unsigned char md5[MD5_DIGEST_LENGTH];MD5(reinterpret_cast<unsigned const char*&g…

Android Studio 自定义Gradle Plugin

一、简介 之前公司的一个项目需要用到Gradle插件来修改编译后的class文件&#xff0c;今天有时间就拿出来整理一下&#xff0c;学习一下Gradle插件的编写还是一件十分有意义的事。 二、Gradle插件类型 一种是直接在项目中的gradle文件里编写&#xff0c;这种方式的缺点是无法复…