现在的位置: 首页 > 综合 > 正文

读《Effective java 中文版》(4)

2013年12月07日 ⁄ 综合 ⁄ 共 5160字 ⁄ 字号 评论关闭

FROM 竹笋炒肉 http://hedong.3322.org/

读《Effective java 中文版》(4)
第30条:了解和使用库
  不要从头发明轮子。
  使用的标准库的益处:

通过使用标准库,可以充分利用这些编写标准库的专家的知识,以及其它人的使用经验。
不必浪费时间为那些与工作关系不大的问题提供特别的解决方案。精力应该在应用上,而不是在底层的细节上。
它的性能会随着版本更新不断提高,而你不必须为它做任何工作。
使自己的代码融入主流。

 

  每一个程序员都应该熟悉java.lang和java.util,以及java.io中的内容。如集合框架(Collections Framework),它以6个集合接口Colection, Set, List, Map, SortedSet, SortedMap为基础, 以前的Vector, HashTable被更新以后也加入到这里。另外,java.util.regex(正则式工具), java.util.prefs(配置信息管理工具), java.nio(高性能的io工具), java.util.LinkedHashSet/LinkedHashMap/IdentityHashMap(新的集合实现)也都值得特别关注。

 

 

读《Effective java 中文版》(32)
第31条:如果要求精确的答案,请避免使用float和double.
  float和double类型的主要设计目标是为了科学计算和工程计算,采用二进制浮点运算,不适于要求精确结果的场合,float与dobule尤其不合适于货币计算。
  对于货币计算的解决方案是:

采用int类型,自己处理小数位,数值不超过9位
采用long类型,自己处理小数位,数值不超过18位
采用BigDecimal类型,不是很方便且运算慢,但可以从8种舍入方式中进行选择以灵活控制

 

读《Effective java 中文版》(33)
第32条:如果其它类型晚适合,则尽量避免使用字符串

  字符器不适合代替其它的值类型:

字符串不适合代替枚举类型,应该采用类型安全枚举类型(参见第21条)或int值。

字符串不适合代替聚集类型,即将实体的多个属性放在一个字符串中,属性间以某一分隔符相间。
最好的办法,是编写一个类,通常会用静态成员类(见18条).

字符器也不适合代替权限对象。如例:
//Broken - inappropriate use of string as capability!
public class ThreadLocal{
private ThreadLocal(){}//noninstantiable
public static void set(String key,Object value);
public static Object get(String key);
}
  key是一种权限描述。这个设计有安全问题。下面是用不可伪造的键来代替字符串。
public class ThreadLoacl{
private ThreadLocal()//noninstantiable
public static class Key{
Key(){}
}
public static key getKey()
return new Key();
}
public static void set(Key key,Object value);
public static Object get(Key key);
}
  根据具体情况,这个类还可进一步优化。

 

读《Effective java 中文版》(34)
第33条:了解字符串连接的性能
  为连接n个字符串而重复地使用字符串连接操作符,将需要n的平方级的时间。这是由于字符器是非可变的(见第13条),当两个字符串被连接的时候,它们的内容都要被拷贝。为了获得可接受的性能,使用StringBuffer替代String.

  下面的例子被优化后,性能是优化前的数十倍。
