一道关于this和super的面试题

lynn_chen 2011-06-14 12:29:17
昨天下午去面试的时候遇到一道题,大家看看。
class SuperClass {
SuperClass() throws Exception {
System.out.println(this.getClass().getName());
}
void show() {
System.out.println(super.getClass().getName());
show2();
}
void show2() {}
}




public class SubClass extends SuperClass {
SubClass() throws Exception {}
void show() {
super.show();
System.out.println(super.getClass().getName());
}

void show2() {
System.out.println(super.getClass().getSuperclass().getName());
}

public static void main(String args[]) throws Exception {
SuperClass s = new SubClass();
s.show();
}
}



先自己做一下,然后再debug看看。

我写的答案是:
SuperClass
Object
SuperClass

我理解的运行流程是:

1. SuperClass s = new SubClass();
首先主线程进入main,此时加载两个.class文件进方法区,当new SubClass()时,在堆区实例化这两个类,依次调用SuperClass()和SubClass()(还有Object)。

(SuperClass)
SuperClass() throws Exception {
System.out.println(this.getClass().getName());
}

这个this应该是父类的实例啊?(没搞懂!)
打印结果是:SuperClass

2. s.show();
应用变量s引用的是SubClass的实例,s.show调用的是子类的覆盖方法show(),

(SubClass)
void show() {
super.show();
System.out.println(super.getClass().getName());
}

进入方法后,压入方法栈,运行super.show()父类实例的show()方法:

(SuperClass)
void show() {
System.out.println(super.getClass().getName());
show2();
}

这个super又应该是SuperClass的父类实例(Object的实例)。
打印结果是:Object

然后show2(),这个应该是父类的实例的方法,是个空方法,所以什么都不打印。

最后出栈,运行(SubClass.show()的第2个语句)
System.out.println(super.getClass().getName());
打印结果是:SuperClass

实际的运行结果是:

SubClass
SubClass
SuperClass
SubClass

大家来讨论下,这是什么原因?
...全文
305 30 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
30 条回复
切换为时间正序
请发表友善的回复…
发表回复
lynn_chen 2011-06-15
  • 打赏
  • 举报
回复
算了,结贴!
谢谢各位!
oO临时工Oo 2011-06-15
  • 打赏
  • 举报
回复
肯定引用的是父类的资源,但这个分两个情况:
假如有这样一个实例:
	class Father extends Object{
//......
}
class Son extends Father{
//......
}


当在Son中通过Super()引用父亲类的构造方法时,Super表示是就是父类Father

当在Son中通过super引用 Father的资源(标记为资源F),资源F分且只分为两种:
一种是Father自己的;
一种是Father他父类Object的,当Father的父类不是Object时,依次类推.

所以可以这样理解为Son通过Super引用其父类(包括父类的父类....)的资源



rockyou666 2011-06-15
  • 打赏
  • 举报
回复
我同意7搂的说法啊!
lynn_chen 2011-06-15
  • 打赏
  • 举报
回复
[Quote=引用 23 楼 trocp 的回复:]
实例有N个,每创建一个类就有一个Objet的实例。

但是这个实例与运行时类是有区别的。Object的getClass()方法返回的是运行时类,不是实例。JDK源代码中Object.getClass()就是这么解释的。

[/Quote]
你说那个帖子我看了下,意思都差不多。

Object的getClass()方法返回的是运行时类”这个细节我还没注意到,
也就是对应类的class对象,父类在产生实例时会伴随着产生他对应的class对象。

所以还是回到老问题:super到底引用的是什么?
qybao 2011-06-15
  • 打赏
  • 举报
回复
其实很简单
LZ只要能理解以下的代码,就能理解问题的结果了

SuperClass sc = new SubClass();
System.out.println(sc.getClass());
SuperClass sc2 = (SupperClass)sc; //强行转换为父类,看看有没有效果?
System.out.println(sc2.getClass());
oO临时工Oo 2011-06-15
  • 打赏
  • 举报
回复
[Quote=引用 20 楼 snakewarhead 的回复:]
引用 16 楼 alexandertech 的回复:

15楼说得很生动阿。

另外我看楼主可能有一点有误解,

创建子类的实例过程中,不管它层层调用了多少个父类的构造方法,实例永远只有一个。

这个实例在逻辑上同时是父类和子类的实例,但是如果拿个显微镜来看,归根到底是子类的实例。

有点象量子力学里面粒子的不确定性,但当观察者注入,波函数坍塌一样。


实例怎么会只有一……
[/Quote]

