关于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) 面向对象的三大特性之一,当多个类中存在相同属性和行为时,将这些内容抽取到一个公共类中,让多个类(子类)吸收公共类(父类.超类)中已有特征和行为,而在多个类型只需要编写自己独 ...
随机推荐
- ASP.NET Core搭建多层网站架构【8.1-使用ViewModel注解验证】
2020/01/29, ASP.NET Core 3.1, VS2019 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构[8.1-使用ViewModel注解验证] 使用V ...
- set的使用-Hdu 2094
产生冠军 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- [数据库] MariaDB安装及使用
一.安装MariaDB 1.使用官方源安装marisdb 如果使用阿里云的源,目前的版本号为5.5.64.如果想安装最新的10.x版本,则需要使用MariaDB的官方源. 1)配置官方源: 在/etc ...
- Card Game for Three
Alice, Bob and Charlie are playing Card Game for Three, as below: At first, each of the three player ...
- Catalyst 3850 升级-1
Cisco Catalyst 3850交换机使用Cisco IOS XE软件. Cisco IOS XE软件是一个包含一组包文件的一个集合. 我们可以使用以下两种模式之一在Cisco Catalyst ...
- node.js express 中文参考手册
https://www.runoob.com/w3cnote/express-4-x-api.html 原文地址:https://www.zybuluo.com/bajian/note/444152 ...
- POJ3268 Silver Cow Party (建反图跑两遍Dij)
One cow from each of N farms (1 ≤ N ≤ 1000) conveniently numbered 1..N is going to attend the big co ...
- linux kali 的ifconfig命令
ifconfig命令 1.ifconfig执行页面 root@localhost:/home/zys# ifconfig lo: flags=73<UP,LOOPBACK,RUNNING> ...
- 单链表 C++ 实现 - 含虚拟头节点
本文例程下载链接:ListDemo 链表 vs 数组 链表和数组的最大区别在于链表不支持随机访问,不能像数组那样对任意一个(索引)位置的元素进行访问,而需要从头节点开始,一个一个往后访问直到查找到目标 ...
- Chrome浏览器 HTML5看视频卡顿
定位问题 起初以为是flash的问题,但是在B站看视频,由html播放改为flash播放后,卡顿现象消失 将相同的B站视频,用edge播放,也无卡顿现象 可以确定,问题出在chrome身上 解决方法 ...