Java开发经验及技巧集03-开发经验及技巧问题汇总

  • 时间:2020-03-24 22:53 编辑: 来源: 阅读:137
  • 扫一扫,手机访问
摘要:接下来我们来总结一些 Java 开发技巧的问题以供大家参考。Java 是目前最流行的编程语言之一——它可以用来编写 Windows 程序或者是 Web 应用,移动应用,网络程序,消费电子产品,机顶盒设备,它无处不在。有超过 30 亿的设备是运行在 Ja

接下来我们来总结一些 Java 开发技巧的问题以供大家参考。

Java 是目前最流行的编程语言之一——它可以用来编写 Windows 程序或者是 Web 应用,

移动应用,网络程序,消费电子产品,机顶盒设备,它无处不在。

有超过 30 亿的设备是运行在 Java 之上的。根据 Oracle 的统计数据,光是使用中的 Java 

Card 就有有 50 亿。

超过 900 万程序员选择使用 Java 进行开发,它是最受开发人员欢迎的语言,同时也是最

流行的开发平台。

本文为那些准 Java 程序员们准备了一系列广为流传的 Java 最佳编程实践和技巧:

技巧 01:优先返回空集合而非 null

如果程序要返回一个不包含任何值的集合,确保返回的是空集合而不是 null。这能节省

大量的”if else”检查。

public class getLocationName {

 return (null==cityName ? "": cityName);

}

技巧 02:谨慎操作字符串

Java 开发经验及技巧集

7

如果两个字符串在 for 循环中使用+操作符进行拼接,那么每次循环都会产生一个新的字

符串对象。这不仅浪费内存空间同时还会影响性能。类似的,如果初始化字符串对象,尽量

不要使用构造方法,而应该直接初始化。比方说:

//Slower Instantiation

String bad = new String("Yet another string object");

//Faster Instantiation

String good = "Yet another string object"

技巧 03:避免无用对象

创建对象是 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;

 }

}

技巧 04:数组与 ArrayList 之争

开发人员经常会发现很难在数组和 ArrayList 间做选择。它们二者互有优劣。如何选择应

该视情况而定。

import java.util.ArrayList;

public class arrayVsArrayList {

 public static void main(String[] args) {

Java 开发经验及技巧集

8 / 38

 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]; 

 }

}

数组是定长的,而 ArrayList 是变长的。由于数组长度是固定的,因此在声明数组时就已

经分配好内存了。而数组的操作则会更快一些。另一方面,如果我们不知道数据的大小,那

么过多的数据便会导致 ArrayOutOfBoundException,而少了又会浪费存储空间。

ArrayList 在增删元素方面要比数组简单。

数组可以是多维的,但 ArrayList 只能是一维的。

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.");

Java 开发经验及技巧集

9

 }

 }

 }

}

从代码来看,貌似 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.");

 }

 }

 }

Java 开发经验及技巧集

10 / 38

}

技巧 05:判断奇数

看下这几行代码,看看它们是否能用来准确地判断一个数是奇数?

public boolean oddOrNot(int num) {

 return num % 2 == 1;

}

看似是对的,但是每执行四便会有一个错误的结果(用数据说话)。考虑到负奇数的情

况,它除以 2 的结果就不会是 1。因此,返回值是 false,而这样是不对的。

代码可以修改成这样:

public boolean oddOrNot(int num) {

 return (num & 1) != 0;

}

这么写不光是负奇数的问题解决了,并且还是经过充分优化过的。因为算术运算和逻辑

运行要比乘除运算更高效,计算的结果也会更快。

技巧 06:单引号与双引号的区别

public class Haha {

 public static void main(String args[]) {

 System.out.print("H" + "a");

 System.out.print('H' + 'a');

 }

}

看起来这段代码会返回”Haha”,但实际返回的是 Ha169。原因就是用了双引号的时候,

字符会被当作字符串处理,而如果是单引号的话,字符值会通过一个叫做基础类型拓宽的操

作来转换成整型值。然后再将值相加得到 169。

技巧 07:一些防止内存泄露的小技巧

Java 开发经验及技巧集

11

内存泄露会导致软件的性能降级。由于 Java 是自动管理内存的,因此开发人员并没有太

多办法介入。不过还是有一些方法能够用来防止内存泄露的。

技巧 08:查询完数据后立即释放数据库连接

尽可能使用 finally 块

释放静态变量中的实例

避免死锁

死锁出现的原因有很多。避免死锁不是一句话就能解决的。通常来说,当某个同步对象

在等待另一个同步对象所拥有的资源上的锁时,便会产生死锁。

试着运行下下面的程序。它会告诉你什么是死锁。这个死锁是由于两个线程都在等待对

方所拥有的资源,因此会产生死锁。它们会一直等待,没有谁会先放手。

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...");

 }

 }

 }

Java 开发经验及技巧集

12 / 38

 }

 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();

Java 开发经验及技巧集

13

 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...");

 }

 }

 }

 }

}

Java 开发经验及技巧集

14 / 38

输出:

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...

技巧 09:替 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 启动后可分配的最大空间

如何计算 Java 中操作的耗时

在 Java 中进行操作计时有两个标准的方法:System.currentTimeMillis()和