实例有N个,每创建一个类就有一个Objet的实例。

但是这个实例与运行时类是有区别的。Object的getClass()方法返回的是运行时类,不是实例。JDK源代码中Object.getClass()就是这么解释的。

lynn_chen 2011-06-15
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 goldenfish1919 的回复:]

引用 13 楼 snakewarhead 的回复:
我先承认我的基础很差,
super引用的是父类的实例,this引用的是当前自身的实例。
在同一类中,super.getClass()怎么会等价于this.getClass().
而且跟是不是final方法没有关系。


你的SubClass类里面定义getClass()了没?没有吧!
那这个getClass()是哪里的呢?当然是……
[/Quote]
你没懂我意思,我是说:
super引用的是父类的实例,this引用的是当前自身的实例。
他们getClass(),当然是返回各自的实例!
lynn_chen 2011-06-15
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 alexandertech 的回复:]

super返回一个逻辑上被视为父类的实例,目的是让你能通过它……
[/Quote]

super实际上引用的是什么?
我的基础确实还很不扎实,麻烦在说一下!
lynn_chen 2011-06-15
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 alexandertech 的回复:]

15楼说得很生动阿。

另外我看楼主可能有一点有误解,

创建子类的实例过程中,不管它层层调用了多少个父类的构造方法,实例永远只有一个。

这个实例在逻辑上同时是父类和子类的实例,但是如果拿个显微镜来看,归根到底是子类的实例。

有点象量子力学里面粒子的不确定性,但当观察者注入,波函数坍塌一样。
[/Quote]

实例怎么会只有一个?
think in java(P129) 上说是:当创建一个导出类时,该对象包含了一个基类的子对象。
在创建子类实例时,会依次创建父类的实例,那不然父类public的成员变量子类是如何访问的。

飞跃颠峰 2011-06-15
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 snakewarhead 的回复:]

引用 7 楼 alexandertech 的回复:

对象s是一个SubClass的实例,
getClass()永远是返回实例的实际Class,所以s.super.getClass仍然返回SubClass


s.super.getClass 是什么意思?
[/Quote]

super返回一个逻辑上被视为父类的实例,目的是让你能通过它访问父类中被重写的方法或被隐藏的字段。
至于getClass方法,从未被重写,所以你用不用super返回的结果都是一样的。
lynn_chen 2011-06-15
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 qybao 的回复:]

其实很简单
LZ只要能理解以下的代码,就能理解问题的结果了

SuperClass sc2 = (SupperClass)sc; //强行转换为父类,看看有没有效果?

[/Quote]
你的意思是说:
super是子类向上转型的父类代表的引用变量吗?

子类show()方法

void show() {
super.show();
System.out.println(super.getClass().getName());
}

这个super引用的还是子类的实例,那这个方法会进入死循环。
lynn_chen 2011-06-15
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 alexandertech 的回复:]

对象s是一个SubClass的实例,
getClass()永远是返回实例的实际Class,所以s.super.getClass仍然返回SubClass

[/Quote]
s.super.getClass 是什么意思?
飞跃颠峰 2011-06-15
  • 打赏
  • 举报
回复
15楼说得很生动阿。

另外我看楼主可能有一点有误解,

创建子类的实例过程中,不管它层层调用了多少个父类的构造方法,实例永远只有一个。

这个实例在逻辑上同时是父类和子类的实例,但是如果拿个显微镜来看,归根到底是子类的实例。

有点象量子力学里面粒子的不确定性,但当观察者注入,波函数坍塌一样。
若鱼1919 2011-06-15
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 snakewarhead 的回复:]
我先承认我的基础很差,
super引用的是父类的实例,this引用的是当前自身的实例。
在同一类中,super.getClass()怎么会等价于this.getClass().
而且跟是不是final方法没有关系。
[/Quote]

你的SubClass类里面定义getClass()了没?没有吧!
那这个getClass()是哪里的呢?当然是继承来的!
从那里继承来的呢?从老祖宗Object继承来的。

老祖宗那里就明确说了:孩子们,你们听着,getClass()这个方法你们都可以调用,但是谁也不能给我覆盖掉。你们也没有必要覆盖,到底是哪个儿子还是孙子调用的,运行时,老祖宗我心里明镜似的,肯定会返回给你实际的类型的。
lynn_chen 2011-06-15
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 hulujava111 的回复:]

1:SubClass //new SubClass()自动调用父类的无参数构造函数,this.getClass().getName(),这里的this还是指向new出来的那个子类实例

[/Quote]

