当前位置:   article > 正文

使用命令行编译、运行Java程序_命令行编译java

命令行编译java

我们一般都是通过IDE(如Eclipse、Intellij Idea,STS等)来开发,调试java项目。

在不借助IDE的情况下,如何编译、运行Java程序。

使用javac 命令,可以通过只敲击javac 看到各种命令参数。

必学参数 -d -cp,这俩下面会讲到

如果javac命令不能用,看一下环境变量是否没配对。

我们从简单到复杂来看java编译、运行命令

单独类如何编译

我们可以用ide(eclipse、idea,甚至高级点的文本编辑工具Emeditor、Notepad++、UE)准备java文件,然后拷贝到硬盘,比如D盘。javac命令需要带.java后缀名,执行java文件不需要带后缀名。

一、不带包名的类如何编译

1、没有中文的java文件。

准备代码

  1. public class A {
  2. public static void main(String[] args) {
  3. System.out.println("abc");
  4. }
  5. }

命令:

javac A.java

java A

输出结果

2、假设输出的是中文,会怎么样?

修改代码与输出结果,两种情况与两种结果

a)

  1. public class A {
  2. public static void main(String[] args) {
  3. System.out.println("你好吗");
  4. }
  5. }

错误:编码GBK的不可映射字符

b)

  1. public class A {
  2. public static void main(String[] args) {
  3. System.out.println("你好");
  4. }
  5. }

能编译成功,但是输出乱码

原因分析:

我们需要了解javac和java命令是什么样的过程。

javac是java compiler的命令,是将.java文件编译成.class文件的过程。我们需要先将文件读入内存,才能进行编译。

读入内存需要知道文件的编码格式,才能正确的将文件读取。我们查看一下java源文件的编码,发现是UTF-8。而java编译器默认的字符集可以通过如下代码查看。

  1. System.out.println(Charset.defaultCharset());

会发现输出GBK。

也就是java编译器认为文件采用GBK编码,而实际上文件是采用UTF-8编码。然后“你好吗”三个字的UTF-8码值,转换成GBK就是"浣犲ソ鍚�",这个问号“�”就是一个GBK不可映射的字符。可以用下面的代码试一下:

  1. String s = "你好吗";
  2. String s1 = new String(s.getBytes("utf-8"), "gbk");
  3. System.out.println(s1);

而如果是"你好"两个字的UTF-8码值转换成GBK是这三个字符"浣犲ソ"。

  1. String s = "你好";
  2. String s1 = new String(s.getBytes("utf-8"), "gbk");
  3. System.out.println(s1);

编译器使用了UTF-8的二进制值来尝试转换成GBK,第一次认识到了一个不认识的字符,因为UTF-8的范围很大,这个码值在GBK中没有,就报了这个错。

而第二次编译通过,是因为“你好”这两个字的UTF-8编码,恰好能转换成GBK编码,所以能编译通过。但是编译通过并不保证内容就是正确的,输出的时候仍然是乱码。

问题:

为什么我们通过IDE就能编译通过。

通过IDE,不可能分开java文件编码和java compiler的编码格式的,文件设置成什么编码,编译器都会知道,就会用什么编码来解析。原生的javac不会这样,它只会按照默认的系统编码来编,这个时候如果文件编码不同,就出现这个问题了。

知道了原因,怎么解决,两种解决方案,最终目的是为了文件编码和解码字符集相同

1)如果文件是UTF-8编码的,我们使用-encoding UTF-8 来显式指定为UTF-8的编码格式。

2)将文件改为GBK编码,如果使用windows自带的记事本,保存为ANSI,中国区域会使用GBK编码。如果使用其它高级文本编辑工具,如:notepad++、Emeditor、UE这样的,另存为指定格式。

然后再编译运行就可以了。

这里的GB2312(936)就是GBK,不是GB2312那个阉割版。

二、带包名的类如何编译

准备代码,我们已经讨论过中文乱码问题了,为了简单起见,下面都用英文示范其它的情况。

  1. package mypack;
  2. public class A {
  3. public static void main(String[] args) {
  4. System.out.println("abc");
  5. }
  6. }

编译运行:

我们会发现编译成功,A.class被编译到了D盘根目录下。运行报错“错误:找不到或无法加载主类A”

原因分析:

这里地方有点绕人,我们先分析为什么现在的命令不行。

java A

有包的java程序,需要用完整包名来执行

由于我们没有指定classpath,jvm准备在当前路径下查找A.class来装载,找了一圈没找到(确实有个A类,但是A类的完整路径是mypack.A,所以不是这个),报错找不到或无法加载主类。

java mypack.A

有包的java程序,文件路径中必须包含包名,并以包名结尾

jvm看了一下有包,于是将包转换为路径,也就是期望在D:/mypack文件夹下,找到A.class文件进行装载。也没找到。

