阿里巴巴Java开发手册快速学习

简介: 善医者“未有形而除之”,提高工程健壮性最好的方式是在代码出现问题之前就排除掉,不给Bug出现的机会。一份好的开发规范就可以起到这样的作用,大大减少产品上线后的问题。《阿里巴巴Java开发手册》是阿里巴巴的内部编码规范,阿里官方的Java代码规范标准,这份开发手册不仅规范了一些开发细节,也提出了很多工程开发的哲学,值得好好阅读。

Java作为一门名副其实的工业级语言,语法友好,学习简单,大规模的应用给代码质量的管控带来了困难,特别是团队开发中,开发过程中的规范会直接影响最终项目的稳定性。

善医者“未有形而除之”,提高工程健壮性最好的方式是在代码出现问题之前就排除掉,不给Bug出现的机会。一份好的开发规范就可以起到这样的作用,大大减少产品上线后的问题。

 《阿里巴巴Java开发手册》是阿里巴巴的内部编码规范,阿里官方的Java代码规范标准, 手册以Java应用开发为维度,分为编程规约、异常日志规约、MYSQL规约、工程规约、安全规约五个章节,给出了强制、推荐、参考三个级别,每条规范都有推荐的约束力度,从命名到项目拆分,不仅规范了一些开发细节,也提出了很多工程开发的哲学,值得好好阅读。

点击下载阿里巴巴Java开发手册》(v1.1.0版)

下面记录一些比较有启发的条款,提纲挈领,快速学习。

 

一、编程规约

1.如果使用到了设计模式,建议在类名中体现出具体模式

将设计模式体现在名字中,有利于阅读者快速理解架构设计思想。 

2.相同参数类型,相同业务含义,才可以使用 Java 的可变参数,避免使用 Object

可变参数必须放置在参数列表的最后,尽量不用可变参数编程。

3.对外暴露的接口签名,原则上不允许修改方法签名,避免对接口调用方产生影响

接口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么。

4.关于基本数据类型与包装数据类型的使用标准如下

1) 所有的POJO类属性必须使用包装数据类型

2) RPC方法的返回值和参数必须使用包装数据类型

3) 所有的局部变量【推荐】使用基本数据类型

POJO 类属性没有初值是醒使用者在需要使用时,必须自己显式地进行赋值,任何 NPE 问题,或者入库检查,都由使用者来保证。数据库的查询结果可能是null,因为自动拆箱,用基本数据类型接收有NPE风险。

5.注意 serialVersionUID 不一致会抛出序列化运行时异常

序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败;如果完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。

 

6.POJO 类必须写 toString 方法

使用 IDE 的中工具:source> generate toString 时,如果继承了另一个 POJO 类,注意在前面加一下 super.toString。 在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排查问题。

7.final 可提高程序响应效率,声明成 final 的情况:

1) 不需要重新赋值的变量,包括类属性、局部变量

2) 对象参数前加final,表示不允许修改引用的指向

3) 类方法确定不允许被重写

8.慎用 Object 的 clone 方法来拷贝对象

对象的 clone 方法默认是浅拷贝,若想实现深拷贝需要重写 clone 方法实现属性对象 的拷贝。

9.类成员与方法访问控制从严

1) 如果不允许外部直接通过new来创建对象,那么构造方法必须是private

2) 工具类不允许有public或default构造方法

3) 类非static成员变量并且与子类共享,必须是protected 4) 类非static成员变量并且仅在本类使用,必须是private

5) 类static成员变量如果仅在本类使用,必须是private

6) 若是static成员变量,必须考虑是否为final

7) 类成员方法只供类内部调用,必须是private

8) 类成员方法只对继承类公开,那么限制为protected

任何类、方法、参数、变量,严控访问范围。过宽泛的访问范围,不利于模块解耦。思考:如果是一个 private 的方法,想删除就删除,可是一个 public 的 Service 方法,或者一个 public 的成员变量,删除一下,不得手心冒点汗吗?变量像自己的小孩,尽量在自己的视线内,变量作用域太大,如果无限制的到处跑,那么你会担心的。

 

10.ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException 异常

subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是 ArrayList 的一个视图,对于SubList子列表的所有操作最终会反映到原列表上。

11.使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法

使用add/remove/clear 方法会抛出 UnsupportedOperationException 异常。asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。

12.不要在 foreach 循环里进行元素的 remove/add 操作

remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁。

13.获取单例对象需要保证线程安全,其中的方法也要保证线程安全

资源驱动类、工具类、单例工厂类都需要注意。

14.线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式

这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 Executors 返回的线程池对象的弊端如下: 1)FixedThreadPool 和 SingleThreadPool:

允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

2)CachedThreadPool 和 ScheduledThreadPool:

允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

 

15.SimpleDateFormat 是线程不安全的类,一般不要定义为static变量

