###### tags: `Java Tutorial`
# Java筆記
`Creater: AluoExpiry`
`Co Writer: Miyago9267`
`Last Updater: AluoExpiry`
`Created Time: 2020-10-09 01:39:16`
`Updated Time: 2024-10-10 12:55:45`
## 起手式
```java=
public class ClassName{
public static void main(String args[]) {
}
}
```
> 包裹`main()`的函式的`ClassName`請務必與檔名相同
> 例如`Main.java`,`ClassName`就是`Main`
## 變數
### 變數宣告
- `int` : 整數型態(integer)
- `float` : 單精度浮點型態
- `long` : 長整數型態
- `double` : 雙精度浮點型態
- `char` : 字元,以''表示
- `String` : 字串,以""表示
:::info
宣告方式為 : **型態 變數名稱;**
:::
ex:
```java=
int i;
String s;
float f;
```
除字串外,其餘型態皆可於宣告時給定初值
ex:
```java=
int i = 0;
float tt = 0.0f; //f很重要,用意是告訴系統這個值是float型態
double dou = 0.123;
```
### 輸出
```java=
System.out.print() //輸出文字且不換行
System.out.println() //輸出文字且換行
System.out.printf() //輸出文字且自動帶入參數
```
> [Java中printf用法](https://www.itread01.com/content/1510988643.html)
### 賦予值 / 給定值
在Java中,將某個設定給變數是用「=」,以下為範例
```java=
int a, b, c;
String str;
a = 10;
b = 12;
c = a + b;
str = "Hello, world!";
```
以c = a + b為例,「=」的作用是將a + b的結果設定給c,也就是給定值
### 運算
運算大致分成以下三大類:
#### 算術運算
算術運算即是常見的四則運算以及平方等數學運算,常用運算子如下:
```
加法 => +
減法 => -
乘法 => *
除法 => /
次方 => ^
取模 => % (讀音為mod 意為取模 講白話叫取餘數)
ex:
int i = 20, x = 3;
double d = 20 % 3;
// d = 2
```
#### 邏輯運算
```
&& 及運算(AND運算)
|| 或運算(OR運算)
! 反向運算(NOT運算),範例如下
boolean flag = false;
boolean result = !flag;
因為flag為false,所以!flag會是true
```
#### 關係運算
```
> 大於
< 小於
== 等於
=> 大於等於
<= 小於等於
!= 不等於
```
## 邏輯
### 判斷式
判斷式主要分為if、switch以及關係運算(又稱三元判斷)
### if的用法
```flow
st=>start: 開始
e=>end: 結束
con=>condition: 條件式(condition)
init=>operation: 起始值(init)
block=>operation: 執行內容(code block)
inc=>operation: 更新值(increment)
st->init->con
con(yes)->block->inc->con
con(no)->e
```
```java=
Boolean flag = false;
if (flag == false) { //條件成立,執行大括號內的語法
System.out.println("flag values is false!");
}
Boolean flag = true;
if (flag == false) { //條件不成立,執行else大括號內的語法
System.out.println("flag values is false!");
} else {
System.out.println("flag values is true!");
}
String flag = "true";
if (flag == false) { //條件不成立,執繼續判斷else if是否成立
System.out.println("flag values is false!");
} else if (flag == true) { //條件不成立,執行else大括號內的語法
System.out.println("flag values is true!");
} else {
System.out.println("flag type is not boolean!");
}
```
以上三者輸出分別為
```
1、flag values is false!
2、flag values is true!
3、flag type is not boolean!
```
### nested if
```java=
import java.util.Scanner;
public class Test {
public static void main(String args[]) {
Scanner scn = new Scanner(System.in);
int num = scn.nextInt(); //假設輸入數字為59
String result;
if (num > 100 | num < 0) {
System.out.println("輸入分數錯誤!");
} else {
if (num >= 90 && num <= 100) { //分數介於90到100之間
result = 'A';
} else { // 分數小於90
if (num >= 80) { //分數介於80到90之間
result = 'B';
} else { // 分數小於80
if (num >= 70) { //分數介於70到80之間
result = 'C';
} else { // 分數小於70
if (num >= 60) { //分數介於60到70之間
result = 'D';
} else { // 分數小於60
result = 'E';
}
}
}
}
System.out.println("result = " + result);
}
}
}
```
輸出 => `result = E`
:::info
註 : 這邊import的Scanner是一種讀入功能的封裝 後面會做介紹
:::
### switch
switch是比對每一個case選項,當case選項的值符合傳入switch中的參數時,就會執行該case語法的判斷方式,可以有效避免大量使用else if的情況。
```java=
int iii = 123;
switch(iii) {
case 1:
System.out.println("hi");
break;
case 123:
System.out.println("Hello, world");
break;
case 2:
System.out.println("123");
break;
default:
System.out.print("default");
}
```
以上語法執行結果如下
```
Hello, world
```
### 關係運算(又稱三元判斷)
三元判斷一般在給定值時使用,它能一定程度上替代if和switch判斷式,用法如下
```java=
int iii = 123;
String str;
str = (iii > 100) ? "iii values > 100" : "iii values < 100";
System.out.println(str);
```
輸出如下
```
iii values > 100
```
## 迴圈
迴圈主要有for()、do...while()和while()迴圈,其用法如下
### for loop
```java=
int result = 0;
for (int i = 0; i < 26; i++) {
//宣告i初值為0,當i<26時執行迴圈,每次執行後i++
result += i;
}
System.out.printf("result的值為: %d", result);
```
以上語法的輸出為
```
result的值為: 325
```
### while
```java=
int i = 0, index = 1;
while(i < 20) {
//當i < 20時執行迴圈
i++;
//每次執行i就+1
index *= 2;
}
System.out.printf("index的值為%d", index);
```
以上語法的輸出為
```
index的值為: 1048576
```
### do...while
```java=
int i = 0, index = 1;
do {
//先執行第一次do底下的語法,然後當i < 20時執行迴圈
i++;
index *= 2;
} while(i < 20);
System.out.printf("index的值為%d", index);
```
以上語法的輸出為
```
index的值為: 1048576
```
### 巢狀迴圈
巢狀迴圈是在一個回圈內再執行一個迴圈,這種寫法在處理大型json資料或大型專案中常見,但處理不好容易成為無限迴圈,因此須謹慎撰寫
#### [延伸練習] 99乘法表
```java=
public class Test {
public static void main(String args[]) {
for (int i = 1; i < 10; i++) {
for (int j = 1; j < 10; j++) {
System.out.print(Integer.toString(i) + "x" + Integer.toString(j) + " = " + Integer.toString(i * j));
System.out.print(((i*j) < 10) ? " " : " ");
}
System.out.println();
}
}
}
```
```
1x1 = 1 1x2 = 2 1x3 = 3 1x4 = 4 1x5 = 5 1x6 = 6 1x7 = 7 1x8 = 8 1x9 = 9
2x1 = 2 2x2 = 4 2x3 = 6 2x4 = 8 2x5 = 10 2x6 = 12 2x7 = 14 2x8 = 16 2x9 = 18
3x1 = 3 3x2 = 6 3x3 = 9 3x4 = 12 3x5 = 15 3x6 = 18 3x7 = 21 3x8 = 24 3x9 = 27
4x1 = 4 4x2 = 8 4x3 = 12 4x4 = 16 4x5 = 20 4x6 = 24 4x7 = 28 4x8 = 32 4x9 = 36
5x1 = 5 5x2 = 10 5x3 = 15 5x4 = 20 5x5 = 25 5x6 = 30 5x7 = 35 5x8 = 40 5x9 = 45
6x1 = 6 6x2 = 12 6x3 = 18 6x4 = 24 6x5 = 30 6x6 = 36 6x7 = 42 6x8 = 48 6x9 = 54
7x1 = 7 7x2 = 14 7x3 = 21 7x4 = 28 7x5 = 35 7x6 = 42 7x7 = 49 7x8 = 56 7x9 = 63
8x1 = 8 8x2 = 16 8x3 = 24 8x4 = 32 8x5 = 40 8x6 = 48 8x7 = 56 8x8 = 64 8x9 = 72
9x1 = 9 9x2 = 18 9x3 = 27 9x4 = 36 9x5 = 45 9x6 = 54 9x7 = 63 9x8 = 72 9x9 = 81
```
## 陣列
### 宣告一維陣列
java的陣列有區別型態,字串陣列只能存字串、整數陣列只能存整數,依此類推。
```java=
int integer_array[] = {1, 3, 99, -8888};
int[] integer_array[] = {8421, 7746, 65536, -65536};
String str_array[] = {"ho", "yu", "shit"};
String str_array[] = {"hi", "llo", "wol"};
```
:::info
值得注意的是,上述兩種宣告方式都是可行的。
:::
### 一維陣列應用
一維陣列常見應用方式的情況有以下幾種
讀取陣列值一般是使用for迴圈,常見寫法如下:
- 讀取陣列值
```java=
public class Test {
public static void main(String args[]) {
int[] int_array = {2, 1, 4, 7, 4, 8, 3, 6, 4, 7};
// int_array.length => 取得int_array的陣列長度
for (int i = 0; i < int_array.length; i++) {
// 印出int_array的第i個元素
System.out.println(int_array[i]);
}
}
}
```
- 取得陣列內所有值的和
```java=
public class Test {
public static void main(String args[]) {
int[] int_array = {2, 1, 4, 7, 4, 8, 3, 6, 4, 7};
int sum = 0;
// int_array.length => 取得int_array的陣列長度
for (int i = 0; i < int_array.length; i++) {
// sum = sum + int_array[i]的值
sum += int_array[i];
}
System.out.println(sum);
// 輸出 46
}
}
```
- 取得陣列內的最大值 / 最小值
```java=
public class Test {
public static void main(String args[]) {
int[] int_array = {1, 1, 4, 5, 1, 4, 2, 2, 8, 9, 2, 2, -6};
int max = int_array[0], min = int_array[0];
// int_array.length => 取得int_array的陣列長度
for (int i = 0; i < int_array.length; i++) {
// 計算最大值
if (max < int_array[i]) {
// 當int_array[i]的值大於max時,將max設為int_array[i]
max = int_array[i];
}
// 計算最小值
if (min > int_array[i]) {
// 當int_array[i]的值小於min時,將min設為int_array[i]
min = int_array[i];
}
}
System.out.println(max); // 輸出 9
System.out.println(min); // 輸出 -6
}
}
```
- 反轉陣列
```java=
public class Test {
public static void main(String args[]) {
int[] int_array = {1, 1, 4, 5, 1, 4, 2, 2, 8, 9, 2, 2, -6};
int temp, len = int_array.length, replace = len / 2;
// int_array.length => 取得int_array的陣列長度
// 宣告replace 是為了讓迴圈執行時不要重複計算陣列長度 / 2的值
// 讓迴圈的執行次數僅到陣列長度的一半
for (int i = 0; i < replace; i++) {
temp = int_array[i]; // 取得陣列當前元素並暫存
/*
取得陣列倒數時的第i個元素
例:
陣列長度為8
當i = 0 時,陣列倒數第i個元素為陣列最後一個元素
當i = 1 時,陣列倒數第i個元素為陣列倒數第二個元素
...
相當於將陣列對折取對應值
取得之後先將陣列第i個元素改寫成倒數第i個元素
*/
int_array[i] = int_array[len - i - 1];
// 將剛剛暫存起來的陣列第i個元素設定到陣列倒數第i的位置
int_array[len - i - 1] = temp;
}
}
}
```
### 宣告二維陣列
java的一維陣列有區別型態,二維陣列同樣如此。
```java=
int integer_array[][] = {
{-21, 3, 87, -8888},
{199, 13, 88, 8889},
{45, 9, 89, -8987}
};
```
### 二維陣列應用
- 遍歷陣列元素(讀取所有元素)
- 橫向遍歷
```java=
public class Test {
public static void main(String args[]) {
int int_array[][] = {
{-21, 3, 87, -8888},
{199, 13, 88, 8889},
{45, 9, 89, -8987}
};
// int_array.length => 取得int_array的陣列長度
for (int i = 0; i < int_array.length; i++) {
// int_array[i].length => 取得int_array中當前子元件的陣列長度
for (int j = 0; j < int_array[i].length; j++) {
System.out.println(int_array[i][j]);
}
}
}
}
```
- 縱向遍歷
使用前提:陣列內的所有子陣列長度必須一致
```java=
public class Test {
public static void main(String args[]) {
int int_array[][] = {
{-21, 3, 87, -8888},
{199, 13, 88, 8889},
{45, 9, 89, -8987}
};
// int_array.length => 取得int_array的陣列長度
for (int i = 0; i < 4; i++) {
for (int j = 0; j < int_array.length; j++) {
System.out.println(int_array[j][i]);
}
}
}
}
```
- 取二維陣列中滿足除某數等於0的數
```java=
public class Test {
public static void main(String[] args) {
int int_array[][] = {
{-21, 3, 87, -8888},
{199, 13, 88, 8889},
{45, 9, 89, -8987}
};
int condition = 3;
for (int i = 0; i < int_array.length; i++) {
for (int j = 0; j < int_array[i].length; j++) {
if (int_array[i][j] % condition == 0) {
System.out.println(int_array[i][j]);
}
}
}
}
}
```
- 取二維陣列中值的總和
```java=
public class Test {
public static void main(String[] args) {
int int_array[][] = {
{-21, 3, 87, -8888},
{199, 13, 88, 8889},
{45, 9, 89, -8987}
};
int sum = 0;
for (int i = 0; i < int_array.length; i++) {
for (int j = 0; j < int_array[i].length; j++) {
sum += int_array[i][j];
}
}
}
}
```
- 取二維陣列中的最大 / 最小值
```java=
public class Test {
public static void main(String[] args) {
int int_array[][] = {
{-21, 3, 87, -8888},
{199, 13, 88, 8889},
{45, 9, 89, -8987}
};
int max = int_array[0][0], min = int_array[0][0];
for (int i = 0; i < int_array.length; i++) {
for (int j = 0; j < int_array[i].length; j++) {
// 取最大值
if (max < int_array[i][j]) {
max = int_array[i][j];
}
//取最小值
if (min > int_array[i][j]) {
min = int_array[i][j];
}
}
}
}
}
```
- 二維陣列反轉
```java=
public class Test {
public static void main(String[] args) {
int int_array[][] = {
{-21, 3, 87, -8888},
{199, 13, 88, 8889},
{45, 9, 89, -8987}
};
int count = int_array.length;
int len = count / 2;
int[] temp = {};
// int_array.length => 取得int_array的陣列長度
for (int i = 0; i < len; i++) {
// int_array[i].length => 取得int_array中當前子元件的陣列長度
temp = int_array[i];
// 將int_array當前的子元件暫存
int_array[i] = int_array[count - (i + 1)];
// 將int_array當前的子元件變成陣列對折時對應位置之子元件
int_array[count - (i + 1)] = temp;
// 將int_array陣列對折時對應位置之子元件變成當前的子元件
}
}
}
```
## 物件導向
### 何謂物件
物件(Object),是一個類別(Class)的實際運用。
講起來很抽象,舉個例子 : 以"人"這個物件作為例子
"人"這個概念本身是一個類別,類別定義了一件事物的抽象特點。
"人"這個類別包含了他本身會觸發的事件(方法),以及其他的屬性(變數)
例如:
"人"是一個類別,而這個類別內包含了許多屬性,如"穿著"或"種族"等等屬性;
而改變這些屬性需要透過方法(Method),假設要改變"穿著"這個屬性,需要透過換衣服來達成,"換衣服"這個動作就是方法的一種。
### 套件
套件(package),通常用於定義程式範圍
定義接近於PHP中的namespace
那套件有甚麼用呢?
可以隔離不同環境下相同物件名稱導致的編譯錯誤
可以用以劃分功能與架構
舉以下例子:
```java=
/* 分析工具 */
package analyze;
class Tool {
}
```
```java=
/* 資料庫操作工具 */
package database;
class Tool {
}
```
可以看到我在兩個package中分別實作了相同名稱的class
如果要分別調用他們,可以像這樣:
```java=
package domainservice;
public class Main {
public static void Main(String args[]) {
// 初始化資料庫操作工具類別
database.Tool dbTool = new database.Tool();
// code...
// 初始化分析工具類別
analyze.Tool analyzeTool = new analyze.Tool();
// code...
}
}
```
由於套件在JAVA中的設計是與檔案系統對應的
因此檔案結構應該要像這樣:
```
/ root directory (根目錄)
├── package directory (package目錄)
│ ├── domainservice
│ │ └── Main.java (java程式)
│ ├── database
│ │ └── Tool.java (java程式)
│ └── analyze
│ └── Tool.java (java程式)
```
### 物件
物件在Java中代表的是類別(Class)、方法(Method,或稱Function)和變數(Variable),層級關係如下:
:::info
類別 > 方法 > 變數
:::
在物件的上層則是package,層級關係如:
:::success
套件 > 類別 > 方法 > 變數
:::
類別又分外部類別與內部類別,其特徵如下:
:::spoiler Code
```java=
// file name = Test.java
// 外部類別
public class Test {
public static void main(String args[]) {
// code...
}
}
// 內部類別
class AAAA {
public AAAA(String shark, int args) {
// code...
}
}
```
:::
可以看到,外部類別的結構就是很常見的java class,裡面一定要定義main function,且class名稱要和檔案名稱一樣;而內部類別則可以任意定義,入口function的名稱則須與class名稱一致。
### 靜態物件
在Java中,方法和變數有分為靜態與物件(非靜態)方法/物件,以下舉個關於靜態方法的簡單例子:
例如一個公開類別名叫"我",有個方法叫"花光我的積蓄當月光仙子"
如果他是物件(非靜態)方法,那麼可以確定只有"我"可以執行"花光我的積蓄當月光仙子"
如果他是靜態方法那就好玩了,就算是路人類別也能執行"我.花光我的積蓄當月光仙子()"方法
然後"我"的薪水就被花光了,而且執行的類別還不是"我"
- 摘錄自[Mr.Wei 的程式筆記](http://weisnote.blogspot.com/2012/08/static.html)
靜態與物件方法的表示方式如下:
```java=
public class Test {
public static void main(String args[]) {
// 物件函數呼叫
Test object = new Test();
object.nonStaticfun();
// 靜態函數呼叫
isStaticfun();
}
// 靜態函數
static void isStaticfun() {
// code
}
// 物件函數
void nonStaticfun() {
// code
}
}
```
### 存取修飾子
存取修飾子決定了屬性、方法或物件的可存取性
但屬性、方法以及物件各自可以應用的修飾子不同,不同語言中也有不同限制
#### 修飾子介紹
修飾子有以下四個種類:
- `public` (公開)
- `protected` (保護)
- `private` (私有)
- ` `(預設)
你沒看錯,有一個修飾子是空的
這是預設的修飾子,存取權限如下:
**公開 / public**
就如同字面意思一樣,任何地方都可以存取
**保護 / protected**
- 同package可以存取
- 不同package中的子類別(sub class)可以存取
**預設 / No Modifier**
- 同package中可以存取
**私有 / private**
- 同class中可以存取
按照可存取範圍由大到小排序分別是:
```java=
public > protected > > private
```
#### 類別
在JAVA中,類別只能應用`public`與` `修飾子
單一`.java`檔案中只能有一個`public class`
並且使用`public`修飾子的**class名稱必須與檔名完全相同**
```java=
/* Human.java */
public class Human {}
class Tool {}
```
以上案例中`public class`是`Human`
其他的類別就不需要攜帶修飾子
#### 方法與屬性
方法與屬性就可以應用全部的修飾子,根據權限不同來決定存取範圍
以下建立一個公司職員的物件,用以說明修飾子的作用:
```java=
package company;
// 職員
class Staff {
// 姓名
public String Name;
// 所屬部門
String Branch;
// 負責項目
protected String ResponsibleProject;
// 電腦密碼
private String PCPassword;
}
```
**姓名**
可以被任何人知道,因此是`public`
**所屬部門**
相同公司(package)的成員可以知道所屬的部門
**負責項目**
只有相關的團隊成員和上司可以知道負責的項目
**電腦密碼**
只有自己可以知道電腦密碼是多少
### 封裝
**請搭配上方的修飾子食用**
物件導向最基本的原則,把真實世界的某個事物包成物件,裡面的資訊不對外公開,只公開某些特定方法讓別人使用,內部的實做及資料都隱藏起來,不讓人直接使用,也不需要讓別人直接使用。
也就是所謂的 資訊隱藏(Information Hiding)
假設我想在程式中建立一系列物件,其中包含了"人類"
:::spoiler 範例 code
```java=
class Human {
// 姓名
private String name;
// 國籍
private String country;
// 性別
private String sex;
// 身分證字號
private String idCradNumber;
// 銀行卡密碼
private String bankPassword;
// 吃東西
public void Eat() {
// code...
}
// 睡覺
public void Sleep() {
// code...
}
// 取得姓名
public String GetName() {
return this.name;
}
// 取得國籍
public String GetCountry() {
return this.country;
}
// 取得性別
public String GetSex() {
return this.sex;
}
// 設定銀行卡密碼
public void SetBankPwd(String password) {
this.bankPassword = password;
}
}
```
:::
這裡構建了"人類"物件後,我們可以將吃飯、睡覺等相關的方法都構建到"人類"物件中
而人類相關屬性不能直接被存取,透過公開出來的**Getter**才可以被存取
相對的,人類相關屬性也不能直接被覆寫,要透過公開出來的**Setter**才可以覆寫
#### Getter
存取物件屬性的方法,可以透過修飾子決定什麼屬性可以被什麼成員存取
並且可以在Getter中實作其他邏輯來判斷能不能存取屬性
#### Setter
覆寫物件屬性的方法,同樣可以透過修飾子決定什麼屬性可以被什麼成員存取
並且同樣可以在Setter中實作其他邏輯來判斷能不能覆寫屬性
### 繼承
在物件導向的語言中,類別能夠透過繼承,藉此引繼父類別的屬性
以下舉幾個簡單的例子 :
"人"是一個類別,裡面包含了基本的屬性跟方法:
```java=
class Human {
// 姓名
public String Name;
// 國籍
public String Country;
// 性別
public String Sex;
// 身分證字號
public String IDCradNumber;
// 吃東西
public void Eat() {
// code...
}
// 睡覺
public void Sleep() {
// code...
}
}
```
如果要建立"女人"物件,就需要在"人"的基礎上建立
```java=
class Woman extends Human {
// 女人的體重是秘密,要使用private
private float Weight;
// 化妝
public void MakeUp() {
// code...
}
}
```
按照這種方式建立的"女人"物件,就同時繼承了"人"的屬性和方法
這時候如果你想讓女人執行睡覺方法,就不需要重複定義`Sleep()`
可以直接調用從父類別`Human`繼承來的`Sleep()`方法
> 不要物化女性
## 補充課程
### 不用BigInteger的極大數值加法運算
不用BigInteger來進行大數運算
:::spoiler 程式碼
```java=
import java.util.Scanner;
public class Test {
public static void main(String args[]) {
Scanner scn = new Scanner(System.in);
String a = scn.nextLine();
String b = scn.nextLine();
Test myclass = new Test();
String result = myclass.doAdd(a, b);
System.out.println(result);
}
private String doAdd(String a, String b) {
String str = "";
//取得 a , b 數字字串長度
int lenA = a.length();
int lenB = b.length();
//比較 a , b ,取得最大長度 & 最小長度
int maxLen = Math.max(lenA, lenB);
int minLen = Math.min(lenA, lenB);
StringBuilder strTmp = new StringBuilder();
for (int i = maxLen - minLen; i > 0; i--) {
strTmp.append("0");
}
//把長度调整到相同
if (maxLen == lenA) {
b = strTmp + b;
} else {
a = strTmp + a;
}
//進位旗標
int JW = 0;
for (int i = maxLen - 1; i >= 0; i--) {
int tempA = Integer.parseInt(String.valueOf(a.charAt(i)));
int tempB = Integer.parseInt(String.valueOf(b.charAt(i)));
int temp;
if (tempA + tempB + JW >= 10 && i != 0) {
temp = tempA + tempB + JW - 10;
JW = 1;
} else {
temp = tempA + tempB + JW;
JW = 0;
}
str = temp + str;
}
return str;
}
}
瑪利歐