前言

在上一篇中回顾了java的修饰符和String类,这篇就来回顾下Java的三大特性:封装、继承、多态。

封装

什么是封装

在面向对象程式设计方法中,封装是指一种将抽象性函式接口的实现细节部份包装、隐藏起来的方法。

封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。要访问该类的代码和数据,必须通过严格的接口控制。

封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。

简单的来说,就是将Java中的经常用到的代码进行封装起来,形成一个方法。比如,我们常用的实体类,使用private修饰变量,用于保护数据;对外提供getter和setter方法,用于调用。这就是一种典型的封装。

代码示例:

    public class packagingTest {

    	public static void main(String[] args) {
User user=new User();
//这里会报错,因为id和name是私有的,用于保护该数据
// user.id=10;
// user.name="张三";
user.setId(1);
user.setName("张三");
System.out.println(user.getId());
System.out.println(user.getName());
} } class User{
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

运行结果:

    1
张三

使用封装的好处

  1. 良好的封装能够减少耦合。

  2. 类内部的结构可以自由修改。

  3. 可以对成员变量进行更精确的控制。

  4. 隐藏信息,实现细节。

继承

什么是继承

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

继承的特性

  • 子类拥有父类非private的属性,方法。
  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
  • 子类可以用自己的方式实现父类的方法。

为什么使用继承

继承主要目的是为了复用代码!简单的来说,就是将重复的代码抽出来,放到父类中,然后在再由子类继承使用,子类也是可以对父类进行扩展的。所以在继承关系中,可以这么理解,父类更通用,子类更具体。

打个比方,在动物世界中,猫和狮子是属于猫科,狗和狼是属于犬科。而它们也都是动物。猫和狮子有个共同的父类猫科,猫和狗有个共同的父类动物。所以它们是符合继承关系的, 只不过它们在行为上有所区别。猫和狗都有吃和睡,不过猫可以爬树,狗不可以。

这里,我们可以使用如下代码来进行说明。

代码示例:

public class extendTest {

	public static void main(String[] args) {
Cat cat=new Cat();
Dog dog=new Dog();
cat.eat();
cat.sleep("cat");
cat.climbTree();
dog.eat("dog");
dog.sleep("dog");
}
} class Animal{
public void eat(String name){
System.out.println(name+"正在吃东西...");
}
public void sleep(String name){
System.out.println(name+"正在睡觉...");
}
} class Cat extends Animal{
private String name="Cat";
public void eat(){
super.eat(name);
System.out.println(name+"吃完了");
}
public void sleep(){
this.sleep(name);
} public void sleep(String name){
System.out.println(name+"刚刚睡觉!");
} public void climbTree(){
System.out.println(name+"正在爬树!");
}
} class Dog extends Animal{ }

运行结果:

Cat正在吃东西...
Cat吃完了
cat刚刚睡觉!
Cat正在爬树!
dog正在吃东西...
dog正在睡觉...

在上述代码中,父类Animal实现了eat和sleep的方法,子类Cat和Dog使用了extends 关键字继承了父类Animal。子类Dog继承父类Animal之后什么都没做,而子类Cat不但继承了父类Animal,而且还增加了climbTree 方法,并且也重写了父类的eat和sleep方法。

在子类Cat中,出现了两个关键字:superthis

这两个关键字的意义如下:

  • super关键字:实现对父类成员的访问,用来引用当前对象的父类。
  • this关键字:指向自己的引用。

在上述代码中,子类Cat使用super关键字调用了父类Animal的eat方法,使用this关键字调用本类中的sleep方法。

说到继承,就不得不提这几个东西: final和protected修饰符、构造器、以及向上转型!

其中final和protected修饰符在上一篇java的修饰符和String类中已经讲解了,这里就简单的描述下。

  • final:修饰的类不可以被继承。
  • protected:修饰的类仅对同一包内的类和所有子类可见。

构造器

虽然子类可以继承父类的属性和方法(private修饰的除外),但是还有一样是子类无法继承的,那就是是构造器!对于构造器而言,它只能够被调用,而不能被继承。如果子类想使用父类的构造器,那么只需使用super关键字调用即可。

注:如果父类的构造器被private所修饰,那么是无法被外部调用的,包括子类!

向上转型

将子类转换成父类,在继承关系上面是向上移动的,所以一般称之为向上转型。

之前的个例子中,猫和动物是属于继承关系,那么我们可以把猫当作动物就是向上转型!

例如:

public class extendTest {

	public static void main(String[] args) {
Animal animal=new Cat();
animal.eat("cat");
animal.sleep("cat");
}
} class Animal{
public void eat(String name){
System.out.println(name+"正在吃东西...");
}
public void sleep(String name){
System.out.println(name+"正在睡觉...");
}
} class Cat extends Animal{
private String name="Cat";
public void eat(){
super.eat(name);
System.out.println(name+"吃完了");
}
public void sleep(){
this.sleep(name);
} public void sleep(String name){
System.out.println(name+"刚刚睡觉!");
} public void climbTree(){
System.out.println(name+"正在爬树!");
}
}

运行结果:

cat正在吃东西...
cat刚刚睡觉!

上述代码中完成了向上转型,但是在向上转型中是存在着一些缺憾的,那就是属性和方法的丢失。例如上述代码中就丢失了Cat类中的climbTree()方法和name属性。所以慎用向上转型!

多重继承

Java的继承是单继承,但是可以实现多重继承!

虽然一个子类只能继承一个父类,但是子类的子类也可以子类。

例如C类继承B类,B类继承A类,所以按照关系就是A类是B类的父类,B类是C类的父类。

继承的缺点

虽然继承大大提升了代码的复用性,但是也提高了类之间的耦合性!父类更改,子类就必须更改!因此可以说继承破坏了封装,因为对于父类而言,它的实现细节对与子类来说都是透明的。

所以慎用继承!!!

多态

什么是多态

多态是指事物在运行过程中存在不同的状态。

多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定。

使用多态的必要条件

多态存在的三个必要条件:

  1. 要有继承关系;
  2. 子类要重写父类的方法;
  3. 父类引用指向子类对象,也就是向上转型。

多态使用的简单示例

在上面的继承讲解中,我们用到了猫和动物这两个之间的关系,这里我们也可以使用这,只需要稍微改下代码,就可以实现多态。

代码示例:

public class Test {

	public static void main(String[] args) {
Animal animal=new Cat();
animal.eat();
}
} class Animal{
private String name="Animal";
public void eat(){
System.out.println(name+"正在吃东西...");
sleep();
}
public void sleep(){
System.out.println(name+"正在睡觉...");
}
} class Cat extends Animal{
private String name="Cat";
public void eat(String name){
System.out.println(name+"吃完了");
sleep();
}
public void sleep(){
System.out.println(name+"正在睡觉");
}
}

输出结果:

Animal正在吃东西...
Cat正在睡觉

看到了运行结果之后,如果不熟悉多态的话,是不是感觉有些奇怪呢?

打印的第一句应该好理解,为什么打印的第二句不是Animal的方法,而是Cat中的方法呢?

我们知道多态是指事物在运行过程中存在不同的状态。而这里,我们用到了继承、重写以及向上转型。

在这里顺便提一下:

在向上转型中,一个父类的引用是可以指向多种子类对象,那么在运行时对于同一个消息是由实际的被引用的对象的类型来决定。

根据上述这段理解,我们再来看刚刚的那个示例。

在Cat类中,重写了父类Animal的sleep方法,并重载了eat方法。重载之后的eat(String name)方法和父类Animal的eat()方法不是同一个方法,因为是会在向上转型丢失的。而Cat子类重写了sleep方法,因此在向上转型的时候是不会丢失的,并且因为指定对对象的引用类型是Cat,所以Animal在调用eat()方法的时候,先是调用本类中eat()方法,然后在调用子类中的sleep()方法!

结论:

当父类引用指向子类方法时,必须调用那些父类中存在的方法,如果子类中对该方法进行了重写,那么在运行时就会动态调用子类中的方法,这就是多态。

使用多态的优点

摘自:https://www.cnblogs.com/jack204/archive/2012/10/29/2745150.html

  1. 可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
  2. 可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
  3. 接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。
  4. 灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
  5. 简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。

在对多态有一定的认识之后,可以尝试看看如下代码。

这是一个经典的多态问题,摘自:

http://blog.csdn.net/thinkGhoster/archive/2008/04/19/2307001.aspx

代码示例:

	public class extendsTest {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D(); System.out.println("1--" + a1.show(b));
System.out.println("2--" + a1.show(c));
System.out.println("3--" + a1.show(d));
System.out.println("4--" + a2.show(b));
System.out.println("5--" + a2.show(c));
System.out.println("6--" + a2.show(d));
System.out.println("7--" + b.show(b));
System.out.println("8--" + b.show(c));
System.out.println("9--" + b.show(d));
}
} class A {
public String show(D obj) {
return ("A and D");
} public String show(A obj) {
return ("A and A");
} } class B extends A{
public String show(B obj){
return ("B and B");
} public String show(A obj){
return ("B and A");
}
} class C extends B{ } class D extends B{ }

