非常好的一篇文章,解释了classpath的一些常识问题。
原文http://kevinboone.net/classpath.html
几个常见的关于classpath的问题,大家可以思考一下,并在下面的文章中寻找答案。
1,为什么在使用jar时,为什么需要在classpath中指定 -classpath /root/lib/1.jar? 可以让java class loader自己去搜索指定目录下的所有jar包吗?
2,-classpath /root/lib/test 时,class loader会去搜索 /root, /lib下的所有jar包或class文件吗?所有test下的子目录呢?
3,在-classpath中指定多个时,用:分割。这时load class有先后顺序吗?
4,在-classpath 中指定某个目录时,如果该目录下包含多个class文件,每次load class的先后顺序固定吗?
This article describes in detail how the Java compiler and the JVM use the classsearch path to locate classes when they are referenced by other Java code. Itdoes this with reference to a very simple example, which uses two classesin the same package. We will see how various operations to compile these twoclasses succeed and fail, depending on the class path setting.
To make things absolutely clear, we will use only simple command-line toolsto carry out the compile operations. Interactive development tools have theirown ways of manipulating the class path, which vary from product to product.
There is no fundamental difference between the way that the Java compilersearches for classes, and the way that the JVM does it at run time. However,the compiler has the ability to compile classes from source code, wherethe JVM does not. In the examples below we will use the compiler, butsimilar issues apply at run time.
com.web_tomorrow.CPTest1
and
com.web_tomorrow.CPTest2
, which are listed below.
package com.web_tomorrow; public class CPTest1 { public static void main(String[] args) { System.out.println ("Run CPTest1.main()"); } } package com.web_tomorrow; public class CPTest2 { public static void main(String[] args) { System.out.println ("Run CPTest2.main()"); CPTest1 cpt1 = new CPTest1(); } }One of the most fundamental rules of Java code organization is that `package name = directory name'. We will begin by setting up a directorystructure that matches the package assignment of these two classes.The classes are in a package
com.web_tomorrow
, so we must createthe directory
com/web_tomorrow
to contain the source code.
[root] com web_tomorrow CPTest1.java CPTest2.javaIn this document I will use the notation
[root]' to mean
whatever directorycontains the structure described above', that is, the root of the directory layout.This will vary, of course, according to how you install the files.
CPTest1.java
on its own using the command-line
javac
program. To disable the class searchpath completely (so any existing setting does not interfere with theexample), we can run
javac
with the option `
-classpath ""
'.
As a first attempt, let's change directory to the location of CPTest1.java
,and try to compile it by specifying its name on thejavac
command line.
cd [root]/com/web_tomorrow javac -classpath "" CPTest1.java
这里加一句:The class path tells the JDK tools and applications where tofind third-party and user-defined classes -- that is, classes thatare not extensions or part of the Java platform.
因此对于上面这种仅使用了java core的代码来说,不需要另外指定classpath了。
This operation succeeds, because the compiler is able to find CPTest1.java
(it is in the working directory), and becauseCPTest1
does notreference any other classes. The output file,CPTest1.class
ends up in thesame directory as CPTest1.java
because, again, you haven't given thecompiler information to do anything else. So far so good.Now let's try the same thing withCPTest2
. Still in the `web_tomorrow'directory, execute this command:
javac -classpath "" CPTest2.javaThis operation should fail, even though the directory is the same as the previous step,and
CPTest1
and
CPTest2
are in the same package. The error message will be something like this:
PTest2.java:7: cannot resolve symbol symbol : class CPTest1 location: class com.web_tomorrow.CPTest2 CPTest1 cpt1 = new CPTest1(); ^The difference between this case and the previous, successful, one is that
CPTest2
contains a reference to
CPTest1
:
CPTest1 cpt1 = new CPTest1();What is going on here? When the compiler encounters the reference to
CP1Test
here, it assumes that this is a class in the samepackage as
CP2Test
that is is currently compiling. This isa correct assumption. So the compiler needs to find
com.web_tomorrow.CP1Test
. But it has nowhere to look, aswe have explicitly set the class search path to "" (i.e., nothing).
You might think this problem can be resolved by telling the compiler tolook in the current directory. The standard symbol for `current directory'is a single period (.) in both Unix and Windows systems. So try somethinglike this:
javac -classpath "." CPTest2.javaThis fails in exactly the same way as the previous example. The problem now is that although
CPTest1.java
is in the currentdirectory, the class that it implements is not just
CPTest1
,but
com.web_tomorrow.CPTest1
. The compiler will look fora directory
com/web_tomorrow
below the current directory.So, overall, it is looking for a Java source or class file in the directory
[home]/com/web_tomorrow/com/web_tomorrow
which, of course,does not exist.
To make this compile operation work, we need to make the class search pathreference not the directory containingCPTest1
, but a directoryroot from whichCPTest1
can be located by the compiler followingthe standard Java `package name = directory name' rule. This shouldwork, although it's rather ugly:
javac -classpath "../.." CPTest2.javaBefore seeing how we can make this less ugly, consider this example (stillin the same directory):
javac -classpath "" CPTest1.java CPTest2.javaThis also works, even though the class path is empty. This is because the Java compiler will look for references betweenany source code explicitly listed on the command line. If there are many classes, allin the same directory, we can simplify this to:
javac -classpath "" *.javaThe `*.java' expands to a list of all the .java files in the current directory.This explains why compiling many files in one operation often succeeds where attemptsto compile a single file fails.
A more convenient way to compile CPTest2
on its own is like this:
cd [root] javac -classpath "." com/web_tomorrow/CPTest2.javaIn this example we specify the full path to
CPTest2.java
, but include`.' in the
-classpath
option. Again, we aren't telling the compiler tolook for files in the current directory, we are telling it to
begin a class searchfrom the current directory. Because the class we are looking for is
com.web_tomorrow.CPTest1
, the compiler will search in
./com/web_tomorrow
(that is, the directory
com/web_tomorrow
below the current directory). This is exactly where
CPTest1.java
islocated.
In fact, even though I only specified CPTest2
on the command line, thispractice does in fact lead to the compilation ofCPTest1
as well.The compiler finds the.java
file in the right place, but it can't tellwhether this Java source really implements the right class, so it has to compileit. But note that if we do this:
cd [root] javac -classpath "." com/web_tomorrow/CPTest1.javait does not cause a compilation of
CPTest2.java
, because the compilerdoes not need to know anything about
CPTest2
to compile
CPTest1
.
.class
files alongside the
.java
files from which they were generated. Thisis a simple scheme, and very widely used. However, many developers like tokeep the source tree free of generated files, and must therefore tell the Javacompiler to maintain separate directories for
.class
files. Let'ssee what impact this has on the class search path.
To begin we will need to delete any .class
files lurking around afterthe previous examples. We will also contain a new directoryclasses
to contain the generated.class
files. The procedure at the command line would be something like this:
cd [root] rm com/web_tomorrow/*.class mkdir classesDon't forget to swap the `/' characters for '\' if you are using a Windows system.The directory structure now looks like this.
[root] com web_tomorrow CPTest1.java CPTest2.java classesLet's compile
CPTest1.java
, specifying
classes
as thedestination directory (using the
-d
option):
cd [root] javac -d classes -classpath "" com/web_tomorrow/CPTest1.javaThis should succeed, but you should notice that the
.class
fileshave not been placed into the
classes
directory at all.Instead, we have a new directory structure like this:
[root] com web_tomorrow CPTest1.java CPTest2.java classes com web_tomorrow CPTest1.classWhat has happened is that the compiler has created a directory structure to matchthe package structure. It has done this to be helpful, as we shall see. When wecome to compile
CPTest2.java
we have two choices. First, we cancompile it as described above, allowing the compiler to compile
CPTest1
as part of the process. Alternatively, we can compile it and use the
-classpath
option to refer to the compiler to the
.class
file generated in theprevious step. This method is superior, as we don't have to repeat the compilation of
CPTest1
.
cd [root] javac -d classes -classpath classes com/web_tomorrow/CPTest2.javaBy doing this, we end up with this directory structure.
[root] com web_tomorrow CPTest1.java CPTest2.java classes com web_tomorrow CPTest1.class CPTest2.classOf course we could have compiled both
.java
files in the same command,and got the same result.
myclasses.jar
in directory
/myclasses
. To have the Java compiler look for classes in this jar,we need to specify:
javac -classpath /myclasses/myclasses.jar ...and not merely the directory
myclasses
.
javac
to search in only onedirectory at a time. In practice, your class search path will containnumerous directories and JAR archives. The
-classpath
option to
javac
and
java
allows multiple entries to be specified, but notice that the syntax is slightly differentfor Unix and Windows systems.
On Unix, we would do this:
javac -classpath dir1:dir2:dir3 ...whereas on Windows we have:
javac -classpath dir1;dir2;dir3 ...The reason for the difference is that Windows uses the colon (:) character as partof a filename, so it can't be used as a filename separator. Naturally the directory separator character is different as well: forward slash (/) for Unix andbackslash (\) for Windows.
javac
command line, wecan make use of a `system' class path. This is the class path that will beused by both the Java compiler and the JVM in the absence of specific instructionsto the contrary. In both Unix and Windows systems, this is done by setting an environmentvariable. For example, in Linux with the
bash
shell:
CLASSPATH=/myclasses/myclasses.jar;export CLASSPATHand in Windows:
set CLASSPATH=c:\myclasses\myclasses.jarThis procedure is fine for short-term changes to the system CLASSPATH, but ifyou want these changes to be persistent you will need to arrange this yourself.Details vary from system to system. On a Linux system, for example, I wouldput the commands in the file
.bashrc
in my home directory. OnWindows 2000/NT there is a `Control Panel' page for this.
Setting the system CLASSPATH is a useful procedure if you have JARs full of classes that you use allthe time. For example, if I am developing Enterprise JavaBean (EJB)applications using Sun's J2EE `Reference Implementation', all the EJB-relatedclasses are in a JAR called `j2ee.jar
' that comes with thedistribution. I want this JAR on the class search path all the time.In addition, most people want to ensure that the current directoryis on the search path, whatever the current directory happens to be.So in my .bashrc
file I have this line:
CLASSPATH=/usr/j2ee/j2ee.jar:.;export CLASSPATHwhere the
.' indicates
current directory'.
It is easy to overlook that the -classpath
optionon the command linereplaces the default, system class path; itdoes not add to it. So what should I do if I want to set the class pathto include the default system classpathplus some other entries?I could simply use the -classpath
option and list thedefault entries in addition to my extras. However, a better way is toreference the CLASSPATH environment variable. The syntax for thisis different — of course — on Windows and Unix systems.On Unix:
javac -classpath $CLASSPATH:dir1:dir2 ...where
$CLASSPATH
expands to the current setting of the CLASSPATHenvironment variable. On Windows:
javac -classpath %CLASSPATH%;dir1:dir2 ...Finally, please note that if directories in your class search path havespaces in their names, you may have to use double-quotes on the commandline to prevent the CLASSPATH being split up. For example:
javac -classpath "%CLASSPATH%";dir1:dir2 ...
下面是另外一个帖子,关于如何找到程序使用的classpath:
http://blog.csdn.net/onlyqi/article/details/8194357
如上面文章所诉,我们通常不会使用默认的CLASSPATH环境变量,而是在运行java程序时,使用-classpath参数来指定class path。因此查看系统进程,就可是知道正在运行的java程序使用的-classpath参数是什么。
另外两篇官网的文章,介绍的更全面一些,必看。
Setting the class path
http://download.java.net/jdk8/docs/technotes/tools/solaris/classpath.html
How Classes are Found
http://download.java.net/jdk8/docs/technotes/tools/findingclasses.html