Java中main方法参数String[] args的使用

Java中main方法参数String[] args的使用

Java development notes document

Source Code


一、Java规定

main方法参数必须为字符串数组(String [ ]),变量名可以随意,通常使用args即是arguments(”参数”的复数形式)的缩写。

1.Main方法

1
2
3
4
5
public static void main(String[] args) {
}

public static void main(String args[]) {
}

两种写法都是一样的,都表示字符串数组args,其中args只是普通变量名,可以随意定义(前提是符合变量名规则)

2.思考讨论

2.1 不按Java规定

1
2
3
4
5
6
public class TestDemo {
public static void main(String args) {
// 将 String[] 改为 String
System.out.println("Test!");
}
}

如果按照上面代码进行编码,那就不能被系统识别为主方法;
系统就会提示类似如下错误:

Error: Main method not found in the file, please define the main method as: public static void main(String[] args)

2.2 同名main方法

如果已经有正确main方法,再有同名的main方法就是方法重载了。

1
2
3
4
5
6
7
public static void main(String[] args){
// 正确标准完美的main方法
}

public static void main(String args){
// 方法重载
}

2.3 回归正题:参数String[] args的作用

参数String[] args的作用就是可以在main方法运行前将参数传入main方法中。

  • 从控制台,输入编译执行命令时传参数。例如下面代码:

    1
    2
    3
    4
    5
    6
    public class TestMain {
    public static void main(String[] args) {
    for(int i=0; i<args.length; i++)
    System.out.println(args[i]);
    }
    }
  • 但是此时args[]并没有赋值,我们需要从控制台命令行进行赋值,就像这样:

所以在命令行中使用String[] args即传入参数的使用为:

1
java java_file_name arg1 arg2 arg3 ...

2.4 eclipse举例

在Eclipse使用String[] args

  • 步骤一:选择运行中的Run Configurations…

  • 步骤二:左边选中Java Application下的TestMain,右边选中选项卡中的Arguments,输入参数,每个参数空格隔开,点击Run即可

  • 控制台结果

二、解决的问题

在CMD上运行java文件时出现错误:找不到或者无法加载主类的问题。

1.解决方案

1.1 首先写一个测试文件

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

2.通过cmd编译如下

2.1 通过命令:

1
javac TestDemo.java

将其编译成字节码文件。要运行一段Java源码,必须先将源码转换为class文件,class文件就是编译器编译之后供虚拟机解释执行的二进制字节码文件。

2.2 通过命令:

1
java TestDemo

将其运行,并打印输出结果。

JAVA

从图中可以看出,在这种情况下编译是正常的。

3. 那么,我们修改刚开始的代码

在首行加入在java中最常见的package关键字。
如下:

1
2
3
4
5
6
7
package CourseExercise;

public class TestDemo {
public static void main(String args[]) {
System.out.println("Run successfully!");
}
}

3.1 通过命令:

1
javac TestDemo.java

将其编译成字节码文件

JAVA

在编译成字节码文件时,是没有问题的。也就是说,在当前文件目录下,可以成class文件。好的,继续往下进行。

3.2 通过命令:

1
java TestDemo

尝试将其运行,并打印输出结果。

JAVA

这个时候就会报错了。提示错误:找不到或无法加载主类。很明显,这个错误是由于在代码首行加入了package CourseExercise;造成的。

4. 那么,遇到这种问题该怎么解决呢

方法如下:

4.1 通过命令:

1
javac -d . TestDemo.java

将目标文件编译成class文件。

JAVA

说明:
-d :表示生成目录,设置编译生成的class文件保存路径,路径与定义的包名和层次相关。
. :表示在当前目录中生成。
#### 4.2 通过命令:
1
2
3
java CourseExercise/TestDemo
# 或者
java CourseExercise.TestDemo
来运行程序。

JAVA

4.3 通过命令:

javap是jdk自带的反解析工具。它的作用就是根据class字节码文件,反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。

一般常用的是-v -l -c三个选项:

1
2
3
4
5
6
# 不仅会输出行号、本地变量表信息、反编译汇编代码,还会输出当前类用到的常量池等信息
javap -v ***.class
# 会输出行号和本地变量表信息
javap -l
# 会对当前class字节码进行反编译生成汇编代码
javap -c
  • 将class文件中的字节码转换为字节码指令:
1
javap -verbose TestDemo.class

