编程规约
6。【强制】抽象类名用Abstract 或 Base 开头;异常类名用Exception结尾;测试类以要测试的类名 + Test后缀
规范和集合源码的规范一致,如ArrayList就继承了AbstractList,LinkedList继承了AbstractSequentialList,以及AbstractSet,AbstractMap等等
实习时测试代码全写到一个类(逃
8。 【强制】POJO类中布尔类型变量都不要加is
前缀,否则部分框架解析时会引起序列化错误
boolean isSuccess; 用IDEA自动生成getter方法时,方法是 public boolean isSuccess() {...}
,然而boolean success;生成的也是这个;
所以一些框架根据getter方法名反向解析会解析到 success
这个域名
为嘛不生成isIsSuccess? 是因为奇怪吗
13。 在常量与变量命名时,表示类型的名词放在词尾
eg. ArrayDeque, PriorityQueue, XXXCOUNT
14。如果在类中用到了设计模式,要体现在命名上
eg. Factory, AbstractFactory,Singleton,Builder,Prototype,Adapter,Bridge,Filter,Composite,Decorator,Facade,Flyweight,Proxy,Chain of Responsibility(???),Command,Interpreter,Iterator,Mediator,Memento,State,Observer,Strategy,Template,Visitor
17。枚举类名带上Enum后缀,枚举成员名称需要全大写,单词间用下划线隔开。
枚举是特殊的类,成员变量全为类常量,而且构造方法强制为私有
eg.
public enum BooleanEnum {
TRUE,FALSE;
}
会被编译成
public static final BooleanEnum TRUE;
descriptor: LBooleanEnum;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
public static final BooleanEnum FALSE;
descriptor: LBooleanEnum;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
6。 equals 容易报空指针异常,应使用常量或确定的值来调用equals。 eg. "test.equals(s)";
7。 Integer之间的比较,全部用equals方法
Integer a = 1, b = 1;
System.out.println(a == b);
上面两行代码编译后的字节码是:
0: iconst_1
1: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_1
5: iconst_1
6: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
9: astore_2
10: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
13: aload_1
14: aload_2
15: if_acmpne 22
18: iconst_1
19: goto 23
22: iconst_0
23: invokevirtual #4 // Method java/io/PrintStream.println:(Z)V
26: return
15行:if_acmpne
语义是比较栈顶两引用的数值(即地址)是否相等,不等则跳转
故 == 对于Integer来说是比较引用而不是值
8。float, double(浮点数)之间的等值判断不能用 ==
,包装类不能用equals来判断
大部分小数存在精度差
可以用BigDecimal计算 或 定义最小精度差
21。循环体内,字符串连接使用StringBuilder
String str = "start";
str += "hello";
// 编译后
0: ldc #2 // String start
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: aload_1
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #6 // String hello
16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_1
23: return
原来字符串拼接是通过StringBuilder实现 > <
23。慎用Object的clone方法拷贝对象。 因为默认实现是浅拷贝,clone对象内成员引用和原对象成员引用相等(引用了同一个堆中的对象)。
1。【强制】关于hashCode和equals方法,遵循如下规则:
- 只要覆写equals,就必须覆写hashCode
原因是在 Set 或 Map 中,hashCode 会和 equals 配合使用来判重,比如HashMap的add方法
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p;
这就要求如果equals
返回true
,则hashCode返回值必须相等。
11。不要在foreach循环里进行元素的remove, add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁 不明白…
16。高度注意Map类集合K/V能不能存储null值的情况
Hashtable not null not null
ConcurrentHashMap not null not null
TreeMap not null null
HashMap null null
TreeMap key 不可以为null可以理解为需要排序
ConcurrentHashMap k/v禁止为null,主要是因为不确定性,比如get() 返回为null时,无法判断value是null 还是原本就不存在这个映射. 对于非并发的HashMap 来说可以通过containsKey判断,并发的ConcurrentHashMap,调用containsKey后,数据可能已经发生变化,这种不确定性没有解决
3。【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程
使用线程池可以避免不必要的创建,销毁线程的开销。线程数量过多会导致过度切换,消耗内存
4。【强制】线程不能用Executors创建,而是要通过ThreadPoolExecutor创建,这样更好设定线程池参数
FixedThreadPool, SingleThreadExecutor 的workQueue为 LinkedBlockingQueue
,capacity 默认为 Integer.MAX_VALUE,可能会堆积大量请求,导致OOM
CachedThreadPool 的maximumPoolSize为Integer.MAX_VALUE,可能会创建大量线程,导致OOM
9。【强制】在使用阻塞等待获取锁(Lock)的方式中,Lock.lock()必须在try代码块之外,并且在加锁与try代码块之间不能存在任何可能抛出异常的方法调用,避免加锁成功后,在finally中无法解锁。
这条设定避免了很多可能出现的问题, unlock()
操作会检查 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException();
。故unlock
操作之前必须保证当前线程成功执行lock
操作。故lock()
要放在try
代码块之前,这样try
包住的内容就可以当成一个临界区,lock
是进入点,即使lock
抛出异常也不会进入临界区,也不会执行unlock
操作。
lock
与try
代码块之间不允许任何可能抛出异常的方法调用,这点原因很明显,如果出现异常,就不会释放锁。
这样分析起来lock
放在try第一句也行,但是代码规范中为什么强制放在try外部呢? 查看Lock接口的一些说明:
* <p>A {@code Lock} implementation may be able to detect erroneous use
* of the lock, such as an invocation that would cause deadlock, and
* may throw an (unchecked) exception in such circumstances. The
* circumstances and the exception type must be documented by that
* {@code Lock} implementation.
某些Lock的实现上,lock操作可能会进行死锁监测,进而抛出unchecked异常
17。 volatile 解决多线程内存不可见的问题,对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。???
2。【强制】当switch括号内的变量类型为String并且此变量为外部参数时,必须先进行null判断
反例: 猜猜下面代码输出什么, 先说结论 java.lang.NullPointerException
public static void main(String[] args) {
new Demo().method(null);
}
private void method(String s) {
switch (s) {
case "sth":
System.out.println("is sth");
break;
case "null":
System.out.println("is null");
break;
default:
System.out.println("default");
}
}
String能用于switch是借助int类型实现,如下:
4: aload_2
5: invokevirtual #5 // Method java/lang/String.hashCode:()I
8: lookupswitch { // 2
114215: 36
3392903: 50
default: 61
}
36: aload_2
37: ldc #6 // String sth
39: invokevirtual #7 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
42: ifeq 61
45: iconst_0
46: istore_3
47: goto 61
50: aload_2
51: ldc #8 // String null
53: invokevirtual #7 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
56: ifeq 61
59: iconst_1
60: istore_3
61: iload_3
62: lookupswitch { // 2
0: 88
1: 99
default: 110
}
首先比较的是hashCode值,之后再通过equals二次判断(hashCode提高速度,equals保证准确), 这也是为嘛要求重写equals()也要同时重些hashCode方法的原因.
猜测如果多个字符串hashCode相同,编译后的字节码是这种形式
lookuoswitch {
12345: 36, 37, 38
…
}
然后 if else if else if else if
为了验证,编译下这个代码
switch (s) {
case "Aa":
System.out.println("is Aa");
break;
case "BB":
System.out.println("is BB");
break;
default:
System.out.println("default");
}
---------------------
8: lookupswitch { // 1
2112: 28
default: 53
}
28: aload_2
29: ldc #4 // String BB
31: invokevirtual #7 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
34: ifeq 42
37: iconst_1
38: istore_3
39: goto 53
42: aload_2
43: ldc #8 // String Aa
45: invokevirtual #7 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
48: ifeq 53
猜错啦:),原来这里也是做了去重的优化
3。【强制】if/else/for/while/do 语句中必须使用大括号
刷LeetCode,如果if代码块只有一行,或者卫语句,还是喜欢不加大括号
7。不要在其他表达式(尤其是条件表达式)中,插入复制语句。影响代码理解
感同身受,点名批评写HashMap源码的大师(逃,可能优化到极致就是反人类
第二遍阅读此书(v1.5.0华山版), 其他的突然就看不懂了…