面向对象
接口
1.接口方法默认public abstract。
接口属性访问控制符默认public statistatic final。
2.接口支持多重继承,抽象类只能单继承。
3.接口可以继承接口。
方法
1.方法参数必须做校验。比如判空。
2.构造方法不能被继承,不能被重写。
3.getter和setter中不要添加业务逻辑。
否则程序出了问题,非常难排查。
如果变量是 boolean 类型,那么 getter 方法可以命名为 isXxx()。
4.异步用于处理耗时操作,处理完毕还可以返回处理结果。
5.在pojo中,属性变量最好设置成包装类,这样在调用时没有经过setter,直接getter会报错空指针,便于发现问题。
public class User {
private Integer age;
private Boolean vip;
//getter、setter..
}
如果age设置成int,当调用时没有先setter,直接getter,会得到默认值0,但是不会报错。
6.构造方法不可以被继承。
7.static代码块只运行一次。
static
0.静态方法如果使用可修改的对象,那在并发时会存在线程安全的问题。
1.阿里巴巴java规范:SimpleDateFormat是线程不安全的,不要定义为static变量。如果定义为static变量,必须加锁。
声明SimpleDateFormat的时候,如果使用的是static定义的,那么这个SimpleDateFormat就是一个共享变量,随之,SimpleDateFormat中的calendar也就可以被多个线程访问到,就会出现线程安全问题。
2.静态代码块只运行一次,在第二次对象实例化时,不会运行。
代码风格
1.魔法值,也就是直接以具体的数值或字符出现在代码中,随处可见,不易管理。魔法值应该改成常量。
2.方法控制在80行内。
3.if else超过三层以上,使用卫语句或状态模式。
4.在if判断中,过长的表达式直接改为一个布尔类型变量表示。
异常
1.受检异常(check)和非受检异常(uncheck)。还有运行时异常(RuntimException)
2.Error属于严重错误,比如OOM,StackOverflowError。
2.受检异常可以通过try和catch捕获。运行时异常是编程错误引起的,需要程序员自己处理。
并发与多线程
线程
1.线程是CPU调度和分派的基本单位,为了更充分地利用CPU资源,一般都会使用多线程进行处理。
2.线程可以拥有自己的操作栈,程序计数器,局部变量表等资源,它与同一进程内的其他线程共享该进程的所有资源。
3线程的生命周期分为NEW(新建状态),RUNNABLLE(就绪状态),RUNNING(运行状态),BLOCKING(阻塞状态),DEAD(销毁状态)。
线程安全
保证高并发场景下的线程安全,可以从以下维度考量:
1.数据单线程内可见。
通过限制数据仅在单线程内可见,可以避免数据被其他线程篡改。最典型的就是线程局部产量,它存储在独立虚拟机栈的局部变量列表,与其他线程毫无瓜葛。ThreadLocal就是用这种方式保证线程安全的。
2.只读对象。
只读对象是线程安全的。一个对象想要拒绝任何写入,必须满足:final修饰,避免被继承;没有任何更新方法;返回值不能为可变对象。
3.线程安全类。比如StringBuffer。
4.同步与锁机制。
线程安全的核心是“要么只读,要么加锁”。
锁
Lock
Lock利用了volatile的可见性。
ReentrantLock对于Lock接口的实现主要依赖了Sync,而Sync继承了AbstractQueuedSynchronizer(AQS),它是JUC包实现同步的基础工具。在AQS中,定义了一个volatile int state 变量作为共享资源,如果线程获取资源失败,则进入同步FIFO队列中等待,如果成功获取资源就执行临界区代码。执行完释放资源时,会通知同步队列中的等待线程来获取资源后出列执行。
继承AQS后,可以使用acquire(),release()方法获取、释放permit。
Synchronized
Synchronized的底层是通过监视锁(monitor)实现的。监视锁是每个对象都有的隐藏字段。如果线程进入同步方法或代码块时,会获取该方法或代码块所属对象的monitor,进行加锁判断。
如果monitor为0,表示线程可以持有代码,monitor加一。如果当前线程已经持有monitor,则monitor继续加一。
volatile
解决的是多线程共享变量的可见性问题。但不具备Synchronied的互斥性。
也不具有原子性。
进行非原子操作,不具备线程安全。
volatile适合一写多读的并发场景。
数据结构与算法
1.ArrayList,HashMap这些集合在初始化时,如果要放入的数据量过大,最好指定初始容量,避免多次扩容。
2.HashMap 在容量不够进行 resize 时由于高并发可能出现死链,导致 CPU 飙升 。HashMap扩容后死链是如何形成的?
疑问:
1.双重检查锁如何理解?
2.操作数栈是什么?