史上最全的 Java 新手問題匯總


Java是目前最流行的編程語言之一——它可以用來編寫Windows程序或者是Web應用,移動應用,網絡程序,消費電子產品,機頂盒設備,它無處不在。

有超過30億的設備是運行在Java之上的。根據Oracle的統計數據,光是使用中的Java Card就有有50億。

超過900萬程序員選擇使用Java進行開發,它是最受開發人員歡迎的語言,同時也是最流行的開發平台。

本文為那些准Java程序員們准備了一系列廣為流傳的Java最佳編程實踐

優先返回空集合而非null

如果程序要返回一個不包含任何值的集合,確保返回的是空集合而不是null。這能節省大量的”if else”檢查。

public class getLocationName {
    return (null==cityName ? "": cityName);
}

謹慎操作字符串

如果兩個字符串在for循環中使用+操作符進行拼接,那么每次循環都會產生一個新的字符串對象。這不僅浪費內存空間同時還會影響性能。類似的,如果初始化字符串對象,盡量不要使用構造方法,而應該直接初始化。比方說:

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

//Faster Instantiation
String good = "Yet another string object"

避免無用對象

創建對象是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;
    }
}

數組與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]; 
    }
}
  • 數組是定長的,而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.");
            }
        }
    }
}

從代碼來看,貌似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.");
                    }

            }
    }
}

判斷奇數

看下這幾行代碼,看看它們是否能用來准確地判斷一個數是奇數?

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

看似是對的,但是每執行四便會有一個錯誤的結果(用數據說話)。考慮到負奇數的情況,它除以2的結果就不會是1。因此,返回值是false,而這樣是不對的。

代碼可以修改成這樣:

public boolean oddOrNot(int num) {
    return (num & 1) != 0;
}

這么寫不光是負奇數的問題解決了,並且還是經過充分優化過的。因為算術運算和邏輯運行要比乘除運算更高效,計算的結果也會更快。

單引號與雙引號的區別

public class Haha {
    public static void main(String args[]) {
    System.out.print("H" + "a");
    System.out.print('H' + 'a');
    }
}

看起來這段代碼會返回”Haha”,但實際返回的是Ha169。原因就是用了雙引號的時候,字符會被當作字符串處理,而如果是單引號的話,字符值會通過一個叫做基礎類型拓寬的操作來轉換成整型值。然后再將值相加得到169。

一些防止內存泄露的小技巧

內存泄露會導致軟件的性能降級。由於Java是自動管理內存的,因此開發人員並沒有太多辦法介入。不過還是有一些方法能夠用來防止內存泄露的。

  • 查詢完數據后立即釋放數據庫連接
  • 盡可能使用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...");
            }
         }
      }
   }
   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...

替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()。問題就在於,什么情況下該用哪個。從本質上來講,他們的作用都是一樣的,但有以下幾點不同:

  1. System.currentTimeMillis()的精度在千分之一秒到千分之15秒之間(取決於系統)而System.nanoTime()則能到納秒級。
  2. System.currentTimeMillis讀操作耗時在數個CPU時鍾左右。而System.nanoTime()則需要上百個。
  3. System.currentTimeMillis對應的是絕對時間(1970年1 月1日所經歷的毫秒數),而System.nanoTime()則不與任何時間點相關。
  4. Float還是double
數據類型 所用字節 有效位數
float 4 7
double 8 15

在對精度要求高的場景下,double類型相對float要更流行一些,理由如下:

大多數處理器在處理float和double上所需的時間都是差不多的。而計算時間一樣的前提下,double類型卻能提供更高的精度。

冪運算

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倍左右。

如何處理空指針異常

空指針異常是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");
    }

JSON編碼

JSON是數據存儲及傳輸的一種協議。與XML相比,它更易於使用。由於它非常輕量級以及自身的一些特性,現在JSON在網絡上已經是越來越流行了。常見的數據結構都可以編碼成JSON然后在各個網頁間自由地傳輸。不過在開始編碼前,你得先安裝一個JSON解析器。在下面的例子中,我們將使用json.simple庫來完成這項工作 (https://code.google.com/p/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"}

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;
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();
            // 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" },
            { "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

簡單字符串查找

Java提供了一個庫函數叫做indexOf()。這個方法可以用在String對象上,它返回的是要查找的字符串所在的位置序號。如果查找不到則會返回-1。

列出目錄下的文件

你可以用下面的代碼來列出目錄下的文件。這個程序會遍歷某個目錄下的所有子目錄及文件,並存儲到一個數組里,然后通過遍歷數組來列出所有文件。

import java.io.*;

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

一個簡單的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();
            }
            if(out != null) {
                out.close();
            }
        }
    }
}

