深入了解 Java 應用與編譯:從原始檔到命令產出 JavaDoc 文件 | JDK 結構

深入了解 Java 應用與編譯:從原始檔到命令產出 JavaDoc 文件 | JDK 結構

Overview of Content

本文將深入介紹Java應用的各個方面,從原始檔的結構到 Java 程式的進入點 main 函數。

我們將探討JDK管理應用的基本結構,包括 javac 編譯原始文件、java 執行應用以及 jar 打包和解壓。此外,我們還將介紹JavaDoc文件的撰寫與生成,包括JavaDoc標籤的使用和javadoc命令的執行。無論您是初學者還是有經驗的開發者,這篇文章都將為您提供全面且實用的指導,幫助您更好地理解和應用Java應用與編譯的相關知識。

寫文章分享不易,如有引用參考請詳註出處,如有指導、意見歡迎留言(如果覺得寫得好也請給我一些支持),感謝 😀


Java 應用與編譯

Java 應用由多附檔名為 .java 的檔案構成(Java 原始檔),以編譯的角度來看則是待編譯的 編譯單元 (Compilation Unit)


public class HelloJava {
    
}

Java 原始檔:結構

● Java 原始檔內可以包含以下內容,這些內容有部份有一些限制

內容關鍵字說明限制
package套件宣告敘述,描述該類在 Java 套建中的位置非必須,但如果存在,那 只能有一個、必須 在檔案最頂端(除了註解之外)
import套件引用0 ~ 多個
class0 ~ 多個,下面會多做說明
interface界面0 ~ 多個

● JDK 有提供一些基本 Java 套件,我們可以用 package 將其做一些簡單的分類;JDK 的所有套件類也被稱為 J2SE API

