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

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

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

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代码  

  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++程序设计语言(特别版)》中文版的374页:

比如这个例子:

Cpp代码  

  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代码  

  1. class Father{
  2. private int i = 5;
  3. public Father() {
  4. System.out.println("Father's i is " + this.i);
  5. test();
  6. }
  7. public void test(){
  8. System.out.println(this.i);
  9. }
  10. }
  11. class Son extends Father{
  12. private int i = 55;
  13. public Son() {
  14. System.out.println("Son's i is " + this.i);
  15. }
  16. @Override
  17. public void test() {
  18. System.out.println(this.i);
  19. }
  20. }
  21. public class Test {
  22. public static void main(String[] args) {
  23. new Son();
  24. }
  25. }

结果是:

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

转载于:https://www.cnblogs.com/taiyuanzhongruan/p/7575170.html

详解java的构造方法相关推荐

  1. java sort 第二个参数_详解java Collections.sort的两种用法

    Collections是一个工具类,sort是其中的静态方法,是用来对List类型进行排序的,它有两种参数形式: public static > void sort(List list) { l ...

  2. 详解 Java NIO

    详解 Java NIO 文件的抽象化表示,字节流以及字符流的文件操作等属于传统 IO 的相关内容,我们已经在前面的文章进行了较为深刻的学习了. 但是传统的 IO 流还是有很多缺陷的,尤其它的阻塞性加上 ...

  3. java 修饰符 详解,详解Java修饰符

    Java语言提供了很多修饰符,主要分为以下两类: 访问修饰符 非访问修饰符 修饰符用来定义类.方法或者变量,通常放在语句的最前端.我们通过下面的例子来说明: 访问控制修饰符 Java中,可以使用访问控 ...

  4. 【java】详解Java的类文件(class文件)结构

    1.概述 转载:详解Java的类文件(class文件)结构 大家好,我是二哥呀,今天我拿了一把小刀,准备解剖一下 Java 的 class 文件. CS 的世界里流行着这么一句话,"计算机科 ...

  5. java callable 详解_详解Java Callable接口实现多线程的方式

    在Java 1.5以前,创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口.无论我们以怎样的形式实现多线程,都需要调用Thread类中的start方法去向操作系统请求 ...

  6. java 代码块_详解java中的四种代码块

    在java中用{}括起来的称为代码块,代码块可分为以下四种: 一.简介 1.普通代码块: 类中方法的方法体 2.构造代码块: 构造块会在创建对象时被调用,每次创建时都会被调用,优先于类构造函数执行. ...

  7. smali语言详解之一般/构造方法(函数)的声明与返回值关键字

    smali语言详解之一般/构造方法(函数)的声明与返回值关键字 一. smali语言的方法声明格式 .method与.end method成对出现,类似于java中的花括号 1.1.非静态的一般方法 ...

  8. java 引用传递_详解java的值传递、地址传递、引用传递

    详解java的值传递.地址传递.引用传递 一直来觉得对值传递和地址传递了解的很清楚,刚才在开源中国上看到一篇帖子介绍了java中的值传递和地址传递,看完后感受颇深.下边总结下以便更容易理解. 按照以前 ...

  9. 异常处理器详解 Java多线程异常处理机制 多线程中篇(四)

    在Thread中有异常处理器相关的方法 在ThreadGroup中也有相关的异常处理方法 示例 未检查异常 对于未检查异常,将会直接宕掉,主线程则继续运行,程序会继续运行 在主线程中能不能捕获呢? 我 ...

  10. 详解Java解析XML的四种方法

    http://developer.51cto.com  2009-03-31 13:12  cnlw1985  javaeye  我要评论(8) XML现在已经成为一种通用的数据交换格式,平台的无关性 ...

最新文章

  1. vb子程序未定义怎么改怎么办_提示子程序或函数未定义怎么修改。。。
  2. JVM - 一个案例反推不同JDK版本的intern机制以及intern C++源码解析
  3. 剑桥大学创业基金和指导:Accelerate Cambridge
  4. 十进制整数(包括负数)和二进制的转换
  5. Java使用UDP聊天程序
  6. redis缓存实现原理php,分析redis原理及实现
  7. cpu计算速度排行榜_CPU速度的计算方法和单位
  8. 阿里云服务器搭配宝塔面板安装Redis为网站提速
  9. php加数据库开发案例,PHP简单数据库操作类实例【支持增删改查及链式操作】
  10. 如何最大化使用BI工具
  11. 图片版坦克大战源代码之图片处理类(一)
  12. 2.1KaliLinux的安装
  13. 虚幻4 ai蓝图_高效AI自我监督学习的迷人蓝图
  14. 【Android】时钟动态图标的定制化
  15. 2016-2017-1 《信息安全系统设计基础》课程总结
  16. Firefox插件开发-入门篇
  17. 线性稳压电源和开关稳压电源
  18. 海量数据大课学习笔记(8)-账号微服务注册模块+短信验证码+阿里云OSS开发实战-小滴课堂
  19. python语言基本语句-Python中的基本语句
  20. 738. Monotone Increasing Digits

热门文章

  1. CGAL中Polyhedron_3中与半边结构有关的具体使用
  2. OpenAI重磅开源多智能体博弈环境Neural MMO
  3. Introduction to dnorm, pnorm, qnorm, and rnorm for new biostatisticians
  4. load data file使用详解
  5. springboot 项目maven 打包错误
  6. [转]编程的首要原则(s)是什么?
  7. 一、IOC和DI的概念
  8. Leetcode 27 Remove Element
  9. 基于jQuery8款超赞的评分插件
  10. C#属性(Attribute)用法实例解析