共计 5250 个字符,预计需要花费 14 分钟才能阅读完成。
用另一双眼睛扫描您的代码总是很有用的,能够帮忙您在中断生产之前发现错误。您不用是审阅某人代码的专家。有肯定的编程语言教训和温习清单能够帮忙您入门。咱们整顿了一份清单,以供您审查 Java 代码时应留神的事项。持续浏览!
1. 遵循 Java 代码约定
遵循语言约定有助于疾速浏览代码并了解代码,从而进步可读性。例如,Java 中的所有程序包名称均以小写模式编写,所有大写字母中的常量,CamelCase 中的变量名称,等等。在此处查找约定的残缺列表。
有些团队会制订本人的约定,因而在这种状况下要灵便一些!
2. 用 Lambda 和 Streams 替换命令性代码
如果您应用的是 Java 8+,则用流和 lambda 替换循环和极其简短的办法会使代码看起来更简洁。Lambda 和流使您能够用 Java 编写性能代码。以下代码段以传统的命令式形式过滤了奇数:
List<Integer> oddNumbers = new ArrayList<>();
for (Integer number : Arrays.asList(1, 2, 3, 4, 5, 6)) {if (number % 2 != 0) {oddNumbers.add(number);
}
}
这是过滤奇数的性能办法:
List<Integer> oddNumbers = Stream.of(1, 2, 3, 4, 5, 6)
.filter(number -> number % 2 != 0)
.collect(Collectors.toList());
3. 当心 NullPointerException
在编写新办法时,请尽量避免返回 null。它可能导致空指针异样。在上面的代码段中,如果列表没有整数,则最高的办法将返回 null。
class Items {
private final List<Integer> items;
public Items(List<Integer> items) {this.items = items;}
public Integer highest() {if (items.isEmpty()) return null;
Integer highest = null;
for (Integer item : items) {if (items.indexOf(item) == 0) highest = item;
else highest = highest > item ? highest : item;
}
return highest;
}
}
在间接在对象上调用办法之前,建议您查看空值,如下所示。
Items items = new Items(Collections.emptyList());
Integer item = items.highest();
boolean isEven = item % 2 == 0; // throws NullPointerException
boolean isEven = item != null && item % 2 == 0 //
然而,在代码中到处都有 null 查看可能十分麻烦。如果您应用的是 Java 8+,请思考应用 Optional 该类来示意可能没有无效状态的值。它使您能够轻松定义代替行为,并且对于链接办法很有用。
在上面的代码段中,咱们应用 Java Stream API 通过返回的办法来找到最大的数字 Optional。请留神,咱们正在应用 Stream.reduce,它返回一个 Optional 值。
public Optional<Integer> highest() {
return items
.stream()
.reduce((integer, integer2) ->
integer > integer2 ? integer : integer2);
}
Items items = new Items(Collections.emptyList());
items.highest().ifPresent(integer -> { //
boolean isEven = integer % 2 == 0;
});
或者,您也能够应用诸如 @Nullable 或的正文,@NonNull 如果在构建代码时存在空抵触,则会产生正告。例如,将 @Nullable 参数传递给承受 @NonNull 参数的办法。
4. 间接将客户代码中的援用调配给字段
即便该字段是最终字段,也能够操纵公开给客户代码的援用。让咱们通过一个例子更好地了解这一点。
private final List<Integer> items;
public Items(List<Integer> items) {this.items = items;}
在以上代码段中,咱们间接将客户端代码中的援用调配给字段。客户能够轻松地更改列表的内容并按如下所示操作咱们的代码。
List<Integer> numbers = new ArrayList<>();
Items items = new Items(numbers);
numbers.add(1); // This will change how items behaves as well
相同,请思考克隆援用或创立新援用,而后将其调配给字段,如下所示:
private final List<Integer> items;
public Items(List<Integer> items) {this.items = new ArrayList<>(items);
}
返回援用时,将利用雷同的规定。您须要放弃审慎,免得裸露外部可变状态。
5. 审慎解决异样
捕捉异样时,如果您有多个捕捉块,请确保捕捉块的程序最不特定。在上面的代码段中,因为 Exception 该类是所有异样的源,因而该异样永远不会被捕捉在第二个块中。
try {stack.pop();
} catch (Exception exception) {// handle exception} catch (StackEmptyException exception) {// handle exception}
如果状况是可复原的,并且能够由客户端(您的库或代码的使用者)解决,则能够应用查看的异样。例如,IOException 是一个已查看的异样,它会强制客户端解决场景,并且如果客户端抉择从新抛出该异样,则应该无意识地呐喊疏忽该异样。
6. 对于数据结构抉择的思考
Java 汇合提供 ArrayList,LinkedList,Vector,Stack,HashSet,HashMap,Hashtable。重要的是要理解每种办法的利弊,以便在正确的上下文中应用它们。一些提醒能够帮忙您做出正确的抉择:
Map:如果您具备键,值对模式的无序我的项目并且须要无效的检索,插入和删除操作,则很有用。HashMap,Hashtable,LinkedHashMap 是所有实现 Map 的接口。
List:十分罕用于创立我的项目的有序列表。此列表可能蕴含反复项。ArrayList 是 List 接口的实现。应用能够使列表成为线程平安的,Collections.synchronizedList 因而无需应用 Vector。以下是一些无关为什么 Vector 实际上已过期的信息。
Set:相似于列表,但不容许反复。HashSet 实现 Set 接口。
7. 曝光前要三思而后行
在 Java 中 public,有很多拜访修饰符可供选择 - protected,,private。除非要向客户端代码公开办法,否则可能要保留所有 private 默认设置。公开 API 后,就再也没有回头路了。
例如,您有一个 Library 具备以下办法的类,用于按名称签出一本书:
public checkout(String bookName) {Book book = searchByTitle(availableBooks, bookName);
availableBooks.remove(book);
checkedOutBooks.add(book);
}
private searchByTitle(List<Book> availableBooks, String bookName) {...}
如果 searchByTitle 默认状况下不将办法设置为公有,并且最终将其公开,则其余类可能会开始应用它,并在该办法之上构建您可能心愿退出 Library 该类的逻辑。这可能会毁坏 Library 类的封装,或者可能在当前不毁坏其他人的代码的状况下进行还原 / 批改。无意识地裸露!
8. 接口代码
如果您对某些接口(例如 ArrayList 或 LinkedList)有具体的实现,并且间接在代码中应用它们,则可能导致高度耦合。保持应用该 List 接口,您能够在未来的任何工夫切换实现,而不会毁坏任何代码。
public Bill(Printer printer) {this.printer = printer;}
new Bill(new ConsolePrinter());
new Bill(new HTMLPrinter());
在下面的代码片段中,应用 Printer 接口能够使开发人员移至另一个具体的类 HTMLPrinter。
9. 不要强行装置接口
看一下以下界面:
interface BookService {List<Book> fetchBooks();
void saveBooks(List<Book> books);
void order(OrderDetails orderDetails) throws BookNotFoundException, BookUnavailableException;
}
class BookServiceImpl implements BookService {...
创立这样的界面有益处吗?此接口是否有范畴由另一个类实现?这个接口是否通用到足以由另一个类实现?如果所有这些问题的答案都是“否”,那么我相对建议您防止应用此不必要的接口,当前您将必须保护该接口。马丁•福勒(Martin Fowler)在他的博客中对此进行了很好的解释。
那么,接口的好用例是什么?假如咱们有一个 class Rectangle 和一个 class Circle 具备计算周长的行为。总结一下,如果须要所有形态的周长(一个多态的用例),那么领有接口将更有意义,如下所示。
interface Shape {Double perimeter();
}
class Rectangle implements Shape {
//data members and constructors
@Override
public Double perimeter() {return 2 * (this.length + this.breadth);
}
}
class Circle implements Shape {
//data members and constructors
@Override
public Double perimeter() {return 2 * Math.PI * (this.radius);
}
}
public double totalPerimeter(List<Shape> shapes) {return shapes.stream()
.map(Shape::perimeter)
.reduce((a, b) -> Double.sum(a, b))
.orElseGet(() -> (double) 0);
}
10. 笼罩等于时笼罩 hashCode
因为值相等而相等的对象称为值对象。例如金钱,工夫。equals 如果值雷同,则此类必须重写该办法以返回 true。equals 其余库通常应用该办法进行比拟和相等性查看。因而,equals 有必要进行笼罩。每个 Java 对象还具备一个将其与另一个对象辨别开的哈希码值。
class Coin {
private final int value;
Coin(int value) {this.value = value;}
@Override
public boolean equals(Object o) {if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Coin coin = (Coin) o;
return value == coin.value;
}
}
在下面的示例中,咱们仅笼罩的 equals 办法 Object。
HashMap<Coin, Integer> coinCount = new HashMap<Coin, Integer>() {{put(new Coin(1), 5);
put(new Coin(5), 2);
}};
//update count for 1 rupee coin
coinCount.put(new Coin(1), 7);
coinCount.size(); // 3 why?
学习材料收费支付群:3907814
咱们心愿 coinCount 将 1 卢比硬币的数量更新为 7,因为咱们笼罩了 equals。然而在 HashMap 外部查看 2 个对象的哈希码是否相等,而后才通过 equals 办法测试相等性。依据 hashCode 办法的约定,两个不同的对象可能具备或不具备雷同的哈希码,然而两个相等的对象必须始终具备雷同的哈希码。因而,首先查看哈希码是一个提前退出的条件。这意味着必须同时重写 equals 和 hashCode 办法来表白相等性。