关于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) 面向对象的三大特性之一,当多个类中存在相同属性和行为时,将这些内容抽取到一个公共类中,让多个类(子类)吸收公共类(父类.超类)中已有特征和行为,而在多个类型只需要编写自己独 ...
随机推荐
- U2000解决备份:服务器不可达、FTP/TFTP/SFTP IP地址与网管地址不一致
只需要将一下几点设置到位这些问题基本解决: 设置->网元软件管理->FTP设置 如果你的U2000服务器和OLT都在一个内网,那么就IP1和IP2就都输入U2000服务器的内网IP,否则按 ...
- 基于 VS2019 配置 opencv4.x
创建新项目 添加主函数文件 配置 注意,如果直接使用项目的属性去配置,那么创建新的项目的时候,还需要再配置一遍,在属性管理器里配置,创建新项目的时候,会自动应用 接下来,开始为软件配置目录和附加项.右 ...
- 创业学习---《预判项目的长期壁垒》--B-3.预判模块---HHR计划---以太一堂
一,<开始学习> 1,投资人经常会问CEO:你的项目的长期壁垒是什么?你是怎么思考的? 2,三个预热思考题: (1)突然有一天,大公司要抄你,你会怎么办? 答:用增长技术来和他竞争. ( ...
- 创业学习--《预判行业机会》--B-2.预判模块---HHR计划--以太一堂
一,<开始学习> 1,行业机会的判断,是可以通过不断地训练提高自己的判准的概率的,要科学思考创业. 2,创业者在行业机会上的三个问题: a. 对市场变化,敏感性太弱,没有洞察行业的意识. ...
- JPA 级联保存的问题
前提:系统有学校-学生关系,学校可以包含多个学生,学生只能属于一个学校 在使用 spring-data-jpa 的时候,保存学校的同时保存学生信息,不需要先逐个保存学生信息,再将学生信息放在学校中保存 ...
- 【PAT甲级】1047 Student List for Course (25 分)
题意: 输入两个正整数N和K(N<=40000,K<=2500),接下来输入N行,每行包括一个学生的名字和所选课程的门数,接着输入每门所选课程的序号.输出每门课程有多少学生选择并按字典序输 ...
- Android 调用系统Email发送带多附件的邮件
转自:http://www.open-open.com/lib/view/open1347005126912.html 众所周知,在Android中调用其他程序进行相关处理,都是使用的Intent.当 ...
- 使用YUM安装软件时提示PackageKit睡眠中解决方法!
报错如图所示: 解决方法一:移除var/run/yum.pid文件 方法二:直接杀掉进程号 报错的时候会跟进程号 直接利用kill -9 +进程号
- 第二章linux网络基础设置总结!
一:查看及测试网络 (1)查看活动的网络接头命令:ifconfig (2)查看所有网络接口命令:ifconfig -a (3)查看指定的网络接口(不论该网络接口是否处于激活状态)命令:ifconfig ...
- Java27个基数点
1.JAVA中的几种基本数据类型是什么,各自占用多少字节. 2.String类能被继承吗,为什么 不能.在Java中,只要是被定义为final的类,也可以说是被final修饰的类,就是不能被继承的. ...