为什么这个this引用的是SubClass的实例?
new SubClass()自动调用父类的无参数构造函数来创建父类的实例(think in java 上说是:当创建一个导出类时,该对象包含了一个基类的子对象),


父类的构造方法:
SuperClass() throws Exception {
System.out.println(this.getClass().getName());
}

这个this在父类构造器中,父类会根据方法区的class字节段在堆区生成字节块用以代表父类的实例,然后初始化成员变量后调用构造器,而此时的this.getClass()怎么不是the runtime class of SuperClass
lynn_chen 2011-06-15
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 goldenfish1919 的回复:]

getClass()是定义在Object中的final方法,因此,子类是无法覆盖getClass()方法的。
因此,super.getClass()等价于this.getClass().
jdk文档里面说:
The Class object that represents the runtime class of this object.
返回的是运行时的实际类型。
[/Quote]

我先承认我的基础很差,
super引用的是父类的实例,this引用的是当前自身的实例。
在同一类中,super.getClass()怎么会等价于this.getClass().
而且跟是不是final方法没有关系。
lynn_chen 2011-06-15
  • 打赏
  • 举报
回复
[Quote=引用 28 楼 alexandertech 的回复:]

说下我个人的理解,实例化某个对象的过程中,实例到底是产生了一个还是N个这主要是个语言表达的问题。

Java文档中对实例没有很严格的定义,相关的主要有实例变量和一个运算符instanceof。(如有遗漏,欢迎补充)

从实例变量的角度来说,每个对象实例化后拥有自身与父亲类的所有实例变量,但只有一份拷贝。如果B继承了A类,A类定义了变量x, b = new B(); b.x和b.super……
[/Quote]

谢谢 alexandertech!
lynn_chen 2011-06-15
  • 打赏
  • 举报
回复
[Quote=引用 28 楼 alexandertech 的回复:]

从实例变量的角度来说,每个对象实例化后拥有自身与父亲类的所有实例变量,但只有一份拷贝.
[/Quote]

恩,这句话没说错!

我查阅了一下JDK Documentation,上面对this和super也没作详细的解释。
只是说super是关键字,及他的3个作用:
use the super keyword to invoke a superclass's constructor.
you can invoke the overridden method through the use of the keyword super.
You can also use super to refer to a hidden field (although hiding fields is discouraged).
飞跃颠峰 2011-06-15
  • 打赏
  • 举报
回复
说下我个人的理解,实例化某个对象的过程中,实例到底是产生了一个还是N个这主要是个语言表达的问题。

Java文档中对实例没有很严格的定义,相关的主要有实例变量和一个运算符instanceof。(如有遗漏,欢迎补充)

从实例变量的角度来说,每个对象实例化后拥有自身与父亲类的所有实例变量,但只有一份拷贝。如果B继承了A类,A类定义了变量x, b = new B(); b.x和b.super.x指向内存中同一个数据拷贝。 从这个角度来说,我觉得把它理解为只有一个实例更自然。

从instanceof来说,如果B继承了A类, b = new B(); b instanceof B 和 b instanceof A 都返回true,就是说b既是B的实例,也是A的实例。这样同样把它理解为只有一个实例更自然。否则就应当是(b instanceof A) == false && (b.super.instanceof A) == true了。

我觉得这样就比较好理解b.super.getClass()了,可以理解成b和b.super指向同一个实例,因为getClass()不曾被重写,指向同一个拷贝,所以返回结果相同,即所谓运行时类。对于被重写过的方法,因为b.super指向一个逻辑上的A实例,所以通过它能访问被重写前的方法。



lh_fengyuzhe 2011-06-15
  • 打赏
  • 举报
