javac、javap命令行执行 java 程序的一点小坑

前言

2016校园招聘季,正在复习 java,想使用 javacjavajavap等命令分析String s = "a" + "b" + "c" + "d" 会创建几个对象,没想到执行java 命令总是报错。

本文链接 http://www.alijava.com/javac-error/ 转载请注明出处

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
TomChens-MBP:util tomchen$ pwd
/Users/tomchen/Documents/javaEclipse/workspaceJava/OnlineOJ/src/util
TomChens-MBP:util tomchen$ ls
Main.java
TomChens-MBP:util tomchen$ cat Main.java
package util; //包名
public class Main {
public static void main(String[] args) {
String s = "a" + "b" + "c" + "d";
System.out.println(s);
}
}

Main.java 是在 Eclipse 工程下的util包里(注意就是这个 util包的影响!!)

javac

在目录src/util 执行 javac,没有问题

1
2
3
TomChens-MBP:util tomchen$ javac Main.java
TomChens-MBP:util tomchen$ ls
Main.class Main.java
  • javac 命令的使用方式是 javac [文件名(带后缀名 .java)]
  • java 命令是 java [文件名(不带后缀名)]

javap 字节码反编译

依然在目录src/util下执行javap

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
TomChens-MBP:util tomchen$ javap -c -v Main.class
Classfile /Users/tomchen/Documents/javaEclipse/workspaceJava/OnlineOJ/src/util/Main.class
Last modified Oct 3, 2016; size 421 bytes
MD5 checksum 0700e51efd4656898d4c1c136394661f
Compiled from "Main.java"
public class util.Main
SourceFile: "Main.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V
#2 = String #16 // abcd
#3 = Fieldref #17.#18 // java/lang/System.out:Ljava/io/PrintStream;
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #21 // util/Main
#6 = Class #22 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 Main.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = Utf8 abcd
#17 = Class #23 // java/lang/System
#18 = NameAndType #24:#25 // out:Ljava/io/PrintStream;
#19 = Class #26 // java/io/PrintStream
#20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
#21 = Utf8 util/Main
#22 = Utf8 java/lang/Object
#23 = Utf8 java/lang/System
#24 = Utf8 out
#25 = Utf8 Ljava/io/PrintStream;
#26 = Utf8 java/io/PrintStream
#27 = Utf8 println
#28 = Utf8 (Ljava/lang/String;)V
{
public util.Main();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: ldc #2 // String abcd
2: astore_1
3: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_1
7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return
LineNumberTable:
line 5: 0
line 6: 3
line 7: 10
}

反编译主要是分析上面的String s = "a" + "b" + "c" + "d";

由上面的#2 = String #16 // abcd
可知:"a" + "b" + "c" + "d"只创建了一个对象,因为"a""b""c""d"等都是字符串常量,对于常量,在编译时候直接存储这几个常量拼接后的结果。

java

用惯了 Eclipse,想试下命令行输出,没想到执行java命令却老是报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
TomChens-MBP:util tomchen$ pwd
/Users/tomchen/Documents/javaEclipse/workspaceJava/OnlineOJ/src/util
TomChens-MBP:util tomchen$ java Main
Exception in thread "main" java.lang.NoClassDefFoundError: Main (wrong name: util/Main)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:482)

报了一堆问题,看网上的答案说可能是 CLASSPATH 的问题。

试验 TomChens-MBP:util tomchen$ java -classpath . Main还是不行。

怀疑可能是环境变量的问题,mac 的环境变量和 windows 有区别。

Mac 设置 JAVA_HOME 和 PATH

设置 JAVA_HOME,网上找到一个方法,该方法的优点是没有把JAVA_HOME写死:

1
2
3
4
5
// 编辑文件'~/.zshrc'或'~/.bash_profile',加入下面两行:
// 对于Java 1.7:
export JAVA_HOME=`/usr/libexec/java_home -v 1.7`
export PATH=${JAVA_HOME}/bin:$PATH

对于 CLASSPATH ,网上大多意见是:不要设置全局的环境变量,每个应用会自己设置。有点疑惑?

  • Mac OS X (Unix/Linux)中,路径分隔符是冒号(:)
  • Windows是分号(;)

但是设置了环境变量还是没能解决上述的问题。

package 的原因

后来看到一篇文章,发现要在 src 目录下执行 java 命令,而不是在/src/util 下执行。

1
2
3
4
5
6
7
TomChens-MBP:util tomchen$ cd ..
TomChens-MBP:src tomchen$ pwd
/Users/tomchen/Documents/javaEclipse/workspaceJava/OnlineOJ/src
TomChens-MBP:src tomchen$ java util.Main
abcd
TomChens-MBP:src tomchen$ java util/Main
abcd

可以看到,用util.Main或者util/Main指定包名都可以。

如果不是在src执行,而是在 src/util执行java util/Main,还是会报错

1
2
3
4
TomChens-MacBook-Pro:util tomchen$ pwd
/Users/tomchen/Documents/javaEclipse/workspaceJava/OnlineOJ/src/util
TomChens-MacBook-Pro:util tomchen$ java util/Main
Error: Could not find or load main class util.Main

总结与思考

javac 命令与 java 命令的区别

  • javac 命令,在 src下执行javac util/Main.java可以,在src/util下执行javac Main.java也可以。
  • 执行 java 命令需在 包的上层目录(比如包util的上层目录: src),并且类名前面要带上包名。因为Java存在类名相同但包名不同的情况,比如 java.awt.Listjava.util.List,包名+类名才能唯一识别某个类。
  • javac是一个平台命令,它对具体的平台文件进行操作,要指明被编译的文件路径
  • java是一个虚拟机命令,它对类操作,即对类的描述要用点分的描述形式,并且不能加扩展名,还要注意类名的大小写。

参考文章