效率编程 之「通用程序设计」
第 1 条:将局部变量的作用域最小化
要使局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方声明。局部变量的作用域从它被声明的点开始扩展,一直到外围块的结束处。如果变量是在“使用它的块”之外被声明的,当程序退出该块之后,该变量仍然是可见的;如果变量在它的目标使用区域之前或者之后被意外地使用的话,后果将可能是灾难性的。
几乎每个局部变量的声明都应该包含一个初始化表达式。如果我们还没有足够的信息来对一个变量进行有意义的初始化,就应该推迟这个声明,直到可以初始化为止。这条规则有一个例外的情况与try-catch
语句有关。如果一个变量被一个方法初始化,而这个方法可能会抛出一个受检的异常,该变量就必须在try
块的内部被初始化;如果变量的值必须在try
块的外部使用,它就必须在try
块之前被声明,但是在try
块之前,它还不能被“有意义地初始化”。
循环中提供了特殊的机会来将变量的作用域最小化。无论是传统的还是高级的for
循环,都允许声明循环变量,它们的作用域被限定在正好需要的范围之内,这个范围包括循环体,以及循环体之前的初始化、测试、更新部分。因此,如果在循环终止之后不再需要循环变量的内容,for
循环就优先于while
循环。考虑下面的代码片段,它包含两个while
循环以及一个Bug
:
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println("列表一中的元素:" + it.next());
}
Iterator<String> it2 = list2.iterator();
while (it.hasNext()) {
System.out.println("列表二中的元素:" + it.next());
}
如上述代码所示,第二个循环中包含了一个“剪切-粘贴”错误:它本来是要初始化一个新的循环变量it2
,却使用了旧的的循环变量it
,遗憾的是,这是it
仍然还在有效范围之内。结果就是,代码仍然可以通过编译,运行的时候也不会抛出异常,但是它所做的事情却是错误的。反之,如果上述的“剪切-粘贴”错误出现在for
循环中,结果代码就根本不可能通过编译。此外,高级的for-each
循环优于传统的for
循环。考虑下面的代码片段,它也包含一个Bug
:
Collection<Face> faces = Arrays.asList(Face.values());
for (Iterator<Face> i = faces.iterator(); i.hasNext(); ) {
for (Iterator<Face> j = faces.iterator(); j.hasNext(); ) {
System.out.println(i.next() + " " + j.next());
}
}
上面的问题在于,在迭代器上对外部的集合调用了太多次next()
方法了。它应该从外部循环进行调用,以便在每一次内部循环中前一次骰子的每一面只调用一次,但它却是从内部循环调用,因此它是每一面调用一次。在用完所有面之后,就会抛出NoSuchElementException
异常。如果真的那么不幸,并且外部集合的大小是内部集合大小的几倍,可能因为它们是相同的结合,循环就会正常终止,但是不会完成我们想要的工作。如果使用的嵌套的for-each
循环,则不会出现上面的Bug
,例如:
for (Face face1 : faces) {
for (Face face2 : faces) {
System.out.println(face1 + " " + face2);
}
}
最后一种“将局部变量的作用域最小化”的方式是使方法小而集中。如果把两个操作合并到同一个方法中,与其中一个操作相关的局部变量就有可能会出现在执行另一个操作的代码范围之内。为了防止这种情况发生,只要把这个方法分成两个,每个方法各执行一个操作即可。
第 2 条:基本类型优先于装箱基本类型
在基本类型和装箱基本类型之间,有三个主要的区别:
- 第一,基本类型只有值,而装箱基本类型则具有与它们的值不同的统一性。换句话说,两个装箱基本类型可以具有相同的值和不同的统一性。
- 第二,基本类型只有功能完备的值,而每个装箱类型除了它对应基本类型的所有功能值之外,还有个非功能值
null
。 - 最后一点区别是,基本类型通常比装箱基本类型更节省时间和空间。
对装箱基本类型运用==
操作符几乎总是错误的。几乎在任何一种情况下,当在一项操作中混合使用基本类型和装箱基本类型时,装箱基本类型都会自动拆箱,这种情况无一例外。如果`null
对象引用被自动拆箱的话,就会得到一个NPE
异常。那么什么时候应该使用装箱基本类型呢?它们有几个合理的用处:
- 第一个,作为集合中的元素、键和值。我们不能将基本类型放在集合中,因此必须使用装箱基本类型。
- 第二个,在参数化类型中,必须使用装箱基本类型作为参数,因为 Java 不允许使用基本类型。例如,我们不能将变量声明为
List<int>
,而应该用List<Integer>
来代替。 - 最后,在进行反射的方法调用时,必须使用装箱基本类型。
总之,当可以选择的时候,基本类型要优先于装箱基本类型。基本类型更加简单,也更加快速。如果必须使用装箱基本类型,要特别小心!自动装箱减少了使用装箱基本类型的繁琐性,但是并没有减少它的风险。
- 有故事的微信小游戏“跳一跳”
- 机器学习敲门砖:任何人都能看懂的TensorFlow介绍
- 跟小编来体验一下微信小程序
- DeepVO:基于深度循环卷积神经网络的端到端视觉里程计
- 是人工智障还是蹭热度?我一定是抱走了假的二次元老婆
- 重庆日报:丹妮拉·鲁斯认为人工智能是人类的助手
- 深度学习系列(2):前向传播和后向传播算法
- 未来5年,什么样的人最抢手
- 腾讯AI Lab俞栋:AI的发展需要合作伙伴一起AI in ALL
- NLP+2vec︱认识多种多样的2vec向量化模型
- 深度学习数据集(一)
- 将Spring Boot应用程序部署到Bluemix
- Opencv学习(一)——安装配置遇到的问题
- The .NET of Tomorrow
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- redis学习(十一)
- 互联网软件常见开发方法
- 利用Vue实现简易tab切换效果
- 第3天:最近笔试编程题汇总
- 设计模式学习(四)-UML中的类图及类图之间的关系
- 数据结构与算法(一)——学习工具的推荐
- sparc v8架构的异常处理
- 设计模式(五)-工厂方法模式
- html 的div或其他元素监听 resize事件不生效的解决办法
- 基于docker部署skywalking实现全链路监控
- 数据结构与算法(二)——十大排序算法
- VUE项目使用.env文件配置全局环境变量
- 设计模式学习(六)-抽象工厂模式
- 彻底完美解决安卓苹果手机点击输入框网页页面自动放大缩小
- 第22天:NLP实战(六)——基于PaddleHub的疫情期间网民情绪识别