Java编程中的一些常见问题汇总
作者:junjie 发布时间:2022-01-08 18:05:46
本文列举了我在周围同事的Java代码中看到的一些比较典型的错误。显然,静态代码分析(我们团队用的是qulice)不可能发现所有的问题,这也是为什么我要在这里列出它们的原因。
如果你觉得少了什么,请不吝赐教,我会很乐意把它们加上。
下面列出的所有这些错误基本都与面向对象编程有关,尤其是Java的OOP。
类名
读下这篇短文“什么是对象”。类应该是真实生活中的一个抽象实体,而不是什么“validators”,“controller”, “managers”这些东西。如果你的类名以”er”结尾的话——那它就是个糟糕的设计。
当然了,工具类也是反模式,比如说Apache的StringUtils, FileUtils, 以及IOUtils。上面这些都是糟糕设计的代表。延伸阅读:OOP中工具类的替代方案。
当然,不要使用前缀或者后缀来区分类和接口。比方说,这些名字就是错误的:IRecord, IfaceEmployee, 或者RecordInterface。通常来说,接口名应该是真实生活中的实体的名字,类名应该可以说明它的实现细节。如果这个实现没有什么特别可说明的,可以把它叫作Default, Simple或者类似的什么。比如说:
class SimpleUser implements User {};
class DefaultRecord implements Record {};
class Suffixed implements Name {};
class Validated implements Content {};
方法名
方法可以返回值也可以返回void。如果方法返回值的话,它的名字应该能说明它返回了什么,比如说(永远也不要使用get前缀):
boolean isValid(String name);
String content();
int ageOf(File file);
如果它返回void,那么它的名字应该要说明它做了什么。比如:
void save(File file);
void process(Work work);
void append(File file, String line);
刚才提到的这些规则只有一个例外——JUnit的test方法不算。下面将会说到这个。
test方法的名字
在JUnit的测试用例中,方法名应该是没有空格的英文语句。用一个例子来说明会更清楚一些:
/**
* HttpRequest can return its content in Unicode.
* @throws Exception If test fails
*/
public void returnsItsContentInUnicode() throws Exception {
}
你的JavaDoc里的第一句话的开头应该是你要测试的那个类的名字,然后是一个can。因此,你的第一句话应该是类似于“somebody can do something”。
方法名也是一样的,只是没有主题而已。如果我在方法名中间加一个主题的话,我就能得到一个完整的句子,正如上面那个例子中那样:“HttpRequest returns its content in unicode”。
请注意test方法的名字是不以can开头的。只有JavaDoc里的的注释会以can开头。除此之外,方法名不应该以动词开头。
实践中最好将测试方法声明为抛出Exception的。
变量名
避免组合的变量名,比如说timeOfDay, firstItem,或者httpRequest。类变量及方法内的变量都是如此。变量名应该足够长,避免在它的可见作用域内产生歧义,但是如果可以的话也不要太长。名字应该是单数或复数形式的名词,或者是一个适当的缩写。比如:
List<String> names;
void sendThroughProxy(File file, Protocol proto);
private File content;
public HttpRequest request;
有的时候,如果构造方法要将入参保存到一个新初始化的对象中的时候,它的参数和类属性的名字可能会冲突。这种情况,我建议是去掉元音,使用缩写。
示例:
public class Message {
private String recipient;
public Message(String rcpt) {
this.recipient = rcpt;
}
}
很多时候,看一下变量的类名就知道变量该取什么名字了。就用它的小写形式就好了,像这样就很靠谱:
File file;
User user;
Branch branch;
然而,基础类型的话,永远不要这么做,比如Integer number或者String string。
如果存在多个不同性质的变量的话,可以考虑下使用形容词。比如:
String contact(String left, String right);
构造方法
不考虑异常的话,应该只有一个构造方法用来将数据存储到对象变量中。其它构造方法则使用不同的参数来调用这个构造方法。比如说:
public class Server {
private String address;
public Server(String uri) {
this.address = uri;
}
public Server(URI uri) {
this(uri.toString());
}
}
一次性变量
无论如何都应该避免使用一次性变量。这里我所说的“一次性“指的是只使用一次的变量。比如下面这个:
String name = "data.txt";
return new File(name);
上述的变量只会使用一次,因此这段代码可以重构成这样:
return new File("data.txt");
有的时候,比较罕见的情况中——主要是为了格式更好看些——可能会用到一次性变量。然而,还是应当尽量避免这种情况。
异常
毋庸赘言,永远不要自己吞掉异常,而是应该当它尽量往上传递。私有方法应该始终把受检查异常往外面抛。
不要使用异常来进行流程控制。比方说下面这段代码就是错误的:
int size;
try {
size = this.fileSize();
} catch (IOException ex) {
size = 0;
}
那如果IOException提示“磁盘已满”的话该怎么办?你还会认为这个文件大小为0,然后继续往下处理?
缩进
关于缩进,主要的规则就是左括号要么在该行的末尾,要么就在同一行上闭合(对于右括号来说则相反)。比如说,下面这个就不正确,因为第一个左括号没有在同一行上闭合,而它后面还有别的字符。第二个括号也有问题,因为它前面有字符,但对应的开括号又没在同一行上:
final File file = new File(directory,
"file.txt");
正确的缩进应该是这样的:
StringUtils.join(
Arrays.asList(
"first line",
"second line",
StringUtils.join(
Arrays.asList("a", "b")
)
),
"separator"
);
关于缩进,第二条重要的规则就是同时一行中应该尽量多写一些——上限是80个字符。上面的那个例子并不满足这点,它还可以收缩一下:
StringUtils.join(
Arrays.asList(
"first line", "second line",
StringUtils.join(Arrays.asList("a", "b"))
),
"separator"
);
多余的常量
当你希望在类的方法中共享信息的时候,应当使用类常量,这些信息应该是你这个类所特有的。不要把常量当作字符串或数值字面量的替代品来使用——这是非常糟糕的实践方式,它会对代码造成污染。常量(正如OOP中的任何对象一样)应当在真实世界中有它自己的含义。看下这些常量在真实生活中的意思是什么:
class Document {
private static final String D_LETTER = "D"; // bad practice
private static final String EXTENSION = ".doc"; // good practice
}
另一个常见的错误就是在单元测试中使用常量来避免测试方法中出现冗余的字符串或者数值的字面量。不要这么做!每个测试方法都应该有自己专属的输入值。
在每个新的测试方法中使用新的文本或者数值。它们是相互独立的。那么为什么它们还要共享同样的输入常量呢?
测试数据耦合
下面是测试方法中数据耦合的一个例子:
User user = new User("Jeff");
// maybe some other code here
MatcherAssert.assertThat(user.name(), Matchers.equalTo("Jeff"));
最后一行中,”Jeff”和第一行中的同一个字符串字面值发生了耦合。如果过了几个月,有人想把第三行这个值换一下,那么他还得花时间找出同一个方法中哪里也使用了这个”Jeff”。
为了避免这种情况,你最好还是引入一个变量。
猜你喜欢
- 前言在原生的 Android 或 iOS 中,都提供了基本的键值对存储方式,Android 是 SharedPreferences,iOS
- [LeetCode] 2. Add Two Numbers 两个数字相加You are given two non-empty&n
- 本文实例讲述了java实现的简单猜数字游戏代码。分享给大家供大家参考。具体代码如下:import java.util.InputMismat
- Servlet3.0的出现是servlet史上最大的变革,其中的许多新特性大大的简化了web应用的开发,为广大劳苦的程序员减轻了压力,提高了
- 本文主要介绍了Spring Security OAuth2 实现登录互踢的示例代码,分享给大家,具体如下:背景说明一个账号只能一处登录,类似
- 默认情况下Spring Boot使用了内嵌的Tomcat服务器,项目最终被打成jar包运行,每个jar包可以被看作一个独立的Web服务器。传
- 本文实例为大家分享了Java实现颜色渐变效果的具体代码,供大家参考,具体内容如下RGB色彩,在自然界中肉眼所能看到的任何色彩都可以由红(R)
- IDEA 报错:无效的源发行版问题描述从SVN拉项目代码到本地后用idea运行,发现几个报错,关键的一个是:无效的源发行版,考虑是JDK版本
- Java定义Long数据类型Long lg=10L;只需要在定义的的整型后面加个L;就和定义float数据类型一样Float ft=5.20
- 在线程间通信方式中,我们了解到可以使用Semaphore信号量来实现线程间通信,Semaphore支持公平锁和非公平锁,Semaphore底
- 本文实例为大家分享了java + dom4j.jar提取xml文档内容的具体代码,供大家参考,具体内容如下资源下载页:点击下载本例程主要借助
- 在我们实现某些功能时,可能会有倒计时的需求。比如发送短信验证码,发送成功后可能要求用户一段时间内不能再次发送,这时候我们就需要进行倒计时,时
- 0x00:前言参考之前的《MyBatis 中 SqlMapConfig 配置文件详解》记了一下 MyBatis 中的核心配置文件各个标签的作
- 数据库里面表的字段中带有“”_“下划线,我们知道插件默认的是将这些带有下划线的字段默认的变成“优美的驼峰式”的。表是肯定不能动的,实体类的字
- 骑缝章是用于往来业务合同,以确保合同真实、有效的印章加盖方法,是一种防范风险的重要方式。在Java程序中,可以通过使用工具来辅助加盖这种骑缝
- 本文主要给大家介绍java的InputStream 流的使用。(1)FileInputstream: 子类,读取数据的通道使用步骤:1.获取
- ActiveMQ是什么ActiveMQ是消息队列技术,为解决高并发问题而生ActiveMQ生产者消费者模型(生产者和消费者可以跨平台、跨系统
- Android中获取资源 id 及资源 id 的动态获取我们平时获取资源是通过 findViewById 方法进行的,比如我们常
- 门面模式又叫外观模式(Facade Pattern),主要用于隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。我们知道电视剧
- 初次使用IDEA,创建一个maven工程,发现在目录结构中产生了两个不一样的东西——.iml文件和.idea文件夹。非常好奇,所以立刻上网查