java文件怎么运行程序运行

简单说来,一个java程序的运行需要编辑源码、编译生成class文件、加载class文件、解释或编译运行class中的字节码指令。
下面有一段简单的java源码,通过它来看一下java程序的运行流程:
1 class Person
private int
public Person(int age, String name){
this.age =
this.name =
public void run(){
29 interface IStudyable
public int study(int a, int b);
37 public class Student extends Person implements IStudyable
private static int cnt=5;
public Student(int age, String name, String sid){
super(age,name);
this.sid =
public void run(){
System.out.println("run()...");
public int study(int a, int b){
int c = 10;
int d = 20;
return a+b*c-d;
public static int getCnt(){
public static void main(String[] args){
Student s = new Student(23,"dqrcsc","");
s.study(5,6);
Student.getCnt();
1.编辑源码:无论是使用记事本还是别的什么,编写上面的代码,然后保存到Student.java,我直接就放到桌面了
2.编译生成class字节码文件:
在桌面上,按住shift,然后按下鼠标右键:
点击&在此处打开命令窗口&
输入命令javac Student.java将该源码文件编译生成.class字节码文件。
由于在源码文件中定义了两个类,一个接口,所以生成了3个.clsss文件:
这样能在java虚拟机上运行的字节码文件就生成了
3.启动java虚拟机运行字节码文件:
在命令行中输入java Student这个命令,就启动了一个java虚拟机,然后加载Student.class字节码文件到内存,然后运行内存中的字节码指令了。
我们从编译到运行java程序,只输入了两个命令,甚至,如果使用集成开发环境,如eclipse,只要ctrl+s保存就完成了增量编译,只需要按下一个按钮就运行了java程序。但是,在这些简单操作的背后还有一些操作&&
1.从源码到字节码:
字节码文件,看似很微不足道的东西,却真正实现了java语言的跨平台。各种不同平台的虚拟机都统一使用这种相同的程序存储格式。更进一步说,jvm运行的是class字节码文件,只要是这种格式的文件就行,所以,实际上jvm并不像我之前想象地那样与java语言紧紧地捆绑在一起。如果非常熟悉字节码的格式要求,可以使用二进制编辑器自己写一个符合要求的字节码文件,然后交给jvm去运行;或者把其他语言编写的源码编译成字节码文件,交给jvm去运行,只要是合法的字节码文件,jvm都会正确地跑起来。所以,它还实现了跨语言&&
通过jClassLib可以直接查看一个.class文件中的内容,也可以给JDK中的javap命令指定参数,来查看.class文件的相关信息:
javap &v Student
好多输出,在命令行窗口查看不是太方便,可以输出重定向下:
&&&&&& javap &v Student & Student.class.txt
桌面上多出了一个Student.class.txt文件,里面存放着便于阅读的Student.class文件中相关的信息
部分class文件内容,从上面图中,可以看到这些信息来自于Student.class,编译自Student.java,编译器的主版本号是52,也就是jdk1.8,这个类是public,然后是存放类中常量的常量池,各个方法的字节码等,这里就不一一记录了。
&&&&&& 总之,我想说的就是字节码文件很简单很强大,它存放了这个类的各种信息:字段、方法、父类、实现的接口等各种信息。
2.Java虚拟机的基本结构及其内存分区:
&&&&&& Java虚拟机要运行字节码指令,就要先加载字节码文件,谁来加载,怎么加载,加载到哪里&&谁来运行,怎么运行,同样也要考虑&&
上面是一个JVM的基本结构及内存分区的图,有点抽象,有点丑&&简单说明下:
&&&&&& JVM中把内存分为直接内存、方法区、Java栈、Java堆、本地方法栈、PC寄存器等。
&&&&&& 直接内存:就是原始的内存区
&&&&&& 方法区:用于存放类、接口的元数据信息,加载进来的字节码数据都存储在方法区
&&&&&& Java栈:执行引擎运行字节码时的运行时内存区,采用栈帧的形式保存每个方法的调用运行数据
&&&&&& 本地方法栈:执行引擎调用本地方法时的运行时内存区
&&&&&& Java堆:运行时数据区,各种对象一般都存储在堆上
&&&&&& PC寄存器:功能如同CPU中的PC寄存器,指示要执行的字节码指令。
&&&&&& JVM的功能模块主要包括类加载器、执行引擎和垃圾回收系统。
3.类加载器加载Student.class到内存:
&&&&&& 1)类加载器会在指定的classpath中找到Student.class这个文件,然后读取字节流中的数据,将其存储在方法区中。
&&&&&& 2)会根据Student.class的信息建立一个Class对象,这个对象比较特殊,一般也存放在方法区中,用于作为运行时访问Student类的各种数据的接口。
&&&&&& 3)必要的验证工作,格式、语义等
&&&&&& 4)为Student中的静态字段分配内存空间,也是在方法区中,并进行零初始化,即数字类型初始化为0,boolean初始化为false,引用类型初始化为null等。
在Student.java中只有一个静态字段:
&private static int cnt=5;&
此时,并不会执行赋值为5的操作,而是将其初始化为0。
&&&&&& 5)由于已经加载到内存了,所以原来字节码文件中存放的部分方法、字段等的符号引用可以解析为其在内存中的直接引用了,而不一定非要等到真正运行时才进行解析。
&&&&&& 6)在编译阶段,编译器收集所有的静态字段的赋值语句及静态代码块,并按语句出现的顺序拼接出一个类初始化方法&clinit&()。此时,执行引擎会调用这个方法对静态字段进行代码中编写的初始化操作。
在Student.java中关于静态字段的赋值及静态代码块有两处:
1 private static int cnt=5;
将按出现顺序拼接,形式如下:
1 void &clinit&(){
可以通过jClassLib这个工具看到生成的&clinit&()方法的字节码指令:
iconst_5指令把常数5入栈
putstatic #6将栈顶的5赋值给t这个静态字段
getstatic #6 获取t这个静态字段的值,并将其放入栈顶
iconst_1 把常数1入栈
iadd 取出栈顶的两个整数,相加,结果入栈
putstatic #6 取出栈顶的整数,赋值给t
return 从当前方法中返回,没有任何返回值。
从字节码来看,确实先后执行了cnt =5 及 cnt++这两行代码。
在这里有一点要注意的是,这里笼统的描述了下类的加载及初始化过程,但是,实际中,有可能只进行了类加载,而没有进行初始化工作,原因就是在程序中并没有访问到该类的字段及方法等。
此外,实际加载过程也会相对来说比较复杂,一个类加载之前要加载它的父类及其实现的接口:加载的过程可以通过java &XX:+TraceClassLoading参数查看:
如:java -XX:+TraceClassLoading Student,信息太多,可以重定向下:
查看输出的loadClass.txt文件:
可以看到最先加载的是Object.class这个类,当然了,所有类的父类。
直到第390行才看到自己定义的部分被加载,先是Student实现的接口IStudyable,然后是其父类Person,然后才是Student自身,然后是一个启动类的加载,然后就是找到main()方法,执行了。
4.执行引擎找到main()这个入口方法,执行其中的字节码指令:
要了解方法的运行,需要先稍微了解下java栈:
JVM中通过java栈,保存方法调用运行的相关信息,每当调用一个方法,会根据该方法的在字节码中的信息为该方法创建栈帧,不同的方法,其栈帧的大小有所不同。栈帧中的内存空间还可以分为3块,分别存放不同的数据:
局部变量表:存放该方法调用者所传入的参数,及在该方法的方法体中创建的局部变量。
操作数栈:用于存放操作数及计算的中间结果等。
其他栈帧信息:如返回地址、当前方法的引用等。
只有当前正在运行的方法的栈帧位于栈顶,当前方法返回,则当前方法对应的栈帧出栈,当前方法的调用者的栈帧变为栈顶;当前方法的方法体中若是调用了其他方法,则为被调用的方法创建栈帧,并将其压入栈顶。
注意:局部变量表及操作数栈的最大深度在编译期间就已经确定了,存储在该方法字节码的Code属性中。
简单查看Student.main()的运行过程:
简单看下main()方法:
1 public static void main(String[] args){
Student s = new Student(23,"dqrcsc","");
s.study(5,6);
Student.getCnt();
对应的字节码,两者对照着看起来更易于理解些:
注意main()方法的这几个信息:
Mximum stack depth指定当前方法即main()方法对应栈帧中的操作数栈的最大深度,当前值为5
Maximum local variables指定main()方法中局部变量表的大小,当前为2,及有两个slot用于存放方法的参数及局部变量。
Code length指定main()方法中代码的长度。
开始模拟main()中一条条字节码指令的运行:
创建栈帧:
局部变量表长度为2,slot0存放参数args,slot1存放局部变量Student s,操作数栈最大深度为5。
new #7指令:在java堆中创建一个Student对象,并将其引用值放入栈顶。
dup指令:复制栈顶的值,然后将复制的结果入栈。
bipush 23:将单字节常量值23入栈。
ldc #8:将#8这个常量池中的常量即&dqrcsc&取出,并入栈。
ldc #9:将#9这个常量池中的常量即&&取出,并入栈。
invokespecial #10:调用#10这个常量所代表的方法,即Student.&init&()这个方法
&init&()方法,是编译器将调用父类的&init&()的语句、构造代码块、实例字段赋值语句,以及自己编写的构造方法中的语句整合在一起生成的一个方法。保证调用父类的&init&()方法在最开头,自己编写的构造方法语句在最后,而构造代码块及实例字段赋值语句按出现的顺序按序整合到&init&()方法中。
注意到Student.&init&()方法的最大操作数栈深度为3,局部变量表大小为4。
此时需注意:从dup到ldc #9这四条指令向栈中添加了4个数据,而Student.&init&()方法刚好也需要4个参数:
1 public Student(int age, String name, String sid){
super(age,name);
this.sid =
虽然定义中只显式地定义了传入3个参数,而实际上会隐含传入一个当前对象的引用作为第一个参数,所以四个参数依次为this,age,name,sid。
上面的4条指令刚好把这四个参数的值依次入栈,进行参数传递,然后调用了Student.&init&()方法,会创建该方法的栈帧,并入栈。栈帧中的局部变量表的第0到4个slot分别保存着入栈的那四个参数值。
创建Studet.&init&()方法的栈帧:
Student.&init&()方法中的字节码指令:
aload_0:将局部变量表slot0处的引用值入栈
aload_1:将局部变量表slot1处的int值入栈
aload_2:将局部变量表slot2处的引用值入栈
invokespecial #1:调用Person.&init&()方法,同调用Student.&init&过程类似,创建栈帧,将三个参数的值存放到局部变量表等,这里就不画图了&&
从Person.&init&()返回之后,用于传参的栈顶的3个值被回收了。
aload_0:将slot0处的引用值入栈。
aload_3:将slot3处的引用值入栈。
putfield #2:将当前栈顶的值&&赋值给0x2222所引用对象的sid字段,然后栈中的两个值出栈。
return:返回调用方,即main()方法,当前方法栈帧出栈。
重新回到main()方法中,继续执行下面的字节码指令:
astore_1:将当前栈顶引用类型的值赋值给slot1处的局部变量,然后出栈。
aload_1:slot1处的引用类型的值入栈
iconst_5:将常数5入栈,int型常数只有0-5有对应的iconst_x指令
bipush 6:将常数6入栈
invokevirtual #11:调用虚方法study(),这个方法是重写的接口中的方法,需要动态分派,所以使用了invokevirtual指令。
创建study()方法的栈帧:
最大栈深度3,局部变量表5
方法的java源码:
public int study(int a, int b){
&&&&&&&&&&&&& int c = 10;
&&&&&&&&&&&&& int d = 20;
&&&&&&&&&&&&& return a+b*c-d;
对应的字节码:
注意到这里,通过jClassLib工具查看的字节码指令有点问题,与源码有偏差&&
改用通过命令javap &v Student查看study()的字节码指令:
bipush 10:将10入栈
istore_3:将栈顶的10赋值给slot3处的int局部变量,即c,出栈。
bipush 20:将20入栈
istore 4:将栈顶的20付给slot4处的int局部变量,即d,出栈。
上面4条指令,完成对c和d的赋值工作。
iload_1、iload_2、iload_3这三条指令将slot1、slot2、slot3这三个局部变量入栈:
imul:将栈顶的两个值出栈,相乘的结果入栈:
iadd:将当前栈顶的两个值出栈,相加的结果入栈
iload 4:将slot4处的int型的局部变量入
isub:将栈顶两个值出栈,相减结果入栈:
ireturn:将当前栈顶的值返回到调用方。
重新回到main()方法中:
pop指令,将study()方法的返回值出栈
invokestatic #12 调用静态方法getCnt()不需要传任何参数
pop:getCnt()方法有返回值,将其出栈
aload_1:将slot1处的引用值入栈
invokevirtual #13:调用0x2222对象的run()方法,重写自父类的方法,需要动态分派,所以使用invokevirtual指令
return:main()返回,程序运行结束。
以上,就是一个简单程序运行的大致过程,只是今天看书的一些理解,也许有错误的地方,希望不会贻笑大方&&
阅读(...) 评论()Java中main方法,静态,非静态的执行顺序详解
  Java程序运行时,第一件事情就是试图访问main方法,因为main相等于程序的入口,如果没有main方法,程序将无法启动,main方法更是占一个独立的线程,找到main方法后,是不是就会执行mian方法块里的第一句话呢?答案是不一定
&看看下面两种最常见的情况:
  第一种情况:
    main方法在一个具有其他方法或属性的类中;
public class Test1 {
public static S
public Test1() {
public static void get() {
System.out.println("Test start");
public static void main(String[] args) {
System.out.println("main start");
Test1 bb = new Test1();
  第二种情况:
&&&&&&&&& && &main方法在一个没有其他方法或属性的类中;
public class Test {
public static void main(String[] args) {
Student stu =new Student();
  因为静态部分是依赖于类,而不是依赖于对象存在的,所以静态部分的加载优先于对象存在。
  当找到main方法后,因为main方法虽然是一个特殊的静态方法,但是还是静态方法,此时JVM会加载main方法所在的类,试图找到类中其他静态部分,即首先会找main方法所在的类。
执行顺序大致分类:
  1.静态属性,静态方法声明,静态块。
  2.动态属性,普通方法声明,构造块。
  3.构造方法。
1.1 静态:
  当加载一个类时,JVM会根据属性的数据类型第一时间赋默认值(一举生成的)。然后再进行静态属性初始化,并为静态属性分配内存空间,静态方法的声明,静态块的加载,没有优先级之分,按出现顺序执行,静态部分仅仅加载一次。至此为止,必要的类都已经加载完毕,对象就可以被创建了。
1.2 普通:
  当new一个对象时,此时会调用构造方法,但是在调用构造方法之前,(此刻1.1已经完成,除非被打断而暂停)执行动态属性定义并设置默认值(一举生成的)。然后动态属性初始化,分配内存,构造块,普通方法声明(只是加载,它不需要初始化,只有调用它时才分配内存,当方法执行完毕后内存立即释放),没有优先级之分,按出现顺序执行。最后进行构造方法中赋值。当再次创建一个对象,不再执行静态部分,仅仅重复执行普通部分。
  注意:如果存在继承关系,创建对象时,依然会首先进行动态属性进行定义并设默认值,然后父类的构造器才会被调用,其他一切都是先父类再子类(因为子类的static初始化可能会依赖于父类成员能否被正确初始化),如果父类还有父类,依次类推,不管你是否打算产生一个该父类的对象,这都是自然发生的。
下面是一道小程序:
  代码部分:
public A() {
System.out.println("A的构造方法");
public static int j = print();
public static int print() {
System.out.println("A print");
return 521;
public class Test1 extends A {
public Test1() {
System.out.println("Test1的构造方法");
public static int k = print();
public static int print() {
System.out.println("Test print");
return 522;
public static void main(String[] args) {
System.out.println("main start");
Test1 t1 = new Test1();
  运行结果:
Test print
main start
A的构造方法
Test1的构造方法
下面是一道阿里巴巴的面试题:非常经典,可以不断的改变程序的顺序,来找到执行顺序机制。
  代码部分:
public class Text {
public static int k = 0;
public static Text t1 = new Text("t1");
public static Text t2 = new Text("t2");
public static int i = print("i");
public static int n = 99;
public int j = print("j");
print("构造块");
print("静态块");
public Text(String str) {
System.out.println((++k) + ":" + str + "
i=" + i + "
public static int print(String str) {
System.out.println((++k) + ":" + str + "
i=" + i + "
return ++i;
public static void main(String args[]) {
Text t = new Text("init");
&  运行结果:
总结:只要按照这个步骤,遇到这一类问题就可以解决了。&&&&&
&&&&    & 1-3:类加载过程,不涉及构造方法
&&&&&     1-5: 实例化过程,涉及构造方法
  1.类中所有属性的默认值(一举而成)
  2. 父类静态属性初始化,静态块,静态方法的声明(按出现顺序执行)
  3. 子类静态属性初始化,静态块,静态方法的声明 (按出现顺序执行)
  4.& 调用父类的构造方法,
      首先父类的非静态成员初始化,构造块,普通方法的声明(按出现顺序执行)
      然后父类构造方法
  5.& 调用子类的构造方法,
      首先子类的非静态成员初始化,构造块,普通方法的声明(按出现顺序执行)
      然后子类构造方法
   (注意:类加载过程中,可能调用了实例化过程(因为static可以修饰方法,属性,代码块,内部类),此时则会暂停类加载过程而先执行实例化过程(被打断),执行结束再进行类加载过程,上面阿里那道面试题就是典型的暂停类加载。
下面附加一个小程序来显示新建对象时属性的四个过程:
public class Test {
public static void main(String[] args) {
Student stu = new Student("zhangan", 21);
class Student {
public String name = "李寻欢";
public int age = 20;
public Student(String name, int age) {
this.name =
this.age =
阅读(...) 评论() 上传我的文档
 下载
 收藏
该文档贡献者很忙,什么也没留下。
 下载此文档
Java运行程序写结果(答案)
下载积分:2000
内容提示:Java运行程序写结果(答案)
文档格式:DOC|
浏览次数:125|
上传日期: 15:47:07|
文档星级:
全文阅读已结束,如果下载本文需要使用
 2000 积分
下载此文档
该用户还上传了这些文档
Java运行程序写结果(答案)
官方公共微信JAVA调用系统命令或可执行程序 - why - ITeye博客
博客分类:
通过 java.lang.Runtime 类可以方便的调用操作系统命令,或者一个可执行程序,下面的小例子我在windows和linux分别测试过,都通过。基本原理是,首先通过 Runtime.getRuntime() 返回与当前 Java 应用程序相关的运行时对象,然后调用run.exec(cmd)
另启一个进程来执行命令(cmd为要执行的命令)。
一、运行一个可执行程序
执行一个.exe的文件,或通过已安装的软件打开一个特定格式的文件,如word、chm或mp3等等。
1. 在window下可以直接执行一个.exe文件,如执行我在F盘下的tomcat安装文件,将命令写为:
String cmd = "F:\\apache-tomcat-6.0.20.exe";
2. 打开一个word文档。如果系统已经安装了office应用程序,就可以通过调用word的可执行程序来打开一个word文档:
String cmd = "D:\\Program Files\\Microsoft Office\\OFFICE11\\WINWORD.EXE F:\\test.doc";
当然这样写有点麻烦,我们想打开一个word文档时只要双击就可以了,用不着去找WINWORD.EXE。要是打开每一种格式的文件都得去找它的可执行程序,那可累死了,我们可以通过下面的代码,打开任意一个已知格式的文件(只要安装的打开这种文件格式的软件),相当于用鼠标双击一个文件的图标:
String cmd = "cmd.exe /c start F:\\test.doc";
我用C写了一个进程操作的小例子,放在 linux 下编译出的可执行文件叫“fork_wait”,然后把我的java文件编译成TestRunTime.class后扔到 linux 上,在控制台执行 java TestRunTime 命令,TestRunTime 和 fork_wait 程序均运行成功。
String cmd = "./fork_wait";
二、执行一个有标准输出的系统命令
通过调用进程的 getInputStream() 方法,可以获得执行命令的标准输出。在 windows 的cmd控制台窗口和 linux 控制台执行系统名利的格式是一样的,只是输入的命令不同而已。
如要执行windows控制台中ping命令,可写为:String cmd = "ping ";
执行linux的ls命令,可写为:String cmd = "ls -l";
如果要执行一个带参数的命令,可使用 String 数组形式,如:
String[] cmd=new String[3];
cmd[0]="/bin/sh";
cmd[1]="-c";
cmd[2]="ls -l ./";
下面是我写的小例子:
package com.why.RunT
import java.io.BufferedInputS
import java.io.BufferedR
import java.io.InputStreamR
public class TestRunTime {
public static void main(String[] args) {
String cmd = "F:\\apache-tomcat-6.0.20.exe";
String cmd = "D:\\Program Files\\Microsoft Office\\OFFICE11\\WINWORD.EXE F:\\test.doc";
String cmd = "cmd.exe /c start F:\\test.doc";
String cmd = "ping ";
String cmd = "./fork_wait";
String cmd = "ls -l";
String[] cmd=new String[3];
cmd[0]="/bin/sh";
cmd[1]="-c";
cmd[2]="ls -l ./";
Runtime run = Runtime.getRuntime();//返回与当前 Java 应用程序相关的运行时对象
Process p = run.exec(cmd);// 启动另一个进程来执行命令
BufferedInputStream in = new BufferedInputStream(p.getInputStream());
BufferedReader inBr = new BufferedReader(new InputStreamReader(in));
String lineS
while ((lineStr = inBr.readLine()) != null)
//获得命令执行后在控制台的输出信息
System.out.println(lineStr);// 打印输出信息
//检查命令是否执行失败。
if (p.waitFor() != 0) {
if (p.exitValue() == 1)//p.exitValue()==0表示正常结束,1:非正常结束
System.err.println("命令执行失败!");
inBr.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
浏览 56723
如果是执行一段脚本,bat或则是shell,你如何判断成功与否?p.waitfor的返回值只是脚本中最后一个命令执行返回的结果,万一你中间有那条命令执行错误最后一条却成功,那岂不是判断执行成功?感谢关注先!再说这个p.waitfor返回的并不是脚本中哪个命令的执行结果,而是这个脚本是否正常执行完毕,就算一个脚本中的所有命令都执行失败,只要这个脚本能执行完,就返回成功。
浏览: 313549 次
来自: 哈尔滨
如何在java Web项目中开发WebService接口,地址 ...
如何在java Web项目中开发WebService接口,地址 ...
如何在java Web项目中开发WebService接口,地址 ...
[color=red][/color] 学习了
我很喜欢你对设计模式的理解。}

我要回帖

更多关于 cmd 运行java程序 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信