Java中子类和父类的实例化顺序
2016-06-28 13:12
387 查看
本文主要介绍Java里常用的子类和父类的变量实例化顺序及陷阱,并结合一个Android实例来探讨此问题。日后编程中应尽量避免此陷阱。
首先看下面一段代码:
定义一个虚类Server.java
然后定义一个子类SimpleServer.java
测试代码:
测试结果:
port = 0 DEFAULT_PORT = 900
在测试代码里,传了一个参数600,我们希望getPort得到也是600,但遗憾的是得到的是一个大大的0!!!出现这个问题是因对Java子类和父类实例化顺序存在
bc86
模糊,下面来看下其正确顺序:
1、new一个SimpleServer,SimpleServer的构造函数接收参数600;
2、初始化父类Server的静态变量,DEFAULT_PORT赋值为900;
3、为了实例化子类,首先实例化其父类Server。子类有参数的构造中默认包含了super方法,即调用父类的无参构造函数。因此就到了int port = getPort();这一句,调用子类的getPort方法。子类的getPort方法返回mPort,此时mPort还没有赋值,因此还是0.这就是得到大大的0的原因!!!
4、父类初始化完毕后,开始初始化子类的实例变量,mPort赋值100;
5、执行子类的构造函数,mPort赋值600;
6、子类实例化完毕,对象创建完了!
真相大白了,再做一个测试。将SimpleServer里的实例变量mPort搞成静态变量如下:
测试结果:
port = 100 DEFAULT_PORT = 900
其执行顺序是:
1.第一个步骤同上,SimpleServer接收构造参数600
2.初始化父类的静态代码块,当然包括静态变量,然后初始化子类的静态变量
3.初始化父类的非静态代码,包括非静态的变量等;
4.执行父类的构造函数;
5.初始化子类的非静态代码
6.执行子类的构造函数。
至此完毕,最终的结论就是构造函数越简单越好,不要在构造函数里做太多操作。回过头再来看杂家的前文:Android自定义UI陷阱:LayoutInflater.from().inflate()一定不能工作在父类或虚类里 如果把initView()放在父类里,则子类LAYOUT_ID在使用时还会是0.因此即便要用也要将此弄成static类型的。
本文参考《编写高质量代码:改善Java的151个建议》、链接1 、链接2
变量是静态绑定 ,方法是动态绑定。 这里面变量在编译期间实现了变量调用语句与变量定义赋值语句的绑定,绑定的自然是父类的,因为调用时类型是父类的,所以值是父类中定义的值
其实你可以这么理解 创建了一个子类对象时,在子类对象内存中,有两份这个变量,一份继承自父类,一份子类。
绝对不会产生父类对象,父类中的成员被继承到子类对象中,用指向子类对象的父类引用调用父类成员,只不过是从 子类对象内存空间中找到那个被继承来的父类成员,也就是说实质是用子类对象调用变量a,这样就可以解释成员必须通过对象调用的规定,只不过这时调用的是子类对象中的继承自父类的a(子类对象中有两个a,一个继承自父类,一个属于自己)
哎,话说的有些乱。 这个问题也困惑我很久,上网查询发现很多人是错误的,最后找到几篇好的文章才明白,可能很多java老手也都会犯“产生父类对象”这个错误,最近才搞明白。
你自己想想,如果产生父类对象,如果父类是抽象类,抽象类允许产生对象吗?所以这种说法不严谨
1父类的静态初始化域,
2子类的静态初始化域,
3父类的非静态初始化域,
4父类的构造函数,
5子类的非静态初始化域,
6子类的构造函数
首先看下面一段代码:
定义一个虚类Server.java
package org.yanzi.test1; public abstract class Server { private static final int DEFAULT_PORT = 900; public Server() { // TODO Auto-generated constructor stub int port = getPort(); System.out.println("port = " + port + " DEFAULT_PORT = " + DEFAULT_PORT); } protected abstract int getPort(); }
然后定义一个子类SimpleServer.java
package org.yanzi.test1; public class SimpleServer extends Server { private int mPort = 100; public SimpleServer(int port) { // TODO Auto-generated constructor stub this.mPort = port; } @Override protected int getPort() { // TODO Auto-generated method stub return mPort; } }
测试代码:
package org.yanzi.test1; public class Test1 { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Server s = new SimpleServer(600); } }
测试结果:
port = 0 DEFAULT_PORT = 900
在测试代码里,传了一个参数600,我们希望getPort得到也是600,但遗憾的是得到的是一个大大的0!!!出现这个问题是因对Java子类和父类实例化顺序存在
bc86
模糊,下面来看下其正确顺序:
1、new一个SimpleServer,SimpleServer的构造函数接收参数600;
2、初始化父类Server的静态变量,DEFAULT_PORT赋值为900;
3、为了实例化子类,首先实例化其父类Server。子类有参数的构造中默认包含了super方法,即调用父类的无参构造函数。因此就到了int port = getPort();这一句,调用子类的getPort方法。子类的getPort方法返回mPort,此时mPort还没有赋值,因此还是0.这就是得到大大的0的原因!!!
4、父类初始化完毕后,开始初始化子类的实例变量,mPort赋值100;
5、执行子类的构造函数,mPort赋值600;
6、子类实例化完毕,对象创建完了!
真相大白了,再做一个测试。将SimpleServer里的实例变量mPort搞成静态变量如下:
package org.yanzi.test1; public class SimpleServer extends Server { private static int mPort = 100; public SimpleServer(int port) { // TODO Auto-generated constructor stub this.mPort = port; } @Override protected int getPort() { // TODO Auto-generated method stub return mPort; } }
测试结果:
port = 100 DEFAULT_PORT = 900
其执行顺序是:
1.第一个步骤同上,SimpleServer接收构造参数600
2.初始化父类的静态代码块,当然包括静态变量,然后初始化子类的静态变量
3.初始化父类的非静态代码,包括非静态的变量等;
4.执行父类的构造函数;
5.初始化子类的非静态代码
6.执行子类的构造函数。
至此完毕,最终的结论就是构造函数越简单越好,不要在构造函数里做太多操作。回过头再来看杂家的前文:Android自定义UI陷阱:LayoutInflater.from().inflate()一定不能工作在父类或虚类里 如果把initView()放在父类里,则子类LAYOUT_ID在使用时还会是0.因此即便要用也要将此弄成static类型的。
本文参考《编写高质量代码:改善Java的151个建议》、链接1 、链接2
java 子类实例化时是否同时存在一个父类对象
不会产生父类对象,只是用了父类的构造函数而已,并不是用到构造函数就会产生对象,构造函数只是起对象初始化作用的,而不是起产生对象作用的,如果new A();即只有new语句才会产生父类A的对象。变量是静态绑定 ,方法是动态绑定。 这里面变量在编译期间实现了变量调用语句与变量定义赋值语句的绑定,绑定的自然是父类的,因为调用时类型是父类的,所以值是父类中定义的值
其实你可以这么理解 创建了一个子类对象时,在子类对象内存中,有两份这个变量,一份继承自父类,一份子类。
绝对不会产生父类对象,父类中的成员被继承到子类对象中,用指向子类对象的父类引用调用父类成员,只不过是从 子类对象内存空间中找到那个被继承来的父类成员,也就是说实质是用子类对象调用变量a,这样就可以解释成员必须通过对象调用的规定,只不过这时调用的是子类对象中的继承自父类的a(子类对象中有两个a,一个继承自父类,一个属于自己)
哎,话说的有些乱。 这个问题也困惑我很久,上网查询发现很多人是错误的,最后找到几篇好的文章才明白,可能很多java老手也都会犯“产生父类对象”这个错误,最近才搞明白。
你自己想想,如果产生父类对象,如果父类是抽象类,抽象类允许产生对象吗?所以这种说法不严谨
java实例化顺序?
子类和父类的执行顺序说明:1父类的静态初始化域,
2子类的静态初始化域,
3父类的非静态初始化域,
4父类的构造函数,
5子类的非静态初始化域,
6子类的构造函数
相关文章推荐
- Thinking in Java 第4章 初始化和清除 总结
- eclipse 安装svn插件
- 获得浏览器User-agent的方法
- Eclipse快捷键 10个最有用的快捷键
- spring编程式事务
- 使用Spring处理一个RESTful请求
- java中各种术语
- Eclipse--Web项目中 .classpath、mymetadata、project文件的功用
- spring depends-on
- Java多线程 之 ThreadPoolExecutor(九)
- 第五章 处理器拦截器详解——跟着开涛学SpringMVC
- spring bean parent属性详解
- Java NIO ServerSocketChannel
- mysql属性与java类型对照表
- Spring为某个属性注入值或为某个方法的返回值
- [改善Java代码]使用Throwable获得栈信息
- Spring jar 包作用
- java安全框架-Shiro学习笔记(六)-url匹配方式+shiro标签使用+session会话机制
- java多态-示例
- Java NIO SocketChannel