改善Java程序的151个建议--记录二(持续更新)

转载 2015年11月17日 20:52:24

建议29: 优先选择基本类型

包装类型是一个类,它提供了诸如构造方法、类型转换、比较等非常实用的功能,而且在Java 5 之后又实现了与基本类型之间的自动转换,这使包装类型如虎添翼,更是应用广泛了,在开发中包装类型已经随处可见,但无论是从安全性、性能方面来说,还是从稳定性方面来说,基本类型都是首选方案。我们来看一段代码:

public class BaseTypeClient {
	public static void main(String[] args) {
		BaseTypeClient cilent = new BaseTypeClient();
		int i = 140;
		// 分别传递int 类型和Integer 类型
		cilent.f(i);
		cilent.f(Integer.valueOf(i));
	}
	public void f(long a) {
		System.out.println(" 基本类型的方法被调用");
	}
	public void f(Long a) {
		System.out.println(" 包装类型的方法被调用");
	}
}

在上面的程序中首先声明了一个int 变量i,然后加宽转变成long 型,再调用f() 方法,分别传递int 和long 的基本类型和包装类型,诸位想想该程序是否能够编译?如果能编译输出结果又是什么呢?
这段程序绝对是能够编译的。cilent.f(i)调用f(long)这个是很正常的,但为什么cilent.f(Integer.valueOf(i))也能够编译成功并且调用f(long)这个方法呢?
这是因为自动装箱有一个重要的原则:基本类型可以先加宽,再转变成宽类型的包装类型,但不能直接转变成宽类型的包装类型。简单地说就是,int 可以加宽转变成long,然后再转变成Long 对象,但不能直接转变成包装类型,注意这里指的都是自动转换,不是通过构造函数生成。为了解释这个原则,我们再来看一个例子:

public class BaseTypeClient2 {
	public static void main(String[] args) {
		int i=100;
		f(i);
	}
	public static void f(Long l){
	}
}

这段程序编译是通不过的,因为i 是一个int 类型,不能自动转变为Long 型。但是修改成以下代码就可以编译通过了:

public class BaseTypeClient2 {
	public static void main(String[] args) {
		long i=(long)100;
		f(i);
	}
	public static void f(Long l){
	}
}

这就是int 先加宽转变为long 型,然后自动转换成Long 型。规则说明白了,我们继续来看f(Integer.valueOf(i)) 是如何调用的,Integer.valueOf(i) 返回的是一个Integer 对象, 这没错,但是Integer 和int 是可以互相转换的。没有f(Integer i) 方法?没关系,编译器会尝试转换成int 类型的实参调用,OK,这次成功了,与f(i) 相同了,于是乎被加宽转变成long型—结果也很明显了。
整个f(Integer.valueOf(i)) 的执行过程是这样的:
1、i 通过valueOf 方法包装成一个Integer 对象。
2、由于没有f(Integer i) 方法,编译器“聪明”地把Integer 对象转换成int。

3、int 自动拓宽为long,编译结束。

重申,基本类型优先考虑。


建议30: 不要随便设置随机种子

随机数在太多的地方使用了,比如加密、混淆数据等,我们使用随机数是期望获得一个唯一的、不可仿造的数字,以避免产生相同的业务数据造成混乱。在Java 项目中通常是通过Math.random 方法和Random 类来获得随机数的,我们来看一段代码:

public class Client {
	public static void main(String[] args) {
		Random r = new Random(1000);
		for(int i=1;i<4;i++){
			System.out.println(" 第"+i+" 次:"+r.nextInt());
		}
	}
}
在同一台机器上得到的结果永远是:

 第1 次:-1244746321
 第2 次:1060493871
 第3 次:-1826063944

计算机不同输出的随机数也不同,但是有一点是相同的:在同一台机器上,甭管运行多少次,所打印的随机数都是相同的,也就是说第一次运行,会打印出这三个随机数,第二次运行还是打印出这三个随机数,只要是在同一台硬件机器上,就永远都会打印出相同的随机数,似乎随机数不随机了,问题何在?
1、种子不同,产生不同的随机数。
2、种子相同,即使实例不同也产生相同的随机数。

看完上面两个规则,我们再来看这个例子,会发现问题就出在有参构造上,Random类的默认种子(无参构造)是System.nanoTime() 的返回值(JDK 1.5 版本以前默认种子是System. currentTimeMillis() 的返回值),注意这个值是距离某一个固定时间点的纳秒数,不同的操作系统和硬件有不同的固定时间点,也就是说不同的操作系统其纳秒值是不同的,而同一个操作系统纳秒值也会不同,随机数自然也就不同了。


建议34: 构造函数尽量简化
建议35: 避免在构造函数中初始化其他类

