Java Coding Guidelines
一、编程规约
xxx{ xxx } xxx{ xxx}else{xxx }else{}//表示终止的右大括号后必须换行
StringBuffer sb = new StringBuffer(); // 超过 120 个字符的情况下,换行缩进 4 个空格,点号和方法名称一起换行 sb.append("zi").append("xin")... .append("huang")... .append("huang")... .append("huang");
StringBuffer sb = new StringBuffer(); // 超过 120 个字符的情况下,不要在括号前换行 sb.append("zi").append("xin")...append ("huang"); // 参数很多的方法调用可能超过 120 个字符,不要在逗号前换行 method(args1, args2, args3, ... , argsX);
String str = "a,b,c,,"; String[] ary = str.split(","); // 预期大于 3,结果是 3 System.out.println(ary.length);
反例:String str = "start"; for (int i = 0; i < 100; i++) { str = str + "hello"; }//反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,然后进行 append 操作,最后通过 toString 方法返回 String 对象,造成内存资源浪费。
new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o1.getId() > o2.getId() ? 1 : -1; } };
//普通模式创建一个只能保存字符串的ArrayList集合 List<String> strList=new ArrayList<String>(); // <> diamond 方式 HashMap<String, String> userCache = new HashMap<>(16); // 全省略方式 ArrayList<User> users = new ArrayList(10);
10.集合初始化时,指定集合初始值大小。说明:HashMap 使用 HashMap(int initialCapacity) 初始化。正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loader factor)默认为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值)。反例:HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素不断增加,容量 7 次被迫扩大,resize 需要重建 hash 表,严重影响性能。
11.使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。说明:keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效率更高。如果是 JDK8,使用 Map.foreach 方法。正例:values()返回的是 V 值集合,是一个 list 集合对象;keySet()返回的是 K 值集合,是一个 Set 集合对象;entrySet()返回的是 K-V 值组合集合。(The java.util.HashMap.entrySet() method in Java is used to create a set out of the same elements contained in the hash map. It basically returns a set view of the hash map or we can create a new set and store the map elements into them.)
Hashtable, and includes versions of methods corresponding to each method of Hashtable. However, even though all operations are thread-safe, retrieval operations do not entail需要牵涉到 locking, and there is not any support for locking the entire table in a way that prevents all access. This class is fully interoperable with Hashtable in programs that rely on its thread safety but not on its synchronization details.)ExecutorService that executes each submitted task using one of possibly several pooled threads, normally configured using Executors factory methods.)private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } }; /*说明:如果是 JDK8 的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar, DateTimeFormatter 代替 SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。 */
6.高并发时,同步调用去考量锁的性能损耗。能无锁数,就不要用锁;能锁区块,就不要整个;能用对象,就不用类。 避免RPC(Remote Procedure Call)。
7.同时加锁对多个资源、数据库表、对象时,需保持一致的加锁顺序,否则可能会造成死锁。
8.并发修改同一记录时,需要加锁避免更新丢失。要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用 version 作为更新依据。如果每次访问冲突概率小于 20%,推荐乐观锁,否则悲观锁。乐观锁的重试次数不得小于 3 次。
(Optimistic locking is a technique for SQL database applications that does not hold row locks between selecting and updating or deleting a row. The application is written to optimistically assume that unlocked rows are unlikely to change before the update or delete operation.The pessimistic locking model prevents simultaneous updates to records. As soon as one user starts to update a record, a lock is placed on it. Other users who attempt to update this record are informed that another user has an update in progress.)
9.多线程并行处理定时任务时,只要Timer 运行多个的 TimeTask 的其中之一没有捕获抛出的异常,其它任务便会自动终止运行。 ScheduledExecutorService 则没有这问题。 (An ExecutorService that can schedule commands to run after a given delay, or to execute periodically.The schedule methods create tasks with various delays and return a task object that can be used to cancel or check execution. The scheduleAtFixedRate and scheduleWithFixedDelay methods create and execute tasks that run periodically until cancelled.)
10.用 CountDownLatch 异步转同步时,每个线程退出前必须调用 countDown方法。注意 catch 异常,确保 countDown 方法执行,避免主线程无法执行
CountDownLatch (latch门闩[lætʃ] )is initialized with a given count. The await methods block until the current count reaches zero due to invocations( [ˌɪnvoʊˈkeɪʃ(ə)n] n.祈祷 召唤魔鬼) of the countDown() method, after which all waiting threads are released and any subsequent invocations of await return immediately. This is a one-shot phenomenon -- the count cannot be reset. If you need a version that resets the count, consider using a CyclicBarrier.)Random(long seed)Creates a new random number generator using a single long seed.)。说明:Random 实例包括 java.util.Random 或者 Math.random()。在 JDK7 后,可使用 API ThreadLocalRandom,而在 JDK7 前,需保证每个线程有个实例。-
yield()method pauses the currently executing thread temporarily for giving a chance to the remaining waiting threads of the same priority or higher priority to execute. If there is no waiting thread or all the waiting threads have a lower priority then the same thread will continue its execution. The yielded thread when it will get the chance for execution is decided by the thread scheduler whose behavior is vendor dependent. -
join()If any executing thread t1 callsjoin()on t2 (i.e.t2.join()) immediately t1 will enter into waiting state until t2 completes its execution. -
sleep()Based on our requirement we can make a thread to be in sleeping state for a specified period of time (hope not much explanation required for our favorite method).
class LazyInitDemo { private Helper helper = null; public Helper getHelper() { if (helper == null) synchronized(this) { if (helper == null) helper = new Helper(); } return helper; } // other methods and fields... }
13.volatile 解决多线程内存不可见问题。对于一写多读,可以解决变量同步问题,但是多写,无法解决线程安全问题。如果是 count++操作,使用如下类实现:AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)。
14.HashMap 在容量不够进行 resize 时高并发可能死链。
15.使用 static修饰ThreadLocal 对象解决共享对象的更新问题。此变量针对一个线程内所有操作共享的,所以设置为静态变量。所有此类实例共享此静态变量 ,(在类第一次装载只分配一块空间,所有此类的对象(只要是这个线程内定义的)都可操控此变量。)
if (condition) { ... return obj; } // 接着写 else 的业务逻辑代码。如果非得使用 if()...else if()...else...方式表达逻辑,请勿超过 3 层。
//超过 3 层的 if-else 的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现,其中卫语句示例如下: public void today() { if (isBusy()) { System.out.println(“change time.”); return; } if (isFree()) { System.out.println(“go to travel.”); return; } System.out.println(“ learn Java Coding Guidelines.”); return; }
5.将复杂逻辑判断的结果赋值给一个布尔变量名,提高可读性。
正例: // 伪代码如下 final boolean existed = (file.open(fileName, "w") != null) && (...) || (...); if (existed) { ... } 反例: if ((file.open(fileName, "w") != null) && (...) || (...)) { ... }
6.定义对象、变量、获取数据库连接、 try-catch 操作尽量移至循环体外处理。
7.取反逻辑不利于快速理解。正例:使用 if (x < 628) 来表达 x 小于 628。反例:使用 if (!(x >= 628)) 来表达 x 小于 628。
8.接口入参保护常见地用作批量操作。
9.需要参数校验: 调用频次低的方法。 执行时间开销很大,参数校验时间短。需高稳定性和可用性的。对外提供的接口()0 RPC/API/HTTP 接口)。 敏感权限入口。
10不需要参数校验: 极有可能被循环调用的方法,注明了外部参数检查。底层调用频度高(因为底层可靠)。一般 DAO 层与 Service 层都在同应用, DAO 的参数校验,可以省略。 private 只自己调用的方法。
本文来自博客园,作者:z_s_s,转载请注明原文链接:https://www.cnblogs.com/zhoushusheng/p/15880728.html
浙公网安备 33010602011771号