在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+"/n";
            }
            System.out.println(myoutput);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

使用正則

正則表達式的結構摘錄如下(來源: Oracle官網)

字符

x 字符x
/ 反斜杠
/0n 8進制值為0n的字符(0<=n<=7)
/0nn  
/0mnn 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)
/t 制表符(‘/u0009′)
/n 換行符(‘/u000A’)
/r 回車(‘/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 空格符[ /t/n/x0B/f/r]
/S 非空格符[^/s]
/w 字母[a-zA-Z_0-9]
/W 非字母[^/w]

邊界匹配

^ 行首
$ 行末
/b 單詞邊界
/A 輸入的起始位置
/G 前一個匹配的末尾
/Z 輸入的結束位置,僅用於最后的結束符
/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 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播放音頻

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

   public static void main(String[] args) {
      new playSoundDemo();
   }
}

導出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");
        table.addCell("Female");

        document.add(table);

        document.close();
      }
    }

郵件發送

在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提供了一個System的靜態方法來支持這一功能:

currentTimeMillis():返回當前時間自新紀元時間以來的毫秒值,long類型。

long startTime = System.currentTimeMillis();
long estimatedTime = System.currentTimeMillis() - startTime;

nanoTime():返回系統計時器當前的精確時間,納秒值,這也是long類型。nanoTime()主要是用於計算相對時間而非絕對時間。

long startTime = System.nanoTime();
long estimatedTime = System.nanoTime() - startTime;

圖片縮放

圖片縮放可以通過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"));
  }
}

捕獲鼠標動作

實現了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) 
    {}
}

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

根據Java的接口規范:

FileOutputStream是用於寫入原始字節流比如圖片流數據。如果是要寫入字符流,則應該考慮使用FileWriter。

這樣就很清楚了,寫圖片應該使用FileOutputStream而寫文本則應該選擇FileWriter。

附加建議

集合的使用

Java提供了許多集合類——比如,Vector,Stack,Hashtable等。所以鼓勵開發人員盡可能地使用這些集合類有如下原因:

  • 使用集合使得代碼的可重用度更高。
  • 集合類使得代碼的結構更良好,更易於理解與維護。
  • 最重要的是這些集合類都經過充分的測試,代碼質量很高。

1-50-500規則

在大型軟件系統中,代碼的可維護性是件很有挑戰的工作。新加入的開發人員經常會抱怨這些情況:單片代碼(Monolithic Code),意大利面式代碼(spaghetti code, 常用於描述捆綁在一起並且低內聚的類和方法)。保持代碼的整潔與可維護有一條很簡單的規則:

  • 10:包內的類不超過10個
  • 50:方法的代碼行數不超過50
  • 500:類的代碼行數不超過500
  1. SOLID設計准則
  2. SOLID是Robert Martin提出的一套設計准則的簡稱。根據他的准則:

一個類應當有僅只有一個任務/職責。執行多個任務的類會讓人覺得困惑。

單一職責原則
開閉原則 開發人員應當優先考慮擴展現有的軟件功能,而不是是修改它。
里氏替換原則 子類必須能夠替換掉他們的父類型
接口隔離原則 和單一職責原則類似,但它特指的是接口層。每個接口都應當只負責一項任務。
依賴反轉原則 依賴抽象而不是具體實現。也就是說每個模塊都應當通過一個抽象層與其它模塊進行解耦。

設計模式的使用

設計模式能幫助開發人員更好地在軟件中應用軟件的設計准則。它還為開發人員提供了跨語言的通用平台。設計模式中的標准術語能讓開發人員更容易進行溝通。

關於文檔

不要上來就開始寫代碼。制定計划,准備,編寫文檔,檢查然后再去實現。首先,先把需求記下來。然后去准備設計文檔。合理地去假設舉證。互相review方案然后進行確認。

使用equals而非==

==是用來比較對象引用的,它會檢查兩個操作數指向的是不是同一個對象(不是相同的對象,而是同一個對象)。而”equals”則比較的是兩個字符串是不是相同(假設是字符串對象)。

避免使用浮點數

只有當確實有必要的時候才使用浮點數。比方說,使用浮點數來表示盧比或者派薩就很容易產生問題——這種情況應當使用BigDecimal。而浮點數更多地是用於測量。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM