雜七雜八

      在〈雜七雜八〉中尚無留言

這是原廠電子書裏雜七雜八的東西

The switch Statement

switch 後面僅可以使用 byte, short, char, int四個原生資料, 也可以使用Enum型態, String類別及原生資料封裝類別 Character, Byte, Short, Integer.

public class SwitchDemo {
    public static void main(String[] args) {

        int month = 8;
        String monthString;
        switch (month) {
            case 1:  monthString = "January";
                     break;
            case 2:  monthString = "February";
                     break;
            case 3:  monthString = "March";
                     break;
            case 4:  monthString = "April";
                     break;
            case 5:  monthString = "May";
                     break;
            case 6:  monthString = "June";
                     break;
            case 7:  monthString = "July";
                     break;
            case 8:  monthString = "August";
                     break;
            case 9:  monthString = "September";
                     break;
            case 10: monthString = "October";
                     break;
            case 11: monthString = "November";
                     break;
            case 12: monthString = "December";
                     break;
            default: monthString = "Invalid month";
                     break;
        }
        System.out.println(monthString);
    }
}

以上會列出 ” August

如果用if…else if…則為如下程式碼

int month = 8;
if (month == 1) {
    System.out.println("January");
} else if (month == 2) {
    System.out.println("February");
}
...  // and so on

switch區塊內, 如果沒有break, 則會一直往下穿透

public class SwitchDemoFallThrough {

    public static void main(String[] args) {
        java.util.ArrayList<String> futureMonths =
            new java.util.ArrayList<String>();

        int month = 8;

        switch (month) {
            case 1:  futureMonths.add("January");
            case 2:  futureMonths.add("February");
            case 3:  futureMonths.add("March");
            case 4:  futureMonths.add("April");
            case 5:  futureMonths.add("May");
            case 6:  futureMonths.add("June");
            case 7:  futureMonths.add("July");
            case 8:  futureMonths.add("August");
            case 9:  futureMonths.add("September");
            case 10: futureMonths.add("October");
            case 11: futureMonths.add("November");
            case 12: futureMonths.add("December");
                     break;
            default: break;
        }

        if (futureMonths.isEmpty()) {
            System.out.println("Invalid month number");
        } else {
            for (String monthName : futureMonths) {
               System.out.println(monthName);
            }
        }
    }
}

輸出結果為 :

August
September
October
November
December

最後的 break並不需要. 如果所有的case都不符合, 則會進入 default中

class SwitchDemo2 {
    public static void main(String[] args) {

        int month = 2;
        int year = 2000;
        int numDays = 0;

        switch (month) {
            case 1: case 3: case 5:
            case 7: case 8: case 10:
            case 12:
                numDays = 31;
                break;
            case 4: case 6:
            case 9: case 11:
                numDays = 30;
                break;
            case 2:
                if (((year % 4 == 0) && 
                     !(year % 100 == 0))
                     || (year % 400 == 0))
                    numDays = 29;
                else
                    numDays = 28;
                break;
            default:
                System.out.println("Invalid month.");
                break;
        }
        System.out.println("Number of Days = "
                           + numDays);
    }
}

輸出結果為 :

Number of Days = 29

Using Strings in switch Statements

Java SE 7 以後, 可以在switch 後面使用 String 物件

public class StringSwitchDemo {
    public static int getMonthNumber(String month) {
        int monthNumber = 0;
        if (month == null) {
            return monthNumber;
        }
        switch (month.toLowerCase()) {
            case "january":
                monthNumber = 1;
                break;
            case "february":
                monthNumber = 2;
                break;
            case "march":
                monthNumber = 3;
                break;
            case "april":
                monthNumber = 4;
                break;
            case "may":
                monthNumber = 5;
                break;
            case "june":
                monthNumber = 6;
                break;
            case "july":
                monthNumber = 7;
                break;
            case "august":
                monthNumber = 8;
                break;
            case "september":
                monthNumber = 9;
                break;
            case "october":
                monthNumber = 10;
                break;
            case "november":
                monthNumber = 11;
                break;
            case "december":
                monthNumber = 12;
                break;
            default: 
                monthNumber = 0;
                break;
        }
        return monthNumber;
    }
    public static void main(String[] args) {
        String month = "August";
        int returnedMonthNumber =
            StringSwitchDemo.getMonthNumber(month);
        if (returnedMonthNumber == 0) {
            System.out.println("Invalid month");
        } else {
            System.out.println(returnedMonthNumber);
        }
    }
}

