那些年我踩过的坑——Java

Java基础

  • 复用构造器:只能在构造函数第一行调用其他构造函数,且只能调用一个
  • 接口适合用来实现mixin(将一个类变量完全复制到另一个类中)
  • 类被load时,static初始化->内存申请->默认初始化->调用构造器
  • Arrays类用来对数组进行操作,Collections对Collection
  • Map中的key需要有equals方法,HashMap要有hashCode,TreeMap要实现Comparable
  • RuntimeException不需声明就能抛出
  • Input(Output)Stream是字节流,Reader/Writer是字符流
  • 一旦出现Foo.class,classloader已经把Foo的二进制读到了内存中,可能会出现内存占用问题,所以框架多存className,然后用反射loadclass
  • Java的泛型会根据赋值进行类型参数推断,但是返回结果被直接作为方法参数时不会进行类型参数推断。可以显示说明类型,形如:bar(Class.<Type>foo()),奇葩语法
  • 泛型可以使用自限定类型,实现类的泛型参数就是自己class Foo<T extends Foo<T>>,此时,可以声明一些参数为T的函数,只接受与自己相同类型的参数,例如Compare时,即jdk5以后支持的参数协变
  • ConcurrentHashMap只是给put与对象hash相同的对象链表加锁,速度会快,完全依赖会有问题
  • Exception是函数声明的一部分,所以,Runnable是不能throw一般的Exception的,只能throw RuntimeException。对于Thread中的Exception,使用UncaughtExceptionHandler
  • Exception在二进制里是用
Exception table:
         from    to  target type
             0     8    19   Class java/lang/Exception
             0     8    39   any
            19    28    39   any

来表示的。对于finally,是将finally中的代码(可能是因为测试代码很少,编译器做的优化)附加到每一个try/catch可能流程的末尾,然后由finally goto到return行。一个更复杂的分析

  • 构造函数中非final变量的初始化可能被重排到构造函数结束后!!!
  • synchronized块只保证A线程块内部的执行是在B线程进入块之前完成的,并不保证A线程块前的代码会被执行!!!
  • 有几种方法能够做到等待所有子任务完成后继续:
    • ExecutorService.awaitTermination,没什么坑
    • CountDownLatch,专门做这个的多线程工具类,适用性广,非常赞
    • Thread.join + for,这个有一个小点。for循环调用join的时候是顺序的,也就是说,join是按序等待结束的

黑科技

  • DCL式的Singleton在jdk1.5以前是有bug的,因为JMM规定的一致性协议是单线程的as if serial。除非使用volatile强制内存写通
  • Proxy也很慢,平均下来比直接new对象要慢30倍左右(高通615 Android 4.4)
  • 可以通过更改jni层class和method struct中的指针指向替换类和方法,大阿里的一个实现
  • Proxy只能搞interface的实现,想用这个做类功能的替换是不行的

你可能感兴趣的:(java基础)