JDK 套件名功能概述
java.lang包含 ThreadExceptionSystemIntegerString… 等等類(它是 JVM 自動引用的套件
java.awtjava.swing抽象工具箱套件,可用來建立 GUI 畫面
java.ioInput/Output 套件
java.util工具類別,包含 DateCollection… 等等
java.net支援 TCP/IP 網路協定,包含 Socket、URL 相關的類別
javax.*對基本套件的擴充 (聲音程式 javax.sound)

package 套件說明

套件 package 非必須,而如果有,那 只能有一個、必須 在檔案最頂端(除了註解之外;其路徑使用 . 取代 / 符號

package 描述是相對於 Java 包的路徑 (路徑為 src/main/java 或是 src/main/kotlin 之下)

假設我的原始擋在專案中的路徑為 /src/main/java/hello/FirstJava.java,那 package 則是描述為 package hello;

● 而套件的作用如下

A. 透過 package 對路徑的描述,能區分名稱相同的類名

同類名用在同一個檔案中,就必須使用 全路徑呼叫類 編譯器才不會搞錯

B. 受到存取許可全的約束(Java 有檔案區域許可全的約束)

C. 有助於劃分、組織 Java 應用的是的類別,將相關類匯聚且分類

class 類說明class 關鍵字用來聲明該檔案中有哪些類,而 Java 檔名、public 類 關係 有兩種狀況,如下

A. 一個 Java 檔中,可以不必有 public 類


class HelloJava {

}

class WorldJava {
    
}

B. 如果有 public 描述的類,那 Java 原始檔檔名也必須與這個 public 描述的類相同


// 檔名必須為 HelloJava.java
public class HelloJava {

}

class WorldJava {
    
}

import 類說明

import 關鍵字用來引入不同 package 路徑的類,並且 依照寫法會引入不同的類

A. 不同的引入方式


// 只引入 ~/hello/world/FirstImport 類
import hello.world.FirstImport;

// 引入 ~/hello/world/ 包下的所有類
import hello.world.*;

// 引入 ~/hello/ 包下的所有類
import hello.*;

B. 要注意的是 * 號只會引入該目錄下的所有類,並不包括子資料夾的類


// 只引入 ~/hello/world/FirstImport 類
import hello.world.FirstImport;

// 引入 ~/hello/ 包下的所有類,**不包括 world 資料夾內的類!**
import hello.*;

對編譯速度的影響

import` 用來告訴編譯器該檔案存在套件中的哪裡,如果你能 清晰的指出套件位置,那就可以加快編譯器搜尋檔案的速度

引入就代表了初始化

Nonono~ 使用 import 時,不會導致類別的初始化(不會觸發 ClassLoader 加載類)

Java 程式進入點 main

main 方法是 Java 應用程式的進入點,它必須存在於某個類中(包含程式進入點 main 的類,稱之為主程式類別(mainclass);main 方法須符合以下四個條件

A. 存取限制 public

B. 靜態方法 static

C. 參數限制 String[] args

D. 返回 void


// 主程式類別為 HelloJava

public class HelloJava {

    // 必須四個條件都符合
    public static void main(String[] args) {

    }

}

● 怎樣對 main 函數傳入參數?

首先先使用 javac 編譯源碼,再使用 java 命令執行以下格式就可以對 main 函數傳入參數


javac <javasource>

java [options] <mainclass> [args...]

JDK 管理應用:結構、編譯、打包

管理 Java 應用程式是指 建立 Java 應用程式的目錄結構、編譯、執行、發布 Java 應用程式的操作;下表為 Java 應用常用的開發目錄

目錄說明
src存放 Java 原始檔
classes編譯過後的 Java 類別檔 (路徑與 src 原始檔批配)
lib其他 .jar 包
docdec/api存放 JavaDoc 相關文件
deploy存放 Java 應用程式的包裹檔案(Jar 文件)

JDK 結構簡介

● JDK Java Development Kit 它提供 Java 應用程式基礎的開發、執行環境;其中包括以下重點組件

A. Java 虛擬機:負責 解析執行 Java 程式,它代替我們與各平台差異的機器溝通(因此 Java 應用不受限於平台限制)

B. JDK 庫:如先前介紹,提供開發者基礎的各個套件使用

C. 開發工具:開發工具都是些可執行程式,如下

JDK 提供的開發工具功能
javac編譯工具
java執行工具
javadoc生成 JavaDoc 文件的工具
jar打包、解開 Java 包的工具

javac 編譯原文件

● javac 命令用於編譯 Java 原始檔,命令格式如下


javac [options] [sourcefiles]

常見的 option 如下

javac option說明
-nowarn不輸出警告資訊
-verbose輸出編譯執行中的詳細資訊
-deprecation輸出程式中 Deprecated API 的具體位置
-classpath <路徑>覆蓋 classpath 環境變數,用來指定編譯時搜尋相關檔案的路徑(常用在第三方 jar 包),如果沒指定則預設當前目錄
-sourcepath <路徑>指定 Java 原始檔的路徑
-d <目錄>編譯後預計輸出 class 到哪個目標目錄

-classpath 指定的是編譯時尋找可用 .class 的目錄;

如果你要編譯的檔案他所需要的 .class 檔案在同層目錄,那你應該指定它的上層目錄,而不是上層目錄

A. 源碼目錄apple.java 依賴 banana.java


# 目錄
src test/
|-- apple.java
|-- banana.java

B. 分開來編譯

● 先編譯 apple.java,下達以下命令會產生 apple.class


# 切換到目錄
cd test/

javac apple.java

● 再編譯 banana.java,下達以下命令會產生 banana.class


# 由於你已經在源碼目錄內,所以必須指定其上層目錄!
# 因為 javac 會尋找的是目錄,假設你指定當前目錄 . 或是 `pwd` 
# 那它會找不到 class 檔案

javac -classpath .. banana.java

範例:當前目錄如下

A. CLI 切換到 Main.java 目錄之下並執行以下命令


javac -verbose \
    -sourcepath `pwd` \
    -classpath ../classes \
    -d ../classes \
    Main.java 

● 編譯器先到 -classpath 指定的目錄下尋找是否有 Main.class 檔案,如果沒有再到 -sourcepath 指定的目錄下尋找 Main.java

● 如果同時找到 Main.classMain.java 檔案,那就會比較 class 檔案是否過期,如果過期就重新編譯 java 檔

● 如果只找到 Main.class 就直接用

● 如果只找到 Main.java 就將其編譯程 class 檔

● 都找不到就拋出錯誤!

B. javac 就會將 Java 原始檔編譯,並放置 classes 目錄之下

java 執行應用

● java 命令用於執行編譯過後的 class,命令格式如下


java [options] <classfiles>

常見的 option 如下

java option說明
-verbose輸出執行中的詳細資訊
-classpath <路徑>覆蓋 classpath 環境變數,用來指定編譯時搜尋相關檔案的路徑(常用在第三方 jar 包),如果沒指定則預設當前目錄
-D<屬性=數性值>這個數性值會定應到 System API,System.getProperty("屬性") 中
-jar執行 Jar 檔案中的特定 Java 類別(必須給予完整路徑

範例

A. 沒有包(Package)的類

停留在 src/ 目錄下,並執行以下命令就可以執行 class 檔


java -classpath ../classes/ Main

如果有多個 classpath 可以使用 ; 符號分離不同路徑

eg. java -classpath ../classes/; ../classesA/; ../classes/B; Main

尋找 classpath

classpath 會從最近的設定開始尋找,尋找的優先級如:命令設定的 -classpath > shell 設定的 classpath > 系統設定的 classpath

package 設定

運行時 java 會判斷 class 檔案內的 package 值,並到對應資料夾中找 class 檔案,如果找不到則會拋出

B. 有包(Package)的類

呼叫某個包內 Class 內部的 Main,呼叫的格式 <包名*>.<類名>


package samplePackage;

public class Apple {

    public static void main(String[] args) {
        System.out.println("Hello Apple");
    }

}

編譯、執行指令如下


javac src/samplePackage/Apple.java 

# 指定到 src 目錄下找 class
# 找 samplePackage 包下的 Apple
java -classpath ./src samplePackage.Apple 

● 而 -classpath 的設定則要依照你要呼叫的類的 package & 你當前的路徑去做設定

當前路徑是 /Users/Hy-KylePan/IdeaProjects/JavaTest

Class 檔案是在當前路徑下的 src/samplePackage/Apple.class

而 Apple.class 的 package 是 samplePackage,相較之下是缺少了 ./src 目錄,所以 -classpath 要設定 ./src

jar 打包、解開

● jar (Java archive) 命令用於打包 class 檔案(可多個),將其包裹到 JAR 檔,命令格式如下


jar [options] [sourcefiles]

常見的 option 如下

jar option說明
-c創建 JAR 檔案
-v輸出打包、解開時的詳細資訊
-f指定輸出的目錄
-x解開 JAR 檔案

範例

A. 停留在 src/ 目錄下,並執行以下命令,打包一個 myJar.jar 檔案到 deploy 目錄下


# 尚未編譯請先編譯
javac ../src/com/example/*.java -sourcepath ../src/ -d ../classes/

jar -cvf ./myJar.jar ../classes/com/example/*.class 

可使用 *.class 代表該目錄下的所有文件

B. JAR 包也可以透過 jar 命令解開;以下將 myJar.jar 解開,並將內容放置當前目錄(沒有指定)


jar -xvf ./myJar.jar

META-INF/MANIFEST.MF 檔案

該檔案描述了 JAR 檔的資訊


Manifest-Version: 1.0
Created-By: 17.0.7 (JetBrains s.r.o.)

● 而我們前面有提到 java 命令可以指定 -jar 並找到對應的類執行,如果要達成這個條件就要稍微修改一下解開的 jar 包;步驟如下

A. classes 目錄下創建 Manifest.txt 檔案


touch classes/Manifest.txt

B. 將主程式類別寫入 Manifest.txt 檔案


Main-Class: com.example.Main

要寫入 全類名!(由於當前範例就是直接在 java 目錄下,所以很單純)

● 坑:必須以換行結束

C. 重新打包 classes 目錄下的檔案,在這邊會 m 來指定 MANIFEST


jar -cvfm ../deploy/specificManifest.jar \
    ./classes/Manifest.txt *.*

● 測試使用 java 並指定 jar 執行


java -jar /home/alien/IdeaProjects/java_learn/deploy/specificManifest.jar

產出 JavaDoc 文件

Java 類可以透過 JavaDoc 文件來對外公佈方法用法;JavaDoc 文件是 基於 HTML 格式 的說明文件,而 JDK 也提供給我們產生說明檔的 javadoc 命令

在原始 Java 檔中,從 /** 註解到 */ 結束,內部包含的文件就會輸出到 HTML 中

● 如果註解是在 package 描述前,那就會被 javadoc 命令忽略


/**
 * <p><strong>This is main class</strong></p>
 */
public class TestMain {
    /**
     * @param  args is input from user or other process.
     */
    public static void main(String[] args) {
        System.out.println("Hello world!");
    }
}

JavaDoc 標籤

● 可在註解中使用特殊符號幫助 JavaDoc 建立標籤,符號如下

JavaDoc 標籤描述
@version指定版本資訊
@since可說明該方法、類最早出現的版本
@author指定作者
@see生成參考其他 JavaDoc 文檔
@link生成參考其他 JavaDoc 文件的連結(與 @see 差別在,它可嵌入到註解中,並為特定註解中的特定詞彙生成連結)
@deprecated標示該方法、類已經被棄用
@param描述方法的參數(不可以用來描述類)
@return描述方法的返回值
@throws描述方法拋出的例外

A. @see 範例


/**
 * <p><strong>This is main class</strong></p>
 */
public class TestMain {
    /**
     * @param  args is input from user or other process.
     */
    public static void main(String[] args) {
        System.out.println("Hello world!");
    }

    /**
     * Default value is 0
     */
    private int value = 0;

    /**
     * @see #value show self param of `value`.
     * @see #setValue(int) Use this method to set value.
     */
    public void showValue() {
        System.out.println("Value: " + value);
    }

    /**
     * Set new value for instance that TestMain.
     * @param value new value.
     */
    public void setValue(int value) {
        this.value = value;
    }
}

執行以下命令生成檔案


javadoc -public -protected -private -sourcepath src/ -d ./doc com.example 

B. @link 範例:與 @see 差別在,它可嵌入到 HTML 註解中形成連接,範例如下


/**
 * <p><strong>This is link class</strong></p>
 */
public class TestLink {

    boolean isStart = false;

    /**
     * <ul>
     *     <li>if {@link #isStart isStart} is false, show sleeping.</li>
     *     <li>if {@link #isStart isStart} is true, show working.</li>
     * </ul>
     */
    public void showState() {
        if (isStart) {
            System.out.println("Start working.");
        } else {
            System.out.println("Sleeping.");
        }
    }

}

執行以下命令生成檔案


javadoc -public -protected -private -sourcepath src/ -d ./doc com.example 

javadoc 命令

● javadoc 命令可以用來分析源碼並通過源碼的註解去生產 HTML,指令格式如下


javadoc [options] [packagenames] <sourcefiles>

● 如果 sourcefiles 要使用目錄的話,須使用 . 替換 / 符號;如果是指定某個檔案就不用替換,繼續保持 / 符號

以下幾是 javadoc 常用的 Options

Options作用是否預設
-public只為 public 級別生成 Javadoc 文件N
-protected只為 public、protected 級別生成 Javadoc 文件Y
-package只為 public、protected、package 級別生成 Javadoc 文件N
-private為所有級別生成 Javadoc 文件N
-version解析 @version 標籤N
-author解析 @author 標籤N
-splitindex將索引分為每個字母對應一個索引檔N
-sourcepath <路徑>指定 Java 原始碼路徑Y (預設為當前目錄)
-classpath <路徑>指定 classpathY (預設為當前目錄)
-d <目錄>指定 Javadoc 文件的輸出目錄Y (預設為當前目錄)

範例

A. 生成指定指定文件


javadoc -protected \
    -sourcepath src/ \
    -d ./doc \
    src/com/example/TestLink.java

● 如果有特別指定文件,那就 javadoc 不會自動處理別的文件

B. 透過 index 標記生成的檔案(方便尋找)


javadoc -splitindex \
    -protected \
    -sourcepath src/ \
    -d ./doc \
    com.example

更多的 Java 語言相關文章

Java 語言深入

● 在這個系列中,我們全方位地探討了 Java 語言的各個核心主題,旨在幫助你徹底掌握這門強大的編程語言。無論你是想深入理解 Java 的基礎類型與變數作用域,還是探索異常處理與運算子的細節,這些文章都將為您提供寶貴的知識

深入 Java 物件導向

● 探索 Java 物件導向的奧妙,掌握介面、抽象類、繼承等重要概念!幫助你針對物件導向設計有更深入的了解!


Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

發表迴響