輸出結果為 : 8

字串比對是使用 String.equals的方法

注意 : 為確保不發生NullPointerException, 請使用if 判斷是否字串為 null

Primitive Data Types

Java變數在使用前都需事先宣告

int gear = 1;

Java使用八種原生資料

  • byte:  1byte, -128~127
  • short:  2 byte, -32768~32767
  • int: 4 byte,  -231 ~ 231-1.  Java SE 8 以後, 可以使用Integer類別當作無號數 int ,  讓範圍在 0 到 232-1 之間. 並新增了幾個 Static 方法, 如 compareUnsigned, divideUnsigned.
  • long: 8 bytes,  -263 ~ 263-1. Java SE 8 以後也可以使用無號數, 範圍為 0 ~ 264-1. Long類別也新增了 compareUnsigned, divideUnsigned 等static 方法.
  • float: 4 bytes, 精準到小數第七位
  • double: 8bytes, 為小數的預設型態
  • boolean: 只有true 及false二種可能
  • char: 2byte 無號數,  '\u0000' ~'\uffff'

除了這八種之外, Java提供了特別的String類別, 使用 “”將字串包含住, 字串一但被建立, 就不能更改其值

Default Values

Fields 變數宣告時即會自動初始化, 預設值如下

DATA TYPE DEFAULT VALUE (FOR FIELDS)
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char ‘\u0000’
String (or any object) null
boolean false

區域變數則不會自動初始化. 存取未初始化的區域變數時, 會發生compile error.

Literals

八種原生資料直接使用即可, 不需用new 來創建

boolean result = true;
char capitalC = 'C';
byte b = 100;
short s = 10000;
int i = 100000;

Integer Literals

數字預設為int, 如加 L, l, 則為long, 建議用大寫的L

數字可以使用如下的進位系統

  • Decimal: 十進位
  • Hexadecimal: 十六進位
  • Binary: 二進位, Java SE 7以後, 可以使用二進位指定數值

數字開頭為0x表示十六進位, 0b表示二進位 :

// The number 26, in decimal
int decVal = 26;
//  The number 26, in hexadecimal
int hexVal = 0x1a;
// The number 26, in binary
int binVal = 0b11010;

Floating-Point Literals

浮點數預設為double, 若結尾為F or f, 表示為float, D or d為double

指定浮點數可使用科學記號  E or e

double d1 = 123.4;
// same value as d1, but in scientific notation
double d2 = 1.234e2;
float f1  = 123.4f;

Character and String Literals

char及String使用Unicode(UTF-16)字元, 編輯器如果支援特殊字元, 則可以直接使用, 不然也可以用 “Unicode escape” 如'\u0108'(capital C with circumflex==>Ĉ ), or "S\u00ED Se\u00F1or" (Sí Señor in Spanish). 字元使用單引號包含, 字串使用雙引號包含

跳脫字元有 \b (backspace), \t (tab), \n (line feed), \f (form feed), \r (carriage return), \" (double quote), \' (single quote), and \\(backslash).

參考型態可以使用null, 但原生資料型態不可以使用

.class為特殊的字元, 如 String.class 表示為類別或物件他自己本身的型態

Using Underscore Characters in Numeric Literals

Java SE 7 以後, 數字中可以使用底線分隔, 以利可讀性

long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityNumber = 999_99_9999L;
float pi =  3.14_15F;
long hexBytes = 0xFF_EC_DE_5E;
long hexWords = 0xCAFE_BABE;
long maxLong = 0x7fff_ffff_ffff_ffffL;
byte nybbles = 0b0010_0101;
long bytes = 0b11010010_01101001_10010100_10010010;

不可放置底線的地方

  • 數字的啟始或結尾
  • 緊憐小數點的位置
  • F 或 L 之前
