`
idearye
  • 浏览: 5431 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

值得写一写的javac

阅读更多

很多书籍和网络教程关于Java编程环境搭建的内容,一般都言简意赅,赘述如下:

1、安装JDK and JRE,然后设置环境变量
2、新建"JAVA_HOME”变量,设置为JDK的安装路径
3、新建"CLASSPATH”变量,设置为"%JAVA_HOME%\lib\tools.jar;%JAVA_HOME%\lib\dt.jar;."
4、修改"Path”变量,补充"%JAVA_HOME%\bin”
5、编写一段程序,然后javac&java,Hello World

 

确实,搞定"Hello World”是一件挺简单的事,如果使用某些IDE甚至连上述步骤都可以略掉,javac和java他俩点了回名就被扔到了墙角。当然这无可厚非,强大的IDE让我们的编码工作变得单纯了许多。然而,他们果真不值一提么?

 

先想想下面这个小场景,如果刚刚读完场景你就完全明白怎么回事,请略过本文,很抱歉浪费了你看上文的时间;如果你确实还有些含糊,那不妨花上几分钟一起研究下。

 

场景描述:com.idearye.main.MyMain.java和com.idearye.friend.MyFriend.java两个文件,MyMain类import了MyFriend类,并在main方法中创建一个MyFriend对象,异常简单的场景。如果使用IDE,工程中创建两个包两个文件,然后build完事。但,如果不使用IDE,你是否还能正确的应用javac和java编译及运行这个程序?如果稍微再搞复杂一些,MyFriend类又import了另一个类com.idearye.enemy.MyEnemy,三个文件分处在三个包中,对你是否依然简单?

 

MyMain.java和MyFriend.java源代码如下:

 

package com.idearye.main;
import com.idearye.friend.*;
public class MyMain{
public static void main(String[] args){
    MyFriend myFriend = new MyFriend();
    }
}

package com.idearye.friend;
public class MyFriend{
    public MyFriend(){
    }
}
 

 

package是Java引入的非常漂亮的组织结构理念,合理的打包能让应用程序各模块得到有序的维护,因此在有点规模有点心思的程序中,基本不会出现所有文件都在同一目录的情况。

我的尝试:

 

第一步,直接在main目录中编译的后果是:

 

>> javac MyMain.java
>> MyMain.java:3: package com.idearye.friend does not exist
>> import com.idearye.friend.*;
>> ^
>> MyMain.java:7: cannot find symbol
>> symbol  : class MyFriend
>> location: class com.idearye.main.MyMain
>>         MyFriend myFriend = new MyFriend();
>>         ^
>> MyMain.java:7: cannot find symbol
>> symbol  : class MyFriend
>> location: class com.idearye.main.MyMain
>>         MyFriend myFriend = new MyFriend();
                                ^
>> 3 errors

 

 

 第二步,提示MyMain找不到com.idearye.friend.MyFriend类,如果我预先编译一下MyFriend类呢?并将MyFriend.class文件拷贝到main目录中,这下你总该能找到了吧?

 

>> cd friend
>> javac MyFriend.java  
>> 这步没有任何错误,将生成的class文件拷贝到main目录中
>> javac MyMain.java
>> MyMain.java:3: package com.idearye.friend does not exist
>> import com.idearye.friend.*;
>> ^
>> MyMain.java:7: cannot find symbol
>> symbol  : class MyFriend
>> location: class com.idearye.main.MyMain
>>         MyFriend myFriend = new MyFriend();
>>         ^
>> MyMain.java:7: cannot find symbol
>> symbol  : class MyFriend
>> location: class com.idearye.main.MyMain
>>         MyFriend myFriend = new MyFriend();
                                ^
>> 3 errors

 

嘿,当前路径下明明有class文件,而且系统的CLASSPATH环境变量中也有"."代表当前路径啊,编译器怎么还提示这个错误呢?

 

第三步,先不说原因,我们这样试试先,仍然回到main目录中

 

>> cd main
>> javac -sourcepath "D:\00_code\99_OTHER\Java\src" -classpath "D:\00_code\99_OTHER\Java\classes" -d "D:\00_code\99_OTHER\Java\classes" MyMain.java
>> 没有任何提示,编译成功,回到包的顶层,即"D:\00_code\99_OTHER\Java\",查看目录树
>> tree /f
>> ├─classes
>> │  └─com
>> │      └─idearye
>> │          ├─friend
>> │          │      MyFriend.class
>> │          └─main
>> │                  MyMain.class
>> └─src
>>     └─com
>>         └─idearye
>>             ├─friend
>>             │      MyFriend.java
>>             └─main
>>                     MyMain.java

 

 

或者,我们回到D盘根目录,玩一个跨度更大的!

 

>> javac -sourcepath "D:\00_code\99_OTHER\Java\src" -classpath "D:\00_code\99_OTHER\Java\classes" -d "D:\00_code\99_OTHER\Java\classes" D:\00_code\99_OTHER\Java\src\com\idearye\main\MyMain.java
>> 没有任何提示,编译成功,回到包的顶层,即"D:\00_code\99_OTHER\Java\",查看目录树
>> tree /f
>> ├─classes
>> │  └─com
>> │      └─idearye
>> │          ├─friend
>> │          │      MyFriend.class
>> │          └─main
>> │                  MyMain.class
>> └─src
>>     └─com
>>         └─idearye
>>             ├─friend
>>             │      MyFriend.java
>>             └─main
>>                     MyMain.java

 

 

恩,可以正确编译的程序和编译错误的程序,在于为javac指定了3个参数,"-sourcepath”、"-classpath”和"-d"。这三个参数对于编译器的意味着什么呢?

 

我们还是先说一下为何第一步与第二步会出现失败。一个类需要指明所属的包的,包路径与类名共同组成了该类的完整名称,即某某书上说的专业术语“完全限定类名”云云。编译器要找到一个类,需要这个“完全限定类名”。我们打一个比方,好比警察要找你,光知道你的名字是没用的,警察需要知道你是哪个国家、哪个省、哪个城市、哪个片区、哪个小区、哪个门牌号,当然,作为警察,他可以穷举这个世界所有叫你这个名字的人,然后一个个的排查,好比《盗墓笔记》中张大佛爷把全国叫张起灵的都揪出来,但,这需要大量资源,警察不会这么二,于是蛋生了蛋疼的户口本;编译器也不会这么二,于是蛋生了下一段:

 

MyMain类中引用了MyFriend类,而MyFriend类位于com.idearye.friend包中,编译器会从"classpath”参数指明的目录中寻找"MyFriend.class"文件,然后到"sourcepath"参数指明的目录中寻找"MyFriend.java"文件。如果同时找到这两个东西,则会判断它们蛋生的时间。如果MyFriend.java新于MyFriend.class,则会重新编译MyFriend.java,否则会直接使用MyFriend.class。如果只找到了MyFriend.java,就启动编译,如果只找到了MyFriend.class,就直接使用。如果都找到,就会抛出前两种场景中著名的“Cann't Find Symbol”的错误。

 

而"-d"参数指明了生成的class文件保存的根目录,如果类的源文件声明了所属包,则会在该目录中生成包对应的目录树结构,这个参数很简单。我们可以回想一下诸如Eclips创建工程时,你可以指明output或者bin目录,其实IDE就是根据这个参数来放置生成的class文件,同时也会自动从这个目录中开始寻找所需要的类。

 

MyMain类中引用了MyFriend类,而MyFriend类位于com.idearye.friend包中,编译器会从"classpath”参数指明的目录中寻找"MyFriend.class"文件,然后到"sourcepath"参数指明的目录中寻找"MyFriend.java"文件。如果同时找到这两个东西,则会判断它们蛋生的时间。如果MyFriend.java新于MyFriend.class,则会重新编译MyFriend.java,否则会直接使用MyFriend.class。如果只找到了MyFriend.java,就启动编译,如果只找到了MyFriend.class,就直接使用。如果都找到,就会抛出前两种场景中著名的“Cann't Find Symbol”的错误。

 

而"-d"参数指明了生成的class文件保存的根目录,如果类的源文件声明了所属包,则会在该目录中生成包对应的目录树结构,这个参数很简单。我们可以回想一下诸如Eclips创建工程时,你可以指明output或者bin目录,其实IDE就是根据这个参数来放置生成的class文件,同时也会自动从这个目录中开始寻找所需要的类。

 

上面这段话,其实很直白的告诉我们一个道理,“相对论”是智慧人玩的游戏。不认识你的人想找你,你就不能只说“我在XX房间等你”,如果你们同在一个城市,你可以告诉他“我在XX区XX旅馆XX房间等你”;如果你们在不同的城市甚至不同的国家,你就必须要指定更详细的地址,但无论你们分隔多远,只要你说“我在XX国-XX城市-XX区-XX旅馆-XX房间等你”,他/她想找你就一定能够找到。其实编译器最希望的,就是你明明白白告诉他去哪里找所需要的类。这也是为什么转好JDK后,要设定系统级CLASSPATH环境变量的缘故。你可以打开dt.jar和tools.jar看一看,里面都有哪些包,是不是经常在各种教材上看到过?指定好他们,无论你的JDK安装到哪,这些系统类库总是能够使用的。

 

同理再扩展一下,有过JavaEE开发经验的兄弟,有没有想过为啥Tomcat这样的Servlet容器,JDBC这样的jar驱动文件只要放到一些固定的目录中,你在源程序中就只需要直接引用就可以了?其实这些容器就是利用了这个原理,只是做了一层二次的封装。

 

如果明白了这个道理,我们可以回到上文的“第二步”,为什么把编译好的MyFriend.class文件拷贝到main目录中,MyMain.java仍然编译不过。你想啊,MyMain中引用了com.idearye.friend.MyFriend类,而编译的当前目录是什么,是"com\idearye\main",编译器就会傻傻从main目录中去找"com\idearye\friend\MyFriend.class"。编译器不是人,不会像你一样先想想要找的人,跟自己是不是一个城市的一个区一个旅馆的,它只是傻了吧唧的。

 

这样看,如果你退回到"src",即package的根目录中,直接执行"javac com\idearye\main\MyMain.java",一样编译成功,为啥?就是因为执行javac时的当前目录是src,虽然你没有加上"-sourcepath"和"classpath",但系统环境变量中有"."这个代表当前路径的设置,因此编译器就会从当前目录中同时分别去"com\idearye\main\"和"com\idearye\friend\"中寻找"MyMain.class"和"MyFriend.java"文件,流程同上,当然就能够找到了。这其实是变相告诉编译器相对路径,注意这里是相对路径的概念。

 

IDE工具就是在利用这些概念,帮你解决了寻找类文件的复杂。所以我们在IDE下面,很简单的做一些事情。写了这么一坨东西,并不代表我喜欢自找麻烦,我依然绝对会继续使用IDE,不会自己写常常的一串路径,我不是偏执狂。

 

原来的题目本来是"值得写一写的javac与java",其实虚拟机的运行原理和编译器一样,也是这么寻找与定位,因此不再赘述。java命令会有另外几个参数。我建议自己编译和运行时,多使用"-verbose"参数,看一看编译器和虚拟机对类的定位,这是一个挺有趣的东西。回头准备写一写虚拟机类定位的东西,这块也是一个容易被遗忘和忽略的知识点。

 

好了,全文完,感谢你看了如此多的废话,休息一下眼睛吧。

分享到:
评论

相关推荐

    Atom-linter-javac,关于保存的java Java。贡献给AtomCenter/Linter.zip

    Atom-linter-javac是专为Atom文本编辑器设计的一款插件,用于提供Java语言的实时语法检查功能。Atom是一款由GitHub开发的基于Web技术的开源文本编辑...对于初学者和经验丰富的开发者来说,都是一个值得推荐的插件选择。

    javac-source-code-reading:javac源码调试-java source code

    Java 编译器,javac,是 Java 语言的核心组成部分,负责将源代码转换成可执行的字节码。深入理解 javac 源码对于提升 Java 开发者的技能至关...这是一次深入底层、增强技术深度的旅程,值得每一个 Java 开发者去探索。

    javaC语言试题生成与考试系统.zip

    总的来说,"javaC语言试题生成与考试系统"是一个集成了Java编程、C语言教学、数据库管理和在线考试技术的综合性应用,对于教育行业来说,它是一个值得推广的工具,能够推动教育信息化的发展,提升教学质量。...

    JAVAC5441 学生成绩管理.rar

    标题中的“JAVAC5441 学生成绩管理.rar”表明这是一个关于Java编程的项目,具体是用于管理学生成绩的系统。这个系统可能使用了Java和JSP技术来实现,JSP(JavaServer Pages)是Java Web开发中的一种技术,常用于构建...

    用于文本文件处理的Java语言控制台程序示例

    本例中,会读取一个文本文件,...值得注意的是,javac 命令是 Java 开发工具包 (JDK) 的一部分,而 java 命令则是 Java 运行时环境 (JRE) 的一部分。如果只安装了 JRE 而没有安装 JDK,将无法使用 javac 命令。 ​

    一个简单的编辑器 实现文本的编辑

    值得注意的是,这个编辑器不仅限于文本编辑,它还能直接在内部编译和运行Java代码,这大大简化了开发者的工作流程,无需在多个工具之间切换。 具体来说,编辑器的“打开”功能允许用户从本地文件系统中选择并加载...

    5. 第一个Java程序_java_

    在Java编程中,还有一些基本概念值得了解,例如变量(用来存储数据的容器)、数据类型(如整型、浮点型、字符型等)、运算符(如算术运算符、比较运算符、逻辑运算符等)、流程控制(如条件语句if-else,循环语句for...

    jdk-8u201-linux-x64-rpm.zip,最后一个免费版本

    值得注意的是,尽管Oracle JDK 8u201之后的版本需要付费,但OpenJDK项目提供了一个开源的替代方案,OpenJDK 8和OpenJDK 11等版本仍然提供免费的长期支持。开发者可以选择使用OpenJDK来继续享受Java的最新更新,而...

    一个java程序员必备的10项技能

    - `javac`:Java编译器; - `javadoc`:生成文档; - `javap`:Java反汇编器; - `javaw`:无控制台窗口的Java启动器; - `native2ascii`:处理二进制数据到文本的转换; - `serialver`:验证序列化版本。 通过掌握...

    最新Java JDK 11安装版(Linux 64位)

    Java JDK 11是Oracle公司推出的Java开发工具集的一个重要版本,主要针对开发人员和系统管理员,用于在Linux 64位操作系统上构建、测试和部署Java应用...对于Linux 64位用户来说,这是一个重要的更新,值得安装并使用。

    java笔记.docx

    值得注意的是,一个Java源文件可以包含多个类,但最多只能有一个public类,且该类名必须与源文件名相同。 标识符在Java中扮演着重要角色,包括类名、变量名、方法名等。它们的命名规则遵循以下几点: 1. 可以由英文...

    一个JAVA的登陆系统

    Java是一种广泛使用的面向对象的编程语言,以其跨平台性、高效性和丰富的库而著名。在构建一个简单的Java...以上是构建一个Java登录系统涉及的主要知识点,每个方面都值得深入学习和实践,以提升编程技能和软件质量。

    java学习笔记JDK6课件之二

    值得注意的是,JDK6的`rt.jar`、`tools.jar`等系统库通常无需手动添加到Classpath,因为它们已被默认加载。 编写第一个Java程序通常从创建源文件开始。以“HelloJava.java”为例,文件名必须与主类名相同,并遵循大...

    使用ant打一个包

    在IT行业中,构建工具是开发过程中的重要环节,它们帮助我们自动化地编译、测试、打包和部署项目。Ant是Apache软件基金会的一个开源项目,它是一个基于...对于熟悉Java和XML的开发者来说,Ant是一个值得掌握的工具。

    甲骨文jdk最后最新的一个免费版本:jdk-8u202-windows-x64

    对于那些依赖Java 8特性的项目来说,这是一个值得信赖的选择,尤其是考虑到它作为甲骨文免费提供的最后一个版本。然而,随着Java 11及更高版本的发布,开发者也需要关注新版本带来的功能更新和长期支持计划。

    值得保存的—JAVA环境变量设置———文档

    安装完成后,JDK的bin目录会包含必要的可执行文件,如`javac`(编译器)和`java`(解释器)。 2. **设置JAVA_HOME**:这个环境变量指向JDK的安装目录。例如,如果JDK安装在`C:\Program Files\Java\jdk1.8.0_271`,...

    如何生成jar包

    值得注意的是,这里使用了`cf`参数,其中`c`表示创建一个新的JAR文件,`f`表示指定JAR文件名。另外,`-C`参数在创建和更新JAR包时可以用来切换到指定目录下执行jar命令,不过在示例中并没有使用。 最终,生成JAR包...

    dom4j-1.6.1.jar + jaxen-1.1.1.jar

    DOM4J和Jaxen是两个在Java编程中用于XML处理的重要库,它们在解析、操作和查询XML文档方面发挥着关键作用。 DOM4J是一个非常灵活且功能强大的...对于任何涉及到XML操作的Java项目,这两个库都是值得考虑的优秀选择。

    jdk-8u191-windows-x64.exe,jdk最后一个免费版本

    然而,值得注意的是,随着Oracle JDK 8的免费支持结束,开发者可能需要考虑迁移到OpenJDK或者其他开源实现,或者购买Oracle的商业支持服务,以确保持续的安全更新。 在使用"jdk-8u191-windows-x64.exe"进行安装时,...

Global site tag (gtag.js) - Google Analytics