Java中main方法参数String[] args的使用
Java development notes document
一、Java规定
main方法参数必须为字符串数组(String [ ]),变量名可以随意,通常使用args即是arguments(”参数”的复数形式)的缩写。
1.Main方法
1 | public static void main(String[] args) { |
两种写法都是一样的,都表示字符串数组args,其中args只是普通变量名,可以随意定义(前提是符合变量名规则)
2.思考讨论
2.1 不按Java规定
1 | public class TestDemo { |
如果按照上面代码进行编码,那就不能被系统识别为主方法;
系统就会提示类似如下错误:
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 | public static void main(String[] args){ |
2.3 回归正题:参数String[] args的作用
参数String[] args
的作用就是可以在main
方法运行前将参数传入main
方法中。
从控制台,输入编译执行命令时传参数。例如下面代码:
1
2
3
4
5
6public 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 | public class TestDemo { |
2.通过cmd编译如下
2.1 通过命令:
1 | javac TestDemo.java |
将其编译成字节码文件。要运行一段Java源码,必须先将源码转换为class文件,class文件就是编译器编译之后供虚拟机解释执行的二进制字节码文件。
2.2 通过命令:
1 | java TestDemo |
将其运行,并打印输出结果。
从图中可以看出,在这种情况下编译是正常的。
3. 那么,我们修改刚开始的代码
在首行加入在java中最常见的package关键字。
如下:
1 | package CourseExercise; |
3.1 通过命令:
1 | javac TestDemo.java |
将其编译成字节码文件
在编译成字节码文件时,是没有问题的。也就是说,在当前文件目录下,可以成class文件。好的,继续往下进行。
3.2 通过命令:
1 | java TestDemo |
尝试将其运行,并打印输出结果。
这个时候就会报错了。提示错误:找不到或无法加载主类。很明显,这个错误是由于在代码首行加入了package CourseExercise;
造成的。
4. 那么,遇到这种问题该怎么解决呢
方法如下:
4.1 通过命令:
1 | javac -d . TestDemo.java |
将目标文件编译成class文件。
说明: -d :表示生成目录,设置编译生成的class文件保存路径,路径与定义的包名和层次相关。 . :表示在当前目录中生成。#### 4.2 通过命令:
1 | java CourseExercise/TestDemo |
4.3 通过命令:
javap是jdk自带的反解析工具。它的作用就是根据class字节码文件,反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。
一般常用的是-v -l -c
三个选项:
1 | # 不仅会输出行号、本地变量表信息、反编译汇编代码,还会输出当前类用到的常量池等信息 |
- 将class文件中的字节码转换为字节码指令:
1 | javap -verbose TestDemo.class |
对字节码进行反汇编
将
TestDemo.java
文件改为:1
2
3
4
5
6
7
8
9
10
11public 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
44D:\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 | D:\GitHub\OPP-Java\CourseExercise>javap -v TestDemo.class |
开头的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
7Constant 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 | // 错误方法 |