如果有包,java命令必须在包的上层目录执行完整路径名(完全限定名),上例中A.class的完全限定名是mypack.A。如果在D盘下,有一个A.java,包路径为aaa.bbb.ccc,必须在D盘下,执行java aaa.bbb.ccc.A才行,此处的“在D盘下”,暂时可以看做直接在D盘下,也可以通过-cp指定到D盘下,这个后面还会说。

解决办法

我们可以使用-d . 来让编译器以当前路径为基准,自动创建包路径,这个-d .放在前面,放在后面都可以

这个-d 可以将文件编译到指定目录下。

假设我们在D盘下创建一个aa的目录,然后执行javac -d aa A.java,效果如下。

使用-classpath指定包的上级目录

使用-classpath(或者 -cp,简写,意思相同)指定.class的目录,使用相对路径,绝对路径都可以,这个目录直接通到mypack的上级即可。

我们可以通过-classpath指定.class在哪个根目录下,然后从这个目录拼接上包路径来构成完整路径。

javac的自由性

javac命令使用了可指定编译路径的可选项(option),可以指定不指定,不指定将在当前目录生成.class文件;可以指定为-d . ,将会在当前目录下创建包的全路径。可以指定位-d xx/xxx/xxxx 具体的目录,将会在具体目录下创建包的全路径。

这几种命令产生的.class文件本身完全相同。

等于并不限定.class文件产生的位置,因为javac只是创建。可能java文件本身可能就不放在目标位置。也可能创建的.class文件通过网路传输到别的地方再执行,所以决定了由使用者自己来决定放到什么地方。

三、引入其它自定义类

1、引入非自定义类

假设我们编辑如下的代码:

  1. import java.nio.charset.Charset;
  2. public class A {
  3. public static void main(String[] args) {
  4. System.out.println(Charset.defaultCharset());
  5. }
  6. }

这个先放这边,我们举另外一个例子

2、引入自定义类

准备一个非默认的包,java目前版本不允许引入未命名包的类。让A引用B。

  1. package pack;
  2. public class B {
  3. private String name;
  4. public String getName() {
  5. return name;
  6. }
  7. public void setName(String name) {
  8. this.name = name;
  9. }
  10. public void showMyName(){
  11. System.out.println("my name is " + this.getName());
  12. }
  13. }

  1. package mypack;
  2. import pack.B;
  3. public class A {
  4. public static void main(String[] args) {
  5. B b = new B();
  6. b.setName("abc");
  7. b.showMyName();
  8. }
  9. }

然后将A和B都放入D盘根目录,使用javac -d . 编译A.java

解决方案一:

我们可以用比较无脑的方式

甚至,可以javac -d . *.java,但是我认为.* 不妥,这样把不必要的类也编译了。

解决方案二:

首先,java程序会将被引用的类也打包的。

然后,如果类是相互引用并且不同包的,一定要按照包的路径放好,保持包定义和文件结构同步。

我们新建mypack和pack目录,将A.java丢到mypack目录下,将B.java丢到pack下。

如果B.java在其它路径:

上面的例子隐含了一种情况,A.java和B.java的包都在敲击命令目录的下级目录,包路径和隐含的classpath(也就是当前路径)构成了完整的路径。假设B在其它路径怎么办?

我们将B.java丢到E盘下的aa目录。这个时候只需要使用-cp指定B.java的上级路径即可。

我们打开E:\aa文件夹查看,会发现B.class并不在这里,因为B.java只是一个source路径而已。最终的.class文件仍然是相对当前敲击命令的位置安放。

如果B.class在其它路径。

我们将B.class也移动到E:\aa目录试试

好熟悉的报错。哈哈

这个时候需要使用-cp,但是看以第一条命令,使用-cp只指定了一个目录,会认为mypack.A也在这个路径下,要分开指定,使用"."代表当前路径,使用分号隔开多个class路径。

总结一下:

1、如果有中文乱码,考虑是文件的编码方式和javac的编码方式不同。

javac的编码方式默认是ANSI的,也就是不同区域不同编码,中国区是GBK。可以使用Charset.defaultCharset()来查看。

将java文件编码和javac解码字符集统一。

a)修改java文件编码。

b)使用-encoding指定javac编译时候使用的编码。

2、对于有包的java程序,执行的时候要在包路径的上级路径,使用带有包路径的全限定名来执行。

包路径包含于实际文件路径,并且是实际文件路径的后面部分,当然特殊情况可以和文件路径相同。使用classpath指定包的上级目录,来执行不在当前路径下的java文件。

3、javac 有个可选参数 -d,如果不填会将.class文件放到当前目录,并且不带包名;如果填了会将.class文件放到指定目录,并且会创建包路径,可以用-d . 会在当前目录创建包路径。

4、javac和java都可以使用-cp/-classpath来操作执行路径下的文件。classpath可以有多个值,使用分号隔开,如果是.,表示当前目录。jvm会扫描classpath的所有目录,从中查找源文件和可执行文件。

最后吐槽一下CSDN这个,,,我从有道云拷贝过来,图片要一个一个贴,不知道是哪边的锅,累死我了

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/空白诗007/article/detail/798236
推荐阅读
相关标签
  

闽ICP备14008679号