Overview of Content
在現代 Java、Android 開發中,註解(Annotation
)和 Enum 是兩個非常重要的工具… 本篇文章將全面介紹這兩者的應用與差異,幫助讀者更好地理解如何在實際項目中有效地運用這些技術
我們將從 Android 中最基本的註解開始,帶領讀者了解各種常見註解的作用與用法,進一步探討 Nullness 註解
、資源類型註解
、執行緒註解
、RGB 註解
、註解範圍
、許可權註解
、重新定義函數註解及回傳值註解,這些註解能夠在多方面提升代碼的穩定性、安全性及可讀性。
此外,我們還將深入比較 Enum 和 Annotation 在 Android 開發中的應用,討論它們的優勢和劣勢;包括 Enum 的反編譯解析,以及與 Android IntDef 的比較,幫助開發者在不同場景下做出最佳選擇。
接著,我們將介紹 Java 中的標準註解,包含編譯相關註解、資源註解、自定義註解、元註解等,並解析自定義註解的屬性設置,讓開發者掌握更多的註解應用技巧,提升代碼質量和靈活性。
最後,我們會討論註解提取技術,講解如何在不同的保留策略下進行註解的提取和處理,並詳細介紹 SOURCE 註解在 Android APT 技術中的應用,幫助開發者更好地利用註解處理工具
Java 註解從 Java 1.5 添加到 Java,以下會使用 Android 註解 & Java 註解
寫文章分享不易,如有引用參考請詳註出處,如有指導、意見歡迎留言(如果覺得寫得好也請給我一些支持),感謝 😀
個人程式分享時比較注重「縮排」,所以可能不適合手機的排版閱讀,建議切換至「電腦版」、「平板版」視窗看
Android 基礎註解/概述
Support Annotation Library 從 19.1 引進全新的函數套件,在開發中加入程式可提升程式的品質 (可讓編譯器檢查),增加程式碼的健壯
● Android 中自身有的註解介紹
// Android gradle 加入註解的依賴
dependencies {
implementation 'com.android.support:support-annotations:28.0.0'
}
Nullness 註解
● Nullness 註解是在提醒使用該函數的使用者,傳入的參數是否可以為 Null,或是不可以為 Null,這對於提高程式碼的質量非常有幫助
註解名 | 意義 |
---|---|
@Nullable | 參數 or 回傳值可以為空 |
@NonNull | 參數 or 回傳值不可以為空 |
public class TestAnnotation {
public static void main(String... args) {
String s1 = useAnnotation("Alien");
String s = useAnnotation(null);
}
private static @NonNull String useAnnotation(@NonNull String str) {
return "Hello World, " + str;
}
}
--實作--
資源類型註解
● 資源在 Android 中通常以整數來代表 (存在於 R.java 中),該註解可以避免資源被放置到錯的參數中
註解名 | 意義 |
---|---|
@XXXRes | XXX 代表為 android.R.XXX 類型的資源,有 Animator, Anim, String, Array, Bool, Color, Drawable, Integer, Layout, ID...+Res |
public class TestAnnotation {
public static void main(String... args) {
TestRxxx(new Integer[]{});
TestRxxx(new String[]{});
TestRxxx(R.id.response);
}
private static void TestRxxx(@IdRes int id) {
}
}
--實作--
執行緒註解
註解名 | 意義 |
---|---|
@UiThread | 一個應用而言可能存在多個 UiThread,每個 UI 執行緒對應不同視窗 |
@MainThread | 標記執行在主線程,一個應用只有一個主執行緒 (就是 @UiThread 執行緒),大部分情況 @MainThread 使用在生命週期相關函數 |
@WorkThread | 背景後台工作的執行緒 |
@BinderThread | 標記執行在 Binder 的執行緒 |
● @UiThread
& @MainThread
一般來說是可以互換的,因為 UI Thread 通常就是指 Main Thrad(並非一定)
像是 Android 的輕量級異步執行緒框架
AnsyncTask
就有此用這些
… 有關於 AnsyncTask 解釋請點擊連結了解(Template 設計模式 | 實現與解說 | Android source AsyncTask 微框架)
// AnsyncTask.java
@WorkerThread
protected abstract Result doInBackground(Params... params);
...
@MainThread
protected void onPreExecute() {
}
...
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onPostExecute(Result result) {
}
...
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onProgressUpdate(Progress... values) {
}
...
@SuppressWarnings({"UnusedParameters"})
@MainThread
protected void onCancelled(Result result) {
onCancelled();
}
RGB 註解
● RGB / ARGB 每個字母佔 8Byte
● 它跟 @ColorRes 的差別在 @ColorRes 只接收 Resouce 的資源項目,@ColorInt 接收所有符合的資源內容
註解名 | 意義 |
---|---|
@ColorInt | 需要傳入 RGB 顏色整數 |
--實作--
只要符合
@ColorInt
規範的整數就可以 (以下可以看出 ID 類的標明也是符合,但整數 20 不可以)
註解範圍
註解名 | 意義 | Example |
---|---|---|
@Size(min=x) | 集合下限 | @Size(min=1) |
@Size(max=x) | 集合上限 | @Size(max=3) |
@Size(value=x) | 固定集合數量 | @Size(value=2) |
@Size(multiple=x) | 集合大小可為 x 的倍數數量 | @Size(multiple=2) |
@IntRange(x) | 參數類型可為 int or long | @IntRange(from=0,to=255) |
@FloatRange(x) | 參數類型可為 float or double | @FloatRange(from=-1.0,to=1.0) |
public class TestAnnotation {
public static void main(String... args) {
TestSizeMin(new int[]{1,2});
TestSizeMin(new int[]{});
TestSizeMax(new int[]{1,2});
TestSizeMax(new int[]{1,2,3,4});
TestSizeFixed(new int[]{1,2});
TestSizeFixed(new int[]{1,2,3});
TestSizeMultiple(new int[]{1,2});
TestSizeMultiple(new int[]{1,2,3,4,5});
TestIntRange(123);
TestIntRange(256);
TestFloatRange(1.0F);
TestFloatRange(-2.0F);
}
private static void TestSizeMin(@Size(min = 1) int[] params) {
}
private static void TestSizeMax(@Size(max = 3) int[] params) {
}
private static void TestSizeFixed(@Size(value = 2) int[] params) {
}
private static void TestSizeMultiple(@Size(multiple = 2) int[] params) {
}
private static void TestIntRange(@IntRange(from = 0, to = 255) long a) {
}
private static void TestFloatRange(@FloatRange(from = -1.0, to = 1.0) float a) {
}
}
● 限定於陣列才可判斷,其餘不可判斷
public class TestAnnotation {
public static void main(String... args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
TestSizeInList(list);
TestSizeOther(1,3,5,6,7,8);
}
private static void TestSizeInList(@Size(3) ArrayList<Integer> list) {
}
private static void TestSizeOther(@Size(3) int... params) {
}
}
--實作--
許可權註解
註解名 | 意義 |
---|---|
@RequiresPermission() | 提醒使用者要在 AndroidManifest.mxl 中宣告許可權 |
@RequiresPermission(allof={x,y}) | 以下的權限皆需要 |
@RequiresPermission(anyOf={x,y}) | 最少需要其中一個 |
重新定義函數註解
● Android 的生命週期 onCreate 就有使用;建構函數不需要 @CallSuper
註解名 | 意義 |
---|---|
@CallSuper | 提醒使用者要複寫該方法 |
public class TestAnnotation {
public static void main(String... args) {
new RichMan(new PoorMan()).wear();
}
}
abstract class Decorator {
Decorator() {
}
public abstract void wear();
}
class PoorMan extends Decorator {
@Override
public void wear() {
System.out.println("UnderPant");
}
}
abstract class WorkMan extends Decorator {
protected Decorator d;
protected WorkMan(Decorator d) {
this.d = d;
}
@CallSuper
@Override
public void wear() {
d.wear();
}
}
class RichMan extends WorkMan {
protected RichMan(Decorator d) {
super(d);
}
@Override
public void wear() {
super.wear();
System.out.println("Jacket");
System.out.println("Pant");
System.out.println("T-shirt");
System.out.println("sock");
System.out.println("shoes");
}
}
以 裝飾器模式來說每個實作類都需要呼叫該父類的方法,但使用者有時會忘記呼叫 super()
這時就可以使用,用來提醒使用框架者要記得呼叫父類方法
這種提醒機制並「非強制」,就算使用者不遵從提醒仍可編譯過
--實作--
回傳值註解
● 如果某函數需要對 return
值作處理這時就可以呼叫 @CheckResult,把要寫的警訊寫在 suggest 中,Android 第三方 Lib 很常使用
註解名 | 意義 |
---|---|
@CheckResult(suggest="x") | 提醒使用者要注意回傳值 |
Enum 與 Annotation 差異
我們在撰寫程式時,常常使用 Enum 來表達出可使用的選項(同時限制使用者只能使用 Enum 中的選項);而使用註解也可以達到告訴使用者該參數能接收的選項
同樣注意到的是,這並非是一個硬式限制,更像是一種提醒
Enum 反編譯解析
● Enum 就算是一個「類」,而一個類所占用的標頭最少是 12 個字節(byte),所以會耗費更多的記憶體空間
class MyTestClass {
enum Level {
LEVEL_1,
LEVEL_2,
LEVEL_3,
LEVEL_4,
LEVEL_5,
LEVEL_6,
}
}
上面程式碼經過 ASM 反組譯後會變為,每一個成員皆為一個 enum 類,所 佔用空間過大 (Byte 描述 Class 會耗費多一點空間)
Enum vs. Android IntDef
● @IntDef
註解由 Androidx 提供,並標明 @Target({ANNOTATION_TYPE})
表示它是註解在註解上的註解
InDef 可使用在 IDE 檢查,其階段只保留到 Source,以下是它的源碼
Android 還有
StringDef
... 等等可以使用
// 源碼
@Retention(SOURCE) // 保存到 Source 階段
@Target({ANNOTATION_TYPE})
public @interface IntDef {
/** Defines the allowed constants for this element */
int[] value() default {};
boolean flag() default false;
boolean open() default false;
}
● @IntDef
特性有:
A. 效能更高:
在 Android 平台上,使用 enum 會比使用 int 消耗更多的內存,因為 enum 會引入額外的方法和屬性;使用 @IntDef
則不會有這個問題,因為它本質上還是使用整數值
B. 編譯期檢查:
@IntDef
提供了編譯期檢查,確保只能使用指定的整數值,避免了使用其他不合法的值
C. 提高可讀性:
通過 @IntDef
註解,可以使代碼更加易讀,因為我們可以定義一組有意義的常量來表示不同的狀態或類型
● @IntDef
的使用方式:
要使用 IntDef 需要自定義一個註解,並且標註該註解作用的區域 @Target,還有它的保留階段 @Retention
class MyTestClass {
//"1. "
@Target({ElementType.FIELD, ElementType.PARAMETER})
@IntDef(value = {LEVEL_1, LEVEL_2, LEVEL_3, LEVEL_4, LEVEL_5})
@Retention(RetentionPolicy.SOURCE)
@interface MyLevel {
}
public static final int LEVEL_1 = 1;
public static final int LEVEL_2 = 2;
public static final int LEVEL_3 = 3;
public static final int LEVEL_4 = 4;
public static final int LEVEL_5 = 5;
public static void main(String []args){
System.out.println("Hello World");
setLevel(1111); //"2. "
setLevel(3);
setLevel(LEVEL_3);
}
public static void setLevel(@MyLevel int level) {
System.out.println("level: " + level);
}
}
A. 使用自定義註解,要標示它作用的區域,如果沒有標示 ElementType.PARAMETER
該註解則不能作用於函式參數
B. 放入非標準的格式編譯仍然可以過,但是寫程式時跳出警告訊息,讓程式更加健壯
可看出所占用的空間變少,一個標示只佔 4 個字節
Java 標準註解
● Java 也可使用,不限定 Android,它定義在 java.lang
、java.lang.annotation
、javax.annotation
套件中
編譯相關註解:SafeVarargs 使用
註解名 | 意義 |
---|---|
@Override | 覆寫父類方法,如果並無覆寫則會報錯誤 |
@Deprecated | 已遺棄的方法,編譯器會警告使用者 |
@SuppressWarnings(value={x,y}) | 抑制編譯器警告,可抑制多個警告 |
@SafeVarargs | 支援資料可變長度 |
@Generated | 一般用於給程式自動產生代碼,不建議手動修改 |
@FuntionalInterface | 該介面只有一個方法,可使用在 Lambda |
● 註解 @SafeVargs
目的 & 意義
其主要目的是處理 泛型 的可變長參數(T...t) ,告編譯器此泛型是安全長參數
可變長參數使用數組儲存,而數組 & 泛型不能很好的混合使用
● 數組 & 泛型 ? 可以使用泛型數組不會有警告
數組:編譯期間就已確定(靜態確認)
泛型:運行時才能確定數據,因此編譯時無法判別其正確性,導致產生警告(動態確認)… 這時我們就可以使用
@SafeVargs
去壓制警告
● 註解
@SafeVargs
只能宣告在 static or final 的函數
● 對於 SafeVarargs
、SuppressWarnings
使用範例如下
public class TestProblemOfGeneric {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
//"2. "
FixList.add2List(list1, "Pan", "Kyle", "Alien");
FixList.add2List(list2, "One", "Two", "Three");
List<List<String>> list3 = new ArrayList<>();
//"3-1. "
FixList.add2List(list3, list1, list2);
//"3-2. "
FixList.TestMethod(Arrays.asList("Hello!"),
Arrays.asList("World!"));
}
}
class FixList {
public static <T> void add2List(List<T> list, T...data) {
for(T t : data) {
list.add(t);
}
}
//"1. "
public static void TestMethod(List<String>...list) {
Object[] o = list;
//"4. "
o[0] = Arrays.asList(new Integer(123));
String str = list[0].get(0);
System.out.println("Test: " + str);
}
}
A. ...
代表可變的參數長度,編譯器會將其變成 數組,也就是說 List<String>...list
可以看做 List<String>[]
B. 一般類型可正常轉為可變長度,再轉為數組
C. Java 不允許泛型類類型的數組 (無法確認數據類型,會發出警告),使用 @SafeVarargs
使其安全使用 (確定不會錯誤時再添加這個註解)
● Object[] o = list
這個語句會轉化為 Object[] o = (List[]) list;
這是正確的
但 Arrays.asList(new Integer(123))
,就類型錯誤會拋出 ClassCastException
異常
● Arrays.asList
方法返回的是泛型數組
D. 如果沒有 @SafeVarargs
註解壓住錯誤的話,IDE 會警告發生 HashPollution 錯誤(想了結詳情情點擊連結)
Java 資源註解
● 一般用於 Java EE (Java EnterPrise),Android 較少使用
註解名 | 意義 |
---|---|
@PostConstruct | 用在控制物件生命週期的環境中,在呼叫建構函數後應立即被呼叫 |
@PreDestory | 同上,在銷毀物件之前應被呼叫的方法 |
@Resoure | 用於 Web 容器的資源植入,單一資源 |
@Resoures | 同上,表示陣列資源 |
Java 自定義註解
● 透過 @interface 關鍵字自定義註解,其形式與接口類似,不過多了 @ 符號,可當作是標籤,可在每個類、參數、回傳值上貼上標籤
// Sample 1
@interface HelloWorld {
}
@HelloWorld
class MyAnnotation {
}
// Sample 2 in Android
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface MyAnnotation {
@IdRes int id();
}
@MyAnnotation(id = R.id.sample_text)
TextView tv;
Java 元註解
● 可理解為 註解的註解,不能修飾一般方法,可應用在其他註解上,分為五種…
A. @Retention (註解保留期間)(Java 1.5 加入)
選項在 RetentionPolicy 中 (emum 類)
元註解名 | 保留期間 | 使用 |
---|---|---|
RetentionPolicy.SOURCE | 註解只留在源碼中,編譯過後即捨棄 (保存到 .java 為止) | IDE、APT |
RetentionPolicy.CLASS | 註解留到編譯進行時,並不會加載到 JVM 中 (保存到 .class 為止) | AOT 思想,插件化 |
RetentionPolicy.RUNTIME | 註解留到 runtime,會加載到 JVM 中,所以反射可以使用 (保存在 JVM 中) | 反射 |
根據元註解 (Retention) 不同有不同效果
● Source 編譯器可 使用註解偵錯、警告,APT
● Class 編譯階段:可使用 字節碼插入,但並不會保留到運行期間(也就是你可以在 .class
階段看到它的存在,但運行時就會消失)
● Runtime 註解:可在 Runtime 時提取 反射,這時的註解 也會代碼的一部分,它會保留到運行期間
Runtime 註解通常使用在框架中
如果是開發 Android 或其他專案時使用到 Runtime 反射機制,要特別注意「混淆機制」,混淆會導致反射運作不正常! 要注意去寫混肴文件,跳過混淆
B. @Target (目標):註解所修飾的對象範圍(Java 1.5 加入)
選項在 ElementType 中 (emum 類),
import java.lang.annotation.RetentionPolicy;
元素 | 適用 |
---|---|
ElementType.ANNOTATION_TYPE | 註解型態宣告(給註解貼上標籤) |
ElementType.CONSTRUCTOR | 註解建構函數 |
ElementType.FIELD | 註解屬性 |
ElementType.LOCAL_VARIABLE | 註解區域變數 |
ElementType.METHOD | 註解方法 |
ElementType.PACKAGE | 註解包 |
ElementType.PARAMETER | 註解方法參數 |
ElementType.TYPE | 類別 or 介面 |
ElementType.USE | 類型的用途 |
可註解多個,使用 {}
包覆數值,範例如下…
@Target({
ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR,
ElementType.TYPE}
)
C. @Documented(Java 1.5 加入)
將註解中的元素包含入 JavaDoc 中
D. @Inherited (繼承)(Java 1.5 加入)
該註解可以被繼承 (不是說註解被繼承),如果父類使用過註解,子類沒有使用,則該子類就會繼承該父類註解
E. @Repeatable (重複)(Java 1.8 加入)
該註解可多次使用
自定義註解的屬性
● 在我們自定義註解時,內部 只可以有屬性,不能有方法!
而註解中的 value 是一個特殊的屬性,value 不需要指定
public @interface MyAnnnotation {
// value 接收 String 類型參數
String value();
// StrArray 接收 string array 類型
String[] StrArray(); // 需要指定
}
● 自定義註解中的屬性包含基礎的 8 個類還有 3 個特殊類,分別是…
● 基礎類型:byte
, short
, int
, long
, float
, double
, char
, boolean
● 特殊類型:Class
、enum
、interface
//"1. " 使用預設質、或指定
@MyAnnnotation(id=10, name="ABC", getEnum = MyEnum.THREE)
class ToDo {
}
interface MyInterface {
}
enum MyEnum {
ONE, TWO, THREE
}
@interface MyAnnnotation {
int id() default -1;
String name() default "";
//"2. " 不能設定方法
//void setAddress(String addr);
Class<?> get() default Todo.class;
MyEnum getEnum() default MyEnum.ONE;
enum TestEnum {
ONE, TWO, THREE
}
interface Test {
}
}
註解提取
Android 中最常使用到的就是 APT 註解解析器,解析使用者所定義的註解
CLASS
● 用於插件化,AOT 思想,字節碼插入程式的技術,而要插入的點可以透過註解打上標記(這裡有機會我再補充文章,其思想概念是在編譯時插入 ByteCode)
SOURCE:Android APT 使用
● APT 是作用在 Source 過程中,Java 在編譯 java 檔案前會先儲存註解,而處理該註解的則稱為註解處理器
A. 建立一個 MyAnnotation 的註解,並用元注解標註 SOURCE、FIELD
package com.oo.jnidemo; // 之後會使用到
...
@Retention(RetentionPolicy.SOURCE) // 只保存在源碼階段
@Target(ElementType.FIELD)
public @interface MyAnnotation {
@IdRes int id();
}
B. 新建 Java or Kotlin Library (File > New > New Module...),並取名為 annotations
這個 Module 作用是 註解處理器,在 processor Module 的 build.gradle 添加依賴 (implementation)
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':annotations')
}
sourceCompatibility = "7"
targetCompatibility = "7"
在該 Library 下創建一個類,取名為 ClassProcessor,並繼承 AbstractProcessor,並覆寫以下表格的方法 (目前為了測試是在 source 所以只使用 process 函數)
名稱 | 功能 | 參數功能 |
---|---|---|
init | 將會被註解處理工具調用 | ProcessingEnvironment 提供工具類 |
process | 每個處理器的主函數 | RoundEnvironment 可以查詢特定註解的被註解元素 |
getSupportedAnnotationTypes | 必須指定的方法,==指定該註解處理器是註冊給哪個註解==,也可以使用註解 @SupportedAnnotationTypes (==全類名==) | Non |
getSupportedSourceVersion | Java 版本 | Non |
//"a. "
@SupportedAnnotationTypes("com.oo.jnidemo.MyAnnotation")
public class MyAnnotationProcess extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//"b. "
Messager messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE, "!!!======== Annotation Processor ========!!!");
return false;
}
}
● getSupportedAnnotationTypes
返回的是一個字符串的集合,它包含本處理器將要處理的註解類型的 合法全名,可使用 Class 方法的 getCanonicalName
● 輸出到 console 視窗,因為創建時是使用 Java Lib,所以不能使用 Log,要使用 Message,並使用 Diagnostic.Kind.NOTE
C. 註冊「註解處理器」,就像是 Activity 要在 AndroidManifest;註解處理器也要註冊,有兩種方法 手動 & 自動:
以下說明「Google 提供的自動註冊註解 Library」的使用,Google 開源 AutoService
(可能版本間容不好,要小心使用,但速度較快)
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':annotations')
// new
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc2'
}
在 module processor's gradle 下新增,annotationProcessor 依賴
在註解處理器下新增 @AutoService(Process.class)
的註解
// 自動註冊時,Processor.class 這是固定寫法
@AutoService(Processor.class)
// 代表了該註解處理器會處理這個註解 (包名 + 全類名)
@SupportedAnnotationTypes("xxx.yyy.zzz")
// 指定 JDK 編譯版本
@SupportedSourceVersion(SourceVersion.RELEASE_7)
// 接收的參數
@SupportedOptions({"xxx", "yyy"})
public class ClassProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
...
}
}
D. 使用註解:使用的主工程項目中,添加要使用的 project 依賴(這個 project 就是我們在上面寫的註解處理器),之後主工程在編譯時,指定的註解就會被分析
dependencies {
...
implementation project(':annotations') // 註解
annotationProcessor project(':processor') // 註解處理器
}
在主項目中使用以上提供的註解(@MyAnnotation
)
public class MainActivity extends AppCompatActivity {
@MyAnnotation(id = R.id.sample_text)
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
最後 Rebuild project,在 Build 視窗可以看到 Task **:app:compileDebugJavaWithJavac
,Javac 在編譯 .java 文件,它在處理註解
--結果--
更多的 Java 語言相關文章
Java 語言深入
● 在這個系列中,我們全方位地探討了 Java 語言的各個核心主題,旨在幫助你徹底掌握這門強大的編程語言。無論你是想深入理解 Java 的基礎類型與變數作用域,還是探索異常處理與運算子的細節,這些文章都將為您提供寶貴的知識
此外,我們還涵蓋了物件創建、函數式編程、註解應用以及泛型的深入分析,幫助您提升在實際開發中的技能和效率
點擊以下連結,開始你的學習之旅~
● 深入探索 Java 基礎類型、編碼、浮點數、參考類型和變數作用域 | 探討細節
● 深入了解 Java 應用與編譯:從原始檔到命令產出 JavaDoc 文件 | JDK 結構
● 深入理解 Java 運算子與修飾符 | 重要概念、細節 | equals 比較
● 深入探索 Java 物件創建與引用細節:Clone 和finalize 特性,以及強、軟、弱、虛引用
● 認識 Java 函數式編程:從 Lambda 表達式到方法引用 | 3 種方法引用
Java IO 相關文章
● 探索 Java IO 的奧秘,了解檔案操作、流處理、NIO等精彩內容!
深入 Java 多執行緒
● 這一系列文章將帶你深入了解 Java 多執行緒技術的各個方面,從基礎知識到進階應用,涵蓋了多執行緒編程的核心概念與實踐
深入 Java 物件導向
● 探索 Java 物件導向的奧妙,掌握介面、抽象類、繼承等重要概念!幫助你針對物件導向設計有更深入的了解!