// Invalid: cannot put underscores
// adjacent to a decimal point
float pi1 = 3_.1415F;
// Invalid: cannot put underscores 
// adjacent to a decimal point
float pi2 = 3._1415F;
// Invalid: cannot put underscores 
// prior to an L suffix
long socialSecurityNumber1 = 999_99_9999_L;

// OK (decimal literal)
int x1 = 5_2;
// Invalid: cannot put underscores
// At the end of a literal
int x2 = 52_;
// OK (decimal literal)
int x3 = 5_______2;

// Invalid: cannot put underscores
// in the 0x radix prefix
int x4 = 0_x52;
// Invalid: cannot put underscores
// at the beginning of a number
int x5 = 0x_52;
// OK (hexadecimal literal)
int x6 = 0x5_2; 
// Invalid: cannot put underscores
// at the end of a number
int x7 = 0x52_;

The try-with-resources Statement

try-with-resources statement 是在try後面宣告一個或多個資源. 此資源是指物件於程式結束後需要關閉. 凡實作AutoCloseable 及 Closeable的物件, 都可以當作資源

底下的程式實作BufferedReader讀取檔案的一行. BufferedReader是一個資源, 必需於程式結束後關檔

static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

此例的資源宣告於try之後, 不論是否正常結束, 都會自動關閉

Java SE 7 之前, 使用 finally區塊進行關檔, 如下

static String readFirstLineFromFileWithFinallyBlock(String path)
                                                     throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }
}

try-with-resources statement可以宣告多個資源. 底下的例子開啟 zip 檔案並寫入文字檔

public static void writeToFileZipFileContents(String zipFileName,
                                           String outputFileName)
                                           throws java.io.IOException {

    java.nio.charset.Charset charset =
         java.nio.charset.StandardCharsets.US_ASCII;
    java.nio.file.Path outputFilePath =
         java.nio.file.Paths.get(outputFileName);

    // Open zip file and create output file with 
    // try-with-resources statement

    try (
        java.util.zip.ZipFile zf =
             new java.util.zip.ZipFile(zipFileName);
        java.io.BufferedWriter writer =
            java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
    ) {
        // Enumerate each entry
        for (java.util.Enumeration entries =
                                zf.entries(); entries.hasMoreElements();) {
            // Get the entry name and write it to the output file
            String newLine = System.getProperty("line.separator");//取得系統的換行指令
            String zipEntryName =
                 ((java.util.zip.ZipEntry)entries.nextElement()).getName() +
                 newLine;
            writer.write(zipEntryName, 0, zipEntryName.length());
        }
    }
}

上述的zipFile及BufferedWriter會自動關閉

底下的例子自動關閉 java.sql.Statement 物件

public static void viewTable(Connection con) throws SQLException {

    String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES";

    try (Statement stmt = con.createStatement()) {
        ResultSet rs = stmt.executeQuery(query);

        while (rs.next()) {
            String coffeeName = rs.getString("COF_NAME");
            int supplierID = rs.getInt("SUP_ID");
            float price = rs.getFloat("PRICE");
            int sales = rs.getInt("SALES");
            int total = rs.getInt("TOTAL");

            System.out.println(coffeeName + ", " + supplierID + ", " + 
                               price + ", " + sales + ", " + total);
        }
    } catch (SQLException e) {
        JDBCTutorialUtilities.printSQLException(e);
    }
}

注意 : 任何的catch 及finally區塊, 是在資源關閉後才會執行

例外處理壓制 Suppressed Exceptions

例外可由try區塊丟出, 也可由resource丟出. 若try丟出一個例外, 且資源也丟出一到多個例外, 則由資源丟出的例外會被壓制. 可以在try丟出例外的區塊中, 使用Throwable.etSuppressed方法取得被壓制的例外.

Classes That Implement the AutoCloseable or Closeable Interface

Closeable介面繼承了AutoCloseable介面. 當AutoCloseable的close方法丟出Exception時, Closeable的close方法就會丟出 IOException.

AutoCloseable的子類別可以覆寫 close方法, 丟出特定的例外, 比如IOException, 或則是不丟出例外