如果定义为static,必须加锁,或者使用 DateUtils 工具类。 注意线程安全,使用 DateUtils。亦推荐如下处理:

private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { @Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
} };
AI 代码解读

16.高并发时,同步调用应该去考量锁的性能损耗

能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。

17.并发修改同一记录时,避免更新丢失

要么在应用层加锁,要么在缓存加锁,要么在 数据库层使用乐观锁,使用 version 作为更新依据。 如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于 3 次。

18.对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁

19.使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用countDown

方法,线程执行代码注意 catch 异常,确保 countDown 方法可以执行,避免主线程无法执行 至 await 方法,直到超时才返回结果。注意,子线程抛出异常堆栈,不能在主线程 try-catch 到。

20.避免 Random 实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一 seed 导致的性能下降。

Random 实例包括 java.util.Random 的实例或者 Math.random()实例。

 

21.volatile 解决多线程内存不可见问题

对于一写多读,是可以解决变量同步问题, 但是如果多写,同样无法解决线程安全问题。如果是 count++操作,使用如下类实现: AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)。

22.ThreadLocal 无法解决共享对象的更新问题,建议使用 static 修饰

这个变量是针对一个线程内所有操作共有的,所以设置为静态变量,所有此类实例共享 此静态变量 ,也就是说在类第一次被使用时装载,只分配一块存储空间,所有此类的对象(只要是这个线程内定义的)都可以操控这个变量。

 

二、异常日志

1.对大段代码进行 try-catch,这是不负责任的表现

catch 时请分清稳定代码和非稳 定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的catch尽可能进行区分 异常类型,再做对应的异常处理。

2.捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之

如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。

3.在代码中使用“抛异常”还是“返回错误码”

对于公司外的 http/api 开放接口必须 使用“错误码”;而应用内部推荐异常抛出;跨应用间 RPC 调用优先考虑使用 Result 方式,封 装 isSuccess、“错误码”、“错误简短信息”。

4.避免出现重复的代码(Don’t Repeat Yourself),即DRY原则

随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。

5.对trace/debug/info 级别的日志输出,必须使用条件输出形式或者使用占位符的方

6.异常信息应该包括两类信息:案发现场信息和异常堆栈信息

如果不处理,那么往上抛。

 

三、MySQL 规约

1.表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint ( 1表示是,0表示否),此规则同样适用于odps建表。 任何字段如果为非负数,必须是unsigned。

2.小数类型为 decimal,禁止使用 float 和 double

float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。

3.表必备三字段:id, gmtcreate, gmtmodified

其中id必为主键,类型为unsigned bigint、单表时自增、步长为1。gmtcreate, gmtmodified 的类型均为 date_time 类型。

4.单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表

如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。避免过度设计。

5.业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引

 

6.在 varchar 字段上建立索引时,必须指定索引长度

没必要对全字段建立索引,根据实际文本区分度决定索引长度。 说索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分 度会高达 90%以上,可以使用 count(distinct left(列名, 索引长度))/count(*)的区分度 来确定。

7.利用覆盖索引来进行查询操作,来避免回表操作

能够建立索引的种类:主键索引、唯一索引、普通索引,而覆盖索引是一种查询的一种 效果,用explain的结果,extra列会出现:using index。如果索引包含所有满足查询需要的数据的索引成为覆盖索引(Covering Index),也就是平时所说的不需要回表操作

8.利用延迟关联或者子查询优化超多分页场景

MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行,那当 offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL 改写。

9.SQL 性能优化的目标

至少要达到 range 级别,要求是 ref 级别,如果可以是 consts 最好。

1)consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。 2)ref 指的是使用普通的索引(normal index)。 3)range 对索引进行范围检索。

10.不要使用 count(列名)或 count(常量)来替代 count(*)

count()就是 SQL92 定义 的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。 count()会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。

11.使用 ISNULL()来判断是否为 NULL 值

注意,NULL与任何值的直接比较都为 NULL

12.不得使用外键与级联,一切外键概念必须在应用层解决

外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。

13.iBATIS 自带的 queryForList(String statementName,int start,int size)不推荐使用

其实现方式是在数据库取到 statementName 对应的 SQL 语句的所有记录,再通过 subList 取 start,size 的子集合,线上因为这个原因曾经出现过 OOM。

14.不要写一个大而全的数据更新接口

传入为 POJO 类,不管是不是自己的目标更新字段, 都进行 update table set c1=value1,c2=value2,c3=value3; 这是不对的。

执行 SQL 时,尽量不要更新无改动的字段,一是易出错;二是效率低;三是 binlog 增加存储。

 

四、工程规约

1.高并发服务器建议调小 TCP 协议的 time_wait 超时时间

