命令行执行java命令提示【错误:找不到或无法加载主类】问题分析

Posted by 石福鹏 on 2018-02-17

前言

一些从事java开发的开发人员,从一开始就用集成开发环境,像eclipseIDEA等优秀的IDE,很少再用javajavac这样的命令了,但是有时候就需要我们直接用JDK提供的java命令来运行class文件,但是最常遇到的一个问题就是**“错误: 找不到或无法加载主类 XXXXX”**,但是用IDE却没有任何问题。

image-20200924155311712

百度上一查,太多的文章提供解决方案了,无外乎都是环境变量的配置,尤其是CLASSPATH,这是当然,但是还是不能解决该怎么办?

网上有许多的解决方案,也都是轻描淡写,泛泛而谈

我们来分析下可能会出现的问题:

分析

  • 首先检查环境变量,尤其是CLASSPATH的配置是否正确,如下图:

image-20200924155626863

​ java命令是通过CLASSPATH来寻找编译后的class文件的,这个问题很好解决,基本上网上的每篇文章都有说到,但是需要注意的是,记得在CLASSPATH环境变量中加上.,即指支持在当前目录寻找class文件,例如:

1
2
3
4
5
6
JAVA_HOME=/Users/Steven/Library/Java/JavaVirtualMachines/azul-1.8.0_252/Contents/Home
PATH=$JAVA_HOME/bin:$PATH:.
CLASSPATH=$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:.
export JAVA_HOME
export PATH
export CLASSPATH
  • 理解CLASSPATH是怎么寻找class文件的

    举一个目录结构略微复杂的例子看一下 ⬇️(该例子是使用FileInputStream读取文件中的字节并打印出来)

    image-20200924161017053

    1、上面的例子是直接在idea中创建了一个类文件TestFileInputStream,网上许多人都说是package包的问题,那我们先把这个package删除了试试(即类文件不指定package)

    image-20200924161315255

    使用命令行进入到该类文件所在的目录,然后编译、运行

    image-20200924161555490

    是没有任何问题,难道说就是包名的问题?但是编译的时候带着package太正常了,总不能把package删除了编译运行吧,先试试加上package运行的结果吧

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    package com.shifpeng.scaffold.test;

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;

    public class TestFileInputStream {
    public static void main(String[] args) {
    int b = 0;
    FileInputStream in = null;
    try {
    in = new FileInputStream("/Users/Steven/GitRepositories/scaffold/src/main/java/com/shifpeng/scaffold/test/TestFileInputStream.java");
    } catch (FileNotFoundException e) {
    System.out.printf("找不到指定文件");
    }

    try {
    long num = 0;
    while ((b = in.read()) != -1) {
    System.out.printf((char) b + "");
    num++;
    }
    in.close();
    System.out.printf("一共读到了" + num + "个字节");
    } catch (IOException e) {
    e.printStackTrace();
    System.out.printf("文件读取失败");
    }
    }
    }

    然后编译运行,发现报错:错误: 找不到或无法加载主类 TestFileInputStream

    1
    2
    3
    4
    ➜  test git:(develop) ✗ javac TestFileInputStream.java
    ➜ test git:(develop) ✗ java TestFileInputStream
    错误: 找不到或无法加载主类 TestFileInputStream
    ➜ test git:(develop) ✗

    按照java规定,我们应该按照package定义的路径来存放源文件,该类应该放在src/main/java/com/shifpeng/scaffold/test/,而我们刚刚就是在这个路径下执行的,有问题

    再来~

    一个类的完整的包名应该是什么呢,包名+类名,那是不是在编译的时候指定一下包名就可以了,试试

    image-20200924162539132

    1
    2
    3
    ➜  test git:(develop) ✗ javac TestFileInputStream.java
    ➜ test git:(develop) ✗ java com.shifpeng.scaffold.test.TestFileInputStream
    错误: 找不到或无法加载主类 com.shifpeng.scaffold.test.TestFileInputStream

    还是不对,猜想会不会还是路径的问题,因为我们是在/Users/Steven/GitRepositories/scaffold/src/main/java/com/shifpeng/scaffold/test下执行的,再加上com.shifpeng.scaffold.test.TestFileInputStream,是不是路径重复了呀

    再来~

    按照上面的猜想,我们移步/Users/Steven/GitRepositories/scaffold/src/main/java/

image-20200924163156212

​ 果然成功了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
➜  test git:(develop) ✗ cd /Users/Steven/GitRepositories/scaffold/src/main/java/
➜ java git:(develop) ✗ java com.shifpeng.scaffold.test.TestFileInputStream
package com.shifpeng.scaffold.test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class TestFileInputStream {
public static void main(String[] args) {
int b = 0;
FileInputStream in = null;
try {
in = new FileInputStream("/Users/Steven/GitRepositories/scaffold/src/main/java/com/shifpeng/scaffold/test/TestFileInputStream.java");
} catch (FileNotFoundException e) {
System.out.printf("找不到指定文件");
}

try {
long num = 0;
while ((b = in.read()) != -1) {
System.out.printf((char) b + "");
num++;
}
in.close();
System.out.printf("一共读到了" + num + "个字节");
} catch (IOException e) {
e.printStackTrace();
System.out.printf("文件读取失败");
}
}
}
一共读到了944个字节%

结论

  • java命令在执行class文件的时候是根据CLASSPATH指定的目录寻找的,不是一般理解的当前目录,如果需要支持在当前目录中寻找,需要在CLASSPATH环境变量的配置中加入.;;
  • java执行class文件对package的路径是强依赖的,它在执行的时候会严格以当前用户路径为基础,按照package指定的包路径转化为文件路径去搜索class文件