我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何通过实例学习Java对象的构造过程

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何通过实例学习Java对象的构造过程

这篇文章给大家介绍如何通过实例学习Java对象的构造过程,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

下面提供一个项目中的错误实例,提供对其观察和分析,揭示出Java语言实例化一个对象具体过程,最后总结出设计Java类的一个重要规则。通过阅读本文,可以使Java程序员理解Java对象的构造过程,从而设计出更加健壮的代码。

作者曾经在一个项目里面向项目组成员提供了一个抽象的对话框基类,使用者只需在子类中实现基类的一个抽象方法来画出显示数据的界面,就可使项目内的对话框具有相同的风格。具体的代码实现片断如下(为了简洁起见,省略了其他无关的代码):

public abstract class BaseDlg extends JDialog {
public BaseDlg(Frame frame, String title) {
super(frame, title, true);
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add(createHeadPanel(), BorderLayout.NORTH);
this.getContentPane().add(createClientPanel(), BorderLayout.CENTER);
this.getContentPane().add(createButtonPanel(), BorderLayout.SOUTH);
}

private JPanel createHeadPanel() {
... // 创建对话框头部
}

// 创建对话框客户区域,交给子类实现
protected abstract JPanel createClientPanel();

private JPanel createButtonPanel {
... // 创建按钮区域
}
}
这个类在有的代码中工作得很好,但一个同事在使用时,程序却掷出了一个NullPointerException违例!经过比较,找出了工作正常和不正常的程序的细微差别,代码片断分别如下:

一、工作正常的代码:

public class ChildDlg1 extends BaseDlg {
JTextField jTextFieldName;
public ChildDlg1() {
super(null, "Title");
}
public JPanel createClientPanel() {
jTextFieldName = new JTextField();
JPanel panel = new JPanel(new FlowLayout());
panel.add(jTextFieldName);
... // 其它代码
return panel;
}
...
}
ChildDlg1 dlg = new ChildDlg1(frame, "Title"); // 外部的调用
二、工作不正常的代码:

public class ChildDlg2 extends BaseDlg {
JTextField jTextFieldName = new JTextField();
public ChildDlg2() {
super(null, "Title");
}
public JPanel createClientPanel() {
JPanel panel = new JPanel(new FlowLayout());
panel.add(jTextFieldName);
... // 其它代码
return panel;
}
...
}
ChildDlg2 dlg = new ChildDlg2(); // 外部的调用
你看出来两段代码之间的差别了吗?对了,两者的差别仅仅在于类变量jTextFieldName的初始化时间。经过跟踪,发现在执行panel.add(jTextFieldName)语句之时,jTextFieldName确实是空值。

我们知道,Java允许在定义类变量的同时给变量赋初始值。系统运行过程中需要创建一个对象的时候,首先会为对象分配内存空间,然后在“先于调用任何方法之前”根据变量在类内的定义顺序来初始化变量,接着再调用类的构造方法。那么,在本例中,为什么在变量定义时便初始化的代码反而会出现空指针违例呢?

对象的创建过程和初始化

实际上,前面提到的“变量初始化发生在调用任何方法包括构造方法之前”这句话是不确切的,当我们把眼光集中在单个类上时,该说法成立;然而,当把视野扩大到具有继承关系的两个或多个类上时,该说法不成立。

对象的创建一般有两种方式,一种是用new操作符,另一种是在一个Class对象上调用newInstance方法;其创建和初始化的实际过程是一样的:

首先为对象分配内存空间,包括其所有父类的可见或不可见的变量的空间,并初始化这些变量为默认值,如int类型为0,boolean类型为false,对象类型为null;

然后用下述5个步骤来初始化这个新对象:

1)分配参数给指定的构造方法;
2)如果这个指定的构造方法的第一个语句是用this指针显式地调用本类的其它构造方法,则递归执行这5个步骤;如果执行过程正常则跳到步骤5;
3)如果构造方法的第一个语句没有显式调用本类的其它构造方法,并且本类不是Object类(Object是所有其它类的祖先),则调用显式(用super指针)或隐式地指定的父类的构造方法,递归执行这5个步骤;如果执行过程正常则跳到步骤5;
4)按照变量在类内的定义顺序来初始化本类的变量,如果执行过程正常则跳到步骤5;
5)执行这个构造方法中余下的语句,如果执行过程正常则过程结束。

这一过程可以从下面的时序图中获得更清晰的认识:

如何通过实例学习Java对象的构造过程

对分析本文的实例最重要的,用一句话说,就是“父类的构造方法调用发生在子类的变量初始化之前”。可以用下面的例子来证明:

