Java基础20-从IDE的实现原理聊起,谈谈那些年我们用过的Java命令(二)

简介: Java基础20-从IDE的实现原理聊起,谈谈那些年我们用过的Java命令(二)

Java基础20-从IDE的实现原理聊起,谈谈那些年我们用过的Java命令(一):https://developer.aliyun.com/article/1535731

-verbose

输出详细的编译信息,包括:classpath、加载的类文件信息。

比如,我写了一个最简单的HelloWorld程序,在命令行中输入:

D:Java>javac -verbose -encoding UTF-8 HelloWorld01.java

输出:

    [语法分析开始时间 RegularFileObject[HelloWorld01.java]]
    [语法分析已完成, 用时 21 毫秒]
    [源文件的搜索路径: .,D:\大三下\编译原理\cup\java-cup-11a.jar,E:\java\jflex\lib\J           //-sourcepath
    Flex.jar]
    [类文件的搜索路径: C:\Java\jdk1.7.0_25\jre\lib\resources.jar,C:\Java\jdk1.7.0_25      //-classpath、-bootclasspath、-extdirs
    省略............................................
    [正在加载ZipFileIndexFileObject[C:\Java\jdk1.7.0_25\lib\ct.sym(META-INF/sym/rt.j
    ar/java/lang/Object.class)]]
    [正在加载ZipFileIndexFileObject[C:\Java\jdk1.7.0_25\lib\ct.sym(META-INF/sym/rt.j
    ar/java/lang/String.class)]]
    [正在检查Demo]
    省略............................................
    [已写入RegularFileObject[Demo.class]]
    [共 447 毫秒]复制代码

编写一个程序时,比如写了一句:System.out.println("hello"),实际上还需要加载:Object、PrintStream、String等类文件,而上面就显示了加载的全部类文件。

其他命令

-J <标记>•传递一些信息给 Java Launcher.

    javac -J-Xms48m   Xxx.java          //set the startup memory to 48M.复制代码

-@<文件名>

如果同时需要编译数量较多的源文件(比如1000个),一个一个编译是不现实的(当然你可以直接 javac *.java ),比较好的方法是:将你想要编译的源文件名都写在一个文件中(比如sourcefiles.txt),其中每行写一个文件名,如下所示:

HelloWorld01.java

HelloWorld02.java

HelloWorld03.java

则使用下面的命令:

javac @sourcefiles.txt

编译这三个源文件。

使用javac构建项目

这部分参考:https://blog.csdn.net/mingover/article/details/57083176

一个简单的javac编译

新建两个文件夹,src和 buildsrc/com/yp/test/HelloWorld.javabuild/

├─build
└─src
    └─com
        └─yp
            └─test
                    HelloWorld.java复制代码

java文件非常简单

    package com.yp.test;
    public class HelloWorld {复制代码
        public static void main(String[] args) {
            System.out.println("helloWorld");
        }
    }
编译:
javac src/com/yp/test/HelloWorld.java -d build复制代码

-d 表示编译到 build文件夹下

查看build文件夹
├─build
│  └─com
│      └─yp
│          └─test
│                  HelloWorld.class
└─src
    └─com
        └─yp
            └─test
                    HelloWorld.java复制代码

运行文件

E:codeplacen_learnjavajavacmd> java com/yp/test/HelloWorld.class

错误: 找不到或无法加载主类 build.com.yp.test.HelloWorld.class

运行时要指定main

E:codeplacen_learnjavajavacmdbuild> java com.yp.test.HelloWorld

helloWorld

如果引用到多个其他的类,应该怎么做呢 ?

编译

E:codeplacen_learnjavajavacmd>javac src/com/yp/test/HelloWorld.java -sourcepath src -d build -g

1

-sourcepath 表示 从指定的源文件目录中找到需要的.java文件并进行编译。

也可以用-cp指定编译好的class的路径

运行,注意:运行在build目录下

E:codeplacen_learnjavajavacmdbuild>java com.yp.test.HelloWorld

怎么打成jar包?

生成:

E:codeplacen_learnjavajavacmdbuild>jar cvf h.jar *

运行:

E:codeplacen_learnjavajavacmdbuild>java h.jar

错误: 找不到或无法加载主类 h.jar

这个错误是没有指定main类,所以类似这样来指定:

E:codeplacen_learnjavajavacmdbuild>java -cp h.jar com.yp.test.HelloWorld

生成可以运行的jar包

需要指定jar包的应用程序入口点,用-e选项:

    E:\codeplace\n_learn\java\javacmd\build> jar cvfe h.jar com.yp.test.HelloWorld *
    已添加清单
    正在添加: com/(输入 = 0) (输出 = 0)(存储了 0%)
    正在添加: com/yp/(输入 = 0) (输出 = 0)(存储了 0%)
    正在添加: com/yp/test/(输入 = 0) (输出 = 0)(存储了 0%)
    正在添加: com/yp/test/entity/(输入 = 0) (输出 = 0)(存储了 0%)
    正在添加: com/yp/test/entity/Cat.class(输入 = 545) (输出 = 319)(压缩了 41%)
    正在添加: com/yp/test/HelloWorld.class(输入 = 844) (输出 = 487)(压缩了 42%)复制代码

直接运行

    java -jar h.jar复制代码
    额外发现 
    指定了Main类后,jar包里面的 META-INF/MANIFEST.MF 是这样的, 比原来多了一行Main-Class….
    Manifest-Version: 1.0
    Created-By: 1.8.0 (Oracle Corporation)
    Main-Class: com.yp.test.HelloWorld复制代码

如果类里有引用jar包呢?

先下一个jar包 这里直接下 log4j

    * main函数改成复制代码
    import com.yp.test.entity.Cat;
    import org.apache.log4j.Logger;复制代码
    public class HelloWorld {复制代码
        static Logger log = Logger.getLogger(HelloWorld.class);复制代码
        public static void main(String[] args) {
            Cat c = new Cat("keyboard");
            log.info("这是log4j");
            System.out.println("hello," + c.getName());
        }复制代码
    }复制代码

现的文件是这样的

├─build
├─lib
│      log4j-1.2.17.jar
└─src
    └─com
        └─yp
            └─test
                │  HelloWorld.java
                └─entity
                        Cat.java复制代码
    这个时候 javac命令要接上 -cp ./lib/*.jar
    E:\codeplace\n_learn\java\javacmd>javac -encoding "utf8" src/com/yp/test/HelloWorld.java -sourcepath src -d build -g -cp ./lib/*.jar
    复制代码
    运行要加上-cp, -cp 选项貌似会把工作目录给换了, 所以要加上 ;../build
    E:\codeplace\n_learn\java\javacmd\build>java -cp ../lib/log4j-1.2.17.jar;../build com.yp.test.HelloWorld复制代码

结果:

    log4j:WARN No appenders could be found for logger(com.yp.test.HelloWorld).
    log4j:WARN Please initialize the log4j system properly.
    log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
    hello,keyboard复制代码

由于没有 log4j的配置文件,所以提示上面的问题,往 build 里面加上 log4j.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
    <log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
        <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
            <layout class="org.apache.log4j.PatternLayout">
                <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n" />
            </layout>
        </appender>复制代码
        <root>
            <level value="info" />
            <appender-ref ref="stdout" />
        </root>
    </log4j:configuration>复制代码

再运行

    E:\codeplace\n_learn\java\javacmd>java -cp lib/log4j-1.2.17.jar;build com.yp.tes t.HelloWorld
    15:19:57,359 INFO  [HelloWorld] 这是log4j
    hello,keyboard复制代码

说明:这个log4j配置文件,习惯的做法是放在src目录下, 在编译过程中 copy到build中的,但根据ant的做法,不是用javac的,而是用来处理,我猜测javac是不能copy的,如果想在命令行直接 使用,应该是用cp命令主动去执行 copy操作

ok 一个简单的java 工程就运行完了但是 貌似有些繁琐, 需要手动键入 java文件 以及相应的jar包 很是麻烦,so 可以用 shell 来脚本来简化相关操作shell 文件整理如下:

    #!/bin/bash  
    echo "build start"  复制代码
    JAR_PATH=libs  
    BIN_PATH=bin  
    SRC_PATH=src  复制代码
    # java文件列表目录  
    SRC_FILE_LIST_PATH=src/sources.list  复制代码
    #生所有的java文件列表 放入列表文件中 
    rm -f $SRC_PATH/sources  
    find $SRC_PATH/ -name *.java > $SRC_FILE_LIST_PATH  复制代码
    #删除旧的编译文件 生成bin目录  
    rm -rf $BIN_PATH/  
    mkdir $BIN_PATH/  复制代码
    #生成依赖jar包 列表  
    for file in  ${JAR_PATH}/*.jar;  
    do  
    jarfile=${jarfile}:${file}  
    done  
    echo "jarfile = "$jarfile  复制代码
    #编译 通过-cp指定所有的引用jar包,将src下的所有java文件进行编译
    javac -d $BIN_PATH/ -cp $jarfile @$SRC_FILE_LIST_PATH  复制代码
    #运行 通过-cp指定所有的引用jar包,指定入口函数运行
    java -cp $BIN_PATH$jarfile com.zuiapps.danmaku.server.Main  复制代码

有一点需要注意的是, javac -d $BIN

PATH/ -cp $jarfile @$SRC

FILE

LIST

PATH

在要编译的文件很多时候,一个个敲命令会显得很长,也不方便修改,

可以把要编译的源文件列在文件中,在文件名前加@,这样就可以对多个文件进行编译,

以上就是吧java文件放到 $SRC

FILE

LIST_PATH 中去了

    编译 :
         1. 需要编译所有的java文件
         2. 依赖的java 包都需要加入到 classpath 中去
         3. 最后设置 编译后的 class 文件存放目录  即 -d bin/
         4. java文件过多是可以使用  @$SRC_FILE_LIST_PATH 把他们放到一个文件中去
    运行:
       1.需要吧 编译时设置的bin目录和 所有jar包加入到 classpath 中去
      复制代码

javap

javap是jdk自带的一个工具,可以对代码反编译,也可以查看java编译器生成的字节码。

情况下,很少有人使用javap对class文件进行反编译,因为有很多成熟的反编译工具可以使用,比如jad。但是,javap还可以查看java编译器为我们生成的字节码。通过它,可以对照源代码和字节码,从而了解很多编译器内部的工作。

javap命令分解一个class文件,它根据options来决定到底输出什么。如果没有使用options,那么javap将会输出包,类里的protected和public域以及类里的所有方法。javap将会把它们输出在标准输出上。来看这个例子,先编译(javac)下面这个类。

    import java.awt.*;
    import java.applet.*;复制代码
    public class DocFooter extends Applet {
            String date;
            String email;复制代码
            public void init() {
                    resize(500,100);
                    date = getParameter("LAST_UPDATED");
                    email = getParameter("EMAIL");
            }
    }复制代码

在命令行上键入javap DocFooter后,输出结果如下

Compiled from "DocFooter.java"

    public class DocFooter extends java.applet.Applet {
      java.lang.String date;
      java.lang.String email;
      public DocFooter();
      public void init();
    }复制代码

如果加入了-c,即javap -c DocFooter,那么输出结果如下

Compiled from "DocFooter.java"

    public class DocFooter extends java.applet.Applet {
      java.lang.String date;复制代码
      java.lang.String email;复制代码
      public DocFooter();
        Code:
           0: aload_0       
           1: invokespecial #1                  // Method java/applet/Applet."<init>":()V
           4: return       复制代码
      public void init();
        Code:
           0: aload_0       
           1: sipush        500
           4: bipush        100
           6: invokevirtual #2                  // Method resize:(II)V
           9: aload_0       
          10: aload_0       
          11: ldc           #3                  // String LAST_UPDATED
          13: invokevirtual #4                  // Method getParameter:(Ljava/lang/String;)Ljava/lang/String;
          16: putfield      #5                  // Field date:Ljava/lang/String;
          19: aload_0       
          20: aload_0       
          21: ldc           #6                  // String EMAIL
          23: invokevirtual #4                  // Method getParameter:(Ljava/lang/String;)Ljava/lang/String;
          26: putfield      #7                  // Field email:Ljava/lang/String;
          29: return       复制代码
    }
上面输出的内容就是字节码。复制代码

-help 帮助-l 输出行和变量的表-public 只输出public方法和域-protected 只输出public和protected类和成员-package 只输出包,public和protected类和成员,这是默认的-p -private 输出所有类和成员-s 输出内部类型签名-c 输出分解后的代码,例如,类中每一个方法内,包含java字节码的指令,-verbose 输出栈大小,方法参数的个数-constants 输出静态final常量总结

javap可以用于反编译和查看编译器编译后的字节码。平时一般用javap -c比较多,该命令用于列出每个方法所执行的JVM指令,并显示每个方法的字节码的实际作用。可以通过字节码和源代码的对比,深入分析java的编译原理,了解和解决各种Java原理级别的问题。


目录
相关文章
|
1月前
|
存储 算法 Java
滚雪球学Java(65):深入理解Java中的Map接口:实现原理剖析
【6月更文挑战第19天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
21 3
滚雪球学Java(65):深入理解Java中的Map接口:实现原理剖析
|
1月前
|
存储 缓存 算法
滚雪球学Java(62):HashSet的底层实现原理解析
【6月更文挑战第16天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
20 3
滚雪球学Java(62):HashSet的底层实现原理解析
|
1月前
|
存储 Java 测试技术
滚雪球学Java(61):从源码角度解读Java Set接口底层实现原理
【6月更文挑战第15天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
31 1
滚雪球学Java(61):从源码角度解读Java Set接口底层实现原理
|
28天前
|
Java Linux
Java执行Linux命令
Java执行Linux命令
29 2
|
1月前
|
存储 监控 算法
深入理解Java的垃圾回收机制(GC)实现原理
深入理解Java的垃圾回收机制(GC)实现原理
48 1
|
18天前
|
存储 Java
Redis08命令-Hash类型,也叫散列,其中value是一个无序字典,类似于java的HashMap结构,Hash结构可以将对象中的每个字段独立存储,可以针对每字段做CRUD
Redis08命令-Hash类型,也叫散列,其中value是一个无序字典,类似于java的HashMap结构,Hash结构可以将对象中的每个字段独立存储,可以针对每字段做CRUD
|
22天前
|
Java
Java中多态的实现原理解析
Java中多态的实现原理解析
|
26天前
|
存储 Java 调度
深入探索Java并发编程:ConcurrentSkipListSet的高效使用与实现原理
深入探索Java并发编程:ConcurrentSkipListSet的高效使用与实现原理
|
26天前
|
存储 缓存 算法
ConcurrentHashMap的演进:从Java 8之前到Java 17的实现原理深度剖析
ConcurrentHashMap的演进:从Java 8之前到Java 17的实现原理深度剖析
|
1月前
|
安全 Java 数据安全/隐私保护
深入理解java中Unsafe类及其实现原理
深入理解java中Unsafe类及其实现原理
27 0