0815实习培训随记
魔鬼数字
用有意义的常量代替字面量数字
- 便于理解和替换
常用枚举类型代替枚举含义的变量
- 典型的反例是C中习惯用
return 0
表示正确执行
对于局部的字面量可以只用注释说明,如单位换算没必要用常量替换10/100/1000等
反例
int ONE=1
不推荐但允许的情况
int HOUR_12 = 12 //12小时制
并发工具
略
单例模式
1 | public class Single { |
lazyinit
1 | public class Single { |
把getInstance
整个方法都同步,实际应用中抢占并初始化单例对象并非常见情况,这么做会严重影响性能
DoubleCheck
1 | public class Single { |
只锁静态类,保证了日常调用的性能,同时doublecheckinst == null
保证了不会初始化多个对象
利用classLoader的特性(更推荐)
1 | public class Single { |
利用类加载的特性,只有在调用getInstance
的时候才会加载SingleHolder
内部类,同样实现了lazyinit的效果
验证Volatiole
1 | public class StopThread { |
基本型偏执
1 | public int deposit(int money) |
- 便于理解
- 编译器的语义检查
- 提供了对Money的行为放置场所
- 便于扩展,例如精度修改等
线程不安全的对象
DateFormat
等不一定是线程安全的
位运算
有符号右移:>>
无符号右移:>>> 负数补0而非1
1 | byte i = (byte) 0xf3; //1111 0011 |
对于>>>运算左边如果是byte/char/short,会先转换为int
1 | byte i = (byte) 0xf3; //1111 0011 |
非短路运算
短路与:&&
位与运算:&
常量右置:左侧倾向于变化、右侧倾向于不变
- 在C中会采用常量左置,如
if(null == a)
,此时如果写成=
编译器报警 - Java中的例外
"foo".equals(var)
,反之则需要if(var != null)...
静态成员类
非静态成员
- 可以访问外围类的成员,等效于外围类的一个非静态方法
- 必须依附于外围类的实例对象
- 需要维护和实例对象的关联关系
静态成员(更加常用)
- 只能访问静态成员
- 不需要维护关联关系,少了空间和时间开销
equals覆写
覆写equals时也应当覆写hashcode
Hash集合依赖于hashCode
决定索引和返回
- 保证equals的对象,hashcode必须相等
- 否则会出现找不到值的情况
其中equals的原型为:
1 | public boolean equals(Object o) |
重写HashCode
- 把某个非零常数保存在一个叫result的的变量(通常是质数,如17)
- 对于每一个成员字段,完成以下步骤
- result = 31 * result +c
- boolean f?0:1
- byte/char/short/int (int)f
- long (int)(f^(f>>>32))
- float Float.floatToIntBits(f)
- double Double.doubleToIntBit
Locale
对于不同语言的大小写可能有不同规则,要指定Locale转换方式
调用toUpperCase()
时,默认调用toUpperCase(Locale.getDefault())
例如String name=aquickbrownfoxjumpoverthelazydog
应当调用name.toUpperCase(Locale.ENGLISH)
size
- byte - 1字节
- char - 2字节
- String - char串而非byte串,二进制协议编解码时应当注意
常量池
Integer a = -128
和Integer a=Integer.valueOf(-128)
都会应用常量池
只有new
出来的会在堆里创建新对象
永远用valueOf和equals进行包装类的创建和比较
不要对包装类使用synchronized,否则会因为常量池意外获得同一把锁
Long/Short
- -128~127
Byte/Boolean
- 全部缓存
Float/Double
- 全部不缓存
String.intern()
- 永远返回常量池的String对象
- 当且仅当s.equals(t)时,有s.intern()==t.intern()
异常关闭
在fianlly中关闭资源
- close某个资源时如果close方法本身也抛异常,如果不捕获会导致后面的资源无法关闭
建议使用try-with-resource写法
- 多个资源抛出异常时会保留第一个异常,并将后续异常作为
Supressed Exceptions
,可以通过getSuppressed()
捕获
1 | try(...){ |
暴露和拷贝
1 | public void getA(){ |
根据实际情况设计采用暴露还是拷贝
substring在Java7之后变为深拷贝
引用和指针
Java中的引用在行为上更接近C中的指针
1 | public static void main(String[] args){ |
类的访问修饰符
- public > protected > default > private
- protected除内部文件的类可见外,外部文件的子类也可见
- default仅有内部文件的类可见
对于同一个package的default类,他们互为友元,可以让同一个package内的类互相访问private对象
- default打通了package的类
- protected打通了继承树的类
暴露增加了修改的困难程度,暴露n个方法就会被外界调用n个方法,想要修改逻辑就要同时考虑n个对外的接口
多态的优先级
不建议利用如下优先级
- this.func(args)
- super.func(args)
- this.func((super)args)
- super.func((super)args)
建议多使用@Override增加可读性
多态的设计模式——策略模式
SAP Breaker——破坏软件抽象
最稳定的类/被依赖的类应当是最抽象的类,反之亦然
- 依赖倒置
业务->基础设施
业务->基础设施接口<-基础设施
正方形悖论
正方形 is a 长方形
SOLID原则
- SIngle Responsibility Principle 单一职责
- Open-Closed Principle 开闭原则
- 只是一个期望,往往难以实现
- Liskov Substitution Principle 里氏替换原则
- 最重要的,不可违背
- Interface Segregation Principle 接口隔离原则
- Dependency Inversion Principle 依赖倒置原则