System.nanoTime()。问题就在于,什么情况下该用哪个。从本质上来讲,他们的作用都是一

样的,但有以下几点不同:

 System.currentTimeMillis()的精度在千分之一秒到千分之 15 秒之间(取决于系统)

而 System.nanoTime()则能到纳秒级。

 System.currentTimeMillis 读操作耗时在数个 CPU 时钟左右。而 System.nanoTime()

则需要上百个。

 System.currentTimeMillis 对应的是绝对时间(1970 年 1 月 1 日所经历的毫秒数),

而 System.nanoTime()则不与任何时间点相关。

技巧 10:Float 还是 double

数据类型 所用字节 有效位数

Java 开发经验及技巧集

15

float 4 7 

double 8 15 

在对精度要求高的场景下,double 类型相对 float 要更流行一些,理由如下:

大多数处理器在处理 float 和 double 上所需的时间都是差不多的。而计算时间一样的前

提下,double 类型却能提供更高的精度。

技巧 11:幂运算

Java 是通过异或操作来进行幂运算的。Java 对于幂运算有两种处理方式:

乘积:

double square = double a * double a; // 

Optimized

double cube = double a * double a * double a; //

Non-optimized

double cube = double a * double square; // Optimized

double quad = double a * double a * double a * double a; // 

Non-optimized

double quad = double square * double square; // Optimized

pow 方法:在无法使用乘积的情况下可以使用 pow 方法。

double cube = Math.pow(base, exponent);

不到万不得已不要使用 Math.pow。比方说,当指数是小数的时候。因为 Math.pow 要比

乘积慢 300-600 倍左右。

技巧 12:如何处理空指针异常

空指针异常是 Java 中很常见的异常。当你尝试调用一个 null 对象上的方法时便会抛出这

个异常。比如:

int noOfStudents = school.listStudents().count; 

在 上 述 例 子 中 , school 为空或者 listStudents() 为 空 都 可 能 会 抛 出 了

NullPointerException。因此最好检查下对象是否为空以避免类似情况。

private int getListOfStudents(File[] files) {

 if (files == null)

 throw new NullPointerException("File list cannot be null");

 }

Java 开发经验及技巧集

16 / 38

技巧 13:JSON 编码

JSON 是数据存储及传输的一种协议。与 XML 相比,它更易于使用。由于它非常轻量级

以及自身的一些特性,现在 JSON 在网络上已经是越来越流行了。常见的数据结构都可以编

码成 JSON 然后在各个网页间自由地传输。不过在开始编码前,你得先安装一个 JSON 解析

器。在下面的例子中,我们将使用 json.simple 库来完成这项工作下面是编码成 JSON 串的一

个简单的例子。

import org.json.simple.JSONObject;

import org.json.simple.JSONArray;

public class JsonEncodeDemo {

 public static void main(String[] args) {

 JSONObject obj = new JSONObject();

 obj.put("Novel Name", "Godaan");

 obj.put("Author", "Munshi Premchand");

 JSONArray novelDetails = new JSONArray();

 novelDetails.add("Language: Hindi");

 novelDetails.add("Year of Publication: 1936");

 novelDetails.add("Publisher: Lokmanya Press");

 obj.put("Novel Details", novelDetails);

 System.out.print(obj);

 }

}

输出:

