大家好,我是程序员青戈,一个被Bug耽误了才艺的程序员👦专注于Java领域的知识分享和技术交流,每天会给大家带来Java学习的干货教程📚
微信搜索公众号 Java学习指南,回复 面试 领取一线大厂面试题一套😋加号主微信 xia_qing2012,可以进技术交流学习群一起共同进步哦😊
文章目录
前言
本文为Java程序员们准备了一系列广为流传的Java最佳编程实践,请刚入门的程序员们认真读几遍。认真看完后,我敢保证,日后你会感谢我的。
1.优先返回空集合而非null
如果程序要返回一个对象,确保返回的对象有值而不是null。这能节省大量的"if else"检查。
public String getLocationName {
return (null == cityName ? "": cityName);
}
2.谨慎操作字符串
如果两个字符串在for循环中使用+操作符进行拼接,那么每次循环都会产生一个新的字符串对象。这不仅浪费内存空间同时还会影响性能。类似的,如果初始化字符串对象,尽量不要使用构造方法,而应该直接初始化。比方说:
//Slower Instantiation
String bad = new String("Yet another string object");
//Faster Instantiation
String good = "Yet another string object"
3.避免无用对象
创建对象是Java中最昂贵的操作之一。因此最好在有需要的时候再进行对象的创建/初始化。如下:
import java.util.ArrayList;
import java.util.List;
public class Employees {
private List Employees;
public List getEmployees() {
//initialize only when required
if(null == Employees) {
Employees = new ArrayList();
}
return Employees;
}
}
4.数组与ArrayList之争
开发人员经常会发现很难在数组和ArrayList间做选择。它们二者互有优劣。如何选择应该视情况而定。
- 数组是定长的,而ArrayList是变长的。由于数组长度是固定的,因此在声明数组时就已经分配好内存了。而数组的操作则会更快一些。另一方面,如果我们不知道数据的大小,那么过多的数据便会导致- - ArrayOutOfBoundException,而少了又会浪费存储空间。
- ArrayList在增删元素方面要比数组简单。
- 数组可以是多维的,但ArrayList只能是一维的。
import java.util.ArrayList;
public class arrayVsArrayList {
public static void main(String[] args) {
int[] myArray = new int[6];
myArray[7]= 10; // ArraysOutOfBoundException
//Declaration of ArrayList. Add and Remove of elements is easy.
ArrayList<Integer> myArrayList = new ArrayList<>();
myArrayList.add(1);
myArrayList.add(2);
myArrayList.add(3);
myArrayList.add(4);
myArrayList.add(5);
myArrayList.remove(0);
for(int i = 0; i < myArrayList.size(); i++) {
System.out.println("Element: " + myArrayList.get(i));
}
//Multi-dimensional Array
int[][][] multiArray = new int [3][3][3];
}
}
5.try块的finally块没有被执行
看下下面这段代码:
public class shutDownHooksDemo {
public static void main(String[] args) {
for(int i=0;i<5;i++)
{
try {
if(i==4) {
System.out.println("Inside Try Block.Exiting without executing Finally block.");
System.exit(0);
}
}
finally {
System.out.println("Inside Finally Block.");
}
}
}
}
从代码来看,貌似finally块中的println语句应该会被执行5次。但当程序运行后,你会发现finally块只执行了4次。第5次迭代的时候会触发exit函数的调用,于是这第5次的finally便永远也触发不到了。原因便是——System.exit会挂起所有线程的执行,包括当前线程。即便是try语句后的finally块,只要是执行了exit,便也无力回天了。
在调用System.exit时,JVM会在关闭前执行两个结束任务:
首先,它会执行完所有通过Runtime.addShutdownHook注册进来的终止的钩子程序。这一点很关键,因为它会释放JVM外部的资源。
接下来的便是Finalizer了。可能是System.runFinalizersOnExit也可能是Runtime.runFinalizersOnExit。finalizer的使用已经被废弃有很长一段时间了。finalizer可以在存活对象上进行调用,即便是这些对象仍在被其它线程所使用。而这会导致不可预期的结果甚至是死锁。
public class shutDownHooksDemo {
public static void main(String[] args) {
for(int i=0;i<5;i++)
{
final int final_i = i;
try {
Runtime.getRuntime().addShutdownHook(
new Thread() {
public void run() {
if(final_i==4) {
System.out.println("Inside Try Block.Exiting without executing Finally block.");
System.exit(0);
}
}
});
}
finally {
System.out.println("Inside Finally Block.");
}
}
}
}
6.奇数判断
看下这几行代码,看看它们是否能用来准确地判断一个数是奇数?
public boolean oddOrNot(int num) {
return num % 2 == 1;
}
看似是对的,但是每执行四便会有一个错误的结果(用数据说话)。考虑到负奇数的情况,它除以2的结果就不会是1。因此,返回值是false,而这样是不对的。
代码可以修改成这样:
public boolean oddOrNot(int num) {
return (num & 1) != 0;
}
这么写不光是负奇数的问题解决了,并且还是经过充分优化过的。因为算术运算和逻辑运行要比乘除运算更高效,计算的结果也会更快。
7.单引号与双引号的区别
public class Haha {
public static void main(String args[]) {
System.out.print("H" + "a");
System.out.print('H' + 'a');
}
}
看起来这段代码会返回"Haha",但实际返回的是Ha169。原因就是用了双引号的时候,字符会被当作字符串处理,而如果是单引号的话,字符值会通过一个叫做基础类型拓宽的操作来转换成整型值。然后再将值相加得到169。
8.一些防止内存泄露的小技巧
内存泄露会导致软件的性能降级。由于Java是自动管理内存的,因此开发人员并没有太多办法介入。不过还是有一些方法能够用来防止内存泄露的。
- 查询完数据后立即释放数据库连接
- 尽可能使用finally块
- 释放静态变量中的实例
9.避免死锁
死锁出现的原因有很多。避免死锁不是一句话就能解决的。通常来说,当某个同步对象在等待另一个同步对象所拥有的资源上的锁时,便会产生死锁。
试着运行下下面的程序。它会告诉你什么是死锁。这个死锁是由于两个线程都在等待对方所拥有的资源,因此会产生死锁。它们会一直等待,没有谁会先放手。
public class DeadlockDemo {
public static Object addLock = new Object();
public static Object subLock = new Object();
public static void main(String args[]) {
MyAdditionThread add = new MyAdditionThread();
MySubtractionThread sub = new MySubtractionThread();
add.start();
sub.start();
}
private static class MyAdditionThread extends Thread {
public void run() {
synchronized (addLock) {
int a = 10, b = 3;
int c = a + b;
System.out.println("Addition Thread: " + c);
System.out.println("Holding First Lock...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Addition Thread: Waiting for AddLock...");
synchronized (subLock) {
System.out.println("Threads: Holding Add and Sub Locks...");
}
}
}
}
private static class MySubtractionThread extends Thread {
public void run() {
synchronized (subLock) {
int a = 10, b = 3;
int c = a - b;
System.out.println("Subtraction Thread: " + c);
System.out.println("Holding Second Lock...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Subtraction Thread: Waiting for SubLock...");
synchronized (addLock) {
System.out.println("Threads: Holding Add and Sub Locks...");
}
}
}
}
}
输出:
Addition Thread: 13
Subtraction Thread: 7
Holding First Lock…
Holding Second Lock…
Addition Thread: Waiting for AddLock…
Subtraction Thread: Waiting for SubLock…
但如果调用的顺序变一下的话,死锁的问题就解决了。
public class DeadlockSolutionDemo {
public static Object addLock = new Object();
public static Object subLock = new Object();
public static void main(String args[]) {
MyAdditionThread add = new MyAdditionThread();
MySubtractionThread sub = new MySubtractionThread();
add.start();
sub.start();
}
private static class MyAdditionThread extends Thread {
public void run() {
synchronized (addLock) {
int a = 10, b = 3;
int c = a + b;
System.out.println("Addition Thread: " + c);
System.out.println("Holding First Lock...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Addition Thread: Waiting for AddLock...");
synchronized (subLock) {
System.out.println("Threads: Holding Add and Sub Locks...");
}
}
}
}
private static class MySubtractionThread extends Thread {
public void run() {
synchronized (addLock) {
int a = 10, b = 3;
int c = a - b;
System.out.println("Subtraction Thread: " + c);
System.out.println("Holding Second Lock...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Subtraction Thread: Waiting for SubLock...");
synchronized (subLock) {
System.out.println("Threads: Holding Add and Sub Locks...");
}
}
}
}
}
输出:
Addition Thread: 13
Holding First Lock…
Addition Thread: Waiting for AddLock…
Threads: Holding Add and Sub Locks…
Subtraction Thread: 7
Holding Second Lock…
Subtraction Thread: Waiting for SubLock…
Threads: Holding Add and Sub Locks…
10.替Java省点内存
某些Java程序是CPU密集型的,但它们会需要大量的内存。这类程序通常运行得很缓慢,因为它们对内存的需求很大。为了能提升这类应用的性能,可得给它们多留点内存。因此,假设我们有一台拥有10G内存的Tomcat服务器。在这台机器上,我们可以用如下的这条命令来分配内存:
export JAVA_OPTS="$JAVA_OPTS -Xms5000m -Xmx6000m -XX:PermSize=1024m -XX:MaxPermSize=2048m"
- Xms = 最小内存分配
- Xmx = 最大内存分配
- XX:PermSize = JVM启动时的初始大小
- XX:MaxPermSize = JVM启动后可分配的最大空间
感谢大家的阅读,创作不易,能否请您小手点一点下方的 一键三连 支持一下作者呢😊