文章目录
  1. 1. 第45条:将局部变量的作用域最小化
  2. 2. 第46条:for-each循环优先于传统的for循环
  3. 3. 第47条:了解和使用类库
  4. 4. 第48条:如果需要精确地答案,请避免使用float和double
  5. 5. 第49条:基本类型优于装箱的基本类型
  6. 6. 第50条:如果其他类型更合适,则尽量避免使用字符串
  7. 7. 第51条:当心字符串连接的性能
  8. 8. 第52条:通过接口引用对象
  9. 9. 第53条:接口优于反射机制
  10. 10. 第54条:谨慎的使用本地方法
  11. 11. 第55条:谨慎的进行优化
  12. 12. 第56条:遵守普遍接受的命名惯例

第45条:将局部变量的作用域最小化

将局部变量的作用域最小化,可以增强代码的可读性和可维护性,并降低出错的可能性。

这一条看字面意思应该已经非常明了了,就是教我们在申明局部变量的时候注意其作用域,将其作用域最小化:

  • 比如有些判断的变量在if语句里面完成就好
  • 还有循环里面for优于while,因为for往往不需要其他的额外局部变量
  • 另一种就是使方法小而集中

第46条:for-each循环优先于传统的for循环

利用for-each循环不会有性能损失,甚至用于数组也是一样的

1
2
3
4
5
6
7
8
9
10
11
Collection<Integer> c=Arrays.asList(1,2,3);
//普通的for循环
for(Iterator iter=c.iterator();iter.hasNext())
{
doSomething(iter.next());
}
//使用for-each的版本
for(Integer v:c)
{
doSomething(v);
}

还有一点是如果你使用普通的for,那么它的next会走光所有的元素,也就无法再利用。

第47条:了解和使用类库

程序猿的类库就是武器弹药,没有弹药如何去写出一首好程序

通过使用标准类库:

  1. 可以充分利用这些数据编写标准类库和专家知识,以及在你之前的其他人得使用经验
  2. 就是相当于不必浪费时间去重复造轮子
  3. 它的性能往往回伴随着时间推移而不断提高
  4. 还可以使自己的代码融入主流。

第48条:如果需要精确地答案,请避免使用float和double

floatdouble为了再广泛地数值范围上提供较为精确地快读近似计算,它们并没有提供完全精确地经过,所以不应该用于需要精确结果的场合。

比如:

1
System.out.println(1.03-.42);

你会惊奇的发现它输出的结果为:

0.6100000000000001

关于这点可以使用BigDecimal这个类来代替它们

1
2
BigDecimal f=new BigDecimal("1.03");
System.out.println(f.subtract(new BigDecimal("0.42")));

这样就能支持得到精确地结果了。

总而言之,对于任何需要精确答案的计算任务,请不要使用float或者double。如果你想让系统记录十进制的小数点,并且不介意因为不使用基本类型而带来的不便,那么就请使用BigDecimal

第49条:基本类型优于装箱的基本类型

Java的类型系统由两部分组成:

  • 基本类型:int,double,boolean还有引用类型StringList等。
  • 装箱基本类型:对应上面的分别为Integer,DoubleBoolean

基本类型和基本装箱类型有三个主要区别:

  1. 基本类型只有值,而装箱基本类型则具有与它们的值不同的同一性。
  2. 基本类型只有功能完备的值,而每个装箱类型还具有肺功能值:null
  3. 基本类型通常比装箱基本类型更加省空间和时间。(我觉得这点才是本条的关键点吧^_^)

现在先来看一个比较器:

1
2
3
4
5
6
Comparator<Integer> naturalOrder=new Comparator<Integer>(){
public int compare(Integer first,Integer second)
{

return first<second?-1 :(first==second?0:1);
}
};

我们如果使用这个比较器

1
System.out.println(naturalOrder.compare(new Integer(42), new Integer(42)));

你会发现输出的结果竟然是1(我估计是装箱基本类型同一性的问题)

1
2
3
4
5
6
//直接调用Integer比较的API
return first.compareTo(second);
//或者自己进行拆箱
int f=first;
int s=second;
return f<s?-1 :(f==s?0:1);

还有再来看这个程序

1
2
3
4
5
6
7
8
9
static Integer i;

public static void main(String[] args)
{

if(i==42)
{
System.out.println("unbelievable");
}
}

你会收到一个NPE的异常,因为Integer的默认初始值为null,当然是无法进行拆箱啦。

再来看一个性能问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args)
{

long curTime=System.currentTimeMillis();
Long s1=0L;
for(long i=0;i<Integer.MAX_VALUE;i++)
s1+=i;
System.out.println("s1="+s1+",and spend="+(System.currentTimeMillis()-curTime));

curTime=System.currentTimeMillis();
long s2=0L;
for(long i=0;i<Integer.MAX_VALUE;i++)
s2+=i;
System.out.println("s2="+s1+",and spend="+(System.currentTimeMillis()-curTime));
}

你会惊奇的发现

s1=2305843005992468481,and spend=6286
s2=2305843005992468481,and spend=720

使用Long运算的效率要比基本类型慢了很多,这是因为要都要拆箱装箱的缘故。

所以:基本类型优于装箱的基本类型

但是,比如类型HashMap的定义的类型还真必须是Integer之类的引用

第50条:如果其他类型更合适,则尽量避免使用字符串

字符串,我们都知道,这个很好用。。。

但是其实很多情况下,有比用字符串更加好的解决方案:

  • 字符串不适合代替其他的值类型
  • 字符串不适合代替聚集类型
  • 字符串不适合代替枚举类型
  • 字符串也不适合代替能力表