操作系统默认 240 秒后,才会关闭处于 timewait 状态的连接,在高并发访问下,服 务器端会因为处于 timewait 的连接数太多,可能无法建立新的连接,所以需要在服务器上 调小此等待值。 正例:在 linux 服务器上请通过变更/etc/sysctl.conf 文件去修改该缺省值(秒): net.ipv4.tcpfintimeout = 30

2.调大服务器所支持的最大文件句柄数(File Descriptor,简写为fd)

主流操作系统的设计是将 TCP/UDP 连接采用与文件一样的方式去管理,即一个连接对应于一个 fd。主流的 linux 服务器默认所支持最大 fd 数量为 1024,当并发连接数很大时很 容易因为 fd 不足而出现“open too many files”错误,导致新的连接无法建立。 建议将 linux 服务器所支持的最大句柄数调高数倍(与服务器的内存数量相关)。

 

五、安全规约

1. 隶属于用户个人的页面或者功能必须进行权限控制校验

防止没有做水平权限校验就可随意访问、操作别人的数据,比如查看、修改别人的订单。

2. 用户敏感数据禁止直接展示,必须对展示数据脱敏

查看个人手机号码会显示成:158****9119,隐藏中间 4 位,防止隐私泄露。

3. 用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定,防止 SQL 注入, 禁止字符串拼接 SQL 访问数据库

4. 用户请求传入的任何参数必须做有效性验证

忽略参数校验可能导致: page size过大导致内存溢出 恶意order by导致数据库慢查询 任意重定向 SQL注入 反序列化注入 正则输入源串拒绝服务ReDoS——Java 代码用正则来验证客户端的输入,有些正则写法验证普通用户输入没有问题, 但是如果攻击人员使用的是特殊构造的字符串来验证,有可能导致死循环的效果。

5. 禁止向 HTML 页面输出未经安全过滤或未正确转义的用户数据

6. 表单、AJAX 交必须执行 CSRF 安全过滤

CSRF(Cross-site request forgery)跨站请求伪造是一类常见编程漏洞。对于存在 CSRF 漏洞的应用/网站,攻击者可以事先构造好 URL,只要受害者用户一访问,后台便在用户 不知情情况下对数据库中用户参数进行相应修改。

7. 在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防重放限制, 如数量限制、疲劳度控制、验证码校验,避免被滥刷、资损

如注册时发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其 它用户,并造成短信平台资源浪费。

8. 发贴、评论、发送即时消息等用户生成内容的场景必须实现防刷、文本内容违禁词过滤等风控策略

目录
打赏
0
0
0
2
383
分享
相关文章
Java线程池ExecutorService学习和使用
通过学习和使用Java中的 `ExecutorService`,可以显著提升并发编程的效率和代码的可维护性。合理配置线程池参数,结合实际应用场景,可以实现高效、可靠的并发处理。希望本文提供的示例和思路能够帮助开发者深入理解并应用 `ExecutorService`,实现更高效的并发程序。
47 10
【潜意识Java】深度分析黑马项目《苍穹外卖》在Java学习中的重要性
《苍穹外卖》项目对Java学习至关重要。它涵盖了用户管理、商品查询、订单处理等模块,涉及Spring Boot、MyBatis、Redis等技术栈。
267 4
【潜意识Java】Java基础教程:从零开始的学习之旅
本文介绍了 Java 编程语言的基础知识,涵盖从简介、程序结构到面向对象编程的核心概念。首先,Java 是一种高级、跨平台的面向对象语言,支持“一次编写,到处运行”。接着,文章详细讲解了 Java 程序的基本结构,包括包声明、导入语句、类声明和 main 方法。随后,深入探讨了基础语法,如数据类型、变量、控制结构、方法和数组。此外,还介绍了面向对象编程的关键概念,例如类与对象、继承和多态。最后,针对常见的编程错误提供了调试技巧,并总结了学习 Java 的重要性和方法。适合初学者逐步掌握 Java 编程。
63 1
|
6月前
|
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
136 43
Java学习十六—掌握注解:让编程更简单
14天Java基础学习——第1天:Java入门和环境搭建
本文介绍了Java的基础知识,包括Java的简介、历史和应用领域。详细讲解了如何安装JDK并配置环境变量,以及如何使用IntelliJ IDEA创建和运行Java项目。通过示例代码“HelloWorld.java”,展示了从编写到运行的全过程。适合初学者快速入门Java编程。
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。
《阿里巴巴Java开发手册》条例解读(一)
《阿里Java开发规范》应该是众多程序猿多年来,在使用Java的过程中,根据踩过的雷趟过的坑,总结出来的“血的教训”或“踩坑手册”。但就像《葵花宝典》,即便是读过一百遍也成功自宫,也不见得能马上能成为武林高手,因为没练过。
《阿里巴巴Java开发手册》条例解读(一)
|
2月前
|
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
173 60
【Java并发】【线程池】带你从0-1入门线程池
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
73 23
目录
目录
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等