回复
学习了
加载更多回复(8)
《java面试800题(包括java,数据库,前台等,绝对全面)》 Q0027 哪些SQL语句在执行时是自动提交的? 数据定义语言DDL是自动提交的。 Q0028 索引对数据库的影响? 提高查询速度 Q0029 主外键有什么关系? 外键是从某个表的一个字段指向另外一个表的主健字段,两个字段的类型和精度应该一致,外键的值必须在主键中存在 Q0030 在数据库中什么代表一条记录? 主健 Q0031 如何编写效率高的SQL语句? "1.根据查询条件建立合适的index 2.因为SQL是从右向左解析,多表查询时,记录数少的表放在右边 3.多个条件时,收敛快的条件放在右边。 4.避免使用复杂的集合函数,象not in等。 5.避免在条件中对字段进行函数操作 6.尽量避免使用select *,应该写出需要查询的字段 7.在java中尽量使用preparestatement执行sql,从而共享执行计划" Q0032 Oracle的集合操作函数,如sum(),avg(),max(),min(),与select,where,grouby,having的先后顺序,使用方法 Oracle集合查询基本知识,只有进行分组的列,才可以取在集合查询SQL语句中取字段,先Group By,再Having作为集合查询的条件 Q0033 在Oracle数据库中,给定一个表,其中一列有索引,现在用这个列作为查询条件,因为用到了索引,速度一定会快吗? 答案是否定的,比如在这个列中使用‘%sdfd%’来进行模糊查询 Q0034 给定了一些创建数据库试图的SQL语句问什么条件下才可以对试图执行修改,增加,删除操作 特别强调了WITH CHECK OPTION这个约束的含义,使用,产生的不同结果。参考Oracle 视图的基本知识,单个表上的视图,多个表的联合试图,更新视图与表之间的关系.http://www.gzu521.com/it/oracle/zonghe/200904/20748_2.htm Q0035 是不是表或者其他对象不存在,就一定不能在Oracle中创建视图? 否,可通过FORCE选项执行强制生成视图,好处是在表不存在的时候,先创建视图 Q0036 如何创建oracle函数索引 "SQL>create index non_fbi on sale_contacts (surname); SQL>analyze index non_fbi compute statistics; SQL>:analyze table sale_contacts compute statistics; SQL>SELECT count(*) FROM sale_contacts WHERE UPPER(surname) = 'ELLISON'; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1 Bytes=17) 1 0 SORT (AGGREGATE) 2 1 TABLE ACCESS (FULL) OF 'SALES_CONTACTS' (Cost=3 Card=16 Bytes=272) " Q0037 "ORACLE锁的管理 " "ORACLE里锁有以下几种模式: 0:none 1:null 空 2:Row-S 行共享(RS):共享表锁 3:Row-X 行专用(RX):用于行的修改 4:Share 共享锁(S):阻止其他DML操作 5:S/Row-X 共享行专用(SRX):阻止其他事务操作 6:exclusive 专用(X):独立访问使用 数字越大锁级别越高, 影响的操作越多。" Q0038 创建XML文件的格式? " …" Q0039 java接口与抽象类的区别 "1.接口可以多重继承 ,抽象类不可以 2.接口定义方法,不给实现;而抽象类可以实现部分方法 3.接口中基本数据类型的数据成员,都默认为static和final,抽象类则不是 如果事先知道某种东西会成为基础类, 那么第一个选择就是把它变成一个接口。 只有在必须使用方法定义或者成员变量的时候,才应考虑采用抽象类。" Q0040 Java关键字 "51个:abstract, boolean, break, byte, case, catch, char, class, const, continue, default, do, double, else, extends, final, finally, float, for, goto, if, implements, import, instanceof, int, interface, long, native, new, package, private, protected, public, return, short, static, strictfp, super, switch, synchronized, this, throw, throws, transient, try, void, volatile, while. " Q0041 Java保留字 11个:byValue, cast, false, future, generic, inner, operator, outer, rest, true, var. Q0042 Java的值传递的规则? Java基本类型的都是值传递,对象使用的都是引用传递 Q0043 java相关概念 "static:静态,无需实例化,可直接引用,全局只有一份copy,修饰变量和方法 final:最终的,不可继承、不可修改,修饰变量、方法、类 volatile:volatile变量表示保证它必须是与主内存保持一致,它实际是""变量的同步"", 也就是说对于volatile变量的操作是原子型的,如用在long 或 double变量前,一般用于多线程编程。 abstract:抽象,必须重载,修饰类和方法 native:把java代码和其他语言的代码集成起来 synchronized:控制多个并发线程对共享数据的访问 throwsException:异常处理" Q0044 this&super的异同 "this :引用当前对象 super:引用当前对象的父类 使用情况: (1) super.variable //用来访问父类被隐藏的成员变量 (2) super.Method([paramlist]) //用来调用父类中被重载的方法 (3) super.([paramlist]) //调用父类中的构造函数 在类方法中(static),不能使用this或super修饰符 " Q0045 Java中是怎样捕获异常的? "try { //statement01 } catch(Exception e) { //statement02 } finally { //statement03 }" Q0046 一个文件中是否可以有多个public类? 不可以 Q0047 子类是否可以访问父类的私有成员? 不可以 Q0048 NULL是否是Java的关键字? 不是。null,false,true是保留字 Q0049 一个有序数组和一个无序数组,从无序数组中取出每条记录与有序数组比较,如果符合条件,把无序数组中的值加入到有序数组中,问这是什么排序? 插入排序法 Q0050 程序与进程的区别? 程序是为了完成某种任务而设计的软件,比如OpenOffice是程序。什么是进程呢?进程就是 运行中的程序。 一个运行着的程序,可能有多个进程。 Q0051 设计模式主要几种 "创建模式 factory工厂模式、prototype原始模型模式、singleton单例模式、builder建造模式 结构模式 facade门面模式、proxy代理模式、adapter适配器(变压器)模式、composite合成模式、decorator装饰模式、bridge桥梁模式、flyweight享元模式 行为模式 template模板方法模式、memento备忘录模式、observer观察者模式、command命令模式、state状态模式、strategy策略模式、mediator调停者模式、interpreter解释器模式、visitor访问者模式、chain of responsibility责任链模式" Q0052 构造函数的相关知识? "构造函数(constructor )在对象创建时初始化。 构造函数是和类同名的函数,没有返回类型,构造函数不能在普通的程序里面调用,只有当这个类被应用new实例化的时候才会被运行。构造函数没有返回类型,实际上,构造函数返回的就是这个class本身。 类初始化时构造函数调用顺序: (1)初始化对象的存储空间为零或null值; (2)调用父类构造函数; (3)按顺序分别调用类成员变量和实例成员变量的初始化表达式; (4)调用本身构造函数。" Q0053 "Public class Servlet extends HttpServlet{ int i; doget(){ i++; out.print(i); } } 每次访问时i是否变化?" 会 Q0054 类的加载过程? "类的初始化过程 当创建一个对象时,对象的各个变量根据其类型被设置为相应的默认初始值,然后调用构造方法,而每次调用构造方法都是要执行三个阶段: 1.调用超类的构造方法; 2.由初始化语句对给变量进行初始化; 3.执行构造方法的体。" Q0055 系统运行时的最小单位是什么? 线程 Q0056 Java的编码规范? Q0057 Java的命名规范? Q0058 一个Java抽象类声明了一个方法并会抛出一个异常,问继承这个抽象类的子类,实现了这个方法,这个方法声明是不是一定要抛出一模一样的异常,可不可以不抛,或者抛出的异常比抽象类的异常范围大,或者小? 可以不抛,或者比抽象类的小,但绝对不能抛出的异常比抽象类的大 Q0059 找出weblogic-ejb-jar.xml文件中的错误。 "正确的文档 Catalog WebLogic_CMP_RDBMS 7.0 META-INF/weblogic-cmp-rdbms-jar.xml com.ejb.CatalogHome " Q0060 JDBC批量更新的作用和用法 "提高执行效率。减少执行时间。 Statement sm = cn.createStatement(); sm.addBatch(sql1); sm.addBatch(sql2); ... sm.executeBatch() 或者 PreparedStatement ps = cn.preparedStatement(sql); { ps.setXXX(1,xxx); ... ps.addBatch(); } ps.executeBatch();" Q0061 事务的特性是什么? "事务有四种特性:ACID Atomicity(原子性) 事务中的操作或者全部完成,或者全部不完成。 Consistency(一致性) 事务执行的结果是从一个一致性状态转移到另一个一致性状态。 Isolation(隔离性) 一个事务的执行不能被其它事务干扰,即并发事务间内部数据是隔离的。 Durability(持久性) 事务开始执行后,它对系统中数据的改变应该是恒定的,不应受其它操作或故障的影响。 " Q0062 事务有几种属性?分别是什么? "事务的属性有6种 1.Required:当处于事务范围内的客户端应用调用组件商务方法时,组件商务方法执行在原有的客户端事务范围内; 2.RequiredNew:当处于事务范围内的客户端应用调用组件商务方法时,EJB容器启动一个新的事务过程,组件商务方法执行在新事务过程范围内; 3.Mandatory:如果调用EJB组件商务方法的客户端应用不处于事务范围内,则EJB容器抛出TransactionRequiredException异常,强制客户端启动事务过程; 4.NotSupported:EJB组件的商务方法不需要运行在事务过程中。如果调用EJB组件方法的客户端应用处于事务过程中,则调用组件商务方法时原有事务过程挂起,直至组件方法运行结束; 5.Supports:组件方法必须处于事务范围内。如果调用组件商务方法的客户端不处于事务过程中,则EJB容器启动新的事务过程;6.Never:组件方法不需要运行在事务过程中。如果调用组件商务方法的客户端应用处于事务范围内,则EJB容器抛出RemoteException异常。 "

62,635

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