zoukankan      html  css  js  c++  java
  • 【JDK命令行 一】手动编译Java源码与执行字节码命令合集(含外部依赖引用)

    写作目标

    记录常见的使用javac手动编译Java源码和java手动执行字节码的命令,一方面用于应对 Maven 和 Gradle 暂时无法使用的情况,临时生成class文件(使用自己的jar包);另一方面了解下构建工具做了哪些工作。

    作者水平有限,行文中如有错误,希望评论告知,自当尽快修复。

    一、编译源码

    1. javac 命令

    编译Java源码都是使用 javac 命令完成的,其语法如下:

    javac [ options ] [ sourcefiles ] [ classes] [ @argfiles ]
    
    • options:选项参数,比如-cp,-d
    • sourcefiles:java源文件,多个文件以空格分开
    • classes:用来处理处理注解
    • @argfiles,就是包含 option 或 java 文件列表的文件路径,用@符号开头,就像上面的@javaOptions.txt和@javaFiles.txt

    2. 编译仅使用 JDK 类库源码

    javac sourcefiles
    

    示例:

    Main.java

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

    编译Main.java

    javac Main.java
    

    3. 指定文字编码

    默认将使用平台的字符集,如Windows是GBK。为了防止乱码一般指定为 utf-8

    javac -encoding encoding sourcefiles 
    

    示例:

    #指定utf-8编码编译
    javac -encoding utf-8 Main.java
    

    4. 指定输出字节码路径

    class文件将输出到指定路径下,如果有package,也会一并在指定路径下创建

    javac -d path sourcefiles 
    

    示例:

    #生成字节码到classes目录中
    javac -d classes Main.java
    

    注意:指定的目录需要提前创建

    5. 指定classpath

    指定JVM查找用户类文件、注解解释器和源文件的目录,即字节码、源码等的查找位置。

    classpath确定流程:先从环境变量 CLASSPATH 中获取,当用户指定classpath时将覆盖环境变量,如果没有环境变量且未用户设定,将以执行javac的路径向下查找。

    #有以下两种写法,二者等效
    javac -cp path sourcefiles 
    javac -classpath path sourcefiles 
    #path可以使用通配符*来匹配目录下一级jar包或class文件,比如下列写法
    #javac -cp "libs/*" sourcefiles 
    

    示例:

    引用 FastJson 的Main.java

    import com.alibaba.fastjson.JSONObject;
    
    public class Main  {
        public static void main(String[] args) {
            JSONObject json = new JSONObject();
            json.put("hello", "world");
            System.out.println(json.toJSONString());
        }
    }
    

    编译Main.java,fastjson的jar与Main.java同级目录,直接写jar包作为classpath仅适用于单个jar包引用时

    javac -cp fastjson-1.2.73.jar Main.java
    javac -classpath fastjson-1.2.73.jar Main.java
    

    当设置需要设置多个目录作为classpath时,在不同平台的写法不大一样

    Linux/Unix平台

    javac -cp "path1/*:path2/*" sourcefiles
    javac -classpath "path1/*:path2/*" sourcefiles
    

    Windows平台

    javac -cp "path1*;path2*" sourcefiles
    javac -classpath "path1*;path2*" sourcefiles
    

    不同点仅在于多个目录间使用 : 还是 ; 作为路径分割符、目录分割符是 / 还是

    6. 指定外部目录

    指定外部目录,javac 在编译字节码时将会从下列目录中读取字节码或Jar包,完成编译。

    #有以下两种写法,二者等效
    javac -extdirs directories sourcefiles 
    -Djava.ext.dirs=directories sourcefiles 
    

    示例:

    编写Main.java引用多个jar包,指定外部目录编译

    import com.alibaba.fastjson.JSONObject;
    import org.apache.commons.lang3.StringUtils;
    
    public class Main  {
        public static void main(String[] args) {
            JSONObject json = new JSONObject();
            json.put("hello", "world");
            System.out.println(json.toJSONString());
            System.out.println(StringUtils.equals("1", "1"));
        }
    }
    

    fastjsoncommons.lang3 处于同目录 libs 时,编译命令:

    javac -extdirs libs Main.java
    javac -Djava.ext.dirs=libs Main.java
    

    7. 带package源码编译

    Java使用目录作为package定位字节码,减少了重名问题,编译方式是类似的,可使用通配符来匹配待编译的 .java 文件

    #src目录下一级目录查找java源码文件编译
    javac src/*.java
    #使用以上方式可能会少编译一些深层次目录下的源码,推荐使用操作系统的命令来查找
    #Linux平台
    javac $(find src -name "*.java")
    #Windows平台
    where -r src *.java #收集源文件列表
    javac <源文件列表> #手动拼接源文件路径,多个源文件以空格分开,如 javac package/A.java package/B.java
    

    此种方式编译少量源文件还可以,源文件过长就会出现命令参数过长报错,可以参考下面章节中的 使用参数文件简化命令 解决此问题

    8. 编译有依赖关系的源码

    两种方法:

    1. 按顺序编译分别编译(编译被依赖,再编译依赖)
    2. javac 自动编译(将要编译的源文件列表全部给到 javac 命令后,顺序无所谓)

    示例:

    PrintService.java

    public class PrintService {
        public void print(String msg){
            System.out.println(msg);
        }
    }
    

    Main.java

    public class Main  {
        public static void main(String[] args) {
            PrintService printService = new PrintService();
            printService.print("Hello World!");
        }
    }
    

    1.按顺序编译:

    javac PrintService.java
    javac Main.java
    

    2.自动编译

    javac Main.java PrintService.java
    

    9. 使用参数文件简化命令

    参数文件可以是javac命令中的部分内容,比如可以是java文件的路径列表文件,将参数保存为文本中供编译时引用,缩短执行命令长度,避免命令行参数过长报错。

    可匹配源码目录下的java文件列表作为参数文件

    find src -name "*.java" > sourcefiles.txt
    

    sourcefiles.txt

    Main.java
    PrintService.java
    

    接着就可以通过 @+sourcefiles.txt 对列表文件进行引用,放到javac命令行中

    #生成字节码与源码同目录
    javac @sourcefiles.txt
    #指定存在的目录输出字节码
    javac -d target @sourcefiles.txt
    

    当然不仅是源码列表,还可以加上参数,如图

    10. 编译脚本示例

    10.1. Linux编译脚本 compile.sh

    源码文件处在 src 目录中,创建输出目录 target, 依赖包目录 lib

    在工程目录下创建如下编译脚本:

    compile.sh

    #!/bin/bash
    # 编译脚本
    # @author: Hellxz
    
    #出现变量取值失败、报错立即停止
    set -eu
    
    #定义变量
    SOURCE=src
    TARGET=target
    LIBRARY=libs
    
    #清理历史编译结果
    [ -d "${TARGET}" ] && rm -rf ${TARGET}/*
    
    #输出参数文件
    echo "-d ${TARGET} -encoding utf-8" > argsfile
    find ${SOURCE} -name "*.java" >> argsfile
    #编译源文件
    if [ -d "${LIBRARY}" ]; then
      javac -cp "${LIBRARY}/*" @argsfile
    else
      javac @argsfile
    fi
    #删除参数文件
    rm -rf argsfile
    echo "compile success!"
    

    10.2. Windows编译脚本 compile.bat

    @echo off
    REM 源码目录
    set srcdir=src
    REM 目标目录
    set targetdir=target
    REM jar包外部依赖目录
    set libsdir=libs
    
    REM 清理缓存
    if exist %targetdir% (
      echo clean target caches...
      del /f /s /q %targetdir%*.*
      rd /s /q %targetdir%
      md %targetdir%
      echo clean caches success!
    ) else (
      md %targetdir%
    )
    
    REM 生成参数文件
    echo generating argsfile.txt
    echo -d %targetdir% -encoding utf-8 > argsfile.txt
    where -r %srcdir% *.java >> argsfile.txt
    echo generate argsfile success!
    
    REM 编译
    echo compiling...
    if exist %libsdir% (
     javac -cp "%libsdir%*" @argsfile.txt
    ) else (
     javac @argsfile.txt
    )
    
    REM 删除参数文件
    del /f /q /a argsfile.txt
    echo compile success!
    pause
    

    二、执行字节码

    1. java 执行字节码命令

    java 命令用于执行 javac编译出的字节码文件,启动 Java 虚拟机。

    java 命令语法为:

    java [options] classname [args]
    
    • options:选项参数,包含Java虚拟机参数等设定
    • classname:字节码文件,.class 后缀的文件
    • args:参数,将作为 main 方法的参数传入程序中

    options参考:https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html#CBBIJCHG

    2. 执行字节码文件

    一般而言,执行 Java 程序直接用 java 命令就可以

    #执行带main方法的字节码
    java mainclass
    

    3. 执行带package的字节码

    当源码中有package提示包名时,执行的class需要放在层层包名目录中,举例:

    package samples;
    public class Main  {
        public static void main(String[] args) {
            System.out.println("Hello World");
        }
    }
    

    编译后Main.class 的位置在 /opt/target/samples/Main.class

    执行java命令就需要进到 /opt/target 下,与第一层包目录平级

    cd /opt/target
    java samples/Main.class
    

    不进入包名目录上级,可以设置 classpath 来指定待执行查找class的起点

    java -cp /opt/target samples/Main
    

    4. 执行有外部依赖关系的字节码

    src/samples/Main.java

    package samples;
    import com.alibaba.fastjson.JSONObject;
    
    public class Main  {
        public static void main(String[] args) {
            JSONObject json = new JSONObject();
            json.put("hello", "world");
            System.out.println(json.toJSONString());
        }
    }
    

    外部依赖libs目录、源码目录src、生成class目录target,src下有一个包为samples的Main.java,如下图

    编译src目录源码,生成字节码到target下

    javac -cp "src:libs/*" -d target $(find src -name "*.java")
    

    设置classpath,执行字节码文件

    java -cp "target:libs/*" samples.Main
    

    参考资料:

    https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javac.html

    https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html

    https://zhuanlan.zhihu.com/p/74229762

    本文首发博客园Hellxz博客,https://www.cnblogs.com/hellxz/p/14819191.html
    同步于CSDN 拾级而上,https://blog.csdn.net/u012586326/article/details/117335227

  • 相关阅读:
    Linux系统下手把手完成无人值守安装服务
    2019年国际网络安全趋势
    Wi-Fi Mesh网络技术
    如何用nginx在本地把9000端口转发到80端口上
    LAMP架构部署和动态网站环境的配置
    智能校服受到多数学生追捧
    热心网友设计出更美的Windows 10开始菜单
    一起来全面解析5G网络领域最关键的十大技术
    如何跟上时代的步伐进入云世界
    c#写入文件流
  • 原文地址:https://www.cnblogs.com/hellxz/p/14819191.html
Copyright © 2011-2022 走看看