计算机编程中,最佳实际是许多开发人员遵循的一组非正式规定,以进步软件品质、可读性和可维护性。在应用程序长时间放弃应用的状况下,最佳实际尤其无益,这样它最后是由一个团队开发的,而后由不同的人组成的保护团队进行保护。
本教程将提供 Java 最佳实际的概述,以及每个条目标解释,包含 Java 编程的顶级最佳实际列表中的每一项。
Java 编程最佳实际概览
尽管 Java 最佳实际 的残缺列表可能很长,但对于那些正在努力提高代码品质的编码人员来说,有几个被认为是一个很好的终点,包含应用适当的命名标准、使类成员 私有化 、防止应用空的catch 块、防止内存透露以及正确地正文代码块:
- 应用适当的命名标准
- 将 类成员 设置为公有
- 在长数字文字中应用下划线
- 防止空的 catch 块
- 应用 StringBuilder 或StringBuffer进行字符串连贯
- 防止冗余初始化
- 应用增强型 for 循环代替带计数器的 for 循环
- 正确处理空指针异样
- Float或Double:哪一个是正确的抉择?
- 应用单引号和双引号
- 防止内存透露
- 返回空集合而不是返回 Null 元素
- 高效应用 字符串
- 防止创立不必要的对象
- 正确正文代码
Java 中的类成员应该是公有的
在 Java 中,类的成员越不可拜访,越好!第一步是应用 private 拜访 修饰符。指标是促成现实的封装,这是面向对象编程(OOP)的基本概念之一。太多时候,新的开发人员没有正确地为类调配拜访修饰符,或者偏向于将它们设置为 public 以使事件更容易。
思考以下字段被设置为 public 的类:
public class BandMember {
public String name;
public String instrument;
}
在这里,类的封装性被毁坏了,因为任何人都能够间接更改这些值,如下所示:
BandMember billy = new BandMember();
billy.name = "George";
billy.instrument = "drums";
应用 private 拜访修饰符与类成员一起能够将字段暗藏起来,避免用户通过 setter 办法之外的形式更改数据:
public class BandMember {
private String name;
private String instrument;
public void setName(String name) {this.name = name;}
public void setInstrument(String instrument)
this.instrument = instrument;
}
}
在 setter 办法中也是搁置验证代码和 / 或治理工作(如减少计数器)的现实地位。
在长数字文字中应用下划线
得益于 Java 7 的更新,开发人员当初能够在长数字字面量中应用下划线(_),以进步可读性。以下是在容许下划线之前一些长数字字面量的示例:
int minUploadSize = 05437326;
long debitBalance = 5000000000000000L;
float pi = 3.141592653589F;
我想您会批准下划线使值更易读:
int minUploadSize = 05_437_326;
long debitBalance = 5_000_000_000_000_000L;
float pi = 3.141_592_653_589F;
防止空的 Catch 块
在 Java 中,把 catch 块留空是十分不好的习惯,有两个起因:一是它可能导致程序默默地失败,二是程序可能会持续运行而不会产生任何异样。这两种后果都会使调试变得十分艰难
思考以下程序,它从命令行参数中计算两个数字的和:
public class Sum {public static void main(String[] args) {
int a = 0;
int b = 0;
try {a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[1]);
} catch (NumberFormatException ex) { }
int sum = a + b;
System.out.println(a + "+" + b + "=" + sum);
}
}
Java 的 parseInt() 办法会抛出 NumberFormatException 异样,须要开发人员在其调用四周应用 try/catch 块来捕捉异样。可怜的是,这位开发人员抉择疏忽抛出的异样!因而,传入有效参数,例如“45y”,将导致关联的变量被赋为其类型的默认值,对于 int 类型来说是0。
通常,在捕捉异样时,程序员应采取以下三个口头中的一个或多个:
- 开发人员最起码应该告诉用户异常情况,要么让他们从新输出有效的值,要么让他们晓得程序必须提前终止。
- 应用 JDK Logging 或 Log4J 记录异样日志。
- 将异样封装并作为一个新的、更适宜应用程序的异样从新抛出。
以下是从新编写后的 Sum 应用程序,用于告诉用户输出有效并因而终止程序:
public class Sum {public static void main(String[] args) {
int a = 0;
int b = 0;
try {a = Integer.parseInt(args[0]);
} catch (NumberFormatException ex) {System.out.println(args[0] + "is not a number. Aborting...");
return;
}
try {b = Integer.parseInt(args[1]);
} catch (NumberFormatException ex) {System.out.println(args[1] + "is not a number. Aborting...");
return;
}
int sum = a + b;
System.out.println(a + "+" + b + "=" + sum);
}
}
以下是咱们察看到的后果:
应用 StringBuilder 或 StringBuffer 进行字符串拼接
“+”运算符是在 Java 中疾速和简便地组合 字符串 的办法。在 Hibernate 和 JPA 时代之前,对象是手动长久化的,通过从头开始构建 SQL INSERT 语句来实现!以下是一个存储一些用户数据的示例:
String sql = "Insert Into Users (name, age)";
sql += "values ('" + user.getName();
sql += "','" + user.getage();
sql += "')";
很遗憾,当像下面那样连贯多个字符串时,Java 编译器必须创立多个两头字符串对象,而后将它们合并成最终连贯的字符串。
相同,咱们应该应用 StringBuilder 或StringBuffer类。两者都蕴含函数,能够连贯字符串而无需创立两头 String 对象,从而节俭解决工夫和不必要的内存应用。
以前的代码能够应用 StringBuilder 重写,如下所示:
StringBuilder sqlSb = new StringBuilder("Insert Into Users (name, age)");
sqlSb.append("values ('").append(user.getName());
sqlSb.append("','").append(user.getage());
sqlSb.append("')");
String sqlSb = sqlSb.toString();
这对开发者来说可能要费点事儿,然而这样做十分值得!
StringBuffer 与 StringBuilder
尽管 StringBuffer 和StringBuilder类都比“+”运算符更可取,但它们并不相同。StringBuilder比 StringBuffer 更快,但不是线程平安的。因而,在非多线程环境中进行字符串操作时,应应用 StringBuilder;否则,请应用StringBuffer 类。
防止冗余初始化
只管某些语言如 TypeScript 强烈建议在申明时初始化变量,但在 Java 中并非总是必要的,因为它在申明时将默认初始化值(如 0、false 和null)调配给变量。
因而,Java 的最佳实际是要晓得成员变量的默认初始化值,除非您想将它们设置为除默认值以外的其余值,否则不要显式初始化变量。
以下是一个计算从 1 到1000的自然数之和的短程序。请留神,只有局部变量被初始化:
class VariableInitializationExample {public static void main(String[] args) {
// automatically set to 0
int sum;
final numberOfIterations = 1000;
// Set the loop counter to 1
for (int i = 1; i &= numberOfIterations; ++i) {sum += i;}
System.out.println("Sum =" + sum);
}
}
应用增强型 for 循环代替须要计数器的 for 循环
只管 for 循环在某些状况下很有用,然而计数器变量可能会引起谬误。例如,计数器变量可能会在稍后的代码中被无心中更改。即便从 1 而不是从 0 开始索引,也可能导致意外行为。出于这些起因,for-each 循环(也称为增强型 for 循环)可能是更好的抉择。
思考以下代码:
String[] names = {"Rob", "John", "George", "Steve"};
for (int i = 0; i < names.length; i++) {System.out.println(names[i]);
}
在这里,变量 i 既是循环计数器,又是数组 names 的索引。只管这个循环只是打印每个名称,但如果上面有批改 i 的代码,就会变得辣手。咱们能够通过应用上面所示的 for-each 循环轻松避开整个问题:
for (String name : names) {System.out.println(name);
}
应用加强的 for 循环,出错的机会要少得多!
正当解决空指针异样
空指针异样在 Java 中是一个十分常见的问题,可能是因为其面向对象的设计所致。当您试图在 Null 对象援用上调用办法时,就会产生 Null Pointer 异样。这通常产生在在类实例化之前调用实例办法的状况下,如下例所示:
Office office;
// later in the code...
Employee[] employees = office.getEmployees();
尽管你无奈齐全打消 Null Pointer Exceptions,但有办法能够将其最小化。一种办法是在调用对象的办法之前查看对象是否为 Null。以下是应用三元运算符的示例:
Office office;
// later in the code...
Employee[] employees = office == null ? 0 : office.getEmployees();
你可能还想抛出本人的异样:
Office office;
Employee[] employees;
// later in the code...
if (office == null) {throw new CustomApplicationException("Office can't be null!");
} else {employees = office.getEmployees();
}
Float 或 Double:应该应用哪个?
浮点数和双精度数是类似的类型,因而许多开发人员不确定该抉择哪种类型。两者都解决浮点数,但具备十分不同的个性。例如,float 的大小为 32 位,而 double 调配了 64 位的内存空间,因而 double 能够解决比 float 大得多的小数。而后有一个精度问题:float 只能包容 7 位精度。极小的指数大小意味着一些位是不可避免的失落的。相比之下,double 为指数调配了更多的位数,容许它解决高达 15 位精度。
因而,当速度比准确性更重要时,通常倡议应用 float。只管大多数程序不波及大量计算,但在数学密集型利用中,精度差别可能十分显著。当须要的小数位数已知时,float 也是一个不错的抉择。当精度十分重要时,double 应该是你的首选。只需记住,Java 强制应用 double 作为解决浮点数的默认数据类型,因而您可能须要附加字母 ”f“ 来明确示意 float,例如,1.2f。
单引号和双引号在字符串连贯中的应用
在 Java 中,双引号(“)用于保留字符串,单引号用于字符(由 char 类型示意)。当咱们尝试应用 + 连贯运算符连贯字符时,可能会呈现问题。问题在于应用 + 连贯字符会将 char 的值转换为ascii,从而产生数字输入。以下是一些示例代码,以阐明这一点:
char a, b, c;
a = 'a';
b = 'b';
c = 'c';
str = a + b + c; // not "abc", but 294!
就像字符串连贯最好应用 StringBuilder 或StringBuffer类一样,字符连贯也是如此!在下面的代码中,变量 a、b 和c能够通过以下形式组合成一个字符串:
new StringBuilder().append(a).append(b).append(c).toString()
咱们能够在上面察看到冀望的后果:
防止 Java 中的内存透露
在 Java 中,开发人员并没有太多对于内存治理的控制权,因为 Java 通过垃圾回收主动地进行内存治理。尽管如此,有一些 Java 最佳实际能够帮忙开发人员防止内存透露,例如:
- 防止创立不必要的对象。
- 防止应用 ”+” 运算符进行字符串连贯。
- 防止在会话中存储大量数据。
- 在会话不再应用时,及时让会话超时。
- 防止应用动态对象,因为它们在整个应用程序的生命周期内存在。
- 在与数据库交互时,不要遗记在 finally 块中敞开 ResultSet、Statements 和Connection对象。
返回空集合而不是 null 援用。
你晓得吗,null援用常常被称为软件开发中最重大的谬误吗?1965 年,Tony Hoare 在设计面向对象语言(OOP)的援用的第一个全面类型零碎时创造了 null 援用。起初在 2009 年的一次会议上,Hoare 为本人的创造赔罪,抵赖他的发明“导致了有数的谬误、破绽和零碎解体,在过来四十年中可能造成了数十亿美元的苦楚和损失。”
在 Java 中,通常最好返回空值而不是 null,特地是当返回汇合、可枚举对象或对象时更为重要。只管你本人的代码可能会解决返回的null 值,但其余开发人员可能会遗记编写空值查看,甚至没有意识到 null 是可能的返回值!
以下是一些 Java 代码,它以 ArrayList 的模式获取库存中的书籍列表。然而,如果列表为空,则返回一个空列表:
private final List<Book> booksInStock = ...
public List<Book> getInStockBooks() {return booksInStock.isEmpty() ? Collections.emptyList() : new ArrayList<>(booksInStock);
}
这使得办法的调用者能够在不用首先查看 null 援用的状况下迭代列表:
(Book book: getInStockBooks()) {// do something with books}
Java 中字符串的高效应用
咱们曾经探讨了应用 + 连贯操作符可能产生的副作用,但还有其余一些办法能够更无效地应用字符串,以避免浪费内存和处理器周期。例如,在实例化 String 对象时,通常最好间接创立 String 而不是应用构造函数。起因是什么?应用间接创立 String 比应用构造函数更快(更不用说更少的代码!)。
这里是在 Java 中创立 字符串 的两种等效形式:间接创立和应用构造函数:
// directly
String str = "abc";
// using a constructor
char data[] = {'a', 'b', 'c'};
String str = new String(data);
尽管两种办法都是等效的,但间接创立字符串的形式更好。
Java 中不必要的对象创立
你晓得吗?在 Java 中,对象的创立是耗费内存最多的操作之一。因而,在没有充沛理由的状况下,最好防止创建对象,并仅在相对必要时才这样做。
那么,如何将这个实际起来呢?具备讥刺象征的是,像咱们下面看到的间接创立 字符串 就是防止不必要创建对象的一种形式!
以下是一个更简单的示例:
以下是一个 Person 类的例子,它包含一个 isBabyBoomer() 办法,用于判断此人是否属于“婴儿潮”年龄段,出生于 1946 年至 1964 年之间:
public class Person {
private final Date birthDate;
public boolean isBabyBoomer() {
// Unnecessary allocation of expensive object!
Calendar gmtCal =
Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
Date boomStart = gmtCal.getTime();
gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
Date boomEnd = gmtCal.getTime();
return birthDate.compareTo(boomStart) >= 0 &&
birthDate.compareTo(boomEnd) < 0;
}
}
isBabyBoomer()办法每次被调用都会创立一个新的 Calendar、TimeZone 和两个 Date 实例,这是不必要的。纠正这种低效的办法之一是应用动态初始化器,以便只在初始化时创立 Calendar、TimeZone 和Date对象,而不是每次调用 isBabyBoomer() 办法。
class Person {
private final Date birthDate;
// The starting and ending dates of the baby boom.
private static final Date BOOM_START;
private static final Date BOOM_END;
static {
Calendar gmtCal =
Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_START = gmtCal.getTime();
gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_END = gmtCal.getTime();}
public boolean isBabyBoomer() {return birthDate.compareTo(BOOM_START) >= 0 &&
birthDate.compareTo(BOOM_END) < 0;
}
}
Java 中适当的正文
清晰简洁的正文在浏览其余开发人员的代码时十分有用。以下是写出高质量正文的几个指南:
- 正文不应该反复代码。
- 好的正文不能补救代码不清晰的问题。
- 如果您无奈编写清晰的正文,则代码可能存在问题。
- 在正文中解释不合乎习用形式的代码。
- 在最有用的中央蕴含指向内部参考文献的链接。
- 在修复谬误时增加正文。
- 应用正文标记未实现的实现,通常应用标记“TODO:”结尾。
总结
在本文中,咱们理解了 15 个 Java 最佳实际,并探讨了类成员封装、在简短的数字字面值中应用下划线、防止空 catch 块、正确实现 字符串 连贯、如何防止冗余初始化以及应用加强的 for 循环。
【注】本文译自:Java Best Practices | Developer.com