关于Java构造类与对象的思考
简单记录一下Java构造类与对象时的流程以及this和super对于特殊例子的分析。
首先,接着昨天的问题,我做出了几个变形:
Pic1.原版:

Pic2.去掉了T.foo方法中的this关键字:

Pic3.在原版的基础上抹掉了B.foo方法:

Pic4.抹掉了原版的B.foo方法,同时去掉了T.foo方法中的this关键字:

这四个图全方位地对this和super两个关键字的有无进行了测试,测试结果表明:这些版本的程序的运行结果都一样...
在这里,还是插一张流程图:

首先说一下super关键字,这些代码在使用super关键字时都是这样使用的:在B.foo中调用super.foo。
但事实是,如果B中没有foo方法,其效果和B.foo中只执行一句super.foo效果是一样的。
我们可以这样去想:super.foo是将B.foo执行过程“扔到”了其父类(super)中去执行,但是B如果没有显示声明并实现foo方法,其默认行为依旧是调用B的父类的foo方法。
所以... 在这里,B类中的“void foo { super.foo(); } ”语句,是没有任何意义的一个语句(如果我是一个足够聪明的Java编译器,我是不会理这个语句的,我只需要copy一下其父类对于这个方法的实现即可,至少在这个程序中是这样)。
到这里,上述的四段不同代码因此变成了两种不同代码...
根据程序输出结果初步判断:这里this关键字的有无,对程序最终的结果没有影响。
原因分析起来也简单,尽管有个super关键字(没有super关键字的情况分析起来更容易)让我们跳到了T中去寻找代码,但当前的对象依旧是已经实例化了的B类的对象,并且此时B类已经实现了bar方法,所以当程序调用bar方法时,自然会先从B类中去寻找bar方法的实现,找到了,便直接输出“B.bar”(因此我们甚至可以这样去理解:其实每个方法前,都隐含着一个this关键字,如方法method()其实就是this.method(),事实表明,这也很可能就是Java中实现方法的机制)。
那么如何让程序经过小改动,在不改变T和B的继承关系的前提下,使得输出变为“T.bar”呢?
上述分析使得我们对于this关键字已经不抱有任何期望了。顺着上面的分析,既然要找到一个T.bar方法的实现,那直接把B中的bar方法注释掉即可。
当然,另一种思路就是:既然要输出T.bar,这个方法是T类的对象能够做到的,那么在B类中弄一个T类对象即可,就像这样(如图):

这就是两种比较简单的改动代码的方式(可能有人会觉得第二种方法或多或少不太合适,毕竟此时程序的输出实质上是靠B类中一个T类实例对象来帮忙搞定的)。
下面简单说一下类和对象构造的过程。
为了更普遍地表示类的构造过程,我画了一张这样的图:

这张图中左侧每个类之间的箭头,都表示extends,其方向由子类指向父类,每个类实现的方法,同这个类的颜色相同。
当然,图中每个子类中的从父类继承的方法,要么原封不动地保留下来,要么是覆盖掉父类原有的具体实现。我在这张图中并没有画出覆盖掉父类方法的例子(至少每种方法的颜色从始至终未曾变化),但这种情况实际上是很普遍的,这种情况画出来的话自然也会让这张图更加“多姿多彩”。
此时,我们不妨这样想一下,Java中的继承机制为单继承,但Java支持接口扩展,同时加上每个类中还含有成员变量(或者称之为类的属性),当这样一张庞大的图用上述采用不同颜色画图的方式来描述时,许许多多不同的实例对象定会五彩斑斓,这将很容易让我们理解“什么是Java中的多态”。
继续对上述的知识做个总结:在构造器实例化对象的过程中,是按照从父类到子类一步步构造的过程来实现的。下面举个例子:
class Grandpa {
protected String status = "Grandpa";
protected int age = 70;
void func() {
System.out.println("Grandpa.func");
}
void who() {
System.out.println(status);
}
void jump() {
this.func();
}
}
class Father extends Grandpa {
Father() {
status += " --> Father";
}
void func() {
System.out.println("Father.func");
}
void who() {
super.who();
}
void jump() {
super.jump();
}
}
class Son extends Father {
Son() {
status += " --> Son";
}
void func() {
System.out.println("Son.func");
}
void who() {
super.who();
}
void jump() {
super.jump();
}
}
class Family {
public static void main(String args[]) {
Son son = new Son();
son.who();
son.func();
son.jump();
}
}
Family.java
此程序的输出为:
Grandpa --> Father --> Son
Son.func
Son.func
这也就很好地说明了构造的过程是一级一级地进行构造的(毕竟程序不是单纯地输出 “Grandpa --> Son”)。
至于后面两个测试方法以及程序输出,只要弄清楚当前的对象究竟是谁、它的直接构造类是否含有程序要调用的方法,判断起来就容易多了。
既然是一级一级调用构造器来实例化对象,那么一个构造器环节出错,肯定就无法实例化一个对象出来。例如让Father类的构造器原型声明为Father(int require)。此时编译器就会优雅地告诉我们:

