1 ) 【强制】对于 Service
和 DAO
类,基于 SOA
的理念,暴露出来的服务一定是接口,内部
的实现类用 Impl 的后缀与接口区别。
正例: CacheServiceImpl
实现 CacheService
接口。
2 ) 【推荐】 如果是形容能力的接口名称,取对应的形容词为接口名 ( 通常是– able 的形式 ) 。
正例: AbstractTranslator
实现 Translatable
。
1 ) 数据对象: xxxDO , xxx
即为数据表名。
2 ) 数据传输对象: xxxDTO , xxx
为业务领域相关的名称。
3 ) 展示对象: xxxVO , xxx
一般为网页名称。
4 ) POJO
是 DO / DTO / BO / VO
的统称,禁止命名成 xxxPOJO
。
说明:大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
正例:缓存相关常量放在类 CacheConsts
下 ; 系统配置相关常量放在类 ConfigConsts
下。
内共享常量、类内共享常量。
1 ) 跨应用共享常量:放置在二方库中,通常是 client . jar 中的 constant 目录下。
2 ) 应用内共享常量:放置在一方库中,通常是 子模块 中的 constant 目录下。
反例:易懂变量也要统一定义成应用内共享常量,两位攻城师在两个类中分别定义了表示
“是”的变量:
类 A 中: public static final String YES = " yes "
;
类 B 中: public static final String YES = " y "
;
A . YES . equals(B . YES)
,预期是 true ,但实际返回为 false ,导致线上问题。
3 ) 子工程内部共享常量:即在当前子工程的 constant 目录下。
4 ) 包内共享常量:即在当前包下单独的 constant 目录下。
5 ) 类内共享常量:直接在类内部 private static final 定义
说明:可变参数必须放置在参数列表的最后
。 ( 提倡同学们尽量不用可变参数编程 )
正例: public User getUsers(String type, Integer… ids) {…}
正例:" test " .equals(object);
反例: object.equals( " test " );
说明:推荐使用 java . util . Objects # equals(JDK 7 引入的工具类 )
说明:对于 Integer var = ? 在-128 至 127 范围内的赋值, Integer 对象是在
IntegerCache . cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行
判断,但是这个区间之外的所有数据,都会在堆上产生
,并不会复用已有对象,这是一个大坑,
推荐使用 equals 方法进行判断
入门总结(一)RPC定义和原理
1 ) 【强制】所有的 POJO 类属性必须使用包装数据类型
。
2 ) 【强制】 RPC 方法的返回值和参数必须使用包装数据类型
。
3 ) 【推荐】所有的局部变量使用基本数据类型。
说明: POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何
NPE 问题,或者入库检查,都由使用者来保证。
正例:数据库的查询结果可能是 null ,因为自动拆箱,用基本数据类型接收有 NPE 风险。
反例:比如显示成交总额涨跌情况,即正负 x %, x 为基本数据类型,调用的 RPC 服务,调用
不成功时,返回的是默认值,页面显示为 0%,这是不合理的,应该显示成中划线。所以包装
数据类型的 null 值,能够表示额外的信息,如:远程调用失败,异常退出。
说明:反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,然后进行
append
操作,最后通过 toString
方法返回 String 对象,造成内存资源浪费。
反例:
String str = "start";
for (int i = 0; i < 100; i++) {
str = str + "hello";
}
1 ) 如果不允许外部直接通过 new 来创建对象,那么构造方法必须是 private 。
2 ) 工具类不允许有 public 或 default 构造方法。
3 ) 类非 static 成员变量并且与子类共享,必须是 protected 。
4 ) 类非 static 成员变量并且仅在本类使用,必须是 private 。
5 ) 类 static 成员变量如果仅在本类使用,必须是 private 。
6 ) 若是 static 成员变量,必须考虑是否为 final 。
7 ) 类成员方法只供类内部调用,必须是 private 。
8 ) 类成员方法只对继承类公开,那么限制为 protected 。
1) 只要重写 equals ,就必须重写 hashCode 。
2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的
对象必须重写这两个方法。
3) 如果自定义对象作为 Map 的键,那么必须重写 hashCode 和 equals 。
说明: String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象
作为 key 来使用。
异常,即 java . util . RandomAccessSubList cannot be cast to java . util . ArrayList .
说明: subList 返回的是 ArrayList 的内部类 SubList ,并不是 ArrayList ,而是
ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上
List list=new ArrayList<>();
list.add("d");
list.add("33");
list.add("44");
list.add("55");
list.add("66");
List list2 = list.subList(0, 2);
System.out.println(list.size());//5
System.out.println(list2.size());//2
总结
使用sublist()返回的只是原list对象的一个视图,因此Sublist内部类和ArrayList的内部保存数据的地址是一样得;即它们在内存中是同一个List(集合),只是parentOffset ,size等参数不同
对SubList子列表的所有操作都会最终反映到原列表上
ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException异常。
如果达到的效果要对子集进行操作,原始list不改变。建议以下方式:
List
一样的数组,大小就是 list . size() 。
说明:使用 toArray 带参方法,入参分配的数组空间不够大时, toArray 方法内部将重新分配
内存空间,并返回新数组地址 ; 如果数组元素个数大于实际所需,下标为 [ list . size() ]
的数组元素将被置为 null ,其它数组元素保持原值,因此最好将方法入参数组大小定义与集
合元素个数一致。
正例:
List list = new ArrayList(2);
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array = list.toArray(array);
反例:直接使用 toArray
无参方法存在问题,此方法返回值只能是 Object[]
类,若强转其它
类型数组将出现 ClassCastException
错误。
把数组转换成集合时,不能使用其修改集合相关的方法,它的 add / remove / clear
方法会抛出 UnsupportedOperationException
异常。
说明: asList
的返回对象是一个 Arrays
内部类,并没有实现集合的修改方法。 Arrays.asList
体现的是适配器模式,只是转换接口,后台的数据仍是数组。
String[] str = new String[] { "you", "wu" };
List list = Arrays.asList(str);
第一种情况: list.add("yangguanbao");
运行时异常。
第二种情况: str[0] = "gujin";
那么 list.get(0)
也会随之修改。
泛型通配符 extends T >
来接收返回的数据,此写法的泛型集合不能使用 add
方
法,而 super T>
不能使用 get
方法,作为接口调用赋值时易出错。
说明:扩展说一下 PECS(Producer Extends Consumer Super) 原则
:
第一、频繁往外读取内容的,适合用 extends T >。
第二、经常往里插入的,适合用 super T>
remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁。
正例:
//可以加锁
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (删除元素的条件) {
iterator.remove();
}
}
反例:
List list = new ArrayList();
list.add("1");
list.add("2");
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
说明:以上代码的执行结果肯定会出乎大家的意料,那么试一下把“1”换成“2”,会是同样的
结果吗?
不然 Arrays . sort
,Collections . sort
会报 IllegalArgumentException
异常。
说明:三个条件如下
1 ) x , y 的比较结果和 y , x 的比较结果相反。
2 ) x > y , y > z ,则 x > z 。
3 ) x = y ,则 x , z 比较结果和 y , z 比较结果相同。
反例:下例中没有处理相等的情况,实际使用中可能会出现异常:
new Comparator() {
@Override
public int compare(Student o1, Student o2) {
return o1.getId() > o2.getId() ? 1 : -1;
}
};
说明: HashMap 使用 HashMap(int initialCapacity) 初始化,
正例: initialCapacity = (需要存储的元素个数 / 负载因子) + 1
。注意负载因子(即loader
factor)默认为 0.75, 如果暂时无法确定初始值大小,请设置为 16(即默认值)。
反例: HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素不断增加,容
量 7 次被迫扩大, resize 需要重建 hash 表,严重影响性能。
说明:有序性是指遍历的结果是按某种比较规则依次排列的。稳定性指集合每次遍历的元素次
序是一定的。如:
ArrayList | order / unsort |
---|---|
HashMap | unorder / unsort |
TreeSet | order / sort |
如果定义为static ,必须加锁,或者使用 DateUtils 工具类。
正例:注意线程安全,使用 DateUtils
。亦推荐如下处理:
private static final ThreadLocal df = new ThreadLocal() {
@ Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
说明:如果是 JDK 8 的应用,可以使用 Instant
代替 Date
, LocalDateTime
代替 Calendar
,
DateTimeFormatter
代替 SimpleDateFormat
,官方给出的解释: simple beautiful strong
immutable thread - safe
参考链接
尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用 RPC 方法
要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁
,使用 version 作为更新依据。
说明:如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次
数不得小于 3 次
参考链接
需要使用数据库的锁机制,比如SQL SERVER 的TABLOCKX
(排它表锁) 此选项被选中时,SQL Server 将在整个表上置排它锁直至该命令或事务结束。这将防止其他进程读取或修改表中的数据。
SqlServer中使用
Begin Tran
select top 1 @TrainNo=T_NO from Train_ticket with (UPDLOCK) where S_Flag=0
update Train_ticket set T_Name=user, T_Time=getdate(), S_Flag=1 where T_NO=@TrainNo
commit
我们在查询的时候使用了with (UPDLOCK)
选项,在查询记录的时候我们就对记录加上了更新锁,表示我们即将对此记录进行更新. 注意更新锁和共享锁是不冲突的,也就是其他用户还可以查询此表的内容,但是和更新锁和排它锁是冲突的.所以其他的更新用户就会阻塞.
Timer与TimerTask的真正原理&使用介绍
Timer和TimerTask源码解析
用法场景:
1.在并发场景下,通过双重检查锁 (double - checked locking) 实现延迟初始化的优
化问题隐患 ( 可参考 The " Double - Checked Locking is Broken " Declaration),可将目标属性声明为 volatile 型
2.volatile 解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,
但是如果多写,同样无法解决线程安全问题
比如: NullPointerException , IndexOutOfBoundsException 等等
当一个外部资源的句柄对象(比如FileInputStream对象)实现了AutoCloseable
接口,那么就可以将上面的板式代码简化为如下形式:参考链接
public static void main(String[] args) {
try (FileInputStream inputStream = new FileInputStream(new File("test"))) {
System.out.println(inputStream.read());
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
参考链接
装箱 | 拆箱 |
---|---|
(auto)基本数据类型->包装器类型 | (auto)包装器类型->基本数据类型 |
Integer total = Integer.valueOf (99); |
int totalprim = total.intValue (); |
1)i >= 128 || i < -128 =====> new Integer(i)
2)i < 128 && i >= -128 =====> SMALL_VALUES[i + 128]
public static Integer valueOf(int i) {
return i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];
}
@Override
public int intValue() {
return value;
}
@Override
public boolean equals(Object o) {
return (o instanceof Integer) && (((Integer) o).value == value);
}
防止 NPE
,是程序员的基本修养,注意 NPE
产生的场景:
1)返回类型为基本数据类型, return 包装数据类型的对象时,自动拆箱有可能产生 NPE 。
反例: public int f() { return Integer 对象}
, 如果为 null ,自动解箱抛 NPE 。
2 ) 数据库的查询结果可能为 null 。
3 ) 集合里的元素即使 isNotEmpty ,取出的数据元素也可能为 null 。
4 ) 远程调用返回对象时,一律要求进行空指针判断,防止 NPE 。
5 ) 对于 Session 中获取的数据,建议 NPE 检查,避免空指针。
6 ) 级联调用 obj . getA() . getB() . getC(); 一连串调用,易产生 NPE 。
正例:使用 JDK8 的 Optional 类来防止 NPE 问题。
注意取值的范围 0≤ x <1 ( 能够取到零值,注意除零异常 ) ,如果想获取整数类型的随机数,不要将 x 放大 10 的若干倍然后取整,直接使用 Random 对象的 nextInt 或者 nextLong
方法
而不是 new Date() . getTime()
因此使用 sum() 时需注意 NPE 问题。
正例:可以使用如下方式来避免 sum 的 NPE 问题:
SELECT IF(ISNULL(SUM(g)) ,0, SUM(g)) FROM table;