默认的classpath
The default class path is the current directory. Setting the CLASSPATH variable or using the -classpath command-line option overrides that default, so if you want to include the current directory in the search path, you must include "." in the new settings.
默认的classpath
是.
, 即当前的工作目录. 设置环境变量CLASSPATH
或者使用java
的-classpath
(或者-cp
)参数会覆盖这一默认值. 如果希望包括.
到classpath
中, 应该将其加入到.
加入到新设置中. 如,
java -cp .:/another/path:another/path2 package.class.to.run
一般不要依赖于全局的CLASSPATH
, 这样很容易导致修改环境变量或者将程序拷贝到其他电脑上后, 影响程序的运行.
Class.getResource()和ClassLoader.getResource()
使用
package path.classpath;
public class ClassPath {
public static void main(String[] args) {
System.out.println(System.getProperty("java.class.path"));
System.out.println(ClassPath.class.getResource(""));
System.out.println(ClassPath.class.getResource("/"));
System.out.println(ClassPath.class.getClassLoader().getResource(""));
}
}
运行:
java path.classpath.ClassPath
输出:
.
file:/home/smallfly/programming_projects/java/JavaLanguageFeatures/out/production/features/path/classpath/
file:/home/smallfly/programming_projects/java/JavaLanguageFeatures/out/production/features/
file:/home/smallfly/programming_projects/java/JavaLanguageFeatures/out/production/features/
null
可以看到Class.getResource
的两种用法:
- 使用相对路径(不以
/
开头)
那么根目录设定为这个类的class文件所在的目录下 - 使用绝对路径(以
/
开头)
那么根目录设定为这个类的顶级package下. 比如说A.class
的路径为App/classes/package1/package2/A.class
, 那么跟目录就设定为App/classes/
ClassLoader.getResource
用法:
- 相对路径
跟目录设置在顶级package下 - 绝对路径
不支持这种用法, 直接会返回null
原理
Class类的getResource
方法:
public java.net.URL getResource(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResource(name);
}
return cl.getResource(name);
}
处理name
参数的resolveName
方法:
/**
* Add a package name prefix if the name is not absolute Remove leading "/"
* if name is absolute
*/
private String resolveName(String name) {
if (name == null) {
return name;
}
if (!name.startsWith("/")) {
Class<?> c = this;
while (c.isArray()) {
c = c.getComponentType();
}
String baseName = c.getName();
int index = baseName.lastIndexOf('.');
if (index != -1) {
name = baseName.substring(0, index).replace('.', '/')
+"/"+name;
}
} else {
name = name.substring(1);
}
return name;
}
可以看到, 实际上Class
类的getResource
最终还是调用了ClassLoader
的getResource
方法.
从resolveName
方法中, 可以看到对name
做了以下处理:
- 文件名以
/
开头, 即绝对路径
直接返回去掉开头的/
字符的子字符串 - 文件名是相对路径
首先得到当前类的类名(全限定名称). 注意对数组类型的处理, 因为数组的类名在Java
里面是比较奇怪的, 所以那个while
循环是为了得到数组的元素类型.
然后将该类的package
中的.
替换为目录分隔符/
, 然后拼接传入的name.