`
ZangXT
  • 浏览: 118304 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

无聊:详解java的构造方法

阅读更多

      关于java的构造方法有几个简单的问题:

1.构造方法有返回值吗?

     没有。构造方法没有任何返回类型,也不允许是void。比如:

public class Test { 
 //这不是构造函数! 
public void Test() { 
System.out.println("void Test()"); 
} 
public static void main(String[] args) { 
Test test = new Test(); 
test.Test(); 
} 
} 

 

 

 

 

      这里我们定义了一个返回类型为void的Test()方法,有了返回类型void,它就不是构造方法了。

 

     Test test = new Test();

     有人用上面的表达式来说明构造方法返回对象引用,这是明显错误的。new关键字有两个作用。一是分配内存,

创建对象。二是调用构造方法,完成对象的初始化工作。完成这两步之后,才算创建了一个完整的Java对象。我们

可以用Test test = new Test();的反编译代码来说明这个问题:

0:    new    #5; //class Test

3:    dup

4:    invokespecial    #6; //Method "":()V

7:    astore_1

      原表达式被编译成四条bytecode指令。

new指令负责根据参数分配内存并创建Test对象,然后将新创建对象的引用置于栈顶。

dup指令复制栈顶的内容,记住,此时栈最顶端的两个单元都是新创建对象的引用。

接着是调用初始化方法,该方法是由构造方法编译而来,栈顶的引用作为此方法的参数消耗了。通过调用初始化方法完成

对象的创建过程。这里注意一下初始化方法Method "":()V,它是void类型的。

最后的astore_1指令将栈顶的对象引用赋给局部变量(前面说了,dup之后栈顶两个引用,一个给初始化方法吃掉了,一个留给astore_1操作用),也就是执行赋值操作。

     因此,得到的引用是new指令的结果,不是构造方法的返回值。

     有一点需要注意:new指令创建对象,同时使对象的各个字段得到其默认值,比如整数为0,浮点数为0.0,引用为null,boolean为false等。也就是说在构造方法执行之前,各个字段都有默认值了。这一点我们在第三条继续说明。

     通过上面说明,我们明确了构造方法的职能(初始化new指令创建的对象,得到一个状态合法的对象,完成对象的

创建过程)。任何类都有构造方法,但是new指令只能创建非抽象类的对象。理解了这一点,也就不要再问"抽象类也有构造方法,为什么不能创建对象"之类的问题了。

 

2.构造方法是静态的?

      错误。

      这是《Thinking In Java》中的一个观点。书里有一段:

Even though it doesn't explicitly use the static keyword, the constructor is actually a static method. So the first time an object of type Dog is created, or the first time a static method or static field of class Dog is accessed, the Java interpreter must locate Dog.class, which it does by searching through the classpath.

《java编程思想》中文第四版96页:

总结一下对象的创建过程,假设有个名为Dog的类:

1.即使没有显示地使用static关键字,构造器实际上也是静态方法。因此,当首次创建类型为Dog的对象时(构造器可以看成

是静态方法),或者Dog类的静态方法/静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件。

 

     这里我并没有看明白作者为什么说构造器实际上是静态方法。但是我们知道,静态方法是不能使用this的。因此,"构造器实际上也是静态方法"这点很好否定。看下面例子:

 

public class Test { 
public Test() { 
this.test2(); 
} 
public static void test(){ 
this.test2(); 
} 
public static void test2(){ 
}
} 

 

 

   test方法编译错误,因为静态方法中不能使用非静态的this,而构造方法使用this是没有问题的。

 

      如果有C++经验的话,可以类比一下。C++里的new操作符有两个作用,调用operator new()来分配内存,然后调用构造函数来完成初始化。

     而这里的operator new()是隐式静态的。参考《C++程序设计语言(特别版)》中文版的374页:

比如这个例子:

class Employee{
//... 
public: 
//.... 
void* operator new(size_t); 
void operator delete(void* ,size_t); 
} 

 

     成员operator new()和operator delete()默认为static成员,因为它们没有this指针,也不会修改任何对象。它们将提供一些存储,供构造函数去初始化,而后由析构函数去清理。

  类比可知,静态的是负责分配内存的工具,而不是构造函数。 不知道《Thinking In Java》的作者是不是把这点弄混了?

 

3.父类的构造方法中调用被子类重写的方法有多态现象。

     这句话很绕,直接看例子:

class Father{   
    private int i = 5;   
    public Father() {   
        System.out.println("Father's i is " + this.i);   
        test();   
    }   
    public void test(){   
        System.out.println(this.i);   
    }   
}   
  
class Son extends Father{   
    private int i = 55;   
  
    public Son() {   
        System.out.println("Son's i is " + this.i);   
    }   
  
    @Override  
    public void test() {   
        System.out.println(this.i);   
    }   
  
}   
public class Test {   
    public static void main(String[] args) {   
        new Son();   
    }   
} 

     

 

 

 结果是:

Father's i is 5
0
Son's i is 55

     结合第一点,构造方法调用之前,首先是new指令创建了一个对象,并将各个成员初始化为其默认值。下面看构造方法的调用过程。

     子类构造方法会调用父类构造方法,父类构造方法首先打印Father's i is 5。然后调用test()方法,注意,我们创建的是Son类的对象,所以test()方法调用的是Son类定义的test()方法,也就是说发生了多态。我们再去看Son类中test方法的实现,就是简单的输出this.i,为什么是0呢,别忘了我们还没有执行子类的构造方法啊,所以此时子类的i还是new指令初始化得到的0。好,test()方法执行完了,总算回到子类构造方法继续执行,先把i赋值为55,下面的输出语句Son's i is 55也就不难理解了。

    在构造方法中调用方法要特别注意这种多态现象。

 

    这种现象和c++里的现象是不同的。在C++中,如果在父类的构造函数中调用虚方法的话,调用的是父类定义的版本,不会发生多态现象。但一个有趣的现象是,C++的经典书籍和Java的经典书籍竟然都建议不要在构造方法里面调用多态方法,因为现象并不是你期待的!这就奇怪了,难道C++程序员和Java程序员天生就有相反的期待吗?

分享到:
评论
4 楼 left.jessica 2010-09-26  
hjg1988 写道
引用

《java编程思想》中文第四版96页:

总结一下对象的创建过程,假设有个名为Dog的类:

1.即使没有显示地使用static关键字,构造器实际上也是静态方法。因此,当首次创建类型为Dog的对象时(构造器可以看成

是静态方法),或者Dog类的静态方法/静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件。

     这里我并没有看明白作者为什么说构造器实际上是静态方法。但是我们知道,静态方法是不能使用this的。因此,"构造器实际上也是静态方法"这点很好否定。

我的理解是,静态方法是不需要通过类的实例来调用的,而是类被加载后放在栈中的,是属于“类方法”。非静态方法是要通过类的实例才调用的,是属于“对象方法”。从这个角度来讲,构造方法可以认为是静态的,因为在调用构造方法时必定是还未实例化的,而实例化后就不能再调用构造方法了。这样理解不知道对不对


我做了个测试程序:
	public static void main(String[] args) {
	  
	   try{     
	    Class c = Class.forName("mypackage.SK");
	    Constructor[] cc = c.getConstructors();
	    Method[] m = c.getMethods();

	    int b = cc[0].getModifiers();
	    System.out.println(b);
	    System.out.println(Integer.toHexString(b));
	    System.out.println("final:" + Modifier.isFinal(b));
	    System.out.println("public:" + Modifier.isPublic(b));
	    System.out.println("abstract:" + Modifier.isAbstract(b));
	    System.out.println("native:" + Modifier.isNative(b));
	    System.out.println("protected:" + Modifier.isProtected(b));
	    System.out.println("Static:" + Modifier.isStatic(b));
	    

	   catch(ClassNotFoundException cl) {
	    System.out.println("ClassNotFound");
	   } 
	   catch(SecurityException se) {
	    System.out.println("SecurityException");
	   }

	}

而SK:
public class SK {
	
	public SK(){
		
	}

}

结果是static:false
3 楼 lord_is_layuping 2010-09-06  
2 楼 hjg1988 2009-09-28  
引用

《java编程思想》中文第四版96页:

总结一下对象的创建过程,假设有个名为Dog的类:

1.即使没有显示地使用static关键字,构造器实际上也是静态方法。因此,当首次创建类型为Dog的对象时(构造器可以看成

是静态方法),或者Dog类的静态方法/静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件。

     这里我并没有看明白作者为什么说构造器实际上是静态方法。但是我们知道,静态方法是不能使用this的。因此,"构造器实际上也是静态方法"这点很好否定。

我的理解是,静态方法是不需要通过类的实例来调用的,而是类被加载后放在栈中的,是属于“类方法”。非静态方法是要通过类的实例才调用的,是属于“对象方法”。从这个角度来讲,构造方法可以认为是静态的,因为在调用构造方法时必定是还未实例化的,而实例化后就不能再调用构造方法了。这样理解不知道对不对
1 楼 wkbulletin 2009-09-24  
package lottery;

class Father {
private int i = 5;
private static int j= 6;
public Father() {
System.out.println("Father's i is " + this.i);
test();
}

public void test() {
System.out.println("father i :"+this.i);
System.out.println("father j :"+j);
}
}

class Son extends Father {
private static int i = 55;

public Son() {
System.out.println("Son's i is " + i);
}

@Override
public void test() {
super.test();
System.out.println("son i :"+i);
}

}

public class Test {
public static void main(String[] args) {
new Son();
}
}

相关推荐

    java错误处理:java.lang.OutOfMemoryError: Java heap space

    ### Java 错误处理:java.lang.OutOfMemoryError: Java heap space 在Java应用程序开发过程中,经常遇到的一个问题就是内存溢出错误,特别是在处理大量数据或长时间运行的应用时。其中,“java.lang....

    简单了解JAVA构造方法

    JAVA构造方法详解 JAVA构造方法是一个特殊的方法,它用于初始化类的对象。构造方法的作用是对类进行初始化,如果你没有定义任何构造方法的形式,编译器会为你提供一个不带任何参数的构造函数,那么你产生类的对象时...

    详解 Java继承关系下的构造方法调用

    Java继承关系下的构造方法调用详解 Java继承关系下的构造方法调用是Java语言中一个非常重要的概念,它关系到类的继承和对象的实例化。在Java中,创建一个类的对象时,如果该类存在父类,则先调用父类的构造方法,...

    Java零基础-构造方法.md

    ### Java零基础-构造方法详解 #### 内容概要 本文旨在深入解析Java语言中的构造方法这一核心概念。构造方法是对象初始化的关键步骤,在Java编程中占据着至关重要的地位。通过对构造方法的学习,读者不仅能理解其...

    Java2编程详解.rar

    11. **反射**:Java反射机制允许在运行时检查类、接口、字段和方法的信息,动态调用方法和修改字段值,是实现元编程的关键。 12. **JavaBeans**:JavaBeans是Java组件模型的一部分,用于创建可重用的组件,遵循特定...

    读书笔记:针对 java高并发编程详解 多线程与架构设计 的学习项目.zip

    读书笔记:针对 java高并发编程详解 多线程与架构设计 的学习项目

    Java中自动生成构造方法详解

    Java中自动生成构造方法详解 Java 中的构造方法是一种特殊的方法,用于初始化对象的状态。Java 中的构造方法有两种形式,一种是无参构造方法,另外一种是带参构造方法。在 Java 中,如果一个类没有声明构造方法,...

    读书笔记:参考Java高并发编程详解多线程与架构设计汪文君学习笔记及源码.zip

    读书笔记:参考Java高并发编程详解多线程与架构设计汪文君学习笔记及源码

    Java教程之javaIO详解

    Java IO 详解 Java IO(Input/Output)是 Java 语言中用于处理输入输出操作的类库,提供了大量的类和方法来实现文件、网络、字节流等方面的输入输出操作。下面对 Java IO 中的重要知识点进行详细说明。 一、File ...

    精通Hibernate:Java对象持久化技术详解(第2版)源码2

    精通Hibernate:Java对象持久化技术详解(第2版)源码精通Hibernate:Java对象持久化技术详解(第2版)源码精通Hibernate:Java对象持久化技术详解(第2版)源码精通Hibernate:Java对象持久化技术详解(第2版)源码

    java IO详解课件

    - **Java I/O系统的重要性**:Java I/O系统是Java编程中的基础组成部分之一,用于处理数据的输入与输出。理解和熟练掌握Java I/O系统对于开发高质量的应用程序至关重要。 - **Java I/O系统的设计原则**:Java I/O...

    java扫雷代码详解

    Java扫雷代码详解 扫雷游戏作为经典的益智类游戏,在计算机编程的学习中经常被作为练习项目。尤其是在Java编程语言学习过程中,由于Java的跨平台特性和丰富的图形用户界面(GUI)组件,Java扫雷成为了许多初学者...

    Java2编程详解

    1. **基础语法**:Java的基础始于它的语法,包括变量、数据类型、运算符、流程控制语句(如if-else、for、while)以及方法的定义与调用。书中会深入讲解这些基础知识,帮助读者构建扎实的编程基础。 2. **面向对象...

    由浅入深详解Java 类的实例化顺序

    java教程 由浅入深详解Java 类的实例化顺序 在子类对象被实例化的过程中,变量、构造方法以及代码块三者的先后顺序为: 1. 父类的静态变量和静态代码块,按代码先后顺序执行 2. 子类的静态变量和静态代码块,按...

    张孝祥Java邮件开发详解

    本教程“张孝祥Java邮件开发详解”深入探讨了如何使用Java来实现邮件的发送功能,涵盖了从基础概念到高级特性的全方位讲解。 首先,Java Mail API是Java中用于处理邮件的核心库,它提供了丰富的类和接口,使得...

    Java核心知识体系:反射机制详解.pdf

    - **动态代理**:Java的`java.lang.reflect.Proxy`类可以基于反射创建动态代理,实现接口的动态实现。 - **单元测试**:单元测试框架如JUnit利用反射来执行注解的方法。 - **插件化**:反射支持在运行时加载和...

Global site tag (gtag.js) - Google Analytics