写代码时应该注意的问题
1, 代码可维护性相关
(1), svn 提交不写注释
(2), 命名太随意
(3), 代码随意排版
(4), 多层嵌套结构
(5), 一个方法包打天下 : 要记住 -- 单一职责原则, 一个方法不应该承载太多,要尽量抽取出来。
(6), 不统一的风格
(7), 混乱的 pom 文件
(8), 晕头转向的配置文件 -- Spring配置文件, MyBatis mapper文件,properties文件等
2, 代码正确性相关
(1), 资源清理
(2), 资源使用
(3), 忽视参数检查
(4), 吞掉异常
(5), 并发环境下错误使用数据结构
(6), 数据库
3, 调试诊断相关
(1), 合理记录日志
(2), 监控记录
(3), 给线程指定名称
4, 设计相关
(1), 不可测试的代码
维护性相关
*混乱的 pom 文件*
对于一个java maven项目,pom.xml文件就是这个项目的脸面,脸面当然要整洁漂亮。那么一个漂亮的pom要符合哪些需求呢?
1. 尽量短
a. 保留尽可能少的必须的依赖,把没有必要的依赖删除掉
b. 减少重复。比如我们经常需要引入同一个系列的几个依赖, 这些依赖的版本一般都一致,那我们应该抽取一个
properties来表示该version。在使用父子项目时,我们还可以尽量将一些子module里都重复使用的东西抽取到父pom
里(推荐引入superpom)。
2. 排版整齐
3. 清晰明确
a. 在父pom里使用dependencyManagement来管理依赖,并且将一些重要依赖的版本抽取到properties,这样打开父
pom对这个系统的依赖就一目了然。
b. 父子项目中几个子项目使用统一的版本号
正确性相关
InputStream stream1 = null ;
InputStream stream2 = null ;
try {
stream1 = initStream1();
stream2 = initStream2();
//porcess stream1 and stream2
} finally {
if (stream1 != null ){
try {
stream1.close();
} catch (Exception e){
//record log
}
}
if (stream2 != null ){
try {
stream2.close();
} catch (Exception e){
//record log
}
}
}
// 这样就可以保证stream1 和stream2 都能正确的关闭。但是上面的代码看起来太繁琐了,而且这样的代码在系统中很常见,所以我们一些工具类库也已经为这样的代码提供了工具类,比如commonslang 里的IOUtils 就提供了IOUtils.closeQuietly() 方法,而guava 里也为我们提供Closeables 帮助类。在java 7 里,jdk 甚至为我们提供了trywithresources statement 这个基础设施。
*系统中常见的需要清理的资源*
(1), 磁盘文件,比如FileInputStream, FileOutStream等(只要带Stream的都要注意)
(2), 网络连接, 比如URLConnection, HttpClient, Socket等
(3), 数据库连接,最好使用类似Spring之类的框架访问数据库,如果自己使用DataSource.getConnection(不推荐)则一定要小心
资源使用 *
在我们的系统中经常需要:
1. 读取文件内容
2. 批量读取数据库
3. 线程池
4. 等等
这些操作有一个共同的特征是 : 对系统有限资源(内存,CPU等)的索取。当我们编写对有限资源使用的代码时,一定要注意资源的使用是否可控。比如对于读取文件来讲,如果文件较大我们最好采用流式读取(文件大小不是拍脑袋得出来的,是经过定量的计算得出的),流式读取使得我们可以一边读取文件内容一边处理,如果将文件内容全部读入内存当遇到特别大的文件的时候可能让内存爆掉。流式读取也不是说说那么容易,这就要求我们的文件以及数据格式是便于流式读取处理的。
而对于批量读取数据库的情况,有的同学直接来一个不加limit的SELECT语句,如果这个时候满足条件的记录较多,就会从数据库中加载特别大的数据,这不仅对数据库产生较大压力,对应用的内存也是一个考验。所以对于批量从数据库读取数据,要求我们也采用一种流式的,每次读取一些处理一些,然后再去读取。
有的时候我们为了并行化或异步处理一些操作时,需要自己创建线程池。在Java中就是创建ThreadPoolExecutor类。我们使用这个类时一定要注意这个类的构造函数中几个参数的确切意义是什么,大部分对这几个参数都不明不白,在出问题的时候往往会使问题恶化。而大部分教科书上推荐使用j.u.c中Executors类更是将这种不明不白加重。
其实就一点,我们对系统任何资源的使用都应该是可控的方式,并且能通过定量的方式分析为什么要使用这些资源。
忽视参数检查 *
参数检查的代码确实很讨厌,写起来啰哩啰嗦,还扰乱了我们代码的核心逻辑。于是一些人为了避免这些麻烦,并且信心满满的说我这个方法的调用源比较可靠不会传递非法的参数给我,如果确实如此当然可以,但理想很丰满,现实很骨感。往往越是我们盲目自信的地方,往往越容易出一些低级的错误。所以对一些公开出去的接口,我们一定要注意参数验证的逻辑。对于一些private接口
如果经过检查确实不会有非法参数传递进来,可以不做参数验证。
好在我们有非常多的第三方库可以使用帮我们降低参数验证的繁琐。比如在Spring Controller这个级别我们有Validator,它可以将参数验证逻辑剥离到另外一个类中处理,让Controller更纯洁。对于内部的一些接口,我们也可以使用Google guava的Preconditions来简化代码。比如: Preconditions.checkNotNull(url);
并发环境下错误使用数据结构 *
设计的时候考虑到即使以后代码在并发环境下执行也不会出问题,如果因为其他原因做不到这点也应该在接口上详细的表明这一点
数据库 *
给表设计主键 如果未给表指定主键,MySQL会从表中选取一个唯一的列作为主键,如果无法找到这样的列,Mysql会给定一个隐藏的列作为主键,这会带来很多问题, 使用非业务字段作为主键 业务字段可能因为需求的变更而发生变化使用按插入先后顺序的字段作为主键(比如自增),并保持主键尽量小(比如自增的整型就挺合适) InnoDB 使用 BTree 作为数据库的索引。如果主键不是顺序的会引起BTree不断分裂,导致性能低下。InnoDB中所有的索引中都会包含主键,所以如果主键特别大的化会严重影响性能。
按照业务需要设计索引 按照业务的需求斟酌每个索引的意义和价值,索引也是有成本的
在使用 SELECT 查询的时候明确指定要查询的列,禁止使用 SELECT * 将不需要的列也给搜索出来。
-------------------------------------------------------------------------
调试诊断相关
合理记录日志
监控记录
给线程指定名称
从现在起,我们都是专业人士,专业的人要做专业的事儿。用什么来标识我们的专业?不是你的衣着,也不是你拥有多酷的电脑,
而是用你的代码质量,你工作的方式来标识。从现在起,认真对待你每一个命名,每一行代码,每一个方法,每一个类。
(1), svn 提交不写注释
(2), 命名太随意
(3), 代码随意排版
(4), 多层嵌套结构
(5), 一个方法包打天下 : 要记住 -- 单一职责原则, 一个方法不应该承载太多,要尽量抽取出来。
(6), 不统一的风格
(7), 混乱的 pom 文件
(8), 晕头转向的配置文件 -- Spring配置文件, MyBatis mapper文件,properties文件等
2, 代码正确性相关
(1), 资源清理
(2), 资源使用
(3), 忽视参数检查
(4), 吞掉异常
(5), 并发环境下错误使用数据结构
(6), 数据库
3, 调试诊断相关
(1), 合理记录日志
(2), 监控记录
(3), 给线程指定名称
4, 设计相关
(1), 不可测试的代码
维护性相关
*混乱的 pom 文件*
对于一个java maven项目,pom.xml文件就是这个项目的脸面,脸面当然要整洁漂亮。那么一个漂亮的pom要符合哪些需求呢?
1. 尽量短
a. 保留尽可能少的必须的依赖,把没有必要的依赖删除掉
b. 减少重复。比如我们经常需要引入同一个系列的几个依赖, 这些依赖的版本一般都一致,那我们应该抽取一个
properties来表示该version。在使用父子项目时,我们还可以尽量将一些子module里都重复使用的东西抽取到父pom
里(推荐引入superpom)。
2. 排版整齐
3. 清晰明确
a. 在父pom里使用dependencyManagement来管理依赖,并且将一些重要依赖的版本抽取到properties,这样打开父
pom对这个系统的依赖就一目了然。
b. 父子项目中几个子项目使用统一的版本号
正确性相关
InputStream stream1 = null ;
InputStream stream2 = null ;
try {
stream1 = initStream1();
stream2 = initStream2();
//porcess stream1 and stream2
} finally {
if (stream1 != null ){
try {
stream1.close();
} catch (Exception e){
//record log
}
}
if (stream2 != null ){
try {
stream2.close();
} catch (Exception e){
//record log
}
}
}
// 这样就可以保证stream1 和stream2 都能正确的关闭。但是上面的代码看起来太繁琐了,而且这样的代码在系统中很常见,所以我们一些工具类库也已经为这样的代码提供了工具类,比如commonslang 里的IOUtils 就提供了IOUtils.closeQuietly() 方法,而guava 里也为我们提供Closeables 帮助类。在java 7 里,jdk 甚至为我们提供了trywithresources statement 这个基础设施。
*系统中常见的需要清理的资源*
(1), 磁盘文件,比如FileInputStream, FileOutStream等(只要带Stream的都要注意)
(2), 网络连接, 比如URLConnection, HttpClient, Socket等
(3), 数据库连接,最好使用类似Spring之类的框架访问数据库,如果自己使用DataSource.getConnection(不推荐)则一定要小心
资源使用 *
在我们的系统中经常需要:
1. 读取文件内容
2. 批量读取数据库
3. 线程池
4. 等等
这些操作有一个共同的特征是 : 对系统有限资源(内存,CPU等)的索取。当我们编写对有限资源使用的代码时,一定要注意资源的使用是否可控。比如对于读取文件来讲,如果文件较大我们最好采用流式读取(文件大小不是拍脑袋得出来的,是经过定量的计算得出的),流式读取使得我们可以一边读取文件内容一边处理,如果将文件内容全部读入内存当遇到特别大的文件的时候可能让内存爆掉。流式读取也不是说说那么容易,这就要求我们的文件以及数据格式是便于流式读取处理的。
而对于批量读取数据库的情况,有的同学直接来一个不加limit的SELECT语句,如果这个时候满足条件的记录较多,就会从数据库中加载特别大的数据,这不仅对数据库产生较大压力,对应用的内存也是一个考验。所以对于批量从数据库读取数据,要求我们也采用一种流式的,每次读取一些处理一些,然后再去读取。
有的时候我们为了并行化或异步处理一些操作时,需要自己创建线程池。在Java中就是创建ThreadPoolExecutor类。我们使用这个类时一定要注意这个类的构造函数中几个参数的确切意义是什么,大部分对这几个参数都不明不白,在出问题的时候往往会使问题恶化。而大部分教科书上推荐使用j.u.c中Executors类更是将这种不明不白加重。
其实就一点,我们对系统任何资源的使用都应该是可控的方式,并且能通过定量的方式分析为什么要使用这些资源。
忽视参数检查 *
参数检查的代码确实很讨厌,写起来啰哩啰嗦,还扰乱了我们代码的核心逻辑。于是一些人为了避免这些麻烦,并且信心满满的说我这个方法的调用源比较可靠不会传递非法的参数给我,如果确实如此当然可以,但理想很丰满,现实很骨感。往往越是我们盲目自信的地方,往往越容易出一些低级的错误。所以对一些公开出去的接口,我们一定要注意参数验证的逻辑。对于一些private接口
如果经过检查确实不会有非法参数传递进来,可以不做参数验证。
好在我们有非常多的第三方库可以使用帮我们降低参数验证的繁琐。比如在Spring Controller这个级别我们有Validator,它可以将参数验证逻辑剥离到另外一个类中处理,让Controller更纯洁。对于内部的一些接口,我们也可以使用Google guava的Preconditions来简化代码。比如: Preconditions.checkNotNull(url);
并发环境下错误使用数据结构 *
设计的时候考虑到即使以后代码在并发环境下执行也不会出问题,如果因为其他原因做不到这点也应该在接口上详细的表明这一点
数据库 *
给表设计主键 如果未给表指定主键,MySQL会从表中选取一个唯一的列作为主键,如果无法找到这样的列,Mysql会给定一个隐藏的列作为主键,这会带来很多问题, 使用非业务字段作为主键 业务字段可能因为需求的变更而发生变化使用按插入先后顺序的字段作为主键(比如自增),并保持主键尽量小(比如自增的整型就挺合适) InnoDB 使用 BTree 作为数据库的索引。如果主键不是顺序的会引起BTree不断分裂,导致性能低下。InnoDB中所有的索引中都会包含主键,所以如果主键特别大的化会严重影响性能。
按照业务需要设计索引 按照业务的需求斟酌每个索引的意义和价值,索引也是有成本的
在使用 SELECT 查询的时候明确指定要查询的列,禁止使用 SELECT * 将不需要的列也给搜索出来。
-------------------------------------------------------------------------
调试诊断相关
合理记录日志
监控记录
给线程指定名称
从现在起,我们都是专业人士,专业的人要做专业的事儿。用什么来标识我们的专业?不是你的衣着,也不是你拥有多酷的电脑,
而是用你的代码质量,你工作的方式来标识。从现在起,认真对待你每一个命名,每一行代码,每一个方法,每一个类。