此时只要在Son的构造器中把“super(666);”添加为第一句即可。(这里或多或少让刚才分析程序中没什么效果的super关键字语句减缓点儿尴尬)。当然,这里的666只是随便说的一个数字,可以替换成任何int型数据。因为这只是Father构造器提出的一个要求(require),毕竟这个require在Father类中并没有被用到。
为了帮super关键字“挽回点儿面子”,说两句我认为的super关键字的重要性:
super关键字其实是构造器中的第一句,当构造器为默认构造器时(即程序中不具体声明实现构造器或父类构造器无需任何参数即可实例化对象),(隐身的)默认构造器第一句就是“super();”。
super关键字可以让我们更方便地调用父类的方法,甚至让我们更快捷地访问父类的成员属性。
以上就是这两天对“类和对象”内容的简单的“复习回忆记笔记”,以及通过老师给出的问题,让我对this和super关键字有了一个新的认识(讲真,之前(至少昨天的时候)真的弄错了)。
以后有了新想法再来更新,恭请欢迎读者访客给出建议修正。
关于Java构造类与对象的思考的更多相关文章
- 第31节:Java基础-类与对象
前言 Java基础-类与对象,方法的重载,构造方法的重载,static关键字,main()方法,this关键字,包,访问权限,类的继承,继承性,方法的重写,super变量. 方法的重载:成员方法的重载 ...
- Java面向对象-类与对象
Java面向对象-类与对象 类与对象的关系 我们通俗的举个例子,比如人类是一种类,张三这个人就是人类的具体的一个个体,也就是java中的对象:这就是一个类与对象的关系: 类的定义 下面看实例 类的创建 ...
- Java深入分析类与对象
深入分析类与对象 1,成员属性封装 在类之中的组成就是属性与方法,一般而言方法都是对外提供服务的,所以是不会进行封装处理的,而对于属性需要较高的安全性,所以往往需要对其进行保护,这个时候就需要采用封装 ...
- Java面向对象——类,对象和方法
1.类的概念 在生活中,说到类,可以联想到类别,同类,会想到一类人,一类事物等等.而这一类人或事物都是具有相同特征或特点和行为的,我们根据不同的特征或特点和行为将他们归类或分类.同时,当我们认识一个新 ...
- Java面向对象类与对象整理
第一章 面向对象: 1.1 什么是面向过程: 遇到某件事的时候,思考 “我该怎么做”然后一步一步实现的过程 1.2 什么是面向对象: 遇到某件事的时 ...
- java笔记 -- 类与对象
封装: 从形式上看, 封装是将数据和行为组合在一个包中, 并对对象的使用者隐藏了数据的实现方式. 对象中的数据称为实例域, 操纵数据的过程称为方法. 对于每个特定的类实例(对象)都有一组特定的实例域值 ...
- Java面向对象--类和对象
面向对象是相对于面向过程而言的,是软件开发方法.面向对象把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统设计,更贴近事物的自然运行模式.本篇博客介绍Java面向对象的类和对象 目录: 面 ...
- java基础---类和对象(4)
一. static关键字 使用static关键字修饰成员变量表示静态的含义,此时成员变量由对象层级提升为类层级,整个类共享一份静态成员变量,该成员变量随着类的加载准备就绪,与是否创建对象无关 使用st ...
- java基础---类和对象(2)
一.继承(extends) 面向对象的三大特性之一,当多个类中存在相同属性和行为时,将这些内容抽取到一个公共类中,让多个类(子类)吸收公共类(父类.超类)中已有特征和行为,而在多个类型只需要编写自己独 ...
随机推荐
- JPA中实现双向多对多的关联关系(附代码下载)
场景 JPA入门简介与搭建HelloWorld(附代码下载): https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/103473937 ...
- 论文阅读|Focal loss
原文标题:Focal Loss for Dense Object Detection 概要 目标检测主要有两种主流框架,一级检测器(one-stage)和二级检测器(two-stage),一级检测器, ...
- input输入框type设置为number,maxlength无效
一个小细节,以前很少注意,直到最近做的一个项目,当我把一个输入数字的input框的类型设置为number时,input框还是可以无限输入,设置maxlengh=10就不起作用了.然后我就去百度了一下, ...
- 解决游览器安装Vue.js devtools插件无效的问题
一: 打开自己写的一个vue.js网页,发现这个图标并没有亮起来,还是灰色 解决方案: 1.我们先看看Vue.js devtools是否生效,打开Bilibili(https://www.bilib ...
- Services: ARP Caching
Online Help for Cisco IOS Release 12.2(15)JA Services: ARP Caching ARP caching on the access point ...
- 如何删除 AppStore 中的恶意评论 iOS
AppStore 中的评论,对于产品的形象影响很大.如果评论榜中出现了恶意评论,会对产品形象影响很大,当然这些差评有可能是用户的真实反馈,需要产品设计人员做好产品设计,满足客户的需求.另外也可能是竞争 ...
- video兼容ie,ckplayer网页播放器
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- CentOS上安装elasticsearch
1.安装docker yum install docker 2.启动docker systemctl start docker 查看docker版本: docker -v 之后只要启动机器就会自动开启 ...
- conda常用命令(待续)
1.常用命名 # 查看虚拟环境列表 conda env list # 创建虚拟环境 conda create -n python36 python=3.6.2 # 切换环境 activate pyth ...
- LeetCode.62——不同路径
问题描述: 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为"Start" ). 机器人每次只能向下或者向右移动一步.机器人试图达到网格的右下角(在下图中标记为 ...