关于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) 面向对象的三大特性之一,当多个类中存在相同属性和行为时,将这些内容抽取到一个公共类中,让多个类(子类)吸收公共类(父类.超类)中已有特征和行为,而在多个类型只需要编写自己独 ...
 
随机推荐
- 转载--php 7.2 安装 mcrypt 扩展
			
在 php 官网下载 mcrypt 包,php 扩展官网 # wget http://pecl.php.net/get/mcrypt-1.0.1.tgz # tar xf mcrypt-1.0.1.t ...
 - python 语法-参数注释
			
python 语法-参数注释 最近碰到的这样的代码: def func(a:"shuoming") -> int: print("函数已运行.") fun ...
 - 进程的用户ID
			
进程创建时,系统会在进程上设置几个用户相关的ID 实际用户ID,实际用户组ID,系统根据当前会话登陆的用户信息设置 有效用户ID,有效用户组ID,系统根据所打开的执行文件的模式位,进行设置.set_u ...
 - mDNS故障排查(译)
			
WLC上mDNS网关的理解及排查 第一部分:介绍 这篇文档描述了Bonjour协议在WLC上的操作,该文档旨在协助工程师理解该工作流量的原理以及提供故障排查的指导. 第二部分:需求和前提 知识需求: ...
 - DMVPN基础配置
			
DMVPN基础拓扑: 配置步骤: 1. 基本IP地址配置实现网络可达 2. 配置GRE多点隧道(mGRE)和NHRP(下一跳解析协议) 3. 配置EIGRP路由协议 4. 配置 ...
 - MAC系统 - 基础知识
			
一.基础操作 设置:触控板设置 - >学习具体手势 手势:MacBook Pro手势大全必学手势触控板手势有哪些 左键,右键,滑屏,切换到应用... 一指操作: 一指敲击:鼠标左键: 一指按下: ...
 - POJ3662 Telephone Lines (dijkstra+二分)
			
Farmer John wants to set up a telephone line at his farm. Unfortunately, the phone company is uncoop ...
 - 【原】Mysql最大连接数
			
MySQL最大连接数的默认值是100, 这个数值对于并发连接很多的数据库的应用是远不够用的,当连接请求大于默认连接数后,就会出现无法连接数据库的错误,因此我们需要把它适当调大一些. 在使用MySQL数 ...
 - 第二章linux网络基础设置总结!
			
一:查看及测试网络 (1)查看活动的网络接头命令:ifconfig (2)查看所有网络接口命令:ifconfig -a (3)查看指定的网络接口(不论该网络接口是否处于激活状态)命令:ifconfig ...
 - vue.js 第九课
			
这次讲红色框框. 方法与事件处理器: 方法处理器: 内联语句处理器: 事件修饰符: 按键修饰符: 为什么在HTML中监听事件? 1.v-on绑定事件 带参数 2.事件对象$event: 有时也需要调用 ...