计算机编程中,最佳实际是许多开发人员遵循的一组非正式规定,以进步软件品质、可读性和可维护性。在应用程序长时间放弃应用的状况下,最佳实际尤其无益,这样它最后是由一个团队开发的,而后由不同的人组成的保护团队进行保护。

本教程将提供Java最佳实际的概述,以及每个条目标解释,包含Java编程的顶级最佳实际列表中的每一项。

Java编程最佳实际概览

尽管Java最佳实际的残缺列表可能很长,但对于那些正在努力提高代码品质的编码人员来说,有几个被认为是一个很好的终点,包含应用适当的命名标准、使类成员私有化、防止应用空的catch块、防止内存透露以及正确地正文代码块:

  • 应用适当的命名标准
  • 类成员设置为公有
  • 在长数字文字中应用下划线
  • 防止空的catch
  • 应用StringBuilderStringBuffer进行字符串连贯
  • 防止冗余初始化
  • 应用增强型for循环代替带计数器的for循环
  • 正确处理空指针异样
  • FloatDouble:哪一个是正确的抉择?
  • 应用单引号和双引号
  • 防止内存透露
  • 返回空集合而不是返回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

通常,在捕捉异样时,程序员应采取以下三个口头中的一个或多个:

  1. 开发人员最起码应该告诉用户异常情况,要么让他们从新输出有效的值,要么让他们晓得程序必须提前终止。
  2. 应用 JDK LoggingLog4J 记录异样日志。
  3. 将异样封装并作为一个新的、更适宜应用程序的异样从新抛出。

以下是从新编写后的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编译器必须创立多个两头字符串对象,而后将它们合并成最终连贯的字符串。

相同,咱们应该应用StringBuilderStringBuffer类。两者都蕴含函数,能够连贯字符串而无需创立两头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

尽管StringBufferStringBuilder类都比“+”运算符更可取,但它们并不相同。StringBuilderStringBuffer更快,但不是线程平安的。因而,在非多线程环境中进行字符串操作时,应应用StringBuilder;否则,请应用StringBuffer类。

防止冗余初始化

只管某些语言如TypeScript强烈建议在申明时初始化变量,但在Java中并非总是必要的,因为它在申明时将默认初始化值(如0falsenull)调配给变量。

因而,Java的最佳实际是要晓得成员变量的默认初始化值,除非您想将它们设置为除默认值以外的其余值,否则不要显式初始化变量。

以下是一个计算从11000的自然数之和的短程序。请留神,只有局部变量被初始化:

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!

就像字符串连贯最好应用StringBuilderStringBuffer类一样,字符连贯也是如此!在下面的代码中,变量abc能够通过以下形式组合成一个字符串:

new StringBuilder().append(a).append(b).append(c).toString()

咱们能够在上面察看到冀望的后果:

防止Java中的内存透露

在Java中,开发人员并没有太多对于内存治理的控制权,因为Java通过垃圾回收主动地进行内存治理。尽管如此,有一些Java最佳实际能够帮忙开发人员防止内存透露,例如:

  • 防止创立不必要的对象。
  • 防止应用"+"运算符进行字符串连贯。
  • 防止在会话中存储大量数据。
  • 在会话不再应用时,及时让会话超时。
  • 防止应用动态对象,因为它们在整个应用程序的生命周期内存在。
  • 在与数据库交互时,不要遗记在finally块中敞开ResultSetStatementsConnection对象。

返回空集合而不是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中创立字符串的两种等效形式:间接创立和应用构造函数:

// directlyString str = "abc";// using a constructorchar 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()办法每次被调用都会创立一个新的CalendarTimeZone和两个Date实例,这是不必要的。纠正这种低效的办法之一是应用动态初始化器,以便只在初始化时创立CalendarTimeZoneDate对象,而不是每次调用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中适当的正文

清晰简洁的正文在浏览其余开发人员的代码时十分有用。以下是写出高质量正文的几个指南:

  1. 正文不应该反复代码。
  2. 好的正文不能补救代码不清晰的问题。
  3. 如果您无奈编写清晰的正文,则代码可能存在问题。
  4. 在正文中解释不合乎习用形式的代码。
  5. 在最有用的中央蕴含指向内部参考文献的链接。
  6. 在修复谬误时增加正文。
  7. 应用正文标记未实现的实现,通常应用标记“TODO:”结尾。

总结

在本文中,咱们理解了15个Java最佳实际,并探讨了类成员封装、在简短的数字字面值中应用下划线、防止空catch块、正确实现字符串连贯、如何防止冗余初始化以及应用加强的for循环。


【注】本文译自: Java Best Practices | Developer.com