JAVA

  • 对字节码进行反汇编

    • TestDemo.java文件改为:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      public class TestDemo {
      public static void main(String args[]) {
      int a=1;
      int b=99;

      int c=a;
      a=b;
      b=c;
      System.out.println("Run successfully!");
      }
      }
    • 执行:

      1
      javap -c -l TestDemo.class
    • 分析结果:

      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
      D:\GitHub\OPP-Java\CourseExercise>javap -c -l TestDemo.class
      Compiled from "TestDemo.java"
      public class CourseExercise.TestDemo {
      # 默认的构造方法,在构造方法执行时主要完成一些初始化操作,包括一些成员变量的初始化赋值等操作
      public CourseExercise.TestDemo();
      Code:
      0: aload_0 # 从本地变量表中加载索引为0的变量的值,也即this的引用,压入栈
      # 出栈,调用java/lang/Object."<init>":()V
      # 初始化对象,就是this指定的对象的init()方法完成初始化
      1: invokespecial #1 // Method java/lang/Object."<init>":()V
      4: return
      # 指令与代码行数的偏移对应关系,每一行第一个数字对应代码行数
      # 第二个数字对应前面code中指令前面的数字
      LineNumberTable:
      line 3: 0

      public static void main(java.lang.String[]);
      Code:
      0: iconst_1 # 将常量1,压入到操作数栈
      1: istore_1
      2: bipush 99
      4: istore_2
      5: iload_1
      6: istore_3
      7: iload_2
      8: istore_1
      9: iload_3
      10: istore_2
      # 11到16对应System.out.println("Run successfully!");
      11: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
      14: ldc #3 // String Run successfully!
      16: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      19: return
      LineNumberTable:
      line 6: 0
      line 7: 2
      line 9: 5
      line 10: 7
      line 11: 9
      line 13: 11
      line 14: 19
      }

      D:\GitHub\OPP-Java\CourseExercise>

三、字节码文件信息

由4.3的字节码为例

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
D:\GitHub\OPP-Java\CourseExercise>javap -v TestDemo.class
Classfile /D:/GitHub/OPP-Java/CourseExercise/TestDemo.class
Last modified 2021-4-7; size 473 bytes
MD5 checksum 781bdc70cd44a8153ff0619ef3a44267
Compiled from "TestDemo.java"
public class CourseExercise.TestDemo
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V
#2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #18 // Run successfully!
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #21 // CourseExercise/TestDemo
#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 TestDemo.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = Class #23 // java/lang/System
#17 = NameAndType #24:#25 // out:Ljava/io/PrintStream;
#18 = Utf8 Run successfully!
#19 = Class #26 // java/io/PrintStream
#20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
#21 = Utf8 CourseExercise/TestDemo
#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 CourseExercise.TestDemo();
descriptor: ()V
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[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: iconst_1
1: istore_1
2: bipush 99
4: istore_2
5: iload_1
6: istore_3
7: iload_2
8: istore_1
9: iload_3
10: istore_2
11: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
14: ldc #3 // String Run successfully!
16: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
19: return
LineNumberTable:
line 6: 0
line 7: 2
line 9: 5
line 10: 7
line 11: 9
line 13: 11
line 14: 19
}
SourceFile: "TestDemo.java"

D:\GitHub\OPP-Java\CourseExercise>

开头的7行信息包括:该class文件当前所在的位置,最后修改时间,文件大小,MD5值,编译来源,类的全限定名,JDK次版本号,主版本号,该类的访问标志。

标志名称 标志值 含义
ACC_PUBLIC 0x0001 会否为public类型
ACC_FINAL 0x0010 是否被声明为final,只有类可以设置
ACC_SUPER 0x0020 是否被允许使用invokespecial字节码指令的新语义
ACC_INTERFACE 0x0200 标志这是一个接口
ACC_ABSTRACT 0x0400 是否为abstract类型,对于接口或者抽象类来说,次标志值为真,其他类型为假
ACC_SYNTHETIC 0x1000 标志这个类并非由用户代码产生
ACC_ANNOTATION 0x2000 标志这是一个注解
ACC_ENUM 0x4000 标志这是一个枚举

Constant pool常量池,主要存放的是两大类常量:字面量(Literal)和符号引用(Symbolic References)。字面量相当于Java中的常量,包括文本字符串,final常量等;符号引用则属于编译原理方面的概念,包括以下:

  • 类和接口的全限定名(Fully Qualified Name)
  • 字段的名称和描述符号(Descriptor)
  • 方法的名称和描述符

JVM是在加载Class文件的时候进行动态链接,也就是说这些字段和方法符号引用只有在运行期转换后才能获得真正的内存入口地址。当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建或运行时解析并翻译到具体的内存地址中。

  • 第一个常量是一个方法定义,指向了6和第15个常量。以此类推查看第6和第15个常量。最后可以拼接成第一个常量右侧的注释内容。

    1
    2
    3
    4
    5
    6
    7
    Constant pool:
    #1 = Methodref #6.#15 // java/lang/Object."<init>":()V
    #6 = Class #22 // java/lang/Object
    #7 = Utf8 <init>
    #8 = Utf8 ()V
    #15 = NameAndType #7:#8 // "<init>":()V
    #22 = Utf8 java/lang/Object

    这段可以理解为该类的实例构造器的声明,由于Main类没有重写构造方法,所以调用的是父类的构造方法。此处也说明了Main类的直接父类是Object。该方法默认返回值是V, 也就是void,无返回值。

四、力扣

最小栈问题,两个栈栈顶元素比较

1
2
3
4
5
// 错误方法
this.minStack.peek()==this.stack.peek(); // 始终返回false
// 正确方法
int num = this.minStack.peek();
num==this.minStack.peek();

评论