关于java:Java最佳实践

45次阅读

共计 7947 个字符,预计需要花费 20 分钟才能阅读完成。

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

本教程将提供 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 中创立 字符串 的两种等效形式:间接创立和应用构造函数:

// 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()办法每次被调用都会创立一个新的 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

正文完
 0