关于Java的十个编码小技巧

时间:2020-9-22 作者:admin

相信每个人都喜欢看简洁易懂的代码,代码可读性也是衡量代码质量的重要标准之一,而本文则通过十个具体的编程小技巧(部分技巧并不局限于Java),希望能够帮助到你。

1. 使用三元表达式

考虑以下代码:

public boolean isOdd(int num) {
    if (num % 2 == 1) {
        return true;
    } else {
        return false;
    }
}

我们可能会经常见到类似上面的代码,只需要一次简单的if ... else ...判断,而判断所得到的内容也很简单,在这种时候,我们就可以使用三元表达式来简化我们的代码,就像下面这样:

public boolean isOdd(int num) {
	return num % 2 == 1 ? true : false;
}

可以发现,通过使用... ? ... : ...三元表达式,我们能够写出更加简单的代码,当然这个代码仍然不够简洁,在接下来第二点会进行讨论,这里先讨论三元表示式。虽然三元表示式可以简化我们的代码,在很多时候也能够简化我们的代码,但是当判断的情况过多并且语句较长的时候,我们就不应该使用三元表达式了,就像下面这种情况,我们就不应该采用三元表达式:

public int getMaxDays(int year, int month) {
    // 当条件过多时, 使用三元表示就无法体现发挥简洁的优势了,我们应该考虑其它的方法
    return month == 2 ? (isLeapYear(year) ? 29 : 28) :
            (month == 4 || month == 6 || month == 9 || month == 11) ? 30 : 31;
}

private boolean isLeapYear(int year) {
    return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
}

2. 简化布尔条件表达式

再次考虑1中的代码:

public boolean isOdd(int num) {
	return num % 2 == 1 ? true : false;
}

由于我们返回的结果是布尔值,所以在这种情况下,我们就可以直接返回布尔表达式即可,而不需要再进行条件判断,让代码不够简洁:

public boolean isOdd(int num) {
	return num % 2 == 1;
}

3. 使用卫语句

在1中我们以一个反例讲诉了三元表示不用改被乱用,这里我们就先通过if语句,来进行改写,写出第一版代码:

public int getMaxDays(int year, int month) {
    int result;
    if (month == 2) {
        result = isLeapYear(year) ? 29 : 28;
    } else if (month == 4 || month == 6 || month == 9 || month == 11) {
        result = 30;
    } else {
        result = 31;
    }
    return result;
}

private boolean isLeapYear(int year) {
    return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
}

我们为了实现单入口单出口原则,而让多次判断都有依赖,如果每个判断条件内存在大量的语句的时候,更会显示出这种编写方式的弊端,因此对于这种情况,我们通常会编写单入口多出口的代码,让简便情况尽快得到判断,每个判断语句之间不必耦合:

public int getMaxDays(int year, int month) {
    if (month == 2) {
        return isLeapYear(year) ? 29 : 28;
    } 
    if (month == 4 || month == 6 || month == 9 || month == 11) {
        return  30;
    }
    return 31;
}

private boolean isLeapYear(int year) {
    return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
}

4. 使用arraycopy拷贝数组

我们有时可能因为不能在原数组上进行修改,需要对原数组进行一份拷贝,然后写出类似下面这样的代码:

int[] arr = {1, 2, 3, 4, 5};
int[] temp = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
    temp[i] = arr[i];
}

不过我们可以通过使用系统自带的arraycopy函数,让代码更易读,效率也更高:

int[] arr = {1, 2, 3, 4, 5};
int[] temp = new int[arr.length];
System.arraycopy(arr, 0, temp, 0, arr.length);

5. 使用try ... with ...进行资源管理

当使用io流时,我们由于需要对资源进行管理,需要写出类似下面的代码,当多个资源需要管理的时候,不仅难以管理,我们还可能经常会忘记对资源的关闭:

FileInputStream fis = null;
try {
	fis = new FileInputStream(new File(""));
	fis.read();
} catch (Exception e) {
	e.printStackTrace();
} finally {
	try {
		fis.close();
	} catch (IOException e) {
		e.printStackTrace();
	}
}

但是通过使用try ... with ...就可以写出下面这样简便的代码,我们不需要再担心资源的管理问题:

try (FileInputStream fis = new FileInputStream(new File(""))) {
	fis.read();
} catch (Exception e) {
	e.printStackTrace();
}

