`

java构造方法和new关键字

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

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

关于java的构造方法有几个简单的问题:
1.构造方法有返回值吗?
     没有。构造方法没有任何返回类型,也不允许是void。比如:
Java代码 
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的。因此,"构造器实际上也是静态方法"这点很好否定。看下面例子:

Java代码 
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++程序设计语言(特别版)》中文版的
比如这个例子:
Cpp代码 
class Employee{  
//...   
public:   
//....   
void* operator new(size_t);   
void operator delete(void* ,size_t);   
}  


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

3.父类的构造方法中调用被子类重写的方法有多态现象。
     这句话很绕,直接看例子:
Java代码
  
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程序员天生就有相反的期待吗?
分享到:
评论

相关推荐

    Java学习笔记---15.面向对象编程10-Java中final关键字,抽象类与接口

    答案是: 抽象类中允许有自己的构造方法,但是该构造方法并不能直接实例化自己的对象. 如果在抽象类中存在有参构造方法,则必须在子类中明确的使用super([参数列表])指明要调用父类中的哪个构造方法. 这里举例如下: ...

    JAVA理论知识基础复习

    JAVA理论知识基础复习 构造方法 new关键字 方法的重载 特殊变量this 关键字static 定义常量 继承 方法的重写 特殊变量super 多态性 等相关知识点总结

    java基础补足和thinking In Java学习.rar

    2.构造器也是方法,满足方法的各种特征,无参数的构造器称为默认构造器,默认构造方法或者无参构造器。在没有其他构造方法的时候可以省略不写 需要注意的是,构造器虽然是方法但是它没有返回值。 3.使用构造器...

    java基础知识共享资格分享

    类名 变量 = 当前类的对象(new 构造方法) 父类 变量 = 子类的对象(new 构造方法) 接口 变量 = 实现类的对象(new 构造方法) (6)编写类的步骤和创建对象的方式 1.创建类 2.编写成员变量 3.生成构造方法(建议:无...

    java关键字.docx

    super:超类,用于调用父类的构造方法或方法。 switch:开关,用于switch语句中的不同情况。 synchronized:同步,用于实现线程同步的方法。 this:这个,用于引用当前对象。 throw:抛出,用于抛出一个异常。

    JAVA基础知识精华总结 收藏

    } // 使用这个构造方法创建对象,要写成C c = new C(a); a是A的对象。 11、异常类 JAVA中除了RunTimeException 类,其他异常均须捕获或抛出。 本文来自CSDN博客,转载请标明出处:...

    java基础.txt

    //创建对象需要调用类的构造方法 构造方法:类名() 特点:方法名和类名相同,没有返回值类型 public Person(){ private String name; public void setName(String name){ this.name = name; } } public ...

    Java中this与super的区别

    肯定是为了满足自己想要完成某些行为的语句,但是又用了super继承父类的构造方法。 那么以前所做的修改就都回到以前了,就是说又成了父类的构造方法了。 2. 在Java中,有时还会遇到子类中的成员变量或方法与...

    Java的面向对象编程课件

     new关键字的作用  缺省构造方法  方法的重载(overload)  特殊变量this  关键字static  常量的定义  类的继承  方法的覆盖(override)  特殊变量super  多态性  面向对象的程序设计

    Java+单例+多例+工厂+枚举+动态代理===》演示

    1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。 2. 在该类内部创建一个唯一的对象 3. 定义一个静态方法返回这个唯一对象。 #### 例设计模式的类型 根据实例化对象的时机单例设计模式又分为...

    java中创建对象的步骤有哪些.txt

    这可以通过使用new关键字和类构造函数来完成。 java Person person1 = new Person("John Doe", 30); 访问对象属性(Attributes):一旦对象被创建,你就可以通过使用.操作符来访问和修改对象的属性。 java System.ou

    Java 语言基础 —— 非常符合中国人习惯的Java基础教程手册

    new 调用对象的构造方法,返 回对该对象的一个引用(即该对象所在的内存地址)。用 new 可以为一个类实例化, 多个不同的对象。这些对象分别占用不同的内存空间,因此改变其中一个对象的状 态不会影响其它对象的状态 。...

    java三大特性说明

    抽象类中有构造方法,但是没有抽象的构造方法。构造方法的存在完全是为了继承关系而存在。  与之形成对比的就是接口,接口中的所有方法要求定义成公开抽象方法,因为这些方法所表示的就是标准,标准的信息必须做到...

    《Java程序设计案例教程》教学课件05深入Java面向对象.pptx

    06 掌握构造方法(隐式、显式)的定义和使用,以及构造方法重载的概念。 07 掌握this关键字的使用。 08 了解基本数据类型变量和引用数据类型变量在数值传递上的区别。 《Java程序设计案例教程》教学课件05深入Java...

    上海交大网络面向对象程序设计Java第一、二、三次作业答案.doc

    但不可用void 声明 C) Java 语言规定构造方法不可重载 D) Java 语言规定构造方法只能通过new自动调用 2、作为Java应用程序入口的main方法,其声明格式可以是( B ) A) public void main(String [] args) B) public ...

    面向对象程序设计与实践-Java抽象类和接口[33页].pptx

    //将move定义为抽象方法 抽象方法目的就是为了实现一个接口多种方法的原理,即所有的子类对外都呈现一个相同名字的方法,抽象方法必须被重写,且构造方法、类方法不能声明为抽象方法。 面向对象程序设计与实践-Java...

    Java测试题2答案

    欲构造ArrayList类的一个实例,此类继承了List接口,下列哪个方法是正确的 ? B A ArrayList myList=new Object(); B List myList=new ArrayList(); C ArrayList myList=new List(); D List ...

    java经典面试2010集锦100题(不看你后悔)

    D) 该程序无法通过编译,因为在Test类中的构造方法被写成私有,那么Test t=new Test() 将无法找到合适的构造方法。 题目12:a 程序如下: class Test { private int day; private Test(int d) { day=d; } ...

    JAVA面试题最全集

    除了使用new关键字创建对象意外,试列举另外三种以上创建实例的方式? 37.classloader中,JDK的API、Classpath中的同web-inf中的class加载方式有什么区别? 38.列举三种以上垃圾回收算法,并比较其优缺点? 39....

    java 程序设计 期中考试.doc

    2.Java程序里,创建新的类对象用关键字new,回收无用的类对象使用关键字free. ( × ) 3.Java有垃圾回收机制,内存回收程序可在指定的时间释放内存对象. ( × ) 4.构造函数用于创建类的实例对象,构造函数名...

Global site tag (gtag.js) - Google Analytics