当前位置: 首页 > 工具软件 > cp-class > 使用案例 >

javac -cp java-cp 看这篇就够了!

仲霍英
2023-12-01

一、背景:

平时我们用javac 或者 java执行程序可能比较少,入门时候用到的也是简单的类,没有package或者没有依赖关系或者没有用其他.jar包的,所以执行起来没啥问题。在Algorithems Froth Edition中,经常要用命令行模式来Test 算法性能。

二、问题

在排序算法-初级排序算法这一章,执行java SortCompare Insertion Selection 1000 100就遇到问题:找不到外部.jar包或者找不到引用的类。网上答案大体表达清楚了意思,但是不够简洁,思路不够清晰。这里就用这个例子梳理一下。
先看下我的SortCompare包结构:


└── com
    └── chm
        ├── algorithms
        │   ├── Example.java
        │   ├── Insertion.java
        │   ├── Selection.java
        │   └── SortCompare.java

我们目的是,流畅的执行以下命令


- javac SortCompare.java
- java SotrCompare Insertion Selection 1000 100 
- 

介绍下类之间依赖关系,SortCompare是关键。
SortCompare.java的头部信息是这样的

package com.chm.algorithms;

import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
import edu.princeton.cs.algs4.Stopwatch;

这表明SortCompare这个类用到了外部jar包(算法提供的基础包algs4.jar);同时该类直接调用了Insertion.sort()、Selection.sort()方法,Insertion.sort()和Selection.sort()调用了Example中封装好的方法。
一言以蔽之,他们都在com.chm.algorithms这个package下最核心部分

最终结果:

3288  0.9993866420331129  0.9998753497357498
For 1000 random Doubles
  Insertion is 1.0 times faster than Selection

如果你对javac cp 或者 java -cp 理解不透,那么过程自然不顺利,这里我们先结论先行

  • javac 编译.java文件,需要关注被编译的.java类是否有package,这个package表示该类所“认识”(默认)的当前目录,即java javac需要在package所在层级执行;(1)
  • javac -cp 或者java -cp 都表明需要手动指定外部.jar包,cp也意味着会重新指定需要编译的类的目录,即包下面相互依赖的类所在的目录;用了cp编译器就不会默认在"当前路径"去找类了,而是都需要你自己去显式指定路径(2)
  • 同一个包下的类依赖,只用关注顶层调用类,编译时,它所依赖的类自然会被编译。而不用像网上说的,先编译它所依赖的,再编译它。(不觉得累嘛?)(3)

知道这2点就够了,这2点不是凭空而来,执行 man javac 后的cp介绍很清楚,后文在回头细说。

没有package的类自然不用说,就在当前目录执行javac java即可;过程像小溪一样顺利;
我们这里的例子是比较复杂的:
我们应该在com这个级别执行javac 或者执行java,而不是跑到SortCompare.java所在的目录.这点最核心的,知道这点+结论中的(2)+(3),你的一切疑问都会烟消云散。
在这个目录执行命令,一会介绍为什么:

/Users/myIdeaTest/my2020/202002/src/test/java

第一步:执行javac命令:


Mac下:
javac -cp ~/Downloads/algs4.jar:  com/chm/algorithms/SortCompare.java

Windows下:
javac -cp ~/Downloads/algs4.jar;  com/chm/algorithms/SortCompare.java(未亲尝试)



注意:
1)algs4.jar包就是SortCompare.java用到的外部.jar包,放在了家目录下;

2)指定多个路径时,Mac和Window区别是:
    1、多个.jar包或者目录Mac用:分开,Wind用;分开;
    2、表达当前目录的方式,Mac用空格或者./;Winddow下大家知道这里不一样即可。
比如我这里的命令

javac -cp ~/Downloads/algs4.jar:  com/chm/algorithms/SortCompare.java
或者
javac -cp ~/Downloads/algs4.jar:./  com/chm/algorithms/SortCompare.java
在Mac下都是可以的。网上CSDN部分博客不介绍清楚上下文,导致用;总是出问题。

你会发现与SortCompare相关的类自动都被编译了,因为它们的包相同。需要提前先编译好Example.class Insertion.class Selection.class嘛?NO!

javac后的包结构:

.
└── com
    └── chm
        ├── algorithms
        │   ├── Example.class
        │   ├── Example.java
        │   ├── Insertion.class
        │   ├── Insertion.java
        │   ├── Selection.class
        │   ├── Selection.java
        │   ├── SortCompare.class
        │   └── SortCompare.java

第二步:执行java命令

Mac下:
java -cp ~/Downloads/algs4.jar: com/chm/algorithms/SortCompare Insertion Selection 1000 100

搞定!

