# **Java Note**
- @BYHUANGJ
- Resource:
- [openhome.cc](https://openhome.cc/Gossip/Java/index.html)
- [package/import](itread01.com/content/1542859869.html)
- JetBrains
## 概念 (Concept)
- Java has a mechanism that `cleans memory automatically.`
- Java follows the "write once, run anywhere" (or WORA) principle. The same program can be run on multiple platforms without any changes.
- compile java program: javac
- 執行`javac xxx.java`後, 會將`xxx.java`編譯成`xxx.class`,其中.class檔是可以運行在JVM的bytecode格式
- execute java program:`java xxx`
## 資料型態 (Data Type)
![](https://i0.wp.com/www.softwaretestingmaterial.com/wp-content/uploads/2018/03/Data-Types.png?resize=1297%2C748&ssl=1&zoom=2)
### Primitive
- `mutable`
- 包含有: boolean, byte, char, short, int, long, float, double
- boolean: `true`, `false` (特別注意是==全小寫==)
- ==1不等於true, 0不等於false==
- NOT: !
- AND: && (跟false做AND都會是false)
- OR: || (跟true做OR都會是true)
- XOR: ^ (兩值不同則true)
- NOR: !(a||b) 或 (!a&&!b)
- char: 'x' ==單引號==, 且最多只能放一個字元)
- int: 1000000 (可以寫成1_000_000)
- long: 1000000L (可以寫成1_000_000L)
- float: 2.71828f
- double: 5e-3, 0.01e2
- ==double d1 = 5 / 4 = 1==
- ==double d1 = 5.0 / 4 = 1.25==
- var: Java 10 support (e.g., var s='x', var i=10)
| name | bits | range |
| ------ | ---- | ------------------------------------------------------------ |
| char | 16 | 0 ~ 65536 |
| short | 16 | -2^15^ ~ (2^15^-1) |
| int | 32 | -2^31^ ~ (2^31^-) |
| long | 64 | -2^63^ ~ (2^63^-1) |
| float | 32 | 1.40239846 x 10^-45^ ~ 3.40282347 x 10^38^ |
| double | 64 | 4.9406564584124654 x 10^-324^ ~ 1.7976931348623157 x 10^308^ |
| byte | 8 | -128 ~ 127 |
### Non-Primitive
- `immutable` (參見 [wrapper_class_example](https://www.geeksforgeeks.org/primitive-wrapper-classes-are-immutable-in-java/))
- 包含有: wrapper class, string, array, ...
- string: "x" (用==雙引號==, 放一個字元仍視為字串)
- wrapper class: Boolean, Byte, Character, Double, Float, Integer, Long, Short
![](https://media.geeksforgeeks.org/wp-content/cdn-uploads/20200806191733/Wrapper-Class-in-Java.png)
### Final
- `final` [[Ref]](http://blog.kenyang.net/2011/03/09/java-staticfinal)
- 用來宣告在類別時, 該類別就不能被繼承
- 用來宣告在函數時, 繼承他的子類別不能覆寫
- 用來宣告在變數時, 其值不能被修改(即常數)
### Static
- `static`
- 只佔一組記憶體位置, 所有new出來的都共享同一個值 (只有唯一值)
- 可以直接透過類別存取使用
```java=
class test{
static int value = 0;
...
}
//直接透過類別access value
System.out.print(test.value);
//new object後去access value
test t = new test();
System.out.print(t.value);
```
### Initialization Block
- static initialization block
- 用來初始化static fields與constants
```java=
static{
...
}
```
- 例如
```java=
class example{
private static String str;
private static final String strConst;
static{
str = "initialization";
strConst = "a string constant";
}
}
```
- 當static field (第二與三行)與static block都做assignment時, 會先做static field再做static block裡的assignment
- instance initialization block
- 在所有的constructor之前執行, 但在superclass的constructor之後執行
- 語法
```java=
{
...
}
```
- 例如
```java=
class example{
private int value;
{
value = 10;
}
}
```
- 當然也可以寫成`private int value = 10;`, 但當你有很複雜的assignment時, 且又想在constructor之前做, 就可以善加利用它
- 有趣的是, 下方的output順序為block A -> block B -> constructor
```java=
class A {
{
System.out.println("block A");
}
public A() {
System.out.println("constructor");
}
{
System.out.println("block B");
}
}
```
- 再看一個例子, 下方的output出來的field (即InitBlock.field) 其值為40
```java=
class InitBlock {
static int field = 20;
static {
field = 30;
}
static {
field = 40;
}
}
```
### Boxing
- primitve → object
```java=
//boxing
int primitiveInt = 10;
Integer boxInt = Integer.valueOf(primitiveInt);
//auto-boxing
Integer boxInt = primitiveInt;
```
### Unboxing
- object → primitive
```java=
//unoxing
int unboxInt = boxInt.intValue();
//auto-unboxing
int unboxInt = boxInt;
```
### 補充資料
- [變數放stack或是heap?](https://stackoverflow.com/questions/3646632/do-java-primitives-go-on-the-stack-or-the-heap)
- [RefCopy](https://hyperskill.org/learn/step/5035)
- [wrapper class是reference type?](https://stackoverflow.com/questions/27283104/arprimitive-wrapper-classes-in-java-treated-as-reference-types)
## Enum (Enumeration)
- 例如
```java=
enum Season {
SPRING, SUMMER, AUTUMN, WINTER // four instances
}
```
- 宣告(有兩種):
- `Season s = Season.AUTUMN`
- `Season s = Season.valueOf("AUTUMN")`
- 看enum value的名字: `s.name()`
- load所有元素: `Season [] sTable = Season.values()`
- 在enum中相對應位置(數值化): `int idx = s.ordinal()` 以AUTUMN為例, idx=2
- 可以搭配用在switch-case
```java=
switch(s){
case SPRING:
...
break;
case SUMMER:
...
break;
case AUTUMN:
...
break;
case WINTER:
...
break;
default:
...
}
```
```java=
public enum Dir{
top("T_Y"), bottom("B_Y"), left("L_X"), right("R_X");
private String dir;
Dir(String){
this.dir = dir;
}
private String getValue(){
return dir;
}
@Override
public String toString(){
return this.getValue();
}
}
String intputDir = "top";
String transformedDir = Dir.valueOf(inputDir).toString(); //"T_Y"
```
## Object
==對物件(包含常見使用到的array)之操作要小心==
- 比較兩物件內實際的值, 不要用`==` 去判斷, 應該要用==obj1.equals(obj2)==, 因為使用在物件上的意思是去判斷兩個物件是否歸屬同一個物件
- ==只有當比較對象為primitive如int, 才能使用`==`做值的判斷==
```java=
//ref:https://openhome.cc/Gossip/Java/Reference.html
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.1");
BigDecimal c = a;
System.out.println(a == b); //false
System.out.println(a == c); //true
System.out.println(a.equals(b)); //true
```
- 再看一個例子, 因為array是object, 而在程式碼第二行將Arr1參考的物件也給Arr2參考, 所以Arr2更動的其實就是Arr1參考的物件
- 其中第二行的assign叫做==參考之複製(shallow copy)==, 而非==物件之複製(deep copy)==, 在Java中, 對於所有非non-primitive資料型態(如object, arraylist), 的`=`符號之assign方法都是參考
- 而在C++的概念中, 第二行程式碼代表Arr1這個物件的內容複製一份給Arr2
```java=
int[] Arr1 = {88, 81, 74, 68, 78, 76, 77, 85, 95, 93};
int[] Arr2 = Arr1;
Arr2[0] = 99;
System.out.println(Arr1[0]); //99
```
- 再多看幾個例子
```java=
//ref: https://openhome.cc/Gossip/Java/InsideString.html
char[] name = {'J', 'u', 's', 't', 'i', 'n'};
String name1 = new String(name);
String name2 = new String(name);
System.out.println(name1 == name2); //false
```
```java=
String name1 = "Justin";
String name2 = "Justin";
System.out.println(name1 == name2); //true
```
- 陣列複製: `System.arraycopy(sourceArr, startIndex, targetArr, startIndex, copyLen)`
- `System.arraycopy(Arr1, 0, Arr2, 0, Arr1.length)`
- 陣列複製(JDK6以上): `Array.copyOf(sourceArr, sourceArrLen)`
- import java.util.Arrays;
int[] Arr2 = Arrays.copyOf(Arr1, Arr1.length)
## Array
### 固定長度
- 語法: `dataType [] name = new dataType[x];`
- 指定: `name[position]=xxx;`
- 元素個數: `name.length;`
- 陣列視覺化(包含陣列的`[]`符號):
```java=
import java.util.Arrays;
byte[] name = {4, 8, 0, 1, 2};
String VisArr = Arrays.toString(name);
(output) >>> [4, 8, 0, 1, 2]
```
- 排序:
```java=
Arrays.sort(name);
>>> [0, 1, 2, 4, 8]
```
- 比對: `Arrays.equals(arr1, arr2)` 如相對應的值都相同則true, 否則false
- 二維陣列: `dataType[][] name = new int[x][]` 跟c++不一樣的是, ==java允許每列可以存不同數量的元素值==
```java=
int[][] twoDArr = new int[3][];
twoDArr[0] = new int[] { 1, 2, 3, 4 };
twoDArr[1] = new int[] { 5, 7, 3};
twoDArr[2] = new int[] { 8 };
```
- 拜訪元素:
- for-loop:
```java=
for(int r=0; r<twoDArr.length; r++)
for(int c=0; c<twoDArr[r].length; c++)
```
- ==foreach-loop:==
`for(int [][] element : twoDArr)`
### 變動長度(ArrayList)
- 必須要先`import java.util.ArrayList;`
- 可以是`mutable`或是`immutable`
- 可以利用for-loop或是foreach-loop去access資料
- 跟c++ vector很像 (java也有vectorㄚ)
- ==不能使用primitive data type (e.g., int, double, ...)==, 但可以使用object(e.g., String)與wrapper class type (e.g., Double, Integer, Boolean, ...)
- 語法: `ArrayList <dataType> name = new ArrayList <dataType>();`
- 讀取: `name.get(index)`, ==不允許name[index]的用法==
- 元素個數: `name.size()`
- 新增: `name.add(dataValue)`
- 新增(指定位置): `name.add(index, dataValue)`
- 移除(指定位置): `name.remove(index)`
- 移除(指定值): `name.remove(new dataType (dataValue))`
- 例如: name: [1, 2, 3, 4], 當要移除元素值2(即name.get(1)), 則用`name.remove(new Integer(2))`
- 移除(特定條件): `name.removeIf(i -> condition)`
- 例如: name: [1, 2, 3, 4], 我想要移除小於3的所有元素, 則用`name.removeIf(i -> (i<3))`
- 全部清空: `name.clear()`
- 取代: `name.set(index, newValue)`
- 新增另一個arrayList(如叫anotherArrList)中所有元素: `name.addAll(anotherArrList)`
- 第一個出現元素E的位置: `name.indexOf(E)`, 回傳-1代表沒有該元素
- 最後一個出現元素E的位置: `name.lastIndexOf(E)`, 回傳-1代表沒有該元素
- 是否存在元素E: `name.contain(E)` 回傳true/false
- 排序: 必須要先`import java.util.Collection`
- 升序: `Collections.sort(name)`
- 降序: `Collections.sort(name,Collections.reverseOrder())`
- ==複製物件: Collections.copy(destinationList, sourceList);==
## String
[[null V.S. 空字串](https://www.itread01.com/content/1547523036.html)]
### 字串反轉
```java=
String str = "xyz";
StringBuilder sb = new StringBuilder();
sb.append(str);
String strRev = sb.reverse(); //strRev = "zyx"
```
### 擷取字串
- `str.substring(beginIndex, endIndexExclude);`
- str = "qazxcv", str.substring(1, 3) = "az"
### 字串長度
- `str.length()`
- str.length() = 6
### 提取特定位置之字元
- `str.charAt(index)`
- str.charAt(1) = 'a'
### 判斷空字串
- `str.isEmpty()`
- str.isEmpty() = false
### 轉大寫
- `str.toUpperCase()`
- str.toUpperCase() = "QAZXCV"
### 轉小寫
- `str.toLowerCase()`
- str.toLowerCase() = "qazxcv"
### 取代特定字元/字串
- `str.replace(old, new)`
- str.replace("az", "") = "qxcv"
- str.replace('a', 'b') = "qbzxcv"
### 移除字串前後空白(包含\n與\t)
- `str.trim()`
- str = "\t A B C \n \t", str.trim() = "A B C"
### 字串串聯
- `str1+str2` 或 `str.concat(str2)`
- str1 = "A", str2 = " B"
- str1+str2 = "A B"
- str.concat(str2) = "A B"
### 字串比較
- `str.eqauls(str2)`
- str1 = "a", str2 = "a"
- str1.equals(str2) = true
### 字串比較(忽略大小寫)
- `str.equalsIgnoreCase(str2)`
- str1 = "aB", str2 = "Ab"
- str.equalsIgnoreCase(str2) = true
### 前綴比對
- `str.startsWith(str)`
- str = "apple", prefix = "ap", prefix2 = "pp"
- str.startsWith(prefix) = true
- str.startsWith(prefix2) = false
### 前綴比對(指定起始位置)
- `str.startsWith(str, pos)`
- str.startsWith(prefix, 1) = true
### 後綴比對
- `str.endsWith(str)`
- str = "apple", suffix = "ple"
- str.endsWith(suffix) = true
### 字串切割
- `str.split(delimiter)`
- str = "apple-pen"
- String [] s = str.split("-")
- s[0] = "apple", s[1] = "pen"
### 正規表達式比對
- 字串只包含數字: `str.matches("\\d+")` 或 `str.matches("[0-9]+")`
- 字串只包含字母: `str.matches("[a-zA-Z]+")`
```java=
str1 = '114554', str2 = '115?239'
str1.matches("\\d+") => true
str2.matches("\\d+") => false
```
## Int to String
```java=
int n;
String s = String.valueOf(n);
//toString(): the string representatation of the object
String s = Integer.toString(n);
String s = "" + n;
```
## Read Input from Stdin
### 必要
- 透過Scanner讀入
```java=
import java.util.Scanner;
Scanner s = new Scanner(System.in);
```
### 判斷是否有無輸入
- `s.hasNext()`
- 有則回傳true, 否則false
### 讀一行
- `s.nextLine()`
- 讀到\n之前
- 如input: "abc ab"
s.nextLine() = "abc ab"
### 讀一個word
- `s.next()`
- 如input: "abc ab"
s.next() = "abc"
s.next() = "ab"
- 讀一個資料型態 (long, double, ...等8種dataType):
- int:`s.nextInt()`
- long: `s.nextLong()`
- double: `s.nextDouble()`
- ==當遇到小數點(如3.14)時scanner會只讀到整數部分(不確定,還沒測試過), 因此在call scanner時應該要==
`Scanner s = new Scanner(System.in).useLocale(Locale.US)`
- 將字串轉成其他型態 (8種dataType): `dataType.parsedataType(s.nextLine())`
- int: int i = Integer.parseInt(s.nextLine())
- long: parseLong
- double: parseDouble
## Print
### 無換行
- `System.out.print('XXX')`
### 有輸出格式
- `System.out.printf('INT = %d', i)`
- %d: int; %o: 8進位int; %x: 16進位int;
%f: float; %s: string
```java=
String s1 = s.next();
int x = s.nextInt();
// %: formatter
// -: left-justified (預設right-justified)
// 15s: (string+空格)佔15格
// 03d: int佔三格,不足的補零
// %n: 換行 (\n也行)
System.out.printf("%-15s%03d%n", s1, x);
```
### 有換行
- `System.out.println('XXX')`
## If-else/for-loop
- syntax is the same as C++
## Get Day
```java=
//s的結果為:MONDAY
int year=2020, month=12, day=21;
String s = LocalDate.of(year, month, day).getDayOfWeek().name();
```
## File
使用時記得要`import java.io.File;`
### 檔案屬性相關的function
- 路徑分隔符號`/` 或是 `\`: `File.separator`
- 檔案絕對路徑: `String getPath()`
- 檔案名稱: `String getName()`
- 檢查路徑名稱的文件是否為目錄: `boolean isDirectory()`
- 檢查路徑名稱的文件是否為檔案: `boolean isFile()`
- 檢查路徑名稱的文件或目錄是否存在: `boolean exists()`
- 父目錄: `String getParent()`
- 使用範例
```java=
File file = new File("/home/username/Documents/javamaterials.pdf");
System.out.println("File name: " + file.getName());
System.out.println("File path: " + file.getPath());
System.out.println("Is file: " + file.isFile());
System.out.println("Is directory: " + file.isDirectory());
System.out.println("Exists: " + file.exists());
System.out.println("Parent path:" + file.getParent());
(output)
File name: javamaterials.pdf
File path: /home/username/Documents/javamaterials.pdf
Is file: true
Is directory: false
Exists: true
Parent path: /home/username/Documents
```
### 建立/移除/改名/移動相關的function
- 建立檔案: `boolean creatNewFile()`
- File file = new File("/home/username/Documents/file.txt");
- boolean createdNew = file.createNewFile();
- 建立目錄(父目錄需存在): `boolean mkdir()`
- File file = new File("/home/art/Documents/dir");
- boolean createdNewDirectory = file.mkdir();
- 建立多集目錄: `boolean mkdirs()`
- File file = new File("/home/art/Documents/dir/dir/dir");
- boolean createdNewDirectory = file.mkdirs();
- 移除檔案: `boolean delete()`
- File file = new File("/home/art/Documents/dir/dir/dir");
- boolean removedFile = file.delete();
- 更改/移動檔案名稱: `booolean renameTo()`
- File file = new File("/home/art/Documents/dir/filename.txt");
- boolean renamed = file.renameTo(new File("/home/art/Documents/dir/newname.txt"));
- boolean moved = file.renameTo(new File("/home/art/Documents/another/file.txt"));
以上function執行成功時, 會回傳true, 但可能因為權限問題return false
### 寫檔案
必須要先`import java.io.FileWriter` 或是 `import.java.io.PrintWriter`
- step 1: 建立空檔案
`File f = new File("path/to/your/file.txt");`
- step 2.0: 寫檔案(覆蓋原始檔案)
```java=
FileWriter w = new FileWriter(f);
w.write("xxxx");
PrintWriter p = new PrintWriter(f);
p.print("xxx"); //無換行
p.println("xxx"); //有換行
String str = "xxx"
p.printf("String is %s", str); //輸出格式
```
- step 2.1: 寫檔案(新增文字在原始檔案後)
```java=
FileWriter w = new FileWriter(f, true);
w.write("xxx");
```
- step 3: 關檔案
```java=
w.close();
```
## Output Stream
[[OutputStram V.S. PrintWriter]](https://ithelp.ithome.com.tw/articles/10212682)
### Character Stream
- 可以output `char` 或是 `String`
- 必須要先`import java.io.StringWriter` 或是 `import Java.io.CharArrayWriter`
- 用`StringWriter`建立String; 用`CharArrayWriter`建立char[]
- 將CharArrayWriter轉成char[]: `.toCharArray()`
```java=
CharArrayWriter caw = new CharArrayWriter();
...
char [] charArr = caw.toCharArray();
```
- 將char[]寫入檔案:
```java=
CharArrayWriter c = new CharArrayWriter();
File f = new File("path/to/your/file.txt");
FileWriter w = new FileWriter(f, true);
//寫入char[]
c.write("xxx; yyy, zzz; qqq");
c.writeTo(f);
f.close();
c.close();
```
- per char讀檔案:
- 必須要`import java.io.FileReader`
- 讀char: 用`.read()`, 會回傳int, 當回傳`-1`==代表已經讀到檔案最後==
```java=
FileReader r = new FileReader("xxx.txt");
int charAsNumber = r.read();
while(charAsNumber != -1) {
char character = (char) charAsNumber;
charAsNumber = r.read();
}
r.close();
```
### Byte Stream
- 寫入的內容0,1 序列 (即寫binary檔案)
- 必須要先`import FileOutputStream` 與`import OutputStream`
- 可以利用`.getBytes()`將String轉成byte[]
- String str = "hby";
- byte[] strByte = str.getBytes();
- 將byte[]寫入檔案:
```java=
byte[] data = new byte[] {'s', 't', 'r', 'e', 'a', 'm'};
OutputStream outputStream = new FileOutputStream("sample.txt", false);
outputStream.write(data);
outputStream.close();
```
- per byte/multiple bytes讀檔案:
- 必須要`import java.io.FileInputStream`
- 讀byte: 用`read()`, 會回傳int, 當回傳`-1`==代表已經讀到檔案最後==
- 目前剩餘可讀取的byte數量: `.available()`
```java=
//per byte
FileInputStream fis = new FileInputStream("xxx.txt");
byte b =(byte) fis.read();
//multiple bytes (例如10bytes)
byte [] mb = new byte[10];
fis.read(mb);
```
## Class
### 語法
```java=
class className{
modifier dataType varName;
...
//constructor
public className(dataType varName){
this.varName = varName;
...
}
//instance method (public/private/protected/default)
public dataType funName(parameter){
...
}
}
```
- 四種可access的modifiers
|modifier|同class|同package|不同package<br>有繼承|不同package<br>無繼承|
|---------|:-:|:-:|:-:|:-:|
|private | V | | | |
|default | V | V | | |
|protected| V | V | V | |
|public | V | V | V | V |
- 舉例來說
```java=
//Bird/Cat/Dog/Mammal are in different packages
package birds;
public class Bird{
public void fly(){...}
protected void sing(){...}
}
public class Cat extends Mammal{
public void meow(){...}
}
public class Dog extends Mammal{...}
package mammals;
public class Mammal{
protected void motherChild(){...}
void yell(){...}
}
public class Main{
public static void main(String[] args){
Cat cat = new Cat();
}
}
```
- 可以看到Cat繼承Mannal, 其中Mammal中有`protected`的motherChild與`default`的yell, 因此當呼叫cat.motherChild()是沒問題的, 但cat.yell()則會出錯
- 當要建立subclass object時有兩種方法
(延續上述例子, Cat與Dog都繼承Mammal)
- subclass reference
```java=
//reference與object皆是Cat
Cat c = new Cat();
//reference與object皆是Dog
Dog d = new Dog();
//reference與object皆是Mammal
Mammal m = new Mammal();
//相互(Cat <-> Dog)沒繼承, 不能assign
Cat c = new Dog(); //wrong!
Dog d = new Cat(); //wrong!
```
- superclass reference
```java=
//reference是Mammal, object是Cat
Mammal c = new Cat();
//Mammal沒有meow(), 不能透過superclass reference去呼叫
//c.meow() //compile-time error!
//reference是Mammal, object是Dog
Mammal d = new Dog();
//不能將parent的object assign給subclass
Cat c = new Mammal(); //wrong!
Dog d = new Mammal(); //wrong!
```
### Nullability
- `className c1 = null`
### 繼承 (Inheritance)
- `class subClass extends superClass { ... }`
- 利用`super`可以access superclass的fields, 或呼叫 methods, constructor
```java=
class A {
protected int a;
protected int b;
public A(int a, int b){
this.a = a;
this.b = b;
}
public void print(){
System.out.println(a);
}
}
class B extends A {
protected int c;
//constructor
public B(int a, int b, int c) {
//呼叫superclass的constructor
super(a, b);
//super(a, b)的寫法可以改成第24,25行
super.a = a;
super.b = b;
this.c = c;
}
public void printSuperClass(){
super.print();
//第30行可以改寫成
print();
}
}
```
### Interface
- 一個class可以實作(implements)多個interface, 卻只能繼承(extends)一個superclass
### Polymorphism
```java=
class Alphabet{}
class A extends Alphabet{}
class B extends Alphabet{}
```
- 在宣告成array時
```java=
Alphabet [] AlphabetList = {A, B}
```
### Overload (重載)
- 在compile time
- 根據參數型態或個數不同, 會呼叫相對應的constructor
```java=
public example{
public void example(){...}
public void example(int i){...}
public void exmaple(float j){...}
public void example(int i, float j){...}
...
}
```
### Override (覆寫)
- 在runtime
- `@Override`
## Method
### 語法
- `modifiers` `return_type` `method_name` `(list_of_parameters)` `{ body }`
- 例如: public static int sum2int (int a, int b) { return a + b; }
- modifiers: public static
- return_type: int
- method_name: sum2int
- list_of_parameters: int a, int b
- body: return a + b
### 修飾詞 (modifier)
- 分成access跟non-access
- access: public, protected, default, private
- non-access: static, final, abstract, synchronized, volatile [[Ref]](https://www.tutorialspoint.com/java/java_nonaccess_modifiers.htm)
### 參數 (list_of_parameters)
- 允許可變動個數之參數量`varargs (即variable-length arguments)`, ==當參數是一個以上時, `varargs`就要放在最後==
- 語法1: `(dataType... xxx)`
- 例如: public static void show`(int... num)`{}
- 語法2(以三個參數為例): `(dataType1 xxx, dataType2 yyy, dataType3... zzz)`
- 例如: public static void show`(double fstPara, long secPara, int... num)`{}
## Exception
Base class 是 `java.lang.Throwable`
![](https://stackify.com/wp-content/uploads/2018/09/types_of_exceptions_in_java_image1.jpg)
### Classes
- Trowable
- Exception
- compile-time exception
- 例如: RuntimeException, IOException,...
- RuntimeException
- unchecked exception (在compile-time不會檢查)
- 例如: ArithmeticException, NumberFormatException, NullPointerException (NPE), ...
```java=
String str = null;
String str2 = null;
//no NPE
if(str == null) ....
//no NPE
if(Object.equals(str, str2)) ....
//no NPE
if("xxx".equals(str)) ....
//throws NPE
if(str.equals("xxx")) ....
```
- Error
- unchecked exception (really?) [Ref](https://hyperskill.org/learn/step/3570)
- 例如: OutOfMemoryError, StackOverflowError, ...
### Exception Handling
- `try-catch-finally` 或 `try-finally`
- 跟python的語法有異曲同工之妙 [[Ref](https://hackmd.io/YX_ji6AZToalWsQh3SSYoA?view#IfForWhileTryRaise)]
- 當進入到catch時並執行完此例外處理時, 不會再進入try裡面, 如範例中的"inside the try block after the exception"就不會顯示出來
```java=
System.out.println("before the try-catch block");
try {
System.out.println("inside the try block before an exception");
//it throws ArithmeticException
System.out.println(2 / 0);
System.out.println("inside the try block after the exception");
} catch (Exception e) {
System.out.println("Division by zero!");
}
System.out.println("after the try-catch block");
```
- Java7 支援multi-catch
```java=
try{
xxxxxx
} catch(yyyException){
} catch(zzzException){
}
```
- 使用mutli-catch時, base class的exception要放在最後判斷, 如範例IOException要放在Exception之前
```java=
try{
} catch(IOException e){
} catch(Exception e){
}
```
- 如有多個exception要放在同個catch判斷時, 用`|`符號作分隔, 寫成`catch(Exception1 | Exception2 e)`, 可以利用`e.getClass().getSimpleName()`得知所handle的exception class, 特別注意到在catch判斷式中, 其中任一一個exception不能是任意其他一個的subclass, 如catch(RuntimeException | Exception e)就會出錯
## Generic Programming
- 類似c++的template ([這裡講的很詳細](https://ethan-imagination.blogspot.com/2018/11/javase-gettingstarted-017.html))
- 注意到`<>` 裡面放的type==不能是primitive type==, 只允許reference type, 包含array (因此primitive array是可以使用的, 如int[]), custom class, standard class (Integer, Double, Boolean, ...)
```java=
class urGenericType<T> {
T t;
//constructor
public urGenericType(T t){
this.t = t;
}
```
- 也允許多個type參數
```java=
class urGenericType<T, U, V> {
T t;
U u;
V v;
...
}
```
- 在使用時直接`urGenericType gtn = new GenericTypeName<>(...)`, 例如`urGenericType<Integer> obj1 = new urGenericType<>(10);`
- 當然也可以建立generic array
```java=
public class urImmutableArray<T>{
T [] tArr;
//constructor
public urImmutableArray(T [] tArr){
this.tArr = tArr;
}
...
}
```
- 在使用時直接`var urArrName = new urImmutableArray<>(...)`, 例如`var stringArray = new urImmutableArray<>(new String[] {"item1", "item2", "item3"});`
[序列化](https://lolikitty.pixnet.net/blog/post/36759934-java-%E5%BA%8F%E5%88%97%E5%8C%96-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96-%28serialization-deserialization%29)
## Gson
- 序列化(serialization): java物件 → String
```java=
publi class MyObject(){
private BigInteger id;
private static String userName;
private transient Data date;
}
List<MyObject> myObjectList = new ArrayList<MyObject();
MyObject myObject = new MyObjec();
myObjectList.add(myObject);
myObjectList.add(myObject);
//List<MyObject> -> String
Gson gson = new GsonBuilder().create();
String myObjectStr = gson.toJson(myObjectList);
//允許序列化null
Gson gson = new GsonBuilder().serializeNulls().create();
//排除那類不序列化
Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create();
```
- 反序列化(deserialization): String → java物件
```java=
JsonReader jsonReader = new JsonReader(new StringReader(xxx));
Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create();
List<MyObject> myObjectList = gson.fromJson(jsonReader, new TypeToken<ArrayList<MyObject>>() {}.getType());
```
- `transient`要小心使用, ==transient = 不序列化 + @Transient==, 如有用到JPA repository的function, 當寫某張table的某個non-nullab欄位, 就會出問題
## Reflection
- 改變modifier (常用在unit test,要mock無法直接access的field)
```java=
public class MyObject(){
private static final String USERNAME = "HBY";
}
Field userNameField = MyObject.class.getDeclaredField("USERNAME");
Field modifierField = Field.class.getDeclaredField("modifiers");
//access private member
modifierField.setAccessible(true);
//set modifier to public
modifierField.setInt(userNameField, userNameField.getModifiers() & ~Modifier.FINAL);
//set USERNAME to ...
userNameField.set(MyObject, "New_HBY");
```