总之,如果可以使用更加合适的数据类型,或者可以编写更加适当的数据类型,就应该避免用字符串来表示对象。

第51条:当心字符串连接的性能

这条是面试常问的问题,众所周知,字符串的连接性能很差,因为字符串是不可变,也就是说没连接一下次就会重新生成一个新的更大的字符串,所以推荐使用StringBuilder

第52条:通过接口引用对象

对应第40条建议:应该使用接口而不是类作为参数的类型,这条的建议是应该优先使用接口而不是类做做引用对象。

现在来看这个

1
2
3
List<Subscriber> subscribers=new Vector<Subscriber>();//好的写法
Vector<Subscriber> subscribers=new Vector<Subscriber>();//这个写法不好,太不灵活了
List<Subscriber> subscribers=new ArrayList<Subscriber>();//这个写法也好,可以直接与第一种替代

如果没有合适的接口存在,可以考虑是否有基类可以用来定义类型,否则完全可以使用实现类来引用对象。

第53条:接口优于反射机制

核心反射机制提供了“通过程序了访问已装载的类的信息”(反射大家应该都懂的)

虽然反射使用起来很方法,并且也很灵活,但是使用它的代价很大:

  • 丧失了编译时类型检查的好处
  • 执行反射访问所需要的代码非常笨拙和冗余
  • 性能损失很大

来看一下 下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//定义一个接口
public interface Base {
public void sayHello();
}

//将该接口进行实现
public class Hello implements Base{
@Override
public void sayHello()
{

System.out.println("hello~~~~~~~~~~~");
}
}

//开始调用sayHello
public static void main(String[] args) throws Exception
{

System.out.println(System.currentTimeMillis());
for(int i=0;i<100;i++)
say(new Hello());
System.out.println(System.currentTimeMillis());
for(int i=0;i<100;i++)
say("yyl.java.study.Hello");
System.out.println(System.currentTimeMillis());
}

/**
* 使用接口方法来调用
* @param hello
*/

public static void say(Base hello)
{

hello.sayHello();
}

/**
* 使用反射的方法来调用
* @param className
* @throws Exception
*/

public static void say(String className) throws Exception
{

Class cl=Class.forName(className);
Object obj=cl.newInstance();
Method m=cl.getMethod("sayHello");

if(m!=null)
m.invoke(obj);//方法的调用
}

最终的输出结果为:

1434817643669
//....
1434817643672
//....
1434817643681

从上面的小实验中可以发现,使用接口比使用反射速度要快3倍左右,但是这其实并不意味着将反射抛弃,然而反射的特点很灵活,比如className可以是当初编译时不存在类,并且反射主要用于组件的开发,一般情况下都是记录缓存,并不是会多次调用反射来取到可执行方法。

第54条:谨慎的使用本地方法

JNI准许Java应用程序可以调用本地方法(比如c/c++)编写的,主要是因为本地方法可以

  • 访问注册表、文件锁
  • 访问遗留代码库
  • 最重要的还是提升性能啊

但是随着JVM的发展,这些功能应该add到最近的JVM中了,并且其本地代码的性能优势也应该没有之前那么明显了,比如BigInteger已经完全用Java重写了。

然而,使用本地方法有一些重要的缺点:

  • 本地方法的语言不是安全的
  • 本地方法语言与平台有关,可移植性不高
  • 本地方法更难调试,如果写得不好,性能也许反而会降低

所以,慎用本地方法

当然,我觉得在某些地方还是得用,Java中的system.arraycopy不就是一个本地方法嘛。

第55条:谨慎的进行优化

优化利大于弊,特别是不成熟的优化 赞同^_^

那难道就不优化嘛?不!这条是建议我们不要费力去编写快速的程序,而应该努力去编写好的程序,速度自然会随之而来(这并不是先思考,再下手嘛)
如果是实在不行,这个优化得专家来做,并且要经常进行性能测试,直到满意。-_-

第56条:遵守普遍接受的命名惯例

Java平台建立了一套很好的命名规范,这些规范可以大大让你的团队更好地开展活动

字面上的命名

    • 包得名称应该是层次状的,用句号分割每个部分
    • 包名称的其余部分应该包括一个或者多个描述的组成部分
  • 类和接口名称,包括枚举和注解,都应该包括一个或者多个单词,每个单词的首字母大写
  • 方法和域的名称一般使用小驼峰法,整个名称的第一个字母小写,以后每个单词的首字母大写
  • 域常量一般全部字母都大写,每个单词使用下划线作为分割
  • 局部变量允许缩写
  • 类型参数名一般由单个大写字母表示,比如T,E,K,V.

语法上的命名

  • 接口建议以able结尾,比如Runnable
  • 执行某个东西的方法根据动作命名,比如返回布尔值的方法一般以is开头
  • 如果被调用方法返回一个非布尔值,一般以get开头
  • 类型转换的方法一般以to开头

这些命名规则一般都是约定俗成了,看着也习惯。

文章目录
  1. 1. 第45条:将局部变量的作用域最小化
  2. 2. 第46条:for-each循环优先于传统的for循环
  3. 3. 第47条:了解和使用类库
  4. 4. 第48条:如果需要精确地答案,请避免使用float和double
  5. 5. 第49条:基本类型优于装箱的基本类型
  6. 6. 第50条:如果其他类型更合适,则尽量避免使用字符串
  7. 7. 第51条:当心字符串连接的性能
  8. 8. 第52条:通过接口引用对象
  9. 9. 第53条:接口优于反射机制
  10. 10. 第54条:谨慎的使用本地方法
  11. 11. 第55条:谨慎的进行优化
  12. 12. 第56条:遵守普遍接受的命名惯例