• Java拾遗(一):浅析Java子类和父类的实例化顺序 及 陷阱


    本文主要介绍Java里经常使用的子类和父类的变量实例化顺序及陷阱,并结合一个Android实例来探讨此问题。日后编程中应尽量避免此陷阱。

    首先看以下一段代码:

    定义一个虚类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子类和父类实例化顺序存在模糊,以下来看下其正确顺序:

    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

  • 相关阅读:
    数据库备份 DBS(Database Backup),知识点
    关系型数据库 RDS(Relational Database Service),知识点
    对象存储服务 OSS(Object Storage Service),知识点(待补充上仓库代码)
    Java 为什么需要包装类,如何使用包装类?
    for each 语句
    缓存中,2个注解:@cacheable 与 @cacheput 的区别
    微信小程序,相关代码
    微信小程序中的事件
    通俗易懂:索引、单列索引、复合索引、主键、唯一索引、聚簇索引、非聚簇索引、唯一聚簇索引 的区别与联系
    MySQL 的各种 join
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4045367.html
Copyright © 2020-2023  润新知