{"Novel Name":"Godaan","Novel Details":["Language: Hindi","Year of 

Publication: 1936","Publisher: Lokmanya Press"],"Author":"Munshi Premchand"}

技巧 14:JSON 解析

开发人员要想解析 JSON 串,首先你得知道它的格式。下面例子有助于你来理解这一点:

import java.io.FileNotFoundException;

import java.io.FileReader;

import java.io.IOException;

import java.util.Iterator;

import org.json.simple.JSONArray;

import org.json.simple.JSONObject;

import org.json.simple.parser.JSONParser;

Java 开发经验及技巧集

17

import org.json.simple.parser.ParseException;

public class JsonParseTest {

 private static final String filePath = 

"//home//user//Documents//jsonDemoFile.json";

 public static void main(String[] args) {

 try {

 // read the json file

 FileReader reader = new FileReader(filePath);

 JSONParser jsonParser = new JSONParser();

 JSONObject jsonObject = 

(JSONObject)jsonParser.parse(reader);

 // get a number from the JSON object

 Long id = (Long) jsonObject.get("id");

 System.out.println("The id is: " + id); 

 // get a String from the JSON object

 String type = (String) jsonObject.get("type");

 System.out.println("The type is: " + type);

 // get a String from the JSON object

 String name = (String) jsonObject.get("name");

 System.out.println("The name is: " + name);

 // get a number from the JSON object

 Double ppu = (Double) jsonObject.get("ppu");

 System.out.println("The PPU is: " + ppu);

 // get an array from the JSON object

 System.out.println("Batters:");

 JSONArray batterArray= (JSONArray) jsonObject.get("batters");

 Iterator i = batterArray.iterator();

 // take each value from the json array separately

 while (i.hasNext()) {

 JSONObject innerObj = (JSONObject) i.next();

 System.out.println("ID "+ innerObj.get("id") + 

 " type " + innerObj.get("type"));

 }

 // get an array from the JSON object

 System.out.println("Topping:");

 JSONArray toppingArray= (JSONArray) 

jsonObject.get("topping");

 Iterator j = toppingArray.iterator();

Java 开发经验及技巧集

18 / 38

 // take each value from the json array separately

 while (j.hasNext()) {

 JSONObject innerObj = (JSONObject) j.next();

 System.out.println("ID "+ innerObj.get("id") + 

 " type " + innerObj.get("type"));

 }

 } catch (FileNotFoundException ex) {

 ex.printStackTrace();

 } catch (IOException ex) {

 ex.printStackTrace();

 } catch (ParseException ex) {

 ex.printStackTrace();

 } catch (NullPointerException ex) {

 ex.printStackTrace();

 }

 }

}

jsonDemoFile.json

{

 "id": 0001,

 "type": "donut",

 "name": "Cake",

 "ppu": 0.55,

 "batters":

 [

 { "id": 1001, "type": "Regular" },

 { "id": 1002, "type": "Chocolate" },

 { "id": 1003, "type": "Blueberry" },

 { "id": 1004, "type": "Devil's Food" }

 ],

 "topping":

 [

 { "id": 5001, "type": "None" },

 { "id": 5002, "type": "Glazed" },

 { "id": 5005, "type": "Sugar" },

Java 开发经验及技巧集

19

 { "id": 5007, "type": "Powdered Sugar" },

 { "id": 5006, "type": "Chocolate with Sprinkles" },

 { "id": 5003, "type": "Chocolate" },

 { "id": 5004, "type": "Maple" }

 ]

}

The id is: 1

The type is: donut

The name is: Cake

The PPU is: 0.55

Batters:

ID 1001 type Regular

ID 1002 type Chocolate

ID 1003 type Blueberry

ID 1004 type Devil's Food

Topping:

ID 5001 type None

ID 5002 type Glazed

ID 5005 type Sugar

ID 5007 type Powdered Sugar

ID 5006 type Chocolate with Sprinkles

ID 5003 type Chocolate

ID 5004 type Maple

技巧 15:简单字符串查找

Java 提供了一个库函数叫做 indexOf()。这个方法可以用在 String 对象上,它返回的是要

查找的字符串所在的位置序号。如果查找不到则会返回-1。

技巧 16:列出目录下的文件

你可以用下面的代码来列出目录下的文件。这个程序会遍历某个目录下的所有子目录及

文件,并存储到一个数组里,然后通过遍历数组来列出所有文件。

import java.io.*;

Java 开发经验及技巧集

20 / 38

public class ListContents {

 public static void main(String[] args) {

 File file = new File("//home//user//Documents/");

 String[] files = file.list();

 System.out.println("Listing contents of " + file.getPath());

 for(int i=0 ; i < files.length ; i++)

 {

 System.out.println(files[i]);

 }

 }

}

技巧 17:一个简单的 IO 程序

Java 提供了 FileInputStream 以及 FileOutputStream 类来进行文件的读写操作。

FileInputStream 的构造方法会接收输入文件的路径作为入参然后创建出一个文件的输入流。

同样的,FileOutputStream 的构造方法也会接收一个文件路径作为入参然后创建出文件的输出

流。在处理完文件之后,一个很重要的操作就是要记得”close”掉这些流。

import java.io.*;

public class myIODemo {

 public static void main(String args[]) throws IOException {

 FileInputStream in = null;

 FileOutputStream out = null;

 try {

 in = new 

FileInputStream("//home//user//Documents//InputFile.txt");

 out = new 

FileOutputStream("//home//user//Documents//OutputFile.txt");

 int c;

 while((c = in.read()) != -1) {

 out.write(c);

 }

 } finally {

 if(in != null) {

 in.close();

 }

Java 开发经验及技巧集

21

 if(out != null) {

 out.close();

 }

 }

 }

}

技巧 18:在 Java 中执行某个 shell 命令

Java 提供了 Runtime 类来执行 shell 命令。由于这些是外部的命令,因此异常处理就显得

异常重要。在下面的例子中,我们将通过一个简单的例子来演示一下。我们会在 shell 命令行

中打开一个 pdf 文件。

import java.io.BufferedReader;

import java.io.InputStream;

import java.io.InputStreamReader;

public class ShellCommandExec {

 public static void main(String[] args) {

 String gnomeOpenCommand = "gnome-open 

//home//user//Documents//MyDoc.pdf";

 try {

 Runtime rt = Runtime.getRuntime();

 Process processObj = rt.exec(gnomeOpenCommand);

 InputStream stdin = processObj.getErrorStream();

 InputStreamReader isr = new InputStreamReader(stdin);

 BufferedReader br = new BufferedReader(isr);

 String myoutput = "";

 while ((myoutput=br.readLine()) != null) {

 myoutput = myoutput+" ";

 }

 System.out.println(myoutput);

 }

 catch (Exception e) {

 e.printStackTrace();

 }

 }

}

Java 开发经验及技巧集

22 / 38

技巧 19:使用正则

正则表达式的结构摘录如下(来源: Oracle 官网)

字符

x 字符 x 

反斜杠

�n 8 进制值为 0n 的字符(0<=n<=7) 

�nn 

�mnn 8 进制值为 0mnn 的字符(0 <= m <= 3, 0<=n<=7) 

xhh 16 进制值为 0xhh 的字符

uhhhh 16 进制值为 0xhhhh 的字符

x{h…h} 16 进制值为 0xh…h 的字符(Character.MINCODEPOINT <= 0xh…h <= 

Character.MAXCODEPOINT) 

制表符(‘u0009′) 

换行符(‘u000A’) 

回车(‘u000D’) 

f 分页符(‘u000C’) 

a 警告符(‘u0007′) 

e ESC(‘u001B’) 

cx ctrl+x 

字符分类

[abc] a, b 或 c 

[^abc] abc 以外的任意字符

[a-zA-Z] a 到 z 以及 A 到 Z 

[a-d[m-p]] a 到 d 或者 m 到 p[a-dm-p]则是取并集

[a-z&&[def]] d,e 或 f(交集) 

[ad-z] 

[a-z&&[^bc]] a 到 z 但不包括 b 和 c 

[a-z&&[^m-p]] a 到 z 但不包括 mp:也就是[a-lq-z] 

预定义字符

. 任意字符,有可能包括换行符

d 0 到 9 的数字

D 0 到 9 以外的字符

s 空格符[ x0Bf ] 

Java 开发经验及技巧集

23

S 非空格符[^s] 

w 字母[a-zA-Z_0-9] 

W 非字母[^w] 

边界匹配

^ 行首

$ 行末

 单词边界

A 输入的起始位置

G 前一个匹配的末尾

 输入的结束位置,仅用于最后的结束符

z 输入的结束位置

import java.util.regex.Matcher;

import java.util.regex.Pattern;

public class RegexMatches

{

 private static String pattern = 

"^[_A-Za-z0-9-]+(.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(.[A-Za-z0-9]+)*(.[A-Za-z]{2,})$";

 private static Pattern mypattern = Pattern.compile(pattern);

 public static void main( String args[] ){

 String valEmail1 = "testemail@domain.com";

 String invalEmail1 = "....@domain.com";

 String invalEmail2 = ".$$%%@domain.com";

 String valEmail2 = "test.email@domain.com";

 System.out.println("Is Email ID1 valid? "+validateEMailID(valEmail1));

 System.out.println("Is Email ID1 valid? 

"+validateEMailID(invalEmail1));

 System.out.println("Is Email ID1 valid? 

"+validateEMailID(invalEmail2));

 System.out.println("Is Email ID1 valid? "+validateEMailID(valEmail2));

 }

 public static boolean validateEMailID(String emailID) {

 Matcher mtch = mypattern.matcher(emailID);

 if(mtch.matches()){

 return true;

 }

 return false;

 } 

Java 开发经验及技巧集

24 / 38

}

技巧 20:Java Swing 的简单示例

有了 Java 的 swing,你便可以编写 GUI 应用了。Java 所提供的 javax 包中就包含了 swing。

使用 swing 来编写 GUI 程序首先需要继承下 JFrame。然后在里面添加 Box,然后便可以往里

面添加诸如按钮,多选按钮,文本框等控件了。这些 Box 是放在 Container 的最外层的。

import java.awt.*; 

import javax.swing.*; 

public class SwingsDemo extends JFrame 

 public SwingsDemo()

 {

 String path = "//home//user//Documents//images";

 Container contentPane = getContentPane(); 

 contentPane.setLayout(new FlowLayout()); 

 Box myHorizontalBox = Box. createHorizontalBox(); 

 Box myVerticleBox = Box. createVerticalBox(); 

 myHorizontalBox.add(new JButton("My Button 1")); 

 myHorizontalBox.add(new JButton("My Button 2")); 

 myHorizontalBox.add(new JButton("My Button 3")); 

 myVerticleBox.add(new JButton(new ImageIcon(path + 

"//Image1.jpg"))); 

 myVerticleBox.add(new JButton(new ImageIcon(path + 

"//Image2.jpg"))); 

 myVerticleBox.add(new JButton(new ImageIcon(path + 

"//Image3.jpg"))); 

 contentPane.add(myHorizontalBox); 

 contentPane.add(myVerticleBox); 

 pack(); 

 setVisible(true);

 } 

 public static void main(String args[]) { 

 new SwingsDemo(); 

 } 

Java 开发经验及技巧集

25

}

技巧 21:使用 Java 播放音频

在 Java 中,播放音频是一个很常见的需求,尤其是在游戏开发里面。

下面这个 DEMO 演示了如何在 Java 中播放音频。

import java.io.*;

import java.net.URL;

import javax.sound.sampled.*;

import javax.swing.*;

// To play sound using Clip, the process need to be alive.

// Hence, we use a Swing application.

public class playSoundDemo extends JFrame {

 // Constructor

 public playSoundDemo() {

 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

 this.setTitle("Play Sound Demo");

 this.setSize(300, 200);

 this.setVisible(true);

 try {

 URL url = this.getClass().getResource("MyAudio.wav");

 AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);

 Clip clip = AudioSystem.getClip();

 clip.open(audioIn);

 clip.start();

 } catch (UnsupportedAudioFileException e) {

 e.printStackTrace();

 } catch (IOException e) {

 e.printStackTrace();

 } catch (LineUnavailableException e) {

 e.printStackTrace();

 }

Java 开发经验及技巧集

26 / 38

 }

 public static void main(String[] args) {

 new playSoundDemo();

 }

}

技巧 22:导出 PDF 文件

将表格导出成 pdf 也是一个比较常见的需求。通过 itextpdf,导出 pdf 也不是什么难事。

import java.io.FileOutputStream;

import com.itextpdf.text.Document;

import com.itextpdf.text.Paragraph;

import com.itextpdf.text.pdf.PdfPCell;

import com.itextpdf.text.pdf.PdfPTable;

import com.itextpdf.text.pdf.PdfWriter;

public class DrawPdf {

 public static void main(String[] args) throws Exception {

 Document document = new Document();

 PdfWriter.getInstance(document, new 

FileOutputStream("Employee.pdf"));

 document.open();

 Paragraph para = new Paragraph("Employee Table");

 para.setSpacingAfter(20);

 document.add(para);

 PdfPTable table = new PdfPTable(3);

 PdfPCell cell = new PdfPCell(new Paragraph("First Name"));

 table.addCell(cell);

 table.addCell("Last Name");

 table.addCell("Gender");

 table.addCell("Ram");

 table.addCell("Kumar");

 table.addCell("Male");

 table.addCell("Lakshmi");

 table.addCell("Devi");

Java 开发经验及技巧集

27

 table.addCell("Female");

 document.add(table);

 document.close();

 }

 }

技巧 23:邮件发送

在 Java 中发送邮件也很简单。你只需装一下 Java Mail 这个 jar 包,放到你的类路径里即

可。在下面的代码中,我们设置了几个基础属性,然后便可以发送邮件了:

import java.util.*;

import javax.mail.*;

import javax.mail.internet.*;

public class SendEmail

{

 public static void main(String [] args)

 { 

 String to = "recipient@gmail.com";

 String from = "sender@gmail.com";

 String host = "localhost";

 Properties properties = System.getProperties();

 properties.setProperty("mail.smtp.host", host);

 Session session = Session.getDefaultInstance(properties);

 try{

 MimeMessage message = new MimeMessage(session);

 message.setFrom(new InternetAddress(from));

 message.addRecipient(Message.RecipientType.TO,new 

InternetAddress(to));

 message.setSubject("My Email Subject");

 message.setText("My Message Body");

 Transport.send(message);

 System.out.println("Sent successfully!");

 }

 catch (MessagingException ex) {

 ex.printStackTrace();

Java 开发经验及技巧集

28 / 38

 }

 }

}

技巧 24:计算时间

许多程序都需要精确的时间计量。Java 提供了一个 System 的静态方法来支持这一功能:

currentTimeMillis():返回当前时间自新纪元时间以来的毫秒值,long 类型。

long startTime = System.currentTimeMillis(); 

long estimatedTime = System.currentTimeMillis() - startTime; 

nanoTime():返回系统计时器当前的精确时间,纳秒值,这也是 long 类型。nanoTime()

主要是用于计算相对时间而非绝对时间。

long startTime = System.nanoTime(); 

long estimatedTime = System.nanoTime() - startTime; 

技巧 25:图片缩放

图片缩放可以通过 AffineTransform 来完成。首先要生成一个输入图片的图片缓冲,然后

通过它来渲染出缩放后的图片。

import java.awt.Graphics2D;

import java.awt.geom.AffineTransform;

import java.awt.image.BufferedImage;

import java.io.File;

import javax.imageio.ImageIO;

public class RescaleImage {

 public static void main(String[] args) throws Exception {

 BufferedImage imgSource = ImageIO.read(new 

File("images//Image3.jpg"));

 BufferedImage imgDestination = new BufferedImage(100, 100, 

BufferedImage.TYPE_INT_RGB);

 Graphics2D g = imgDestination.createGraphics();

 AffineTransform affinetransformation = 

AffineTransform.getScaleInstance(2, 2);

 g.drawRenderedImage(imgSource, affinetransformation);

 ImageIO.write(imgDestination, "JPG", new File("outImage.jpg"));

 }

Java 开发经验及技巧集

29

}

技巧 26:捕获鼠标动作

实现了 MouseMotionListner 接口后,便可以捕获鼠标事件了。 当鼠标进入到某个特定

区域时便会触发 MouseMoved 事件,你便能捕获到这个移动的动作了。通过一个例子来看下:

import java.awt.event.*;

import javax.swing.*;

public class MouseCaptureDemo extends JFrame implements 

MouseMotionListener

{

 public JLabel mouseHoverStatus;

 public static void main(String args[]) 

 {

 new MouseCaptureDemo();

 }

 MouseCaptureDemo() 

 {

 setSize(500, 500);

 setTitle("Frame displaying Coordinates of Mouse Motion");

 mouseHoverStatus = new JLabel("No Mouse Hover Detected.", 

JLabel.CENTER);

 add(mouseHoverStatus);

 addMouseMotionListener(this);

 setVisible(true);

 }

 public void mouseMoved(MouseEvent e) 

 {

 mouseHoverStatus.setText("Mouse Cursor Coordinates => 

X:"+e.getX()+" | Y:"+e.getY());

 }

 public void mouseDragged(MouseEvent e) 

 {}

Java 开发经验及技巧集

30 / 38

}

技巧 27:FileOutputStream Vs. FileWriter

在 Java 中有两种写文件的方式:FileOutputStream 与 FileWriter。开发人员经常会在它们

之间犹豫不决。下面这个例子能帮忙你更好地理解在不同的场景下应该选择何种方案。首先

我们来看一下实现:

使用 FileOutputStream:

File foutput = new File(file_location_string);

FileOutputStream fos = new FileOutputStream(foutput);

BufferedWriter output = new BufferedWriter(new OutputStreamWriter(fos));

output.write("Buffered Content");

使用 FileWriter:

FileWriter fstream = new FileWriter(file_location_string);

BufferedWriter output = new BufferedWriter(fstream);

output.write("Buffered Content");

技巧 28:根据 Java 的接口规范:

FileOutputStream 是用于写入原始字节流比如图片流数据。如果是要写入字符流,则应该

考虑使用 FileWriter。

这样就很清楚了,写图片应该使用 FileOutputStream 而写文本则应该选择 FileWriter。

技巧 29:集合的使用

Java 提供了许多集合类——比如,Vector,Stack,Hashtable 等。所以鼓励开发人员尽可

能地使用这些集合类有如下原因:

使用集合使得代码的可重用度更高。

集合类使得代码的结构更良好,更易于理解与维护。

最重要的是这些集合类都经过充分的测试,代码质量很高。

1-50-500 规则

在大型软件系统中,代码的可维护性是件很有挑战的工作。新加入的开发人员经常会抱

怨这些情况:单片代码(Monolithic Code),意大利面式代码(spaghetti code, 常用于描述捆

绑在一起并且低内聚的类和方法)。保持代码的整洁与可维护有一条很简单的规则:

10:包内的类不超过 10 个

Java 开发经验及技巧集

31

50:方法的代码行数不超过 50 

500:类的代码行数不超过 500 

技巧 30:SOLID 设计准则

SOLID 是 Robert Martin 提出的一套设计准则的简称。根据他的准则:

一个类应当有仅只有一个任务/职责。执行多个任务的类会让人觉得困惑。

技巧 31:单一职责原则

 开闭原则 开发人员应当优先考虑扩展现有的软件功能,而不是是修改它。

 里氏替换原则 子类必须能够替换掉他们的父类型

 接口隔离原则 和单一职责原则类似,但它特指的是接口层。每个接口都应当只负

责一项任务。

 依赖反转原则 依赖抽象而不是具体实现。也就是说每个模块都应当通过一个抽象

层与其它模块进行解耦。

技巧 32:设计模式的使用

设计模式能帮助开发人员更好地在软件中应用软件的设计准则。它还为开发人员提供了

跨语言的通用平台。设计模式中的标准术语能让开发人员更容易进行沟通。

技巧 33:关于文档

不要上来就开始写代码。制定计划,准备,编写文档,检查然后再去实现。首先,先把

需求记下来。然后去准备设计文档。合理地去假设举证。互相 review 方案然后进行确认。

技巧 34:使用 equals 而非== 

==是用来比较对象引用的,它会检查两个操作数指向的是不是同一个对象(不是相同的

对象,而是同一个对象)。而”equals”则比较的是两个字符串是不是相同(假设是字符串

对象)。

Java 开发经验及技巧集

32 / 38

技巧 35:避免使用浮点数

只有当确实有必要的时候才使用浮点数。比方说,使用浮点数来表示卢比或者派萨就很

容易产生问题——这种情况应当使用 BigDecimal。而浮点数更多地是用于测量

技巧 36:什么是 JRE/J2RE?

J2RE 是 Java2RuntimeEnvironment,即 Java 运行环境,有时简称 JRE。如果你只需要运

行 Java 程序或 Applet,下载并安装它即可。如果你要自行开发 Java 软件,请下载 JDK。在

JDK 中附带有 JRE。注意由于 Microsoft 对 Java 的支持不完全,请不要使用 IE 自带的虚拟机

来运行 Applet,务必安装一个 JRE 或 JDK。

技巧 37:什么是 J2SE/J2EE/J2ME?

J2SE 就是一般的 Java。J2ME 是针对嵌入式设备的,比如支持 Java 的手机,它有自己的

JRE 和 SDK。J2EE 是一组用于企业级程序开发的规范和类库,它使用 J2SE 的 JRE。

技巧 38:怎么做可以把 java 编译成 exe 文件?

JDK 只能将 java 源文件编译为 class 文件。class 文件是一种跨平台的字节码,必须依赖

平台相关的 JRE 来运行。Java 以此来实现跨平台性。有些开发工具可以将 java 文件编译为

exe 文件。作者反对这种做法,因为这样就取消了跨平台性。如果你确信你的软件只在

Windows 平台上运行,你可以考虑使用 C++/C#来编程。

技巧 39:类名首字母应该大写。

字段、方法以及对象(句柄)的首字母应小写。对于所有标识符,其中包含的所有单词

都应紧靠在一起,而且大写中间单词的首字母。

例如:

ThisIsAClassName

thisIsMethodOrFieldName

若在定义中出现了常数初始化字符,则大写 staticfinal 基本类型标识符中的所有字母。这

样便可标志出它们属于编译期的常数。

Java 开发经验及技巧集

33

Java 包(Package)属于一种特殊情况:它们全都是小写字母,即便中间的单词亦是如此。

对于域名扩展名称,如 com,org,net 或者 edu 等,全部都应小写(这也是 Java1.1 和 Java1.2

的区别之一)。

技巧 40: package 是什么意思?怎么用?

为了唯一标识每个类并分组,java 使用了 package 的概念。每个类都有一个全名,例如

String 的全名是 java.lang.String,其中 java.lang 是包名,String 是短名。按照 java 命名惯例,

包名是全部小写的,而类名的第一个字母是大写的。这样,如果你自行定义了同样名字的类

String,你可以把它放在 mypackage 中,通过使用全名 mypackage.String 和 java.lang.String 来

区分这两个类。同时,将逻辑上相关的类放在同一个包中,可以使程序结构更为清楚。为了

定义包,你要做的就是在 java 文件开头加一行“packagemypackage;”。注意包没有嵌套或包

含关系,mypackage 包和 mypackage.mysubpackage 包对 JRE 来说是并列的两个包(虽然开发

者可能暗示包含关系)。

技巧 41:没有声明任何 package 会怎么样?

你的类被认为放在默认包中,这时全名和短名是一致的。

技巧 42:对于自己创建的每一个类,都考虑置入一个 main(),

其中包含了用于测试那个类的代码。

为使用一个项目中的类,我们没必要删除测试代码。若进行了任何形式的改动,可方便

地返回测试。这些代码也可作为如何使用类的一个示例使用。

技巧 43:使类尽可能短小精悍,而且只解决一个特定的问题。

下面是对类设计的一些建议。

⑴ 一个复杂的开关语句:考虑采用"多形"机制。

⑵ 数量众多的方法涉及到类型差别极大的操作:考虑用几个类来分别实现。

⑶ 许多成员变量在特征上有很大的差别:考虑使用几个类。

Java 开发经验及技巧集

34 / 38

技巧 44:在 javaxxx 的时候显示“Exceptioninthread"main",

java.lang.NoSuchMethodError:main”。

首先,在你的程序中每个 java 文件有且只能有一个 public 类,这个类的类名必须和文件

名的大小写完全一样。其次,在你要运行的类中有且只能有一个 publicstaticvoidmain

(String[]args)方法,这个方法就是你的主程序。

技巧 45:java 里面怎么定义宏?

java 不支持宏,因为宏代换不能保证类型安全。如果你需要定义常量,可以将它定义为

某个类的 staticfinal 成员。

技巧 46:怎么判断要读的文件已经到了尽头?

在 Reader 的 read 方法中明确说明返回-1 表示流的结尾。

技巧 47:用继承及方法覆盖来表示行为间的差异,而用字段

表示状态间的区别。

一个非常极端的例子是通过对不同类的继承来表示颜色,这是绝对应该避免的:应直接

使用一个"颜色"字段。

技巧 48:阅读代码的时间比写代码的时间多得多,请多阅读

代码。

思路清晰的设计可获得易于理解的程序,但注释、细致的解释以及一些示例往往具有不

可估量的价值。无论对你自己,还是对后来的人,它们都是相当重要的。如对此仍有怀疑,

那么请试想自己试图从联机 Java 文档里找出有用信息时碰到的挫折,这样或许能将你说服。

Java 开发经验及技巧集

35

技巧 49:throw 和 throws 有什么不同?

throws 用于方法声明中,声明一个方法会抛出哪些异常。而 throw 是在方法体中实际执

行抛出异常的动作。如果你在方法中 throw 一个异常,却没有在方法声明中声明之,编译器

会报错。注意 Error 和 RuntimeException 的子类是例外,无需特别声明。

技巧 50:extends 和 implements 有什么不同?

对于 class 而言,extends 用于(单)继承一个类(class),而 implements 用于实现

一个接口(interface)。interface 的引入是为了部分地提供多继承的功能。在 interface 中

只需声明方法头,而将方法体留给实现的 class 来做。这些实现的 class 的实例完全可以当作

interface 的实例来对待。在 interface 之间也可以声明为 extends(多继承)的关系。注意一个

interface 可以 extends 多个其他 interface。

技巧 51:良好的设计能带来最大的回报。

简言之,对于一个特定的问题,通常会花较长的时间才能找到一种最恰当的解决方案。

但一旦找到了正确的方法,以后的工作就轻松多了,再也不用经历数小时、数天或者数月的

痛苦挣扎。我们的努力工作会带来最大的回报(甚至无可估量)。而且由于自己倾注了大量

心血,最终获得一个出色的设计方案,成功的快感也是令人心动的。坚持抵制草草完工的诱

惑——那样做往往得不偿失。

技巧 52:应将方法设计成简要的、功能性单元,用它描述和

实现一个不连续的类接口部分。

理想情况下,方法应简明扼要。若长度很大,可考虑通过某种方式将其分割成较短的几

个方法。这样做也便于类内代码的重复使用(有些时候,方法必须非常大,但它们仍应只做

同样的一件事情)。

技巧 53:java 里面能不能重载操作符?

Java 开发经验及技巧集

36 / 38

不能。String 的+号是唯一一个内置的重载操作符。你可以通过定义接口和方法来实现类

似功能。

技巧 54:在子类的构造方法中如何调用父类的构造方法?

在子类构造方法的第一行调用 super(...)即可。

技巧 55:尽量使用 interfaces,不要使用 abstract 类。

若已知某样东西准备成为一个基础类,那么第一个选择应是将其变成一个 interface(接

口)。只有在不得不使用方法定义或者成员变量的时候,才需要将其变成一个 abstract(抽象)

类。接口主要描述了客户希望做什么事情,而一个类则致力于(或允许)具体的实施细节。

技巧 56:条件断点

如果你不知道如何添加断点,只需点击左边面板(行号前面)断点即被创建。在调试界

面中,“断点”视图会把所有被创建的断点列出来。我们可以给它加一个布尔条件,也就是

说,该断点会被激活并且如果布尔条件为真,就会执行该断点,否则将会跳过往下执行。

技巧 57:异常断点

在断点视图中,有一个 J!标记按钮!我们可以使用该按钮来添加一个 Java 异常断点。例

如,我们想让程序在遇到空指针异常(NullPointerException)时,仍然能继续调试,那么我

们可以使用该按钮来添加一个异常断点!

技巧 58:监视点

这是一个非常好的功能,当选定的属性访问或修改程序时,程序会停止执行并允许进行

调试。在 Outline 视图中选择一个类变量并从上下文菜单中选择切换监视点,属性监视点将

会被创建,在断点(Breakpoints)视图中会把所有监视点用列表的形式显示出来。

技巧 59:评估/检查

Java 开发经验及技巧集

37

按 Ctrl+Shift+D 或者 Ctrl+Shift+I 来显示选定变量或者表达式的值。我们也可以给一个变

量或表达式添加永久观察点,当程序在调试时,这些观察点就会在表达式视图(Expression 

view)中显示出来。

技巧 60:修改变量值

在调试过程中,我们可以修改变量值。先选好一个变量然后进入变量视图(Variables 

view),根据变量类型在其对应的 Value 列里输入值即可。

技巧 61:在 Main 函数里面停止执行

在运行/调试设置中,编辑配置对话框中有“Main”这个选项卡,我们可以勾选“Stop in 

main”这个复选框。如果选中,那么在调试一个基于 main 方法的 Java 程序时,程序会在 main

方法第一行位置便停止执行。

技巧 62:环境变量

并不是在系统属性中添加环境变量,我们可以在编辑配置对话框中很方便地进行添加。

技巧 63:Drop to Frame

这也是我最喜欢的一个功能。调试期间,可以重新跳到调用堆栈框架的开始处执行,并

且变量值也会回到最初。根据回档调整堆栈的深度,这个功能的主要用途是所有变量状态可

以快速回到方法开始执行时候的样子,然后你可以重新进行一遍一遍执行,这样就可以在你

关注的地方进行多次调试,但是在执行过程中也会产生一些副作用,比如插入到数据库里面

的数据是无法删除的!

技巧 64:分布过滤

当我们进入(F5)方法的时候,我们还可以访问其外部库(比如 java.*),我们可能不

需要这个库,就可以在 Perference 选项卡页面添加一个过滤器来排除这个包。

Java 开发经验及技巧集

38 / 38

技巧 65:进入、跳出和返回

我把这个放在最后一点,在调试过程中,这些是必须要了解(最好掌握)的东西:

F5——进入:移动到下一个步骤,如果当前行有一个方法调用,该控件将会跳转到被调

用方法的第一行执行。

F6——跳出:移动到下一行。如果在当前行有方法调用,那么会直接移动到下一行执行。

不会进入被调用方法体里面。

F7——返回:从当前方法中跳出,继续往下执行。

F8——移动到下一个断点处执行。

鸣谢及备注:部分内容收集与网络,仅供交流学习,其版权归原作者所有


  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【技术文章|】Java 开发环境配置(2021-01-16 19:16)
【技术文章|java技术】Java简介 主要特性 发展历史(2021-01-16 19:10)
【技术文章|java技术】eclipse项目如何导入myeclipse项目中 如何把eclipse项目导入myeclipse(2020-12-20 22:06)
【技术文章|】如何将MyEclipse项目导入eclipse (2020-12-20 22:00)
【技术文章|java技术】如何用eclipse运行myeclipse做的项目(2020-12-20 21:55)
【技术文章|java技术】怎么分辨java框架(2020-12-17 16:27)
【技术文章|】Editplus批量转换java utf-8 bom源码文件为utf-8编码文件(2020-12-13 20:21)
【技术文章|java技术】java utf-8 bom源码文件为utf-8编码文件(2020-12-13 20:17)
【服务项目|】java程序代写计算机毕业设计代做(2020-10-20 16:54)
【热点|国内】第七次全国人口普查“查人”又“查房”,还可自主填报(2020-10-20 14:49)
底部广告
网站首页 | 关于我们 | 广告合作 | 联系我们 | 隐私条款 | 免责声明 | 站点地图
CopyRight 2014-2024 学帮网 |粤ICP备18000800号-4