// Petstore.java
class Animal {
Animal() {
System.out.println("Animal");
}
}
class Cat extends Animal {
Cat() {
System.out.println("Cat");
}
}
class Store {
Store() {
System.out.println("Store");
}
}
public class Petstore extends Store{
Cat cat = new Cat();
Petstore() {
System.out.println("Petstore");
}
public static void main(String[] args) {
new Petstore();
}
}
运行这段代码,它的执行结果如下:

Store
Animal
Cat
Petstore
从结果中可以看出,在创建一个Petstore类的实例时,首先调用了它的父类Store的构造方法;然后试图创建并初始化变量cat;在创建cat时,首先调用了Cat类的父类Animal的构造方法;其后才是Cat的构造方法主体,最后才是Petstore类的构造方法的主体。

寻找程序产生例外的原因

现在回到本文开始提到的实例中来,当程序创建一个ChildDlg2的实例时,根据super(null, “Title”)语句,首先执行其父类BaseDlg的构造方法;在BaseDlg的构造方法中调用了createClientPanel()方法,这个方法是抽象方法并且被子类ChildDlg2实现了,因此,实际调用的方法是ChildDlg2中的createClientPanel()方法(因为Java里面采用“动态绑定”来绑定所有非final的方法);createClientPanel()方法使用了ChildDlg2类的实例变量jTextFieldName,而此时ChildDlg2的变量初始化过程尚未进行,jTextFieldName是null值!所以,ChildDlg2的构造过程掷出一个NullPointerException也就不足为奇了。

再来看ChildDlg1,它的jTextFieldName的初始化代码写在了createClientPanel()方法内部的开始处,这样它就能保证在使用之前得到正确的初始化,因此这段代码工作正常。

解决问题的两种方式

通过上面的分析过程可以看出,要排除故障,最简单的方法就是要求项目组成员在继承使用BaseDlg类,实现createClientPanel()方法时,凡方法内部要使用的变量必须首先正确初始化,就象ChildDlg1一样。然而,把类变量放在类方法内初始化是一种很不好的设计行为,它最适合的地方就是在变量定义块和构造方法中。

在本文的实例中,引发错误的实质并不在ChildDlg2上,而在其父类BaseDlg上,是它在自己的构造方法中不适当地调用了一个待实现的抽象方法。

从概念上讲,构造方法的职责是正确初始化类变量,让对象进入可用状态。而BaseDlg却赋给了构造方法额外的职责。

本文实例的更好的解决方法是修改BaseDlg类:

public abstract class BaseDlg extends JDialog {
public BaseDlg(Frame frame, String title) {
super(frame, title, true);
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add(createHeadPanel(), BorderLayout.NORTH);
this.getContentPane().add(createButtonPanel(), BorderLayout.SOUTH);
}


public void initGUI() {
this.getContentPane().add(createClientPanel(), BorderLayout.CENTER);
}

private JPanel createHeadPanel() {
... // 创建对话框头部
}

// 创建对话框客户区域,交给子类实现
protected abstract JPanel createClientPanel();

private JPanel createButtonPanel {
... // 创建按钮区域
}
}
新的BaseDlg类增加了一个initGUI()方法,程序员可以这样使用这个类:

ChildDlg dlg = new ChildDlg();
dlg.initGUI();
dlg.setVisible(true);
总结

类的构造方法的基本目的是正确初始化类变量,不要赋予它过多的职责。

设计类构造方法的基本规则是:用尽可能简单的方法使对象进入就绪状态;如果可能,避免调用任何方法。在构造方法内唯一能安全调用的是基类中具有final属性的方法或者private方法(private方法会被编译器自动设置final属性)。final的方法因为不能被子类覆盖,所以不会产生问题。

关于如何通过实例学习Java对象的构造过程就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何通过实例学习Java对象的构造过程

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何通过实例学习Java对象的构造过程

这篇文章给大家介绍如何通过实例学习Java对象的构造过程,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。下面提供一个项目中的错误实例,提供对其观察和分析,揭示出Java语言实例化一个对象具体过程,最后总结出设计Java类
2023-06-03

Java如何通过反射获取对象的属性和值

Java反射允许程序在运行时获取对象的属性和值。通过获取类的Class对象,可以遍历字段并获取字段名称。要获取属性值,需要获取Field对象,设置可访问性并获取值。示例代码展示了如何获取Person对象属性和值的反射使用。需要注意反射操作可能影响性能,需要谨慎使用。
Java如何通过反射获取对象的属性和值
2024-04-02

java对象实例化过程中的多态特性怎么理解

本篇内容主要讲解“java对象实例化过程中的多态特性怎么理解”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java对象实例化过程中的多态特性怎么理解”吧!java 对象实例化过程中的多态特性执行
2023-06-21

如何用JVM源码分析Java对象的创建过程

这篇文章将为大家详细讲解有关如何用JVM源码分析Java对象的创建过程,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。基于HotSpot实现对Java对象的创建过程进行深入分析。定义两个简单的
2023-06-17

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录