6. 位运算小技巧

相信很多人都知道,位运算在很多情况下都能表现出更好地性能,在某些情况下也能够简化代码,这里就讲两个小例子:

  1. 使用位移代替乘除法:

    a << n // <=> a * 2^n
    a >> n // <=> a / 2^n
    
  2. 使用位运算避免二分法中的加法溢出:

    相信很多人都知道在二分查找中,以下代码可能因为加法运算的溢出,导致无法得到正确的结果:

    int mid = (left + right) / 2;
    

    如果使用下面这种方法就可以避免:

    int mid = left + (right - left) / 2;
    

    但是通过使用无符号位移,我们就可以写出下面这样简洁的代码,而且也可以避免溢出:

    int mid = (left + right) >>> 1;
    
  3. 判断两号是否同号

    如果不使用位运算,我们需要写出下面这样的代码:

    (a >= 0 && b >= 0) || (a < 0 && b < 0)
    

    但是如果使用^的特性,我们就可以写出下面这样简洁的代码:

    (a ^ b) >= 0
    

7. 分割字符串

很多时候,我们为了进行打印调试,都需要对数组的数据或者字符串进行分割打印,例如将[1, 2, 3]或者"123"打印为1, 2, 3的格式,按照传统的方式,我们需要写出下面这样的代码:

int[] arr = {1, 2, 3};
StringBuilder sb = new StringBuilder(String.valueOf(arr[0]));
for (int i = 1; i < arr.length; i++) {
    sb.append(", ").append(arr[i]);
}
// output: 1, 2, 3
System.out.println(sb);

String str = "123";
String[] split = Pattern.compile("").split(str);
StringBuilder sb = new StringBuilder(split[0]);
for (int i = 1; i < split.length; i++) {
    sb.append(", ").append(split[i]);
}
// output: 1, 2, 3
System.out.println(sb);

但是通过使用Stream的特性,我们就能写出下面这样更加方便易读的代码(为了代码的可读性,我们通常会将链式调用拆成多行):

int[] arr = {1, 2, 3};
// output: 1, 2, 3
System.out.println(
        IntStream.of(arr)
                 .mapToObj(String::valueOf)
                 .collect(Collectors.joining(", "))
);

String str = "123";
// output: 1, 2, 3
System.out.println(
        Pattern.compile("")
               .splitAsStream(str)
               .collect(Collectors.joining(", "))
);

8. 使用系统方法打印数组

我们通常需要打印数组进行调试,正如7中所说的那样,但是我们通常只要能够得出数组的数据即可,而不在乎其形式,但是如果直接打印数组,只会得到一个内存地址,不过我们其实可以通过调用Arrays.toString(arr),很容易就实现我们的需求:

int[] arr = {1, 2, 3};
// output: [1, 2, 3]
System.out.println(Arrays.toString(arr));

9. Stream实现计数器

有时,我们需要对数组或者集合中的数据进行统计其次数,我们会写出如下所示的代码:

int[] arr = {1, 2, 3, 4, 5, 6};
Map<Integer, Integer> map = new HashMap<>();
for (int val : arr) {
    map.put(val, map.getOrDefault(val, 0) + 1);
}

如果利用Stream,我们就能够更加专注于我们的业务逻辑,也可加易读:

int[] arr = {1, 2, 3, 4, 5, 6};
Map<Integer, Integer> map = IntStream.of(arr)
                                     .boxed()
                                     .collect(Collectors.toMap(k -> k, k -> 1, Integer::sum));

10. 使用Arrays.asList(arr)将对象数组转换为集合

有时我们需要为了将数组转换为集合,然后写出类似下面的代码:

String[] strs = new String[10];
List<String> list = new ArrayList<>();
for (String str : strs) {
    list.add(str);
}

但是利用Array.asList(arr)就可以写出下面这样简洁的代码:

String[] strs = new String[10];
// Array.asList(arr)生成的结果集合无法进行数据的修改,因此需要使用 new ArrayList<>();
List<String> list = new ArrayList<>(Arrays.asList(strs));

或者使用Stream这样进行转换:

String[] strs = new String[10];
List<String> list = Stream.of(strs).collect(Collectors.toList());

以上就是关于Java的一些编程小技巧,希望能够对你有些帮助。

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。