Think in Java 阅读拾遗

读了Think in Java,就以前不知道的地方做个记录,详细的地方对相关书页也做了标记。

1. 类内成员变量不初始化,会有默认值,方法内临时变量不初始化,没有默认值,会报错

2. 关于无符号右移,对于byte、short类型的负数,会先转化成int再右移。这种流程下,可能会出现因为补码表示出错

详细的说,当负数的byte和short转化成int的时候,因为补码的原因,转化后的高位都是1,无符号右移之后,这些1的高位被移动,这时无符号右移的功能(高位补0)就失去作用了。

举例而言,byte example=-1(对应补码11111111),则example>>>10,首先变成int,转化后不是0,而是二进制的22个1,如果再转回byte,还是-1

3. 关联2,>>>=最后的‘=’,强制转化是直接将移位后的int截取后若干位给byte或者short的,所以也会导致移位错误的问题

4. 关联2,对char、byte、short的算术操作符的操作,会将数据类型提升成int

5. 一个Java文件,允许有一个及一个以下 的公开类,其中,一个公开类的时候,类名和文件名一致,没有公开类的时候,文件名可以随意命名

6. 对于包级私有的类,其内部的公共静态域还是可以被外界访问的(存疑)

7. 对于基类的私有方法,是无法继承的,private方法相当于隐式的final,如果派生类拥有同名的公共方法,二者没有覆盖关系,仅仅是派生类同名的另一个方法。也就是说,无法在向上转型后的基类引用中找到这个方法

8. 用final修饰的类,相当于类内所有方法都是final的,但是final类内的域不一定是,还可以选择用不用final修饰,来表示能否修改

9. 类的构造器可以视为是类的static方法

10. 多态只体现在对普通方法的调用上,对域的调用,是编译时解析的,不会多态;对静态方法的调用,是和类关联的,也不会多态

11. 对于构造器,内部的多态方法的调用,可能因为属性没有初始化完全导致多态方法执行有误,所以,在构造器内,可以调用的,应该是final方法,private方法(自动属于final)

12. 接口和类都可以嵌套在其他类内部。这些嵌套的接口或类,访问权限都可以是private的,这时,依旧可以继承私有的嵌套类或者接口

13. P205 内部类可以作为多重继承的一个解决方案

14. P212 对于其他类继承内部类,其他类需要实现一个有参构造函数,参数是内部类对应外部类的引用,并在其中调用它的super();因为非静态内部类总要初始化一个外部类的引用

举例而言:

class OutClass{
    class InnerClass{}
}
class InheritInner extends OutClass.InnerClass{
    InheritInner(OutClass outClass){
        outClass.super();
    }
}

15. P213 继承外部类,并自定义内部类后,并不会在调用的时候有多态行为。如果需要,新创建的内部类,需要也继承对应的内部类。

16. P220 Arrays.asList方法,如果传入的某个派生类,不会自动转型成基类的集合,需要显式声明

举例:

List<BaseTest> list=Arrays.<BaseTest>asList(new SubTest(),new SubTest());

17. P260 如果仅仅是在catch后抛出新的异常,或者使用throw (Exception)fillInStackTrace(),则原有异常的信息会丢失。可以使用initCause或者入参传入原有exception的方式,保存原有异常堆栈,并增加新的堆栈

18. P269 在finally中如果出现异常,或者提前return而没有catch,则对应try内出现的异常会丢失掉(联想到了lock的逻辑,lock要在try外,unlock在finaly内)

19. P271 派生类覆盖基类方法,或者实现接口方法的时候,是不能声明抛出基类或者接口没有的异常的,这样会导致向上转型之后无法正确捕获异常。但是,构造器的异常声明,却没有限制,而且派生类构造器无法捕获基类构造器抛出的异常

20. P271 对于有两个接口声明了同一个方法,但是声明抛出的异常却不同的情况,实现这两个接口的类,实际上无法声明异常的类型,因为向上转型后,总可能无法正确捕获异常

21. P291 格式化输出格式:%[argument_index][flags][width][.precision]conversion,说明,flags是对齐方式,-代表左对齐,width代表宽度,不足留空,重点是conversion怎么写,详见API

22. P322 使用Class.newInstance()需要类有默认无参构造器,否则会得到异常InstantiationException

23. P361 静态方法无法访问泛型类的类型参数,所以静态方法使用泛型,需要写成静态的泛型方法

24. P380 泛型的类型擦除带来的问题,有时候可以需要显式的传入Class对象来解决

