<编写高质量改善java程序的151个建议>----笔记

1.显示申明serialVersionUID可以避免对象不一致。因为在jvm反序列化时,会比较数据流中的serialVersionUID与类的serialVersionUID是否相同,若相同则认为类没有发生变化,可以把数据流load为实例对象;若不同,则抛异常InnalidClassException.
 2.避免用序列化类在构造函数中为final变量赋值。反序列化时final变量在以下情况不会重新赋值。
    a.通过构造函数为其赋值(反序列化时不会执行构造函数)
    b.通过方法返回值为其赋值 
3.final修饰的属性不是基本类型。(基本类型包括:8种基本类型,数组,未使用new生成的字符串)
注:对象流不序列化static或transient属性。
4.对于final修饰的基本类型和String类型,编译器会认为它是稳定态,所以在编译时就直接把值编译到字节码中了,避免在运行期引用,以提高代码的执行效率。
   而对于final修饰的class,编译器认为它是不稳定的,在编译时建立的则是引用关系,如果client类引入的常量是一个类或实例,即使不重新编译也会输出最新值。
5.包装类型参与运算时,要做null值校验。
6.对于int类型的参数,通过valueOf产生包装对象时,若int参数在-128和127之间,则直接从整型池中获取对象;否则通过new生成包装对象。注:通过包装类的valueOf生成的包装实例可以显著提高空间和时间性能。
7.在java中,随机数的产生取决于种子,当种子不同时,产生的随机数不同;当种子相同时,即使实例不同所产生的随机数也是相同的。故,若非必要,不要设置随机数种子。
8.实例对象一般有两种类型:表面类型和实际类型;表面类型是声明时的类型,实际类型是对象产生时的类型。对于非静态方法,它是根据对象的实际类型来执行的。而对于静态方法来说,当通过对象访问静态方法时,jvm会通过对象的表面类型查找静态方法的入口。
9.避免在构造函数中初始化其他类。
10.静态内部类与普通内部类
  a.静态内部类没有持有外部类的引用
    普通内部类可以直接访问外部内的属性、方法,即使是private类型也可以,这是由于普通内部类持有对外部类的一个引用。
    而静态内部类可以访问外部类的静态方法和属性。
  b.静态内部类不依赖外部类
    普通内部类与外部类之间是相互依赖的关系,内部类实例不能脱离外部类实例,也就是说,他们一起声明,一起被垃圾回收器回收。
    而静态内部类是可以单独存在的,即使外部类消亡了,静态内部类还是可以存在的。
  c.普通内部类不能声明static的方法和变量
    普通内部类不能声明static的方法和变量,但final static变量可以。
    而静态内部类基本没有限制。
11.匿名类的构造函数很特殊:一般类的所有构造函数默认都是调用父类的无参构造函数,而匿名类因为没有名字,只能由构造代码块代替,也就无所谓的有参和无参构造函数了,它在初始化时直接调用了父类的同参数构造,然后再调用自己的代码块。
12.对象的拷贝:浅拷贝,是有选择性的拷贝。拷贝的规则如下:
   a.基本类型:若变量是基本类型,则拷贝其值,如int、float等。
   b.对象:若变量是对象则拷贝地址引用,也就是说此时拷贝出的对象与原有对象共享该     实例变量,不受访问权限的限制。即即使是private的变量。
   c.String字符串:该拷贝也是一个地址,但在修改时它会从字符串池中重新生成新的字符串,原来的字符串对象保持不变。
注:推荐使用序列化实现对象的拷贝。可以使用Apache下的commons工具包的SerializationUtils类。
13.覆写equals方法,建议使用getClass进行类型判断,而不要使用instanceof。最好覆写hashCode方法。
14.对于String,建议使用直接赋值方式,因为其会先查找jvm的常量池,若没找到就创建新的String对象。而new String()会创建新的字符串对象。
  a.使用String类的场景:当字符串不经常变化的场景。
  b.使用StringBuffer类的场景:在频繁进行字符串的运算,且运行在多线程的环境中。
  c.使用StringBuilder类的场景:频繁使用字符串的运算,且运行在单线程的环境中。
15.数组:对于基本类型进行求和计算时,数组的效率是集合类的10倍,故在性能要求较高的场景中使用数组代替集合。   
16.避免基本类型数组转换列表陷阱:由于基本类型不能泛化,故不能作为泛型参数,要想作为泛型参数就必须使用其所对应的包装类型。
        int [] data ={1,2,3,4,5}; List list  = (List) Arrays.asList(data);
  该list集合中只有一个元素,原因是参数data是数组对象可以作为泛型的参数,但是转  后该数组对象作为一个对象整体存进list集合中了。
注:Arrays.asList()返回的List集合的长度是不可变的。
17.列表遍历:ArrayList实现了RandomAccess接口,即标致其支持随机存取,同时也标志其数据元素之间没有关联。如果此时使用foreach遍历,相当于借助iterator生成元素之间的关系,再通过次关系来遍历,故此处foreach的遍历效率低。
           相反,如果是LinkedList类,由于其数据元素本身就是相关联的,这里使用foreach遍历的效率较高。
