`
风子柒
  • 浏览: 56482 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

我不知道的事—多态和对象的故事

    博客分类:
  • java
阅读更多


       对于Java的学习者和使用者来说,对象永远是一个逃不过的劫,虽然我一直认为:学习Java等面向对象语言的人是不愁找不到对象的,因为万物皆对象嘛(但是万物总是令人遐想,此处省略一万字...)。不论你是初学者还是资深的程序员,我相信,关于对象,你总有很多很多要说的:从对象的创建到对象的使用,再到垃圾回收机制,对象的一生总是充满着神奇。

       今天要说的是一些边角料的东西,而且有点杂。我想解决的有以下两个个问题:
       1.构造器里的this关键字
       2.编译时类型和运行时类型

       当然如题,这是我不知道的事,可能在别人看来这个有点过于简单了。首先大家看看这一段代码,试着在你的大脑里运行一下,并给出一个结果。


class A{
		private String str = "a";
		public A(){
			System.out.println("constructor A");
			System.out.println("this in A is : " + this.getClass());
			System.out.println("this.str : " + this.str);
			System.out.println("------------------------------------");
		} 
		public void fun(){
			System.out.println("A.fun() > " + str);
		}
	}
	
	class B extends A{
		private String str = "b";
		public B(){
			System.out.println("constructor B");
			System.out.println("this in B is : " + this.getClass());
			System.out.println("this.str : " + this.str);
			System.out.println("-------------------------------------");
		} 
		public void fun(){
			System.out.println("B.fun() > " + str);
		} 
	}
	
	class C extends B{
		private String str = "c";
		public C(){
			System.out.println("constructor C");
			System.out.println("this in C is : " + this.getClass());
			System.out.println("this.str : " + this.str);
			System.out.println("------------------------------------");
		} 
		public void fun(){
			System.out.println("C.fun() > " + str);
		}
	}
	
	public class Tester {
		public static void main(String[] args) {
			new C();
		}
	} 


      
我不知道这段程序在你的大脑里的运行结果是怎么样的,但是在我的电脑里,打印结果是这样的:

       constructor A
       this in A is : class com.tuyage.control.C
       this.str : a
               -------------------------------------------
       constructor B
       this in B is : class com.tuyage.control.C
       this.str : b
       -------------------------------------------
       constructor C
       this in C is : class com.tuyage.control.C
       this.str : c
       -------------------------------------------
       大家发现了没有,同一个构造器里的this是不是有点乱,比如在A的构造器里,this.getClass()打印的结果是class C,但是调用this.str时,打印的结果却是a而不是C类里的c。

       我们先抛开这个问题不看,毕竟当局者迷。我们看看实例化的过程:首先调用new C()后会发生的事大家都知道了,就是从父类的构造器下溯。所以在创建一个C对象时,首先进入了A的构造器(我们先忽略掉Object吧,毕竟在清明扫墓的时候又有几人还在拜祭炎帝和黄帝),此时,诞生了一个对象,这个对象是什么呢?是C类型的对象!因为我们创建的是C对象,就好比一个新生的婴儿一样,不论怎么追溯他的起源,他也不会变成他的爷爷。这个对象这时有三个实例属性,我们有图为证:



       这是程序进入A构造器之后的信息。这里有三个str,你一看就知道,这三个str分别是A的str,B的str以及C的str。会不会有人问:既然先进入了A的构造器,又怎么知道有三个str呢?

       这时候我打算使用我灰常喜欢的修辞手法:比喻或者拟人。我们假设有一个婴儿刚呱呱坠地,他的家人打算帮他起一个洋气一点的名字,这样就算以后是程序员也能用名字吸引住异性,但是他们家族有一个传统,即要根据上溯三代祖辈的名字选择后代的名字(貌似国外有些国家有这个传统),他们找来了一个仙风道骨的老和尚,这个自称老衲的人翻开了这个家族的族谱,从这个婴儿开始往上看(假设他们的族谱更新得足够快,只剩下名字没有填了),他找到了他的爷爷那一辈,发现他爷爷叫做王六,然后就去看他老爹的,发现叫做王七,于是乎,这个幸运的小孩就拥有了一个霸气内外都露的名字——王八。

       这样,我想解释三个str也不是很难了,而且后面发生的事情也都在情理之中了(或者你可以将以上的A、B、C替换成GrandFather、Father和Son,str替换为name,a、b、c替换为王六、王七和王八)。我们从C类往上看,看到B类有一个str,此时我们不关心它的值,只知道有这个属性就好了,同样的事情也发生在A类中,然后我们再从A类开始,这时,我们发现了A中的str的值为a(如下图所示),这里的this也是C类型的:



       那么我们又回到了原来的问题:this.str为什么是a而不是null?我们似乎忘了还有继承关系。我们可不可以这样假设:此时c有这个str属性但是还不知道它的值,只能拼爹的爹,C对象继承了A的str属性,暂时的值为a。这也就可以解释为什么this.str的值是a了。就好比那个孩子现在还没有名字但是别人问起来了,此时只知道他爷爷的名字,于是就说自己叫王六咯。(这也就诞生了另一个问题:如果是继承,那么为什么会存在三个而不是一个str呢?别急,先这样理解着,等会告诉你!)

       接着就进入了B类的构造器。B的str属性会更新为b,那么此时打印出来的str就是b了。同理可以解释为什么C中打印的是c了。






       那么,我们此时在A、B、C的构造器里分别增加this.fun();这样的代码会发生什么呢(代码大家自己修改吧)?新的结果出来了:
       constructor A
       this in A is : class com.tuyage.control.C
       this.str : a
       C.fun() > null
-      ------------------------------------------
       constructor B
       this in B is : class com.tuyage.control.C
       this.str : b
       C.fun() > null
       -------------------------------------------
       constructor C
       this in C is : class com.tuyage.control.C
       this.str : c
       C.fun() > c
       -------------------------------------------
       都是C.fun()!这个说明了方法调用时是和构造器不一样的(这个大家都知道),直接调用了子类的(如果有的话),也就是说都调用了C里的方法(大家可以在C的fun里面加一个输出语句)。但是str的值就发生了变化,前两个str都是null,这也就证明了前面的假设不正确,亦即不能假设此时C对象的str就是A或B的str。当然前面两个打印null是在情理之中的,因为调用的fun()方法是在C里面的,而此时C的str(即图中的第三个str)还是null的,所以打印时肯定就是null了。

       很多人看到这里肯定有一种用砖头拍死我的冲动。绕了这么一圈居然没给出正确答案!这是因为我觉得正确答案绝不是我们想要的全部,我只是把自己的分析思路记录了下来,和大家分享一下。毕竟我们不能总是处在看到题目就要答案的那种小学生阶段了。如果你稍微平静了下来,就接着看吧!

       那么怎么理解this.str的输出呢?这里就得讲讲编译时类型(编译时是什么类型的)和运行时类型了(要了解运行时类型的请点击:http://februus.iteye.com/blog/1438672)。我们假设有这样一行代码:A a = new B();B是A的子类。那么此时就会出现一个a变量,这个a的编译时类型就是A,运行时类型就换成了B。很熟悉吧,这是多态啊!编译时类型和运行时类型不同就能体现出多态。方法具有多态,而属性是没有多态性的。系统就会选择编译时类的定义作为当前属性的定义,在这个例子里就是this在编译时表示的是A,因此str也表现出A里面所定义的:a。

       为什么会出现混乱呢?很主要的一个原因就是this这个关键字太具有迷惑性了,很容易让我们把this全部当做一个东西看待。如果一个构造器里出现了this这个关键字,那么它代表着正在初始化的Java对象。这个例子的A构造器里,你可以看做A a = new C();那么,this就代表了C类型的对象了,正如我们看到的那样。而在B的构造器里,可以看做B b = new C();this还是C类型的对象。


       那么,至此我相信你应该可以很清晰地理解最初的代码了,也能明白输出结果也在情理之中。如果你还是不明白我建议你敲敲代码,然后好好Debug一下,然后联系一下多态、构造器以及初始化等知识,我相信你定能发现其中的奥妙。
  • 大小: 9.6 KB
  • 大小: 8.4 KB
  • 大小: 8.5 KB
  • 大小: 9.8 KB
  • 大小: 39 KB
分享到:
评论
5 楼 cs小伙 2012-04-06  
学到了很多东西啊.
4 楼 奋斗的西瓜 2012-04-05  
很不错 
3 楼 逸情公子 2012-04-04  
以下是本人就该问题的一些理解,希望可以和博主一起讨论:http://weixiaolu.iteye.com/blog/1473621
2 楼 kuailehuahua 2012-04-04  
讲的很详细,受用!!
1 楼 郭广川 2012-04-03  
顶个

相关推荐

    java多态理解

    理解多态首先要知道“向上转型”。我定义了一个子类Cat,它继承了Animal类,那么后者就是前者是父类。我可以通过Cat c = new Cat();实例化一个Cat的对象,这个不难理解。但当我这样定义时:Animal a = new Cat();这...

    .archivetemp04 - 继承和多态 作业.doc

    1. **灵活性**:多态提高了程序的灵活性,可以在不知道对象的具体类型的情况下调用其方法。 2. **可扩展性**:多态支持动态绑定,可以在不修改现有代码的情况下添加新类。 3. **简化代码**:通过多态,可以使用统一...

    第十二天 06多态【千锋Python人工智能学院】1

    面向对象编程中的多态性是Python等面向对象语言的一个核心特性,它允许不同的子类对象在调用相同父类方法时产生不同的执行效果。在上述的代码实例中,多态的概念得到了很好的展示。 首先,封装是面向对象的三大特性...

    chapter3 Java语言的面向对象特征2.ppt

    描述:不知道是不是真的,反正看过之后很容易及格 标签:Java 语言 面向对象特征 2 知识点 1:对象的定义 在 Java 语言中,对象的定义是通过使用关键字 `new` 和类名来创建的。例如:`MyClass obj1; obj1 = new ...

    设计模式:可复用面向对象软件的基础--详细书签版

    如果要知道怎样恰当定义和描述设计模式,我们应该可以从他们那儿获得启发”--steve billow, journal of object-oriented programming    “总的来讲,这本书表达了一种极有价值的东西。对软件设计领域有着独特的贡献...

    C讲义继承多态PPT课件.pptx

    例如,订单包含商品,但商品并不知道订单的存在。 - **双向关联**:两个类互相了解对方的存在,并且可以相互访问。例如,订单属于某个客户,同时客户也可以访问到与之相关的订单信息。 ### 四、示例练习 #### 练习...

    CppStudy:我不知道的C ++故事;

    "我不知道的C++故事"可能指的是深入探讨C++的一些不为人知或者容易被忽视的特性、概念和设计选择。在这个主题下,我们可以详细讨论C++的三个主要子语言:C语言基础、面向对象的C++以及模板和STL(标准模板库)。 ...

    超级有影响力霸气的Java面试题大全文档

     forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器,浏览器根本不知道服务器发送的内容是从哪儿来的,所以它的地址栏中还是原来的地址。...

    Java程序员应当知道的10个面向对象设计原则

    我经常看到不同经验水平的java程序员,他们有的不知道这些OOPS 和SOLID设计原则,有的只是不知道一个特定的设计原则会带来怎样的益处,甚至不知道在编码中如何使用这些设计原则。  (设计原则)底线是永远追求高内聚...

    设计模式 创建型模式 Abstract Factory模式(抽象工厂)

    Abstract Factory模式 ...我不相信DELL的键盘,那就用HP的话,可以在HPFactory里 生产出HP的键盘和鼠标,然后自行组装。 详细见博客 http://blog.csdn.net/xiaoting451292510/article/details/8290814

    ooDay05_all.zip

    在不知道具体包含的文件类型(如.txt, .pdf, .java, .html等)或文件内容的情况下,我无法生成相关的IT知识点。 通常,"oo" 可能与面向对象(Object-Oriented)编程有关,这可能意味着讨论的是Java、C++、Python等...

    static方法和非staitic方法的调用.zip_718static_arrangertr

    `static`方法不能访问实例变量,因为它们在没有对象的情况下被调用,无法知道具体对象的状态。相反,实例方法可以访问实例变量和`static`变量。`static`变量是所有类实例共享的,而实例变量是每个对象独有的。 在多...

    摩托罗拉C++面试题

    不过,我不建议滥用设计模式,以为它有可能使得简单问题复杂化. 7.介绍一下你对设计模式的理解。(这个过程中有很多很细节的问题随机问的) 设计模式概念是由建筑设计师Christopher Alexander提出:"每一个模式描述...

    DGM-1610:我不知道我在做什么,但我真的希望这能奏效

    标题"DGM-1610:我不知道我在做什么,但我真的希望这能奏效"虽然带有一种不确定感,但这也是许多开发者在尝试新事物时的共同心声,体现了学习和探索的过程。 DGM-1610可能是项目代号或者问题编号,它可能代表了一个...

    黑马面试笔记

    在面试过程中,需要保持良好的互动,回答问题时要明确、自信,遇到不明白的问题可以说“不知道,但是我可以去学习”等。可以自己和宿舍的室友模仿几遍,或者自己对着摄像头或者镜子模拟几次,保持好和面试官的互动。...

    厕所管理艾瑞网群翁群翁群无

    2. **面向对象编程**:Java是面向对象的语言,支持类、接口、继承、封装和多态等概念。理解这些概念对于编写复杂的程序至关重要。 3. **类与对象**:在Java中,一切皆为对象,类是创建对象的蓝图。你需要知道如何...

    java多态的笔试题-converter-app:一个用Java编写的简单测量转换器程序

    对于仍然不知道包在 Java 中的重要性的其他人,它用于管理项目的命名空间。 想象一下,如果没有包,开发人员最终会争论,比如说,谁应该得到MyClass这个名字。 注意:你必须在你的IDE中创建一个新项目,这样这里的...

    AIC的Java课程1-6章

     培养和建立面向对象编程的思维方式,可以运用封装、继承和多态三大基本特性编写面向对象的程序。  理解和应用Java异常,常用类,IO,集合和多线程等开发技术。  课时安排  总学时:52学时 ...

Global site tag (gtag.js) - Google Analytics