今天遇到了这种情况,看到了这篇文章,特转载:
http://blog.csdn.net/wulong710/article/details/6920635
在构造器中调用多态方法进行初始化,也许会产生不可预料的结果。
[java] view plaincopy
import java.io.FileWriter;
import java.io.IOException;
/**
* Created by IntelliJ IDEA.
* User: wulong
* Date: 11-10-31
* Time: 上午1:56
* To change this template use File | Settings | File Templates.
*/
class Shap {
void set() {
try {
System.out.println("Shap.set()");
FileWriter fw = new FileWriter("hello.txt", true);
String s = "Shap.set\n";
fw.write(s);
fw.flush();
fw.close();
} catch (IOException e) {
System.out.println("Shap.set e=" + e.getMessage());
}
}
Shap() {
System.out.println("Shap().in=");
set();
System.out.println("Shap().out=");
}
}
class Line extends Shap {
int counter = 9;
void set() {
try {
System.out.println("Line.set() counter="+counter);
FileWriter fw = new FileWriter("hello.txt", true);
String s = "Line.set counter=" + counter+"\n";
fw.write(s);
fw.flush();
fw.close();
} catch (IOException e) {
System.out.println("Line.set e=" + e.getMessage());
}
}
Line() {
System.out.println("Line().in=" + counter);
set();
System.out.println("Line().out=" + counter);
}
}
public class Mom {
public static final void main(String[] args) {
Line t = new Line();
}
}
预料中hello.txt中内容应该是这样:
[html] view plaincopy
Shap.set
Line.set counter=9
实际hello.txt文件中内容如下:
[html] view plaincopy
Line.set counter=0
Line.set counter=9
原因:
《Thinking in java》中指出,这是由于构造器初始化顺序的问题:
初始化的实际过程是:
1、在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制零。
2、调用基类构造器。这个步骤会不断的反复递归下去,首先是构造这种层次结构的根,然后是下一层导出类,等等。直到最低层的导出类。此时,调用被重载的<pre name="code" class="html">set()方法(是的,是在调用Line构造器之前调用的),由于步骤(1)的缘故,我们此时会发现counter的值为0。
3、按照声明的顺序调用成员的初始化代码。在类的内部,初始化的顺序是先“静态”,(如果它们尚未因前面的对象创建过程而被初始化),后“非静态”。而非静态变量定义的顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。
4、调用导出类的构造器主体。
结论:
如果你要在构造器中调用一个方法时,将该方法声明为private。
对于这个规则是需要一些说明的,假使你的父类构造器中要调用一个非静态方法,而这个方法不是private的又被子类所重载,这样在实际创建子类的过程中递归调用到了父类的构造器时,父类构造器对这个方法的调用就会由于多态而实际上调用了子类的方法,当这个子类方法需要用到子类中实例变量的时候,就会由于变量没有初始化而出现异常(至于为什么子类中的实例变量没有初始化可以参考上边的实例初始化过程),这是Java不想看到的情况。而当父类构造器中调用的方法是一个private方法时,多态就不会出现,也就不会出现父类构造器调用子类方法的情况,这样可以保证父类始终调用自己的方法,即使这个方法中调用了父类中的实例变量也不会出现变量未初始化的情况(变量初始化总是在当前类构造器主体执行之前进行)。
分享到:
相关推荐
总结一下,Java继承中的构造函数调用规则如下: - 子类在实例化时,总会在其构造函数的第一行隐式或显式调用父类的构造函数。 - 如果父类没有无参数构造函数,且子类构造函数没有显式调用父类的有参构造函数,编译器...
Java继承时构造函数的调用机制是:子类的构造函数需要调用父类的构造函数,父类的无参数构造函数是默认的,如果父类没有提供无参数构造函数,而提供了其他类型的构造函数,那么编译器将报错。因此,我们最好给父类...
Java构造函数的相互调用代码示例 Java构造函数的相互调用代码示例是Java编程中的一个重要知识点。通过使用this关键字,可以在一个构造函数中调用另一个构造函数,以减少代码量。下面是相关的知识点: 1. 构造函数...
在这个主题中,“聚合中类的构造函数调用顺序”是一个关键概念,尤其是在多层嵌套的对象创建时。理解这个顺序对于编写健壮、无错误的代码至关重要。 首先,让我们明确构造函数的作用。构造函数是类的一个特殊方法,...
1. **工厂模式**:通过将构造函数设置为私有,可以实现一个工厂方法来代替直接的构造函数调用。这种方法允许类的内部逻辑控制如何创建对象,同时保持了类的封装性。例如,可以通过一个静态工厂方法`makeShape()`来...
以下是关于Java构造函数继承问题的关键知识点: 1. **继承默认构造函数**: - 子类只能继承父类的无参数(默认)构造函数。如果父类没有无参数的构造函数,那么子类无法直接继承任何构造函数。 - 缺省构造函数是...
总结来说,"面向对象-构造函数-调用&内存图解"涵盖了构造函数在对象创建过程中的角色,以及构造函数调用如何影响内存分配和对象实例化。理解这些概念对于编写高效、健壮的面向对象代码至关重要,特别是在处理大量...
Java构造函数是面向对象编程中的一个关键概念,用于在创建对象时初始化对象的状态。当我们创建一个类的实例时,构造函数会被自动调用,它允许我们设置对象的初始属性值,确保对象在创建后处于一个合理的状态。在这个...
根据题目中的描述,我们可以总结出关于构造函数调用的一些关键规则: 1. **默认构造函数的自动调用**:如果父类没有任何显式声明的构造函数,则编译器会为父类自动生成一个默认构造函数。当子类的构造函数被调用时...
4. **调用Java方法**:有了方法ID,就可以通过`env->CallObjectMethod`、`env->CallVoidMethod`等函数调用Java的构造方法或方法。 关于调用Java构造方法,你可以这样做: ```c++ jclass javaClass = env->FindClass...
在Java中,如果子类没有显式地调用父类的构造函数,那么会默认调用父类的无参构造函数。 通过分析给定的代码示例,我们可以清晰地看到这一执行顺序: ```java // Person类的静态块 thisisperson'sstatic // ...
构造函数可以通过链式调用来组合使用,即一个构造函数调用另一个构造函数,这有助于代码复用和简化初始化过程。例如,一个构造函数可以调用另一个具有不同参数的构造函数,通过`this(参数列表)`来实现。 在Java中,...
Java构造函数是编程中至关重要的概念,特别是在面向对象编程中。它们主要用于初始化新创建的对象的状态。构造函数在类中定义,其名字与类名完全相同,并且没有返回类型,连void也不包含。当通过`new`关键字创建一个...
在本文中,我们将详细介绍java使用this调用构造函数的实现方法,并结合实例形式分析了java面向对象程序设计中函数调用相关操作技巧。 一、什么是this关键字? 在java中,this关键字是一个特殊的引用词,它可以用来...
2. **构造函数调用顺序**:在子类构造函数中,必须先调用父类的构造函数(使用`super`),然后再执行子类构造函数中的其他代码。 3. **构造函数的可访问性**:子类只能访问具有相同或更宽松访问级别的父类构造函数。...
Java构造函数与普通函数用法详解 Java构造函数和普通函数是Java语言的两个基本组成部分,它们在Java编程中扮演着非常重要的角色。下面我们将详细讲解Java构造函数和普通函数的用法和相关知识点。 一、函数的作用和...
“深入理解Java构造器机理” 在 Java 编程语言中,构造器是一种特殊的方法,用于初始化对象的创建。它是 Java 类中最重要的一个概念。下面将深入讨论构造器的机理、执行顺序、作用及与其他概念的区别。 一、构造器...
在Java编程语言中,构造函数是用来初始化对象的特殊方法。当创建一个类的实例时,构造函数会被调用。在涉及继承的情况下,构造函数的使用和调用规则变得更加复杂。以下是对构造函数继承问题的详细解释: 1. 缺省...
5. **使用`FormatterServices.GetUninitializedObject`方法**:这个方法可以获取一个未初始化的对象,不调用构造函数。常用于序列化和反序列化场景: ```csharp MyClass instance = (MyClass)FormatterServices....