使用构造代码块精简程序
来源:互联网 发布:美微网络电视柠檬tv 编辑:程序博客网 时间:2024/06/05 15:21
什么叫做代码块(Code Block)?用大括号把多行代码封装在一起,形成一个独立的数据体,实现特定算法的代码集合即为代码块,一般来说代码快不能单独运行的,必须要有运行主体。在Java中一共有四种类型的代码块:
- 普通代码块:就是在方法后面使用"{}"括起来的代码片段,它不能单独运行,必须通过方法名调用执行;
- 静态代码块:在类中使用static修饰,并用"{}"括起来的代码片段,用于静态变量初始化或对象创建前的环境初始化。
- 同步代码块:使用synchronized关键字修饰,并使用"{}"括起来的代码片段,它表示同一时间只能有一个线程进入到该方法块中,是一种多线程保护机制。
- 构造代码块:在类中没有任何前缀和后缀,并使用"{}"括起来的代码片段;
我么知道一个类中至少有一个构造函数(如果没有,编译器会无私的为其创建一个无参构造函数),构造函数是在对象生成时调用的,那现在为你来了:构造函数和代码块是什么关系,构造代码块是在什么时候执行的?在回答这个问题之前,我们先看看编译器是如何处理构造代码块的,看如下代码:
public class Client36 { { // 构造代码块 System.out.println("执行构造代码块"); } public Client36() { System.out.println("执行无参构造"); } public Client36(String name) { System.out.println("执行有参构造"); }}
这是一段非常简单的代码,它包含了构造代码块、无参构造、有参构造,我们知道代码块不具有独立执行能力,那么编译器是如何处理构造代码块的呢?很简单,编译器会把构造代码块插入到每个构造函数的最前端,上面的代码等价于:public class Client36 { public Client36() { System.out.println("执行构造代码块"); System.out.println("执行无参构造"); } public Client36(String name) { System.out.println("执行构造代码块"); System.out.println("执行有参构造"); }}
每个构造函数的最前端都被插入了构造代码块,很显然,在通过new关键字生成一个实例时会先执行构造代码块,然后再执行其他代码,也就是说:构造代码块会在每个构造函数内首先执行(需要注意的是:构造代码块不是在构造函数之前运行的,它依托于构造函数的执行),明白了这一点,我们就可以把构造代码块应用到如下场景中:
- 初始化实例变量(Instance Variable):如果每个构造函数都要初始化变量,可以通过构造代码块来实现。当然也可以通过定义一个方法,然后在每个构造函数中调用该方法来实现,没错,可以解决,但是要在每个构造函数中都调用该方法,而这就是其缺点,若采用构造代码块的方式则不用定义和调用,会直接由编译器写入到每个构造函数中,这才是解决此问题的绝佳方式。
- 初始化实例环境:一个对象必须在适当的场景下才能存在,如果没有适当的场景,则就需要在创建该对象的时候创建次场景,例如在JEE开发中,要产生HTTP Request必须首先建立HTTP Session,在创建HTTP Request时就可以通过构造代码块来检查HTTP Session是否已经存在,不存在则创建之。
以上两个场景利用了构造代码块的两个特性:在每个构造函数中都运行和在构造函数中它会首先运行。很好的利用构造代码块的这连个特性不仅可以减少代码量,还可以让程序更容易阅读,特别是当所有的构造函数都要实现逻辑,而且这部分逻辑有很复杂时,这时就可以通过编写多个构造代码块来实现。每个代码块完成不同的业务逻辑(当然了构造函数尽量简单,这是基本原则),按照业务顺序一次存放,这样在创建实例对象时JVM就会按照顺序依次执行,实现复杂对象的模块化创建。
构造代码块会想你所想
编译器是不是足够聪明,能为我们解决真实的开发问题,有这样一个案例,统计一个类的实例变量数。你可要说了,这很简单,在每个构造函数中加入一个对象计数器补救解决了嘛?或者我们使用上一建议介绍的,使用构造代码块也可以,确实如此,我们来看如下代码是否可行:
public class Client37 { public static void main(String[] args) { new Student(); new Student("张三"); new Student(10); System.out.println("实例对象数量:"+Student.getNumOfObjects()); }}class Student { // 对象计数器 private static int numOfObjects = 0; { // 构造代码块,计算产生的对象数量 numOfObjects++; } public Student() { } // 有参构造调用无参构造 public Student(String stuName) { this(); } // 有参构造不调用无参构造 public Student(int stuAge) { } //返回在一个JVM中,创建了多少实例对象 public static int getNumOfObjects(){ return numOfObjects; }}
这段代码可行吗?能计算出实例对象的数量吗?如果编译器把构造代码块插入到各个构造函数中,那带有String形参的构造函数就可能有问题,它会调用无参构造,那通过它生成的Student对象就会执行两次构造代码块:一次是无参构造函数调用构造代码块,一次是执行自身的构造代码块,这样的话计算就不准确了,main函数实际在内存中产生了3个对象,但结果确是4。不过真的是这样吗?我们运行之后,结果是:
实例对象数量:3;
实例对象的数量还是3,程序没有问题,奇怪吗?不奇怪,上一建议是说编译器会把构造代码块插入到每一个构造函数中,但是有一个例外的情况没有说明:如果遇到this关键字(也就是构造函数调用自身的其它构造函数时),则不插入构造代码块,对于我们的例子来说,编译器在编译时发现String形参的构造函数调用了无参构造,于是放弃插入构造代码块,所以只执行了一次构造代码块。
那Java编译器为何如此聪明?这还要从构造代码块的诞生说起,构造代码块是为了提取构造函数的共同量,减少各个构造函数的代码产生的,因此,Java就很聪明的认为把代码插入到this方法的构造函数中即可,而调用其它的构造函数则不插入,确保每个构造函数只执行一次构造代码块。
还有一点需要说明,大家千万不要以为this是特殊情况,那super也会类似处理了,其实不会,在构造块的处理上,super方法没有任何特殊的地方,编译器只把构造代码块插入到super方法之后执行而已。仅此不同。
注意:放心的使用构造代码块吧,Java已经想你所想了。
- 使用构造代码块精简程序
- 使用构造代码块精炼程序
- 使用构造代码块精炼程序
- JAVA构造代码块函数使用简介
- 一个测试程序看懂静态代码块、构造代码块以及构造方法的执行顺序
- 构造块/静态代码块
- 构造函数、构造代码块
- 构造函数、构造代码块
- 构造函数、构造代码块
- 构造代码块
- 构造代码块
- 构造代码块
- 构造代码块
- 构造代码块、equals
- 构造代码块
- java构造代码块
- 构造代码块
- java-构造代码块
- File类(1)——构造方法和功能实现
- struts出现Developer Notification错误时
- Linux:文件编程
- Android 反编译(一)反编译apk
- 连接CentOS下的Redis的准备工作
- 使用构造代码块精简程序
- Oracle Application Context详解(原创)
- SuperMap iExpress常见问题解答集锦(一)
- Nexus启动失败处理:The nexus service was launched, but failed to start.
- LeetCode Longest Common Prefix
- Kotlin与Java的共存-Java中调用kotlin代码
- Nginx源码阅读(worker进程处理http请求流程)
- 《python数据分析读书笔记》--- (一)数据探索
- 使用Jenkins配置SpringBoot的自动化构建