public String statement(){
String s="";
for (int i=0;i s += lineForItem(i);
return s;
}
  优化后如下:
public String statement(){
StringBuffer s=new StringBuffer(numItems()*LINE_WIDTH);
for (int i=0;i s.append(lineForItem(i));
return s.toString();
}

 

读《Effective java 中文版》(35)
第34条:通过接口引用对象
  第25条中建议过:应使用接口而不是类作为参数的类型。更进一步,应该优先使用接口而不是类来引用对象。只有当创建某个对象的时候,才需要引用这个对象的类。
  这一条好象有点偏激,hehe.

  例子:
//Good -uses interface as type
List subscribers=new Vector();
//Bad - use class as type!
Vector subscribers=new Vector();
  这么做的最大好处,就是使程序更加灵活。接着上面的例子,如果你的程序中使用的Vector的方法,ArrayList方法都有,且你现在由于某种原因更喜欢使用ArrayList,则只需要改动一行就可以了。即:
Vecotr subscribers=new ArrayList();

  当然,也有一些情况,适合于用类来引用对象:

没有合适的接口存在,如值类就很少有多个实现且多是final的。

对象属于一个框架,而框架的基本类型是类,则应该用基类来引用对象。

一个类实现了某个接口,但它提供了接口中不存在的额外方法,而程序依赖于这额外的方法,则只能用类来引用这个对象。

  总之,如果有接口,则用接口来引用对象,如果没有则使用类层次结构中提供了所需功能的最高层的类。

 

 

读《Effective java 中文版》(36)
第35条:接口优先于映像机制
  映像设施(reflection facility)java.lang.reflect,提供了通过程序来访问善于已装载的类的信息。映像机制允许一个类使用另一个类,即使当前者被编译时后者还根本不存在。映像设施最初是为了基于组件的应用创建工具而设计的。这样的工具通常要根据需要装载类,并且用映偈功能找出它们支持哪些方法和构造函数。映像功能是在设计时刻被使用的:通常,普通应用在运行时刻不应该以映像方式访问对象,因为使用映像是有代价的:

损失了编译时类型检查和异常检查的好处
要求执行映像访问的代码非常笨拙和冗长
性能损失

 

  一些复杂的应用程序需要使用映像机制,如浏览器、对象监视器、代码分析工具、内嵌的解释器、RPC系统等。
  如果某个类在程序编译时不可用,但其接口或超类可用,则可以用映像方式创建实例,然后通过它们的接口或超类,以正常的方式访问这些实例。如果存在构造函数没有参数,则只需要Class.newInstance()而无需reflection来创建实例。看例子:
//Reflective instantiation with interface access
public static void main(String[] args){
Class cl=null;
try{
c1=Class.forName(args[0]);
}catch(ClassNotFoundException e){
System.err.println("CLass not found");
System.exit(1);
}
Set s=null;
try{
s=(Set)c1.newInstance();
}catch(IllegalAccessException e){
System.err.println("Class not Accessible");
System.exit(1);
}catch(InstantiationException e){
System.err.println("Class not instantiable");
System.exit(1);
}
s.addAll(Arrays.asList(args)).subList(1,args.length-1));
System.out.println(s);
}
  如果第一个参数为java.util.HashSet则输出是随机顺序,如果是java.util.TreeSet则按字母排序输出。例子演示的技术,可以用来实现服务提供者框架(service provider framewrok)。在这个例子中,三个异常都是在不使用映像时的编译时错误,而且代码比较冗长。

 

读《Effective java 中文版》(37)
第36条:谨慎地使用本地方法
  Java Native Interface允许java应用可以调用本地方法(用C/C++等本地程序语言来编写的特殊方法),用途有三:

访问与平台相关的设施
访问老式代码库或数据库
实现应用的关键部分以提高性能

  使用本地方法也有一些缺点:
本地语言不安全
难以移植
进入、退出本地代码需要开销.

  随着java的发展和优化,其三种用途大都有相应的替代方案。所以谨慎使用本地方法。

 

读《Effective java 中文版》(38)
第37条:谨慎地进行优化
  不要费力地去编写快速的程序--应该努力编写好的程序,速度自然会随之而来。在设计系统的时候,特别是在设计API、线路层协议和永久数据格式的时候,要考虑性能的因素。在每次试图做优化之前和之后,要借助性能分析工具对性能进行分析。
  考虑API设计决定的性能后果。如使一个公有的类型成为可变的,则可能会导致大量的保护性拷贝(参见第24条);该用复合模式时使用了类继承,人为地将子类和超类绑在了一起(参见第14条);在API中使用实现类型则不是接口,会把应用束缚在一个具体的实现上(参见第34条)等。一般而言,好的API设计也伴随着好的性能。

 

读《Effective java 中文版》(39)
第38条:遵守普遍接受的命名惯例
  java的命名惯例分为两大类:字面的和语法的。

  字面命名惯例涉及包、类、接口、方法和域。

包的名字是层次结构的,用句号分隔第一部分。每一部分的长度不要超过8,由小写字母和数字组成(数字少见用),鼓励使用有意义的缩写。除了java和javax外,一般以域名做开头,顺序是顶级域名放在最前面。

类和接口的名字应至少1至多个单词,每个单词的首字母大写,尽量避免缩写。

方法和域的名字与类和接口的名字遵守相同的字面惯例,只是第一个首字母要小写。常量域要全部字母都大写,词之间通过下划线区分。

  语法命名惯例比字面惯例更灵活。
对于包而言,没有语法命名惯例。

类通常用一个名词或名词短语,接口或者与类相同,或者以"-able"或"-ible"结尾的形容词。

执行某个动作的方法,常用一个动词或动词短语,
对于返回boolean类型的方法,名字常以“is"开头后加一个名词或形容词或短语,
如果返回的不是boolean,则常用一个名词/短语,或以"get"开头的动词短语。
如果一方法所在的类是一个Bean,则强制要求以get开头。
如果类包含对属性操作,常用setAttribute或getAttribute格式命名。
转换对象类型的方法,
如果返回不同类型的独立的对象,则称为toType
如果返回一个视图,则用asType,
如果返回与被调用对象同值的原语类型,称为typeValue
静态工厂的方法,常用valueOf或getInstance.

域的命名没有很好地建立。

 

读《Effective java 中文版》(40)
第39条:只针对不正常的条件才使用异常
  异常只应该被用于不正常的条件,它们永远不应被用于正常的控制流。

  下面是一个用异常作遍历结束条件的滥用异常的例子:
//horrible abuse of exceptions. Don't ever do this!
try{
int i=0;
while(true)a[i++].f();
}catch(ArrayIndexOutOfBoundsException e){
}
  其错有三:

创建、抛出和捕获异常的开销是很昂贵的。因为它的初衷是用于不正常的情形,少有jvm会它进行性能优化。

把代码放在try-catch中会阻止jvm实现本来可能要执行的某些特定的优化。

有些现代的jvm对循环进行优化,不会出现冗余的检查。

  这条原则也适用于API设计。一个设计良好的API不应该强迫它的客户为了正常的控制流而使用异常。如果类中有一个”状态相关”的方法,即只有特定的条件下可被调用的方法,则这个类也应有一个单独的“状态测试”方法,以为调用这个状态相关方法前的检查。如Collection类的next方法和hasNext方法。


抱歉!评论已关闭.