The catch Blocks

try之後可以有多個catch, 在try區塊跟catch區塊之間, 不能有任何程式碼

try {

} catch (ExceptionType name) {

} catch (ExceptionType name) {

}

catch區塊的例外參數, 都必需是繼承Throwable類別

底下有二個 exception handlers

try {

} catch (IndexOutOfBoundsException e) {
    System.err.println("IndexOutOfBoundsException: " + e.getMessage());
} catch (IOException e) {
    System.err.println("Caught IOException: " + e.getMessage());
}

Exception handlers可作的事情不是只有印出錯誤而以, 還可以修正錯誤, 提示使用者作出決定等等.

Catching More Than One Type of Exception with One Exception Handler

Java SE 7 之後, 在單一的catch區塊中, 可以抓取多個例外, 每個例外之間使用 | 隔開

catch (IOException|SQLException ex) {
    logger.log(ex);
    throw ex;
}

注意: catch區塊若是有多個例外, 則參數變數是final的. 如上的ex變數, 是final的, 所以不可變更其值.

The Diamond <>

Java SE 7之後可以在建構子或類別後放置空的 <> , Java將<>稱為 diamond.

Box<Integer> integerBox = new Box<>();

Multiple Type Parameters

類別通常有多個資料型態. 如下程式, OrderdPair實作了Pair介面

public interface Pair<K, V> {
    public K getKey();
    public V getValue();
}
public class OrderedPair<K, V> implements Pair<K, V> {
    private K key;
    private V value;
    public OrderedPair(K key, V value) {
	this.key = key;
	this.value = value;
    }
    public K getKey()	{ return key; }
    public V getValue() { return value; }
}

底下的程式創建了二個OderedPair的物件

Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String>  p2 = new OrderedPair<String, String>("hello", "world");

上面第一個例子的 8, 會被自動封箱成Integer, 所以不會出錯

Java SE 7以後, new 後面的泛型資料型態就不用再寫了, 編譯器會從左邊猜測出來

OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8);
OrderedPair<String, String>  p2 = new OrderedPair<>("hello", "world");

Rethrowing Exceptions with More Inclusive Type Checking

Java SE 7允許在方法宣告多個丟出例外

先看下面的例子

  static class FirstException extends Exception { }
  static class SecondException extends Exception { }

  public void rethrowException(String exceptionName) throws Exception {
    try {
      if (exceptionName.equals("First")) {
        throw new FirstException();
      } else {
        throw new SecondException();
      }
    } catch (Exception e) {
      throw e;
    }
  }

此例中try區塊丟出FirstException 或SecondException. 假如要在方法中宣告丟出這二個例外, 在Java SE 7之前是不允許的. 因為catch 區塊的 e 是 Exception型態, 而在catch區塊重新丟出時, 方法中只能指定丟出一樣是Exception的型態

Java SE 7之後, 可以在方法中多重指定這二個型態.  當try不論是丟出FirstException還是SecondException, 都會被catch區塊抓到再丟出. 而編譯器可以知道這個 e 是來自FirstException 還是SecondException.

  public void rethrowException(String exceptionName)
  throws FirstException, SecondException {
    try {
      // ...
    }
    catch (Exception e) {
      throw e;
    }
  }

但如果在catch區塊中改變了 e 的值, 此分析功能就破功了. 不過, 即然改變了e的值, 那在方法中就要再宣告此 e 的型態.

若catch區塊宣告接收多個例外, 然後再丟出, 需符合如下條件

  • try區塊允許丟出.
  • 沒有其他的catch區塊重複處理
  • 是catch 區塊參數的子類別或父類別.

丟出的例外可以是方法宣告丟出例外的子類別, 因為子類別是父類別的型態

    public static void main(String args) throws Exception{
        try{}
        catch(FirstException e){
            throw e; //e 也是Exception的型態
        }
    }

Java SE 7之前, 在catch中是不能重丟參數 e 的父類別, 7以後就可以了

catch(FirstException e){
        throw new Exception();//丟出FirstException的父類別, ok的
    }

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *