1. 首页
  2. IT资讯

阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知

“u003Cpu003E很多人都知道,阿里巴巴在2017发布了《阿里巴巴Java开发手册》,前后推出了很多个版本,并在后续推出了与之配套的IDEA插件和书籍。u003Cu002Fpu003Eu003Cpu003E相信很多Java开发都或多或少看过这份手册,这份手册有7个章节,覆盖了编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构以及设计规约等方面。u003Cu002Fpu003Eu003Cpu003E这份规约可以说是覆盖了Java开发的方方面面,如果还有人没看的话,强烈建议大家好好看看,并且仔细研读。u003Cu002Fpu003Eu003Cpu003E手册中,有那么一些规则,是比较容易理解的。比如一些变量命名规范,有另外一些规则,是不太容易理解的,背后是有很多思考的,有一些则是阿里这么多年来遇到的坑的总结。u003Cu002Fpu003Eu003Cpu003E这份手册在诞生之初,是在阿里内部的,那时候就引起了广泛的讨论。最终外界看到的那份手册,是阿里无数工程师"挑剔"后的结果,可以说是凝聚了无数工程师成功的经验、踩过的坑等。u003Cu002Fpu003Eu003Cpu003E其实,规约最大的价值,应该是促使人去思考规约制定背后的思考。真的去探查规约背后的原理,这个过程中可以学习到很多东西。u003Cu002Fpu003Eu003Cblockquoteu003Eu003Cpu003E《阿里巴巴Java开发手册》还没有的小伙伴可以来私信我【阿里学习手册】领取一份u003Cu002Fpu003Eu003Cu002Fblockquoteu003Eu003Ch1 class=”pgc-h-arrow-right”u003E一、为什么阿里巴巴禁止工程师直接使用日志系统(Log4j、Logback)中的 APIu003Cu002Fh1u003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002Fa68e4552774643719777b5aa2936cadc” img_width=”1080″ img_height=”191″ alt=”阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E在Java生态体系中,围绕着日志,有很多成熟的解决方案。关于日志输出,主要有两类工具。u003Cu002Fpu003Eu003Cpu003E一类是日志框架,主要用来进行日志的输出的,比如输出到哪个文件,日志格式如何等。另外一类是日志门面,主要一套通用的API,用来屏蔽各个日志框架之间的差异的。u003Cu002Fpu003Eu003Cpu003E所以,对于Java工程师来说,关于日志工具的使用,最佳实践就是在应用中使用如Log4j + SLF4J 这样的组合来进行日志输出。u003Cu002Fpu003Eu003Cpu003E这样做的最大好处,就是业务层的开发不需要关心底层日志框架的实现及细节,在编码的时候也不需要考虑日后更换框架所带来的成本。这也是门面模式所带来的好处。u003Cu002Fpu003Eu003Ch1 class=”pgc-h-arrow-right”u003E二、为什么阿里巴巴建议集合初始化时,指定集合容量大小?u003Cu002Fh1u003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F16a5526bb16c40af91f1608533aa8e46″ img_width=”1080″ img_height=”273″ alt=”阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003EHashMap有扩容机制,就是当达到扩容条件时会进行扩容。如果我们没有设置初始容量大小,随着元素的不断增加,HashMap会发生多次扩容,而HashMap中的扩容机制决定了每次扩容都需要重建hash表,是非常影响性能的。u003Cu002Fpu003Eu003Cpu003E默认情况下,当我们设置HashMap的初始化容量时,实际上HashMap会采用第一个大于该数值的2的幂作为初始化容量。u003Cu002Fpu003Eu003Cpu003E但是,为了最大程度的避免扩容带来的性能消耗,我们建议可以把默认容量的数字设置成expectedSize u002F 0.75F + 1.0F 。在日常开发中,可以使用u003Cu002Fpu003Eu003Cpreu003EMap<String,String>map=Maps.newHashMapWithExpectedSize(10);u003Cu002Fpreu003Eu003Cpu003E来创建一个HashMap,计算的过程guava会帮我们完成。u003Cu002Fpu003Eu003Cpu003E但是,以上的操作是一种用内存换性能的做法,真正使用的时候,要考虑到内存的影响。u003Cu002Fpu003Eu003Ch1 class=”pgc-h-arrow-right”u003E三、为什么阿里巴巴禁止在 foreach 循环里进行元素的 removeu002Fadd 操作u003Cu002Fh1u003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002Fb704269acffb4fcfb3ed962108a3079f” img_width=”1080″ img_height=”692″ alt=”阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E我们使用的增强for循环,其实是Java提供的语法糖,其实现原理是借助Iterator进行元素的遍历。u003Cu002Fpu003Eu003Cpu003E但是如果在遍历过程中,不通过Iterator,而是通过集合类自身的方法对集合进行添加u002F删除操作。那么在Iterator进行下一次的遍历时,经检测发现有一次集合的修改操作并未通过自身进行,那么可能是发生了并发被其他线程执行的,这时候就会抛出异常,来提示用户可能发生了并发修改,这就是所谓的fail-fast机制。u003Cu002Fpu003Eu003Cpu003E当然还是有很多种方法可以解决这类问题的。比如使用普通for循环、使用Iterator进行元素删除、使用Stream的filter、使用fail-safe的类等。u003Cu002Fpu003Eu003Ch1 class=”pgc-h-arrow-right”u003E四、为什么阿里巴巴禁止把SimpleDateFormat定义为static类型的?u003Cu002Fh1u003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F822fe3c4d7004d8f851854dbeaffe100″ img_width=”1080″ img_height=”98″ alt=”阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003ESimpleDateFormat主要可以在String和Date之间做转换,还可以将时间转换成不同时区输出。但是在并发场景中SimpleDateFormat是不能保证线程安全的,需要开发者自己来保证其安全性。u003Cu002Fpu003Eu003Cpu003ESimpleDateFormat中的format方法在执行过程中,会使用一个成员变量calendar来保存时间。这其实就是问题的关键。u003Cu002Fpu003Eu003Cpu003E如果一个SimpleDateFormat使用的是static定义的。那么这个SimpleDateFormat就是一个共享变量,随之,SimpleDateFormat中的calendar也就可以被多个线程访问到。就会有并发安全问题。u003Cbru002Fu003Eu003Cu002Fpu003Eu003Ch1 class=”pgc-h-arrow-right”u003E五、为什么阿里巴巴不建议在for循环中使用"+"进行字符串拼接u003Cu002Fh1u003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F6c11b197f6954b37aec7ddd35c621607″ img_width=”1080″ img_height=”310″ alt=”阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E使用+拼接字符串,其实只是Java提供的一个语法糖,通过反编译,我们可以发现,其实字符串常量使用"+"在拼接过程中,是将String转成了StringBuilder后,使用其append方法进行处理的。u003Cu002Fpu003Eu003Cpu003E如果在一个for循环中,使用"+"对字符串进行拼接,那么每一次都会重新new一个StringBuilder,然后再把String转成StringBuilder,再进行append。u003Cu002Fpu003Eu003Cpu003E而频繁的新建对象当然要耗费很多时间了,不仅仅会耗费时间,频繁的创建对象,还会造成内存资源的浪费。u003Cu002Fpu003Eu003Ch1 class=”pgc-h-arrow-right”u003E六、为什么阿里巴巴要求程序员谨慎修改serialVersionUID 字段的值u003Cu002Fh1u003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F436ded387a354214a9ea3f5e24cb2e05″ img_width=”1080″ img_height=”165″ alt=”阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E序列化提供了一种方案,可以让你在即使JVM停机的情况下也能把对象保存下来的方案。就像我们平时用的U盘一样。把Java对象序列化成可存储或传输的形式(如二进制流),比如保存在文件中。这样,当再次需要这个对象的时候,从文件中读取出二进制流,再从二进制流中反序列化出对象。u003Cu002Fpu003Eu003Cpu003E虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致,这个所谓的序列化ID,就是我们在代码中定义的serialVersionUID。u003Cu002Fpu003Eu003Cpu003E在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。u003Cu002Fpu003Eu003Ch1 class=”pgc-h-arrow-right”u003E七、为什么阿里巴巴要求谨慎使用ArrayList中的subList方法u003Cu002Fh1u003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F31c6bbbe6abf49ee83df64792072ae07″ img_width=”1080″ img_height=”186″ alt=”阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003EsubList是List接口中定义的一个方法,该方法主要用于返回一个集合中的一段、可以理解为截取一个集合中的部分元素,他的返回值也是一个List。u003Cu002Fpu003Eu003Cpu003E但是subList 返回的并不是一个全新的List,而是一个视图。就是说,SubList并没有重新创建一个List,而是直接引用了原有的List(返回了父类的视图),只是指定了一下他要使用的元素的范围而已(从fromIndex(包含),到toIndex(不包含))。u003Cu002Fpu003Eu003Cpu003E所以,首先我们无法将subList方法得到的集合直接转换成ArrayList。因为SubList只是ArrayList的内部类,他们之间并没有继承关系,故无法直接进行强制类型转换。u003Cu002Fpu003Eu003Cpu003E另外,视图和原List的修改还需要注意几点,尤其是他们之间的相互影响:u003Cu002Fpu003Eu003Cul class=”list-paddingleft-2″u003Eu003Cliu003E1、对父(sourceList)子(subList)List做的非结构性修改(non-structural changes),都会影响到彼此。u003Cu002Fliu003Eu003Cliu003E2、对子List做结构性修改,操作同样会反映到父List上。u003Cu002Fliu003Eu003Cliu003E3、对父List做结构性修改,会抛出异常ConcurrentModificationException。u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E所以,阿里巴巴Java开发手册中有另外一条规定:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F64e55c3a57224777928350f0364b51a4″ img_width=”1080″ img_height=”94″ alt=”阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Ch1 class=”pgc-h-arrow-right”u003E八、为什么阿里巴巴建议开发者谨慎使用继承?u003Cu002Fh1u003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fpgc-imageu002Fcb6a2bbec24b4331b0712545c7e2b985″ img_width=”1080″ img_height=”163″ alt=”阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003Eu003Cbru002Fu003E作为一门面向对象开发的语言,代码复用是Java引人注意的功能之一。Java代码的复用有继承,组合以及代理三种具体的表现形式。u003Cu002Fpu003Eu003Cpu003E继承,在写代码的时候就要指明具体继承哪个类,所以,类的继承关系是在编译期就确定的。并且从基类继承来的实现是无法在运行期动态改变的,因此降低了应用的灵活性。u003Cu002Fpu003Eu003Cpu003E组合,在写代码的时候可以采用面向接口编程。所以,类的组合关系一般在运行期确定。另外,代码复用方式上也有一定区别:u003Cu002Fpu003Eu003Cul class=”list-paddingleft-2″u003Eu003Cliu003E继承结构中,父类的内部细节对于子类是可见的。所以我们通常也可以说通过继承的代码复用是一种白盒式代码复用。如果基类的实现发生改变,那么派生类的实现也将随之改变。这样就导致了子类行为的不可预知性。u003Cu002Fliu003Eu003Cliu003E组合是通过对现有的对象进行拼装(组合)产生新的、更复杂的功能。因为在对象之间,各自的内部细节是不可见的,所以我们也说通过组合的代码复用是黑盒式代码复用。因为组合中一般都定义一个类型,所以在编译期根本不知道具体会调用哪个实现类的方法。u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E还有,Java中不支持多继承,而组合是没有限制的。就像一个人只能有一个父亲,但是他可以有很很多辆车。u003Cu002Fpu003Eu003Ch1 class=”pgc-h-arrow-right”u003E九、为什么阿里巴巴禁止开发人员使用isSuccess作为变量名u003Cu002Fh1u003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002Fdeb7b0f9ea95459abb129f3feaa80785″ img_width=”1080″ img_height=”135″ alt=”阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003Efastjson和jackson在把对象序列化成json字符串的时候,是通过反射遍历出该类中的所有getter方法,得到getHollis和isSuccess,然后根据JavaBeans规则,他会认为这是两个属性hollis和success的值。u003Cu002Fpu003Eu003Cpu003E但是Gson并不是这么做的,他是通过反射遍历该类中的所有属性,并把其值序列化成jsonu003Cu002Fpu003Eu003Cpu003E可以看到,由于不同的序列化工具,在进行序列化的时候使用到的策略是不一样的,所以,对于同一个类的同一个对象的序列化结果可能是不同的。u003Cu002Fpu003Eu003Cpu003E如果对于同一个对象,我使用fastjson进行序列化,再使用Gson反序列化,并且恰巧这个对象中某个属性是以isXXX命名的,那么就会产生问题。u003Cu002Fpu003Eu003Cpu003E作为开发者,我们应该想办法尽量避免这种问题的发生,对于POJO的设计者来说,只需要做简单的一件事就可以解决这个问题了,那就是把isSuccess改为success。u003Cu002Fpu003Eu003Ch1 class=”pgc-h-arrow-right”u003E十、为什么阿里巴巴不让使用 COUNT(列名)或 COUNT(常量)来替代 COUNT(*)呢u003Cu002Fh1u003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F11d736f271f649679e3600487aba863a” img_width=”1080″ img_height=”141″ alt=”阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E因为COUNT(*)是SQL92定义的标准统计行数的语法,所以MySQL对他进行了很多优化,MyISAM中会直接把表的总行数单独记录下来供COUNT(*)查询,而InnoDB则会在扫表的时候选择最小的索引来降低成本。当然,这些优化的前提都是没有进行where和group的条件查询。u003Cbru002Fu003Eu003Cu002Fpu003Eu003Cpu003E在InnoDB中COUNT(*)和COUNT(1)实现上没有区别,而且效率一样,但是COUNT(字段)需要进行字段的非NULL判断,所以效率会低一些。u003Cu002Fpu003Eu003Cpu003E因为COUNT(*)是SQL92定义的标准统计行数的语法,并且效率高,所以请直接使用COUNT(*)查询表的行数!u003Cu002Fpu003Eu003Ch1 class=”pgc-h-arrow-right”u003E十一、为什么阿里巴巴不允许使用Executors创建线程池?u003Cu002Fh1u003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002Faa18eab35ce14e20804e3980e9f0cf3b” img_width=”1080″ img_height=”311″ alt=”阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003EJava中的BlockingQueue主要有两种实现,分别是ArrayBlockingQueue 和 LinkedBlockingQueue。u003Cu002Fpu003Eu003Cpu003EArrayBlockingQueue是一个用数组实现的有界阻塞队列,必须设置容量。u003Cu002Fpu003Eu003Cpu003ELinkedBlockingQueue是一个用链表实现的有界阻塞队列,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE。u003Cu002Fpu003Eu003Cpu003E这里的问题就出在:不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE。也就是说,如果我们不设置LinkedBlockingQueue的容量的话,其默认容量将会是Integer.MAX_VALUE。u003Cu002Fpu003Eu003Cpu003E而newFixedThreadPool中创建LinkedBlockingQueue时,并未指定容量。此时,LinkedBlockingQueue就是一个无边界队列,对于一个无边界队列来说,是可以不断的向队列中加入任务的,这种情况下就有可能因为任务过多而导致内存溢出问题。u003Cu002Fpu003Eu003Cpu003E上面提到的问题主要体现在newFixedThreadPool和newSingleThreadExecutor两个工厂方法上,并不是说newCachedThreadPool和newScheduledThreadPool这两个方法就安全了,这两种方式创建的最大线程数可能是Integer.MAX_VALUE,而创建这么多线程,必然就有可能导致OOM。u003Cu002Fpu003Eu003Ch1 class=”pgc-h-arrow-right”u003E总结u003Cu002Fh1u003Eu003Cpu003E以上,就是我之前一段时间通过学习《手册》中的部分规则之后,自己总结的一些内容,这个过程中自己也学习到很多东西。u003Cu002Fpu003Eu003Cpu003E所以想说,这才是学习《手册》的正确姿势,这样才能最大程度的成长!u003Cu002Fpu003Eu003Cpu003E这个系列还在继续更新,后面还会会逐步完善。欢迎关注与交流。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E《阿里巴巴Java开发手册》还没有的小伙伴可以来私信我【阿里学习手册】领取一份u003Cu002Fstrongu003Eu003Cu002Fpu003E”

原文始发于:阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知

主题测试文章,只做测试使用。发布者:乾奕,转转请注明出处:http://www.cxybcw.com/26320.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code