这两个建议的主要原因是在于子类在通过构造方法初始化的时候会调用父类的构造方法。


建议39: 使用匿名类的构造函数

阅读如下代码,看看是否可以编译:
public static void main(String[] args) {
	List l1 = new ArrayList();
	List l2 = new ArrayList(){};
	List l3 = new ArrayList(){{}};
	System.out.println(l1.getClass() == l2.getClass());
	System.out.println(l2.getClass() == l3.getClass());
	System.out.println(l1.getClass() == l3.getClass());
}

注意ArrayList 后面的不同点:l1 变量后面什么都没有,l2 后面有一对{},l3 后面有2对嵌套的{},这段程序能不能编译呢?若能编译,那输出是多少呢?
答案是能编译,输出的是3 个false。l1 很容易解释,就是声明了ArrayList 的实例对象,那l2 和l3 代表的是什么呢?
1、l2=new ArrayList(){}
l2 代表的是一个匿名类的声明和赋值,它定义了一个继承于ArrayList 的匿名类,只是没有任何的覆写方法而已,其代码类似于:
// 定义一个继承ArrayList 的内部类
class Sub extends ArrayList{
}
// 声明和赋值
List l2 = new Sub();

2、l3=new ArrayList(){{}}

这个语句就有点怪了,还带了两对大括号,我们分开来解释就会明白了,这也是一个匿名类的定义,它的代码类似于:

// 定义一个继承ArrayList 的内部类
class Sub extends ArrayList{
	{
	// 初始化块
	}
}
// 声明和赋值
List l3 = new Sub();

就是多了一个初始化块而已,起到构造函数的功能。我们知道一个类肯定有一个构造函数,且构造函数的名称和类名相同,那问题来了:匿名类的构造函数是什么呢?
它没有名字呀!很显然,初始化块就是它的构造函数。当然,一个类中的构造函数块可以是多个,也就是说可以出现如下代码:

List l3 = new ArrayList(){{}{}{}{}{}};

上面的代码是正确无误,没有任何问题的。现在清楚了:匿名函数虽然没有名字,但也是可以有构造函数的,它用构造函数块来代替,那上面的3 个输出就很清楚了:虽然父类相同,但是类还是不同的。

相关文章推荐

读书笔记--编写高质量代码:改善java程序的151个建议(二)匿名类与构造代码块

读书笔记--编写高质量代码:改善java程序的151个建议(二)匿名类与构造代码块 使用构造代码块精炼程序 什么叫代码块(Code Block)?用大括号把多行代码封装在一起,形成一个独立的数...

编写高质量代码:改善Java程序的151个建议 勘误 [不断更新]

最近在读这本书,不可否认是本好书,不过错误难免,对于书中的一些有所异议,所以记录在案,仅供参考: 1   20条建议: 34页, 什么会编译进CLASS, 笔者说是final基本类型或者st...

编写高质量代码:改善Java程序的151个建议 (第3章 类、对象及方法)

第3章  类、对象及方法 书读得多而不思考,你会觉得自己知道的很多。 书读得多而思考,你会觉得自己不懂的越来越多。 —伏尔泰 在面向对象编程(Object-Oriented Programmi...

读改善java程序的151个建议(1)

1.不要在常量和变量中出现容易混淆的字母    例如:L 的小写l  所以字母l作为长整型时务必大写 2.务必要让常量的值在运行期间保持不变      interface Const{ ...

改善Java程序的151个建议 笔记

四舍六入五取偶 Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。 包装类型,基本类型的函数参数,可以构成重载。 包装类型,不能自动加宽。 ...

Integer 之 int (来自 改善java程序的151个建议)

java 引入包装类型,是为了解决基本类型实例化问题,以便让一个基本类型也能参与到面向对象的编程世界中。而在java5中,泛型更是对基本类型说了“不”。 1、谨慎包装类型null值 如果想把...

读改善java程序的151个建议(7)

66.asList方法产生的list对象不可更改 这里说的asList方法,是指Arrays工具类中的方法,与建议65相同 67.不同的列表选择不同的遍历方法 这里主要说的是foreac...

读改善java程序的151个建议(8)

72.生成子列表后,不要再操作原列表 先看例子: ListString > list=new ArrayListString>();          ...

编写高质量代码:改善Java程序的151个建议 (第2章 基本类型)

第2章 基本类型 不积跬步,无以至千里; 不积小流,无以成江海。 —荀子《劝学篇》 Java中的基本数据类型(Primitive Data Types)有8个:byte、cha...

读改善java程序的151个建议(6)

60.性能考虑,数组是首选 在性能要求比较高的场景中考虑使用数组替代集合。 61.若有必要,使用变长数组 java中的数组是定义的,一旦经过初始化声明就不可改变长度,这在实际使用中非常不...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)