分享

java构造方法和new关键字

 Y赫尔 2015-04-12
学习java也有两年了,对一些基础还是理解的不够深,上网一搜很的确有不少这样的文章。下面就整理下以免以后忘记了。(理解:java 构造方法不等于创建对象而是初始化对象,new 关键字分配内存和创建对象)如理解有误的话,还请指点!

一、这个文章理解的还是比较有趣的。(转)http://zangxt./blog/472238
http://shukuiyan./blog/1007808

关于java的构造方法有几个简单的问题:
1.构造方法有返回值吗?
     没有。构造方法没有任何返回类型,也不允许是void。比如:
Java代码 
Java代码  收藏代码
  1. public class Test {     
  2.  //这不是构造函数!     
  3. public void Test() {     
  4. System.out.println("void Test()");     
  5. }     
  6. public static void main(String[] args) {     
  7. Test test = new Test();     
  8. test.Test();     
  9. }     
  10. }  





      这里我们定义了一个返回类型为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的。因此,"构造器实际上也是静态方法"这点很好否定。看下面例子:

Java代码 
Java代码  收藏代码
  1. public class Test {     
  2. public Test() {     
  3. this.test2();     
  4. }     
  5. public static void test(){     
  6. this.test2();     
  7. }     
  8. public static void test2(){     
  9. }    
  10. }  
  


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

      如果有C++经验的话,可以类比一下。C++里的new操作符有两个作用,调用operator new()来分配内存,然后调用构造函数来完成初始化。
     而这里的operator new()是隐式静态的。参考《C++程序设计语言(特别版)》中文版的
比如这个例子:
Cpp代码 
C++代码  收藏代码
  1. class Employee{    
  2. //...     
  3. public:     
  4. //....     
  5. void* operator new(size_t);     
  6. void operator delete(void* ,size_t);     
  7. }    


     成员operator new()和operator delete()默认为static成员,因为它们没有this指针,也不会修改任何对象。它们将提供一些存储,供构造函数去初始化,而后由析构函数去清理。
  类比可知,静态的是负责分配内存的工具,而不是构造函数。 不知道《Thinking In Java》的作者是不是把这点弄混了?

3.父类的构造方法中调用被子类重写的方法有多态现象。
     这句话很绕,直接看例子:
Java代码
Java代码  收藏代码
  1.     
  2. class Father{       
  3.     private int i = 5;       
  4.     public Father() {       
  5.         System.out.println("Father's i is " + this.i);       
  6.         test();       
  7.     }       
  8.     public void test(){       
  9.         System.out.println(this.i);       
  10.     }       
  11. }       
  12.       
  13. class Son extends Father{       
  14.     private int i = 55;       
  15.       
  16.     public Son() {       
  17.         System.out.println("Son's i is " + this.i);       
  18.     }       
  19.       
  20.     @Override      
  21.     public void test() {       
  22.         System.out.println(this.i);       
  23.     }       
  24.       
  25. }       
  26. public class Test {       
  27.     public static void main(String[] args) {       
  28.         new Son();       
  29.     }       
  30. }  
  
    


结果是:
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程序员天生就有相反的期待吗?

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多