现在详细解释下结论1)2)3)。如果你想知道我是怎么得出结论的,请耐心往下看:

1)这里的SortCompare有package,它用到的类(Example.java Insertion.java Selection.java)和它在一个包下,所以不用import;外部的.jar包自然需要import。执行javac编译它的时候,它所依赖的自然要能找到。javac怎么知道去哪里找呢?当然是当前目录。这个当前目录就是指的package。也就是SortCompare.java这个类,人家头部已经标明,我在这个包下面。如果我们跑到com/chm/algorithms这个目录去javac SortCompare.java,会提示找不到外部包,类似这样:


[@MacBook-Air:algorithms (develop)]$ pwd
/Users/myIdeaTest/my2020/202002/src/test/java/com/chm/algorithms
[@deMacBook-Air:algorithms (develop)]$ ls
Example.java     Insertion.java   Selection.java   SortCompare.java
[@deMacBook-Air:algorithms (develop)]$ javac SortCompare.java
SortCompare.java:3: 错误: 程序包edu.princeton.cs.algs4不存在
import edu.princeton.cs.algs4.StdOut;
                             ^
SortCompare.java:4: 错误: 程序包edu.princeton.cs.algs4不存在
import edu.princeton.cs.algs4.StdRandom;
                             ^
SortCompare.java:5: 错误: 程序包edu.princeton.cs.algs4不存在
import edu.princeton.cs.algs4.Stopwatch;
                             ^
SortCompare.java:12: 错误: 找不到符号
        Stopwatch timer = new Stopwatch();
        ^
  符号:   类 Stopwatch
  位置: 类 SortCompare

好,找不到外部包还不简单,加上就可以了

javac -cp ~/Downloads/algs4.jar SortCompare.java
SortCompare.java:14: 错误: 找不到符号
            Insertion.sort(a);
            ^
  符号:   变量 Insertion
  位置: 类 SortCompare
SortCompare.java:17: 错误: 找不到符号
            Selection.sort(a);
            ^
  符号:   变量 Selection
  位置: 类 SortCompare
2 个错误

这里提示找不到Insertion 和Selection,他们不是和SortCompare在一个包下嘛?怎么会找不到?让我们再次回头大声朗读边结论(1),还是不大懂吧?没关系。我们在这个目录下执行javac的时候,编译器发现SortCompare.java所依赖的类在com.chm.algorithm包下面,所以就从SortCompare.java所在的目录往下寻找com.chm.algorithm.Insertion.java,这个目录往下哪里还有这个包呢?恍然大悟了吧?

因此,这就是SortCompare.java所"认识"的包的真实含义。所以我们需要到com包所在的这个层级执行,也就是在package的层级执行javac 或者java.

好的,那我们就回到package所在的目录去执行上面命令:

[@deMacBook-Air:java (develop)]$ pwd
/Users/myIdeaTest/my2020/202002/src/test/java
[@deMacBook-Air:java (develop)]$ ls
com
[@deMacBook-Air:java (develop)]$ tree
.
└── com
    └── chm
        ├── algorithms
        │   ├── Example.java
        │   ├── Insertion.java
        │   ├── Selection.java
        │   └── SortCompare.java
      

6 directories, 11 files
[@dzjdeMacBook-Air:java (develop)]$ javac ~/Downloads/algs4.jar com/chm/algorithms/SortCompare.java
javac: 无效的标记: /Users/Downloads/algs4.jar
用法: javac <options> <source files>
-help 用于列出可能的选项 //这里没报错了
[@deMacBook-Air:java (develop)]$ tree
.
└── com
    └── chm
        ├── algorithms
        │   ├── Example.java
        │   ├── Insertion.java
        │   ├── Selection.java
        │   └── SortCompare.java

没报错了,但是没编译成功!没有.class文件。为什么?

这就涉及到结论(2)了,请返回结论,大声朗读几遍结论(2)
使用-cp 意味着指定了java 或者javac去哪里找所编译的类依赖的.jar包,同时也意味着需要指定所编译的类所以来的类的路径。看一下这个

 -cp path or -classpath path
              Specifies where to find user class files, and (optionally) annotation processors
              and source files. This class path overrides the user class path in the CLASSPATH
              environment variable. If neither CLASSPATH, -cp nor -classpath is specified, then
              the user class path is the current directory. See Setting the Class Path.


这里的class files表明去哪里找.java文件;
这里的source files表明去哪里找.jar文件
所以
javac -cp ~/Downloads/algs4.jar SortCompare.java
是不够的,还需要显式的指定被编译的类所在的包路径。因为-cp 表明你要手动指定所有目录,而不是让编译命令自动用”当前“的路径。

好了。介绍结束了,过程有点啰嗦,结论很简洁。

 类似资料: