基础 基本类型的零值
自动类型转换顺序 char>int>long>float>double
关键字 所有成员变量的调用附带上this,防止代码产生歧义或冲突。
synchronized 以某个代码片段为例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 public void fizz(Runnable printFizz) throws InterruptedException { while (i <= n) { synchronized (this) { if (i % 3 == 0 && i % 5 != 0) { printFizz.run(); i++; this.notifyAll(); } else { this.wait(); } } } }
使用javap -v命令查看其class文件,得到如下的字节码命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 public void fizz(java.lang.Runnable) throws java.lang.InterruptedException; descriptor: (Ljava/lang/Runnable;)V flags: ACC_PUBLIC Code: stack=3, locals=4, args_size=2 0: aload_0 1: getfield #2 // Field i:I 4: aload_0 5: getfield #3 // Field n:I 8: if_icmpgt 73 11: aload_0 12: dup 13: astore_2 14: monitorenter // 在这里锁住 15: aload_0 16: getfield #2 // Field i:I 19: iconst_3 20: irem 21: ifne 56 24: aload_0 25: getfield #2 // Field i:I 28: iconst_5 29: irem 30: ifeq 56 33: aload_1 34: invokeinterface #4, 1 // InterfaceMethod java/lang/Runnable.run:()V 39: aload_0 40: dup 41: getfield #2 // Field i:I 44: iconst_1 45: iadd 46: putfield #2 // Field i:I 49: aload_0 50: invokevirtual #5 // Method java/lang/Object.notifyAll:()V 53: goto 60 56: aload_0 57: invokevirtual #6 // Method java/lang/Object.wait:()V 60: aload_2 61: monitorexit // 在这里第一次释放锁 62: goto 70 65: astore_3 66: aload_2 67: monitorexit // 这里会再次释放锁 68: aload_3 69: athrow 70: goto 0 73: return Exception table: from to target type 15 62 65 any 65 68 65 any LineNumberTable: line 47: 0 line 48: 11 line 49: 15 line 50: 33 line 51: 39 line 52: 49 line 54: 56 line 56: 60 line 58: 73 LocalVariableTable: Start Length Slot Name Signature 0 74 0 this Lme/fengorz/leetcode/concurrency/fizz_buzz_multithreaded/FizzBuzz; 0 74 1 printFizz Ljava/lang/Runnable; StackMapTable: number_of_entries = 6 frame_type = 0 /* same */ frame_type = 252 /* append */ offset_delta = 55 locals = [ class java/lang/Object ] frame_type = 3 /* same */ frame_type = 68 /* same_locals_1_stack_item */ stack = [ class java/lang/Throwable ] frame_type = 250 /* chop */ offset_delta = 4 frame_type = 2 /* same */ Exceptions: throws java.lang.InterruptedException
发现上面有两条关键字节码monitorenter
和monitorexit
:
每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter
指令时尝试获取monitor的所有权。
monitorexit会释放锁
上面的字节码文件中出现了一次monitorenter
和二次monitorexit
。
两次monitorexit
指令的原因是为了保证抛异常的情况下也能释放锁,所以javac为同步代码块添加了一个隐式的try-finally,在finally中会调用monitorexit
命令释放锁。
transient https://baijiahao.baidu.com/s?id=1636557218432721275&wfr=spider&for=pc
Class
泛型 1 2 3 4 public <T> T readObjectData(ByteBuffer buffer, Class<T> type) { ... T retVal = (T) summaries; return retVal;
我在stackoverflow发现一个很好的解析:
1 2 3 4 public <T> T readObjectData(... ^ ^ | + Return type + Generic type argument
集合 Unimagined 1 2 // 一个集合并入到另一个集合的安全写法 Collections.addAll(Collection<? super T> c, T... elements);
1 Collections.singletonMap(...);
快速失败机制(fast-fail) 集合常见的快速失败检查都是通过如下代码:
1 2 3 4 final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
来自网上的Java集合体系图
异常 一些自定义常用异常可以抽成公用的静态final异常,如: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public final class PrematureCloseException extends IOException { public static final PrematureCloseException BEFORE_RESPONSE_SENDING_REQUEST = new PrematureCloseException("Connection has been closed BEFORE response, while sending request body"); public static final PrematureCloseException BEFORE_RESPONSE = new PrematureCloseException("Connection prematurely closed BEFORE response"); public static final PrematureCloseException DURING_RESPONSE = new PrematureCloseException("Connection prematurely closed DURING response"); PrematureCloseException(String message) { super(message); } @Override public synchronized Throwable fillInStackTrace() { // omit stacktrace for this exception return this; } }
一些运行时异常可以用Spring内置的断言API比较高逼格
非null异常的优雅写法 1 Objects.requireNonNull(obj);
try资源句式 如果代码调用的类是实现java.lang.AutoCloseable的,用try资源句式来释放关闭资源这样代码就优雅多了。
PropertyDescriptor 1 2 3 PropertyDescriptor descriptor = new PropertyDescriptor("propertyName", param.getClass()); Method getPropertyMethod = descriptor.getReadMethod(); property = (Integer) getPropertyMethod.invoke(param);
String https://blog.csdn.net/u010137760/article/details/82869637
String.intern https://www.cnblogs.com/Yintianhao/p/12273714.html
n次方写法 在Java中一个数的N次方不可以写成:a^0这种形式,算得的数不正确; 正确的写法为Math.pow(a,0);
废弃对象手动赋值为null有助于GC即将生效 1 2 3 Object o = new Object(); o = null; o = new Object(); // 这里将触发上面null的GC,又或者调用System.gc(); 但是System.gc()是Full GC
Integer.valueOf(x)缓存 如果x的范围在-128~127之间,那么会直接返回缓存中的对象,也就是只要数值被调用过一次,以后返回的都是同个对象实例了。
避坑 代码格式 一个团队你的代码格式一定要统一,不然代码冲突时,比对代码非常麻烦。
Service与Controller的交互 Service最终返回给Controller层的数据如果比较复杂,尽量不要用Map,可以封装成DTO,而且这样一来还可以结合JSR303或者其各种扩展,对DTO进行优雅的数据校验。
减少箭头型代码 https://blog.csdn.net/weixin_33858485/article/details/88768985 但是个人认为,一味的追求箭头代码也会有弊端,比如这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Task task = monTask.getTask(); if (task == null) { return; } MonAssignTaskExample monAssignTaskExample = new MonAssignTaskExample(); monAssignTaskExample.createCriteria().andTaskIdEqualTo(task.getTaskId()).andDealObjectIdEqualTo(task.getDealObjectId()) .andIsFinishEqualTo("N"); List<MonAssignTask> monAssignTasks = this.baseMonAssignTaskBO.selectByExample(monAssignTaskExample); if (ObjectUtils.isEmpty(monAssignTasks)) { return; } for (MonAssignTask monAssignTask : monAssignTasks) { // do something }
这样写固然是可以减少箭头代码,但是如若这段代码下面今后要添加一些额外的业务逻辑,这些业务逻辑与task是否为null没有必要的联系, 那么可能会因为task==null退出了整个函数,导致额外的业务逻辑不被执行。
Utils 工具类的命名 每次在使用工具类的时候,总是会出现一个类名出现N个工具类比如: 而且自己也会有自定义的Utils类,为了更好地区分,我喜欢在Utils类价格前缀Enhanced,这样找自己的工具类就方便多了,比如:
@Deprecated 有些被封装的业务方法或者工具类方法如果有做修改,增加了方法的升级版,老旧的方法可以用@Deprecated标记为过时,防止新业务逻辑用到废弃的API。
魔法值应该尽量避免 阿里巴巴说java规范里面说杜绝一切魔法值,但是我觉得也不用可以完全追求零魔法值,我认为需要用常量代替魔法值的有以下这些:
使用频率高的,或者被频繁调用的逻辑块中的魔法值。
可读性很差的魔法值和表达式,应该禁止,最好都声明成可读性较好的常量或者变量。
多余的super(); 在看公司的上古代码发现这种写法:
1 2 3 4 5 public CreateRepairBillThread(SaInterfaceAccept accept) { super(); this.accept = accept; this.timeout = timeout * 60L * 1000L; }
这里面super();是一句多余的代码,因为子类的构造器会默认先调用父类无参构造器。
尽量避免大对象 大对象就是指需要大量连续内存空间的Java对象,最典型的大对象便是那种很长的字符串,或者元素数量很庞大的数组。 比遇到大对象更加坏的消息就是遇到一群“朝生夕灭”的“短命大对象”,我们写程序的时候应注意避免。 在Java虚拟机中要避免大对象的原因是,在分配空间时,它容易导致内存明明还有不少空间时就提前触发垃圾收集,以获取足够的连续空间才能安置好它们,而当复制对象时,大对象就意味着高额的内存复制开销。HotSpot虚拟机提供了-XX: PretenureSizeThreshold
参数, 指定大于该设置值的对象直接在老年代分配,这样做的目的就是避免在Eden区及两个Survivor区之间来回复制,产生大量的内存复制操作。
接口与方法的实现注意形参尽量用可传送的DTO对象(实现Serializable) 这个将来方便各种动态扩展参数。
自动装箱和拆箱的坑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void main(String[] args) { Integer a = 1; Integer b = 2; Integer c = 3; Integer d = 3; Integer e = 128; Integer f = 128; Integer e1 = 127; Integer f1 = 127; Long g = 3L; System.out.println(c.equals(d)); System.out.println(e == f);// 不在-128~127范围内不相等 System.out.println(e.equals(f));// 替换成equals才会相等 System.out.println(e1 == f1);// 只有在-128~127之间,读取缓存中的值才会相等 System.out.println(g == (a + b));// == 会处理自动类型转换 System.out.println(g.equals(a + b));// equals方法不处理自动类型转换 }
涉及到日志输出注意避免无意义日志大量产生 对于某些重复性很高的业务逻辑,日志的输出要注意在可控的范围内,千万不能引起日志巨量产生而导致磁盘爆满。
读取IO流要注意尾部数据失真问题 1 2 3 4 5 6 7 8 9 temps = response.getOutputStream(); in = new DataInputStream(inputStream); // 这个方法写入音频流时有个致命问题,如果是音频流会出现尾部有杂音,因为byte数组如果尾部空流在音频当还是会被当做声音处理 // 如果采用下面注释掉的这种写法的话 byte[] b = new byte[2048]; while ((in.read(b)) != -1) { temps.write(b); temps.flush(); }
可以采用HuTool工具API,或者使用其内部的实现机制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 byte[] b = new byte[length]; int readLength; try { readLength = in.read(b); } catch (IOException e) { throw new IORuntimeException(e); } if (readLength > 0 && readLength < length) { byte[] b2 = new byte[length]; System.arraycopy(b, 0, b2, 0, readLength); return b2; } else { return b; }
1 2 3 4 5 6 byte[] b = IoUtil.readBytes(in, 1024); while (b != null && b.length > 0) { temps.write(b); temps.flush(); b = IoUtil.readBytes(in, 1024); }
集合在new的时候通过构造器传入初始数据时,要避免是null值 1 2 List list = new LinkedLIst(...); // ...里面要避免是null值
Combo jar Mac中jar运行之后窗口无法粘贴 有些情况下需要使用windows的复制粘贴快捷键才能生效
jar指定目录的所有classes去打包 1 jar cvf test.jar -C unwoven-classes .
Jackson Convert Error: Cannot construct instance of java.time.LocalDateTime
LocalDateTime属性加上注解 @JsonDeserialize(using = LocalDateTimeDeserializer.class) @JsonSerialize(using = LocalDateTimeSerializer.class)
Thread.sleep 使用 TimeUnit.XXX.sleep(x) 代替(可读性更好) https://stackoverflow.com/questions/9587673/thread-sleep-vs-timeunit-seconds-sleep
音频格式转换 使用JAVE 音频转换包 库
Mac下JDK目录 可以通过/usr/libexec
目录下的java_home
命令找到Mac下安装过的JDK所在目录
1 2 {/usr/libexec} # ./java_home /Library/Java/JavaVirtualMachines/jdk-11.0.8.jdk/Contents/Home