注:对于不同的列表,更具列表特性选择最有遍历方式;
    subList产生的列表只是当前列表的一个视图,所有的修改都会直接作用于原始列表。故生成子列表后不要轻易修改。
18.Comparable接口可以作为实现类的默认排序法,Comparator接口则是一个类的扩展排序工具。
19.indexof和binarySearch:
  indexof就是顺序遍历,找到第一个就返回结果;
  binarySearch基于二分法查找,但前提是要考虑排序问题,否则查找的结果不准确。
从性能方面,binarySearch是最好的选择。
20。实现了compareTo方法,就应该覆写equals方法,确保两者同步。
21.集合运算时的优雅方式
  a.并集:list1.addAll(list2)
  b.交集:list1.retainAll(list2)//retainAll会删除list1中没有出现在list2中的元素
  c.差集:list1.removeAll(list2)
  d.无重复的并集:list2.removeAll(list1);list1.addAll(list2)
22.使用Collections.shuffle(list)来打乱列表
23.java的泛型是类型擦除的,如:
  List<String>,List<Integer>,List<T>擦除后的类型为:List
  List<String>[] 擦出后为List[]
  List<? extends E>,List<? super E> 擦除类型为List<E>
  List<T extends Serializable & Cloneable> 擦出后为List<Serializable>
24.泛型:基于编译器来实现的
   主要解决了之前集合类框架在使用过程中通常出现的运行时类型错误及安全性更好。
   类型擦除:使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉,即在生成的java字节码中是不会包含泛型中的类型信息的。基本过程是,首先找到用来替换类型参数的具体类,一般这个具体类是Object。如果指定了类型参数的上界,则使用这个然后上界。然后将代码中的类型参数替换成具体类,同时去掉出现的类型声明,即去掉<>的内容。
  泛型类的特性:
     a.泛型类没有自己独有的Class类对象。如不存在List<String>.class或是List<Integer>.class,只有List.class.
     b.静态变量是被范型类的所有实例所共享的。
     c.泛型的类型参数不能用在java异常处理的catch语句中。
  通配符(?)与上下界(extneds,super):
     通配符?:代表未知类型,其实它所代表的是一组类型,但具体的类型是未知的。
     上下界:限制未知类型的范围,如:List<? extendsNumber>说明List中可能包含的元素类型是Number及其子类。而List<? super Number>则说明List中包含的是Number及其父类。
25.类型系统:
   引入泛型之后的类型系统增加了两个维度:一个是类型参数自身的继承体系结构,另外一个是泛型类或接口自身的继承体系结构。
    第一个指的是对于 List<String>和List<Object>这样的情况,类型参数String是继承自Object的。而第二种指的是 List接口继承自Collection接口。对于这个类型系统,有如下的一些规则:
相同类型参数的泛型类的关系取决于泛型类自身的继承体系结构。即List<String>是Collection<String> 的子类型,List<String>可以替换Collection<String>。这种情况也适用于带有上下界的类型声明。
当泛型类的类型声明中使用了通配符的时候, 其子类型可以在两个维度上分别展开。如对Collection<? extends Number>来说,其子类型可以在Collection这个维度上展开,即List<? extends Number>和Set<? extends Number>等;也可以在Number这个层次上展开,即Collection<Double>和 Collectionvo<Integer>等。如此循环下去,ArrayList<Long>和 HashSet<Double>等也都算是Collection<? extends Number>的子类型。
如果泛型类中包含多个类型参数,则对于每个类型参数分别应用上面的规则。
26.volatile不能保证数据是同步的,只能保证线程能够获取最新值。
27.callable与runnable接口
   a.Callable规定的方法是call,Runnable规定的是run方法
   b.Callable的任务执行后可有返回值,而Runnable的任务是不能有返回值的
   c.Call方法可以抛出异常,run方法不可以
   d.运行Callable任务可以拿到一个Future对象,表示异步计算的结果。
28.Lock与synchronized
   a.lock支持更细粒度的锁控制
   b.lock是无阻塞锁,synchronized是阻塞锁
     当线程A持有琐时,线程B也期望获得锁,此时,如果程序中使用的是现实锁,则B线程为等待状态;若使用后者,则为阻塞状态
   c.Lock可实现公平锁,synchronized只能是非公平锁
   d.lock是代码级的,synchronized是JVM级的
29.适当设置阻塞队列的长度
   a.阻塞队列的容量是固定的,非阻塞队列是变长的;阻塞队列可以声明指定队列的容量,若指定了容量,则元素的数量不可超过该容量;若不指定,队列的容量为Integer的最大值。
   b.阻塞队列是为了容纳(或排序)多线程任务而存在的,其服务的对象是多线程应用;而非阻塞队列容纳的是普通的数据元素。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值