您的位置:首页 > 编程语言

构造方法、构造代码块、显式初始化之间的关系

2016-03-08 10:44 483 查看

构造方法、构造代码块、显式初始化之间的关系

[b]概述[/b]

在Java中存在三种初始化成员变量的方法,分别是:显式初始化、构造方法、构造代码块。当这三者同时出现在我们的代码中,究竟Java的编译器是怎么去处理的?这个问题我们应该最想知道。其次可能还有人不知道这三者的定义是什么,在本文中,我会对这些问题进行详细的讲解,并教会你该怎么去判断你的想法究竟是否正确。

[b]定义[/b]

构造方法

构造方法的形式化定义如下:

class 类名{
访问修饰符 类名(参数列表){
//代码块
}
}


从上面的定义可知,构造函数具备以下几个特点:

(1) 构造函数没有返回值;

(2) 构造函数与类名是同名的;

(3) 构造函数可以有多个。

显示初始化

显示初始化的形式化定义如下:

class 类名{
访问修饰符 类型名 变量名 = 初始化值;
}


构造代码块

构造代码块的形式化定义如下:

class 类名{
{
//代码块中执行的内容
}
}


[b]三者同时出现在类中的执行顺序[/b]

这三者同时出现在类中是我们比较关心的问题,究竟Java编译器对他们进行了怎样的处理?在这里我们采用的是Java给我们提供的工具:javap.exe对.class文件进行反编译得到.

javap工具的使用

javap工具有sun公司提供,在JDK/bin目录下,如果不知道怎么使用,按照以下的步骤操作:

Win+R,在其中输入cmd,打开“命令提示符”;

在其中输入:javap,便可以看到其中的命令以及参数列表。

在下文中我只使用三个参数-c (代表反编译),-l(输出行号和本地变量表)、-private(显示所有的成员和类),具体的命令如下:

javap -c -l -private .class文件名(不加.class后缀)


简单的使用一个例子对整个使用过程进行说明:

/**
* @author junpengzhu
* @function 说明构造代码块,构造函数,显式初始化之间的关系
* @FileName Test.java
*/
class Child{
String name ;
String id ;
int age;
//定义一个构造代码块
{
cry() ;
}
//定义一个构造函数
public Child(String name , String id , int age ){
this.name = name ;
this.id = id ;
this.age = age ;
}
//定义一个动作cry,每个小孩天生的自带技能
public void cry(){
System.out.println("小孩正在哭,非常的伤心");
}
}
class Test{
public static void main(String[] args){
Child c1 = new Child("小明","123456789",1);
}
}


接着对其中的代码进行编译:

javac Test.java


接着得到一个Test.class的文件,我们用EditPlus打开,发现其中全部都是乱码,这是因为.class文件是给Java虚拟机(JVM)看的,不是给人看的,那么是不是就没有办法看java编译器究竟对代码进行了怎样的优化呢?回答是有办法,采用sun公司提供给我们的反编译技术。接着在命令提示符下输入:

javap -c -l -private Test


上面代码的执行结果如下:





执行顺序

Java中的构造函数是最后被执行的,而显示初始化和构造代码块是根据两者在类中放置的顺序执行的,并且编译器总是会将构造代码块放到构造函数的最前面调用,那么可能你还会有疑问,如果代码是下面的这种情况,编译器会报错吗?

/**
*@author junpengzhu
*@function 说明构造代码块,构造函数,显式初始化之间的关系
*@FileName Test.java
*/
class Child{
String name ;
String id ;
//定义初始化代码块
{
age++; //构造代码块
cry() ;
}
int age = 0;  //显式初始化
public Child(String name , String id , int age ){
this.name = name ;
this.id = id ;
this.age = age ;
}
public void cry(){
System.out.println("小孩正在哭,非常的伤心");
}
}
class Test{
public static void main(String[] args){
Child c1 = new Child("小明","123456789",1);
}
}


上面的情况中,显式初始化的过程出现在构造代码块之后,那么这种情况下是否会报错呢?不会的,Java编译器会将int age变量的定义提前,这也是编译器所做的非常人性化的优化工作,如图:



[b]使用三者的时机[/b]

在什么情况下应该使用构造代码块,在什么情况下应该使用构造函数,在什么情况下应该使用显式初始化,这个问题是怎么去权衡的呢?在什么情况下使用其实是由它的功能决定的,构造函数应该是在任何情况下都应该有的,但是当我们有很多的构造函数,并且在这些构造函数中有很多相同的操作,此时我们就应该考虑构造代码块来代替这些冗余的代码,也就是说构造代码块最大的一个功能就是消除冗余,消除冗余会在另外的一篇博文中说明。

[b]总结[/b]

首先明三者的区别,然后根据合适的场景去选择该怎么用是我们在做程序过程中首先考虑的。

[b]联系我[/b]

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 编译器 class