25. P383 可以声明一个泛型数组,但是永远无法创建这样一个数组,创建泛型数组的唯一方式是创建raw type的数组,然后强制转型.如果滥用强制转型的话,存在那么泛型带来的类型限定的能力会消失,可能出现鸟笼子里装着猪的情况(某种泛型类型的数组里存放着其他参数类型的元素)

示例:

Test<Integer>[] test=(Test<Integer>[])new Test[5];

26. P386 建议传入泛型class来创建泛型数组,这样,底层的数组类型就不是raw type,进而,底层数组转型不会报错

示例:

public void createGenericArray(Class<T> clazz,int size){
    array=(T[])Array.newInstance(clazz,size);
}

27. P390 泛型的边界限定符,不仅可以限定类,还能限定接口,格式如T extend BaseClass & interface1 & interface2

28. P393 存在

29. P393 泛型通配符 ?中,<? extends OtherClass>禁止了set,因为继承了OtherClass的有若干个类,没办法保证set进去和其中已存在的是一种类型,但是,get出来的至少都是OtherClass类型。同理<? super OtherClass>禁止了get,因为无法保证get出来的都是一种类型,但是set进去的,都是OtherClass类型或其派生类

30. P393 无论是

31. P400 任何基本类型都不可以做泛型的类型参数

32. P401 由于类型擦除,无法实现同一个泛型接口的不同变体

33. P403 对于需要强制转化成泛型的时候,为了消除非受检警告,除了使用@SuppressWarning之外,还可使用Class上的方法cast,其内部也是强制转化了。

举例:

List<Widget> lw=List.class.cast(objectInputStream.readObject());

34. P405 自限定的类型:形如Clazz

35. P456 Arrays.binarySearch方法如果没找到target的话,返回的值是-(插入点+1)。插入点即第一个比target大的元素下标,如果都比target小,插入点就是数组长度

36. P518 引用从强到弱为:强->软->弱->虚,在这里ReferenceQueue的作用是,当引用类型中持有的实例对象销毁之后,将引用类型加入到Queue中,从此,从ReferenceQueue中可以拿到对应的引用类型,但是拿不到引用类型持有的实例

38. P576 对于实现Externalizable接口,要求类一定有公共的无参构造器,因为Externalizable会在反序列化的时候会调用无参构造器。但是Serializable接口的反序列化却不需要,其完全以其存储的二进制位为基础构造对象

39. P579 对于Serializable接口,如果想自定义序列化与反序列化过程,除了改为使用Externalizable接口,还可以实现私有方法readObject和writeObject,是的私有方法,其会被ObjectInputStream或者ObjectOutputStream调用

40. P585 对象的static变量是无法被自动序列化的,即便是序列化Class的实例,其static域也无法自动序列化。为了序列化static域,需要手动编写方法,读写static域,这时要注意读写顺序的一致。

41. P595 对于枚举类型,编译器会增加如一个静态方法values(),返回该枚举类的所有枚举值,还有静态方法valueOf,传入枚举的字符串,返回对应枚举值。在枚举类的父类Enum上,也有方法valueOf,但是是两个参数,还需指定具体枚举类。同样,对于values方法,也有一个类似的方法getEnumConstants,这是在Class上的方法,如果类是Enum类型,那么其将返回所有枚举值,否则返回null

42. P601 EnumSet内部通过long实现,如果小于64个元素,则使用一个long,如果多于64个,会使用long数组,内部对应的,是两个EnumSet的实现列。另外,Enum定义的顺序决定了EnumSet内部的顺序

43. P623 注解内的元素,只接受基本类型、String、Class、Annotation和enum

44. 关于注解RetentionProlicy存在于Source、Class和RunTime,其用处不同:Source用于分析未编译的源代码中的注解,(1)作为文档补充(2)协助编译(3)作为源代码生成器;Class用于字节码修改,比如切面、代理等;Runtime用于反射获取信息,参与业务逻辑编写。

45. P697 synchronized或者IO操作都是不可中断的,为了解决这个问题,可以:(1)对于IO操作,关闭底层阻塞的资源可以解除IO操作上的阻塞,或者使用NIO的方法;(2)对于同步块,如果要中断的话,可以使用lock的lockInterruptibly方法,允许中断的锁。

46. P698 对于使用线程池,如何向线程发送中断命令,可以使用Fature,对于runnable,submit到线程池,也会返回Fature

47. P702 Thread.interrupt()方法可以产生一个中断,但是,其只有在线程将要进入阻塞操作或者已经进入可中断的阻塞操作内时才有效,其他时刻并不会抛出中断异常,这时,可以通过Thread.interrupted()方法检测是否被中断,此方法还会重置中断状态。

猜你喜欢

转载自blog.csdn.net/lqadam/article/details/80444719