详解java的构造方法
关于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++程序设计语言(特别版)》中文版的374页:
比如这个例子:
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程序员天生就有相反的期待吗?
转载于:https://www.cnblogs.com/taiyuanzhongruan/p/7575170.html
详解java的构造方法相关推荐
- java sort 第二个参数_详解java Collections.sort的两种用法
Collections是一个工具类,sort是其中的静态方法,是用来对List类型进行排序的,它有两种参数形式: public static > void sort(List list) { l ...
- 详解 Java NIO
详解 Java NIO 文件的抽象化表示,字节流以及字符流的文件操作等属于传统 IO 的相关内容,我们已经在前面的文章进行了较为深刻的学习了. 但是传统的 IO 流还是有很多缺陷的,尤其它的阻塞性加上 ...
- java 修饰符 详解,详解Java修饰符
Java语言提供了很多修饰符,主要分为以下两类: 访问修饰符 非访问修饰符 修饰符用来定义类.方法或者变量,通常放在语句的最前端.我们通过下面的例子来说明: 访问控制修饰符 Java中,可以使用访问控 ...
- 【java】详解Java的类文件(class文件)结构
1.概述 转载:详解Java的类文件(class文件)结构 大家好,我是二哥呀,今天我拿了一把小刀,准备解剖一下 Java 的 class 文件. CS 的世界里流行着这么一句话,"计算机科 ...
- java callable 详解_详解Java Callable接口实现多线程的方式
在Java 1.5以前,创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口.无论我们以怎样的形式实现多线程,都需要调用Thread类中的start方法去向操作系统请求 ...
- java 代码块_详解java中的四种代码块
在java中用{}括起来的称为代码块,代码块可分为以下四种: 一.简介 1.普通代码块: 类中方法的方法体 2.构造代码块: 构造块会在创建对象时被调用,每次创建时都会被调用,优先于类构造函数执行. ...
- smali语言详解之一般/构造方法(函数)的声明与返回值关键字
smali语言详解之一般/构造方法(函数)的声明与返回值关键字 一. smali语言的方法声明格式 .method与.end method成对出现,类似于java中的花括号 1.1.非静态的一般方法 ...
- java 引用传递_详解java的值传递、地址传递、引用传递
详解java的值传递.地址传递.引用传递 一直来觉得对值传递和地址传递了解的很清楚,刚才在开源中国上看到一篇帖子介绍了java中的值传递和地址传递,看完后感受颇深.下边总结下以便更容易理解. 按照以前 ...
- 异常处理器详解 Java多线程异常处理机制 多线程中篇(四)
在Thread中有异常处理器相关的方法 在ThreadGroup中也有相关的异常处理方法 示例 未检查异常 对于未检查异常,将会直接宕掉,主线程则继续运行,程序会继续运行 在主线程中能不能捕获呢? 我 ...
- 详解Java解析XML的四种方法
http://developer.51cto.com 2009-03-31 13:12 cnlw1985 javaeye 我要评论(8) XML现在已经成为一种通用的数据交换格式,平台的无关性 ...
最新文章
- vb子程序未定义怎么改怎么办_提示子程序或函数未定义怎么修改。。。
- JVM - 一个案例反推不同JDK版本的intern机制以及intern C++源码解析
- 剑桥大学创业基金和指导:Accelerate Cambridge
- 十进制整数(包括负数)和二进制的转换
- Java使用UDP聊天程序
- redis缓存实现原理php,分析redis原理及实现
- cpu计算速度排行榜_CPU速度的计算方法和单位
- 阿里云服务器搭配宝塔面板安装Redis为网站提速
- php加数据库开发案例,PHP简单数据库操作类实例【支持增删改查及链式操作】
- 如何最大化使用BI工具
- 图片版坦克大战源代码之图片处理类(一)
- 2.1KaliLinux的安装
- 虚幻4 ai蓝图_高效AI自我监督学习的迷人蓝图
- 【Android】时钟动态图标的定制化
- 2016-2017-1 《信息安全系统设计基础》课程总结
- Firefox插件开发-入门篇
- 线性稳压电源和开关稳压电源
- 海量数据大课学习笔记(8)-账号微服务注册模块+短信验证码+阿里云OSS开发实战-小滴课堂
- python语言基本语句-Python中的基本语句
- 738. Monotone Increasing Digits
热门文章
- CGAL中Polyhedron_3中与半边结构有关的具体使用
- OpenAI重磅开源多智能体博弈环境Neural MMO
- Introduction to dnorm, pnorm, qnorm, and rnorm for new biostatisticians
- load data file使用详解
- springboot 项目maven 打包错误
- [转]编程的首要原则(s)是什么?
- 一、IOC和DI的概念
- Leetcode 27 Remove Element
- 基于jQuery8款超赞的评分插件
- C#属性(Attribute)用法实例解析