运行结果:

1--A and A
2--A and A
3--A and D
4--B and A
5--B and A
6--A and D
7--B and B
8--B and B
9--A and D

分析

①②③比较好理解,一般不会出错。④⑤就有点糊涂了,为什么输出的不是"B and B”呢?!!先来回顾一下多态性。

运行时多态性是面向对象程序设计代码重用的一个最强大机制,动态性的概念也可以被说成“一个接口,多个方法”。Java实现运行时多态性的基础是动态方法调度,它是一种在运行时而不是在编译期调用重载方法的机制。

方法的重写Overriding和重载Overloading是Java多态性的不同表现。

重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写(Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被“屏蔽”了。如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。

当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。(但是如果强制把超类转换成子类的话,就可以调用子类中新添加而超类没有的方法了。)

好了,先温习到这里,言归正传!实际上这里涉及方法调用的优先问题 ,优先级由高到低依次为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。让我们来看看它是怎么工作的。

比如④,a2.show(b),a2是一个引用变量,类型为A,则this为a2,b是B的一个实例,于是它到类A里面找show(B obj)方法,没有找到,于是到A的super(超类)找,而A没有超类,因此转到第三优先级this.show((super)O),this仍然是a2,这里O为B,(super)O即(super)B即A,因此它到类A里面找show(A obj)的方法,类A有这个方法,但是由于a2引用的是类B的一个对象,B覆盖了A的show(A obj)方法,因此最终锁定到类B的show(Aobj),输出为"B and A”。

再比如⑧,b.show(c),b是一个引用变量,类型为B,则this为b,c是C的一个实例,于是它到类B找show(C obj)方法,没有找到,转而到B的超类A里面找,A里面也没有,因此也转到第三优先级this.show((super)O),this为b,O为C,(super)O即(super)C即B,因此它到B里面找show(Bobj)方法,找到了,由于b引用的是类B的一个对象,因此直接锁定到类B的show(B obj),输出为"B and B”。

按照上面的方法,可以正确得到其他的结果。

问题还要继续,现在我们再来看上面的分析过程是怎么体现出蓝色字体那句话的内涵的。它说:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。还是拿a2.show(b)来说吧。

a2是一个引用变量,类型为A,它引用的是B的一个对象,因此这句话的意思是由B来决定调用的是哪个方法。因此应该调用B的show(B obj)从而输出"B and B”才对。但是为什么跟前面的分析得到的结果不相符呢?!问题在于我们不要忽略了蓝色字体的后半部分,那里特别指明:这个被调用的方法必须是在超类中定义过的,也就是被子类覆盖的方法。

B里面的show(B obj)在超类A中有定义吗?没有!那就更谈不上被覆盖了。实际上这句话隐藏了一条信息:它仍然是按照方法调用的优先级来确定的。它在类A中找到了show(Aobj),如果子类B没有覆盖show(A obj)方法,那么它就调用A的show(Aobj)(由于B继承A,虽然没有覆盖这个方法,但从超类A那里继承了这个方法,从某种意义上说,还是由B确定调用的方法,只是方法是在A中实现而已);现在子类B覆盖了show(A obj),因此它最终锁定到B的show(A obj)。这就是那句话的意义所在。

其它

参考:

http://blog.csdn.net/thinkGhoster/archive/2008/04/19/2307001.aspx

https://www.cnblogs.com/jack204/archive/2012/10/29/2745150.html

https://blog.csdn.net/chenssy/article/details/12786385

到此,本文就结束了,谢谢阅读!欢迎留言和点赞,你的支持是我写作最大的动力!

版权声明:

作者:虚无境

博客园出处:http://www.cnblogs.com/xuwujing

CSDN出处:http://blog.csdn.net/qazwsxpcm    

个人博客出处:http://www.panchengming.com

Java基础知识回顾之三 ----- 封装、继承和多态的更多相关文章

  1. Java基础知识回顾之七 ----- 总结篇

    前言 在之前Java基础知识回顾中,我们回顾了基础数据类型.修饰符和String.三大特性.集合.多线程和IO.本篇文章则对之前学过的知识进行总结.除了简单的复习之外,还会增加一些相应的理解. 基础数 ...

  2. java基础知识回顾之---java String final类普通方法

    辞职了,最近一段时间在找工作,把在大二的时候学习java基础知识回顾下,拿出来跟大家分享,如果有问题,欢迎大家的指正. /*     * 按照面向对象的思想对字符串进行功能分类.     *      ...

  3. Java基础知识回顾(一):字符串小结

    Java的基础知识回顾之字符串 一.引言 很多人喜欢在前面加入赘述,事实上去技术网站找相关的内容的一般都应当已经对相应知识有一定了解,因此我不再过多赘述字符串到底是什么东西,在官网中已经写得很明确了, ...

  4. Java基础知识回顾之四 ----- 集合List、Map和Set

    前言 在上一篇中回顾了Java的三大特性:封装.继承和多态.本篇则来介绍下集合. 集合介绍 我们在进行Java程序开发的时候,除了最常用的基础数据类型和String对象外,也经常会用到集合相关类. 集 ...

  5. Java基础知识回顾之一 ----- 基本数据类型

    前言 在开始工作至今,学习各种各样的技术之中发现自己的很多Java的基础知识都忘了⊙﹏⊙b汗... 而且越是学习越是发现Java基础的重要性,所以准备单独抽一下时间进行Java基础的重新学习.在重新学 ...

  6. java基础知识(5)---继承

    继 承:(面向对象特征之一)好处:1:提高了代码的复用性.2:让类与类之间产生了关系,提供了另一个特征多态的前提. 父类的由来:其实是由多个类不断向上抽取共性内容而来的.java中对于继承,java只 ...

  7. java基础知识回顾之接口

    /* abstract class AbsDemo { abstract void show1(); abstract void show2(); } 当一个抽象类中的方法都是抽象的时候,这时可以将该 ...

  8. java基础语法4--封装,继承,多态

    学习路线: 未封装==>封装==>继承==>多态==>抽象类 首先还是那句话,万物皆对象,对象有行为和属性. 一:封装 1.封装的概念: 信息隐蔽和对象的属性及操作结合成一个独 ...

  9. java基础讲解09-----接口,继承,多态

    还有什么包装类,数字类,这些简单的我就不想过去介绍,前面也大概的介绍了下,继承,多态 1.类的继承 继承的思想:基于某个父类的扩展,制定一个新的子类.子类可以继承父类原有的属性,方法,也可以重写父类的 ...

随机推荐

  1. kvm之三:本地安装虚拟机

    1.格式化新添加的磁盘 [root@kvm ~ ::]#fdisk /dev/sdb Command (m for help): n //新建分区 Command action e extended ...

  2. UI线程异常处理方法

    当应用程序启动,创建了一个叫“main”的线程,用于管理UI相关,又叫UI线程.其他线程叫工作线程(Work Thread). Single Thread Model 一个组件的创建并不会新建一个线程 ...

  3. Bean validation

    公司测试非常严格,要求我们对每个参数的长度进行校验,提了一个参数长度校验的单,然后我们老大就把我们的代码全部打回去了.... 一个bean类中往往有超多变量,如果一个个写if else,够呛,而且圈复 ...

  4. Alpha第七天

    Alpha第七天 听说 031502543 周龙荣(队长) 031502615 李家鹏 031502632 伍晨薇 031502637 张柽 031502639 郑秦 1.前言 任务分配是VV.ZQ. ...

  5. 安装iis8

    -------------------- @echo off     echo 正在添加IIS8.0 功能,依据不同的网络速率,全程大约需要5分钟时间...     start /w pkgmgr / ...

  6. 个人作业2:QQ音乐APP案例分析

    APP案例分析 QQ音乐 选择理由:毕竟作为QQ音乐九年的资深老用户以及音乐爱好者 第一部分 调研 1.第一次上手的体验    我算是很早期的QQ音乐的用户,用QQ音乐七八年,除了体验各方面还不错之外 ...

  7. Hibernate之缓存

    Hibernate为了解决频繁查询数据的效率问题,提供了三种级别的缓存 1.一级缓存 一级缓存 又叫 session缓存 .Session对象会缓存处于持久化状态的每个对象 ,如果下次想用数据表中同一 ...

  8. vue 中获取select 的option的value 直接click?

    我刚开始遇到这个问题的时候 直接用的click进行dom操作获取value 但是发现并灭有什么作用 问了旁边大神 才想起来还有change这个操作 于是乎 答案有了解决方案 1.在你的select中添 ...

  9. linux系统命令学习系列-用户切换命令su,sudo

    先复习一下上节内容: 用户组添加groupadd 用户组修改groupmod 用户组删除groupdel 作业创建一个id为501的组group1,然后改成group2, 同时id变为502,最后删除 ...

  10. ll的命令后面的字段详解

    linux学习 命令ll后字段的解释 分类:linux | 标签: 命令ll后字段的解释  2010-10-25 15:47阅读(4513)评论(0) ls -l 列表信息详解 我们平时用ls -l ...