1. Java初步认知

大家对于Java的执行顺序应该都有一定了解,起码书本上或者印象中有一点了解,
如静态变量、普通变量、类、静态函数、静态代码块、构造函数应该都知道和会用。

这次就讲一下Java入门的基础内容(眼见为实,而且加证明过程,贴答案不算):clint函数、init函数、main函数

2. Javayun.java例子

Javayun.java这个例子的代码如下:里面有main函数,静态变量、类变量、静态函数、静态代码块

public class Javayun {

public static int JavaPrintln(String i) {
    System.out.println(i);
    return 500;
}

private int a1 = JavaPrintln("Log private int a1");

private static int a2 = JavaPrintln("Log private static int a2");

static {
    System.out.println("static code b1");
}

private int a3 = JavaPrintln("Log private int a3");

private static int a4 = JavaPrintln("Log private static int a4");

static {
    System.out.println("static code b2");
}

public static void main(String[] args) {
    System.out.println("main fuction");
}

}

猜测:
1、印象中静态变量属于类,应该先初始化
2、然后静态代码块应该也会初始化吧
3、静态变量、静态代码的顺序,不是特别清楚,可能写在前面就是先初始化吧
4、main函数是java应用程序的入口函数会跑
5、运行java的时候,需要这个对象吗?不是很清楚
6、它们的执行顺序不是清楚,入口是main函数,main函数之前应该也有代码在跑吧?
从上面的java代码无法得到我们想要的全部内容

我们直接看执行结果(注意运行结果并不能直接厘清我们的疑惑,没看到代码的运行过程不能算解惑,大家先初步看看即可,别太关注,后面还是看编译后的代码):

E:\demo>javac Javayun.java
E:\demo>java Javayun
Log private static int a2 //执行静态变量a2
static code b1 //执行静态代码块b1
Log private static int a4 //执行静态变量a4
static code b2 //执行静态代码块b2
main fuction //执行main函数

得到的结论有哪些(仍有疑惑,这是直接看答案,过程呢…)?
1、静态变量和静态代码块的优先级是一样的,谁写在前面就谁先运行
2、main函数确实有在跑,不过肯定不是第一个运行的代码
3、类的普通变量a1、a3,没有运行,代表类对象没有创建,也就是类的构造函数在java main函数运行的时候可以不跑

3. 反编译Javayun.class文件

在输入E:\demo>javac Javayun.java的时候可以在当前目录看到还有一个Javayun.class文件生成。
直接反编译解析一下这个文件。
(.class文件是java编译生成的二进制文件,可以在任何java虚拟机(JVM)中运行,和平台无关, 加载器是Class Loader)

这里提供2中方法:

1、使用java的原生指令:javap -v -p JavaTest.class > JavaTest_javap
2、使用Android SDK提供的工具:D:\tools\SDK\build-tools\30.0.0\dx.bat --dump JavaTest.class > JavaTest_dxdump

这里先以2为例子讲解一下:
打开2中的JavaTest_dxdump文件
一看这个代码就有点长了(也先不看,可以跳过直接看下一章,这里全部贴出来主要是为了部分回头看的同学准备的),constant_pool是常量池,<init>是对象构造函数、<clinit>是类初始化函数

reading Javayun.class...
begin classfile
magic: cafebabe
minor_version: 0000
major_version: 0034
constant_pool_count: 003b

constant_pool:
  0001: method{java.lang.Object.<init>:()V}
  0002: string{"Log private int a1"}
  0003: method{Javayun.JavaPrintln:(Ljava/lang/String;)I}
  0004: field{Javayun.a1:I}
  0005: string{"Log private int a3"}
  0006: field{Javayun.a3:I}
  0007: field{java.lang.System.out:Ljava/io/PrintStream;}
  0008: method{java.io.PrintStream.println:(Ljava/lang/String;)V}
  0009: string{"main fuction"}
  000a: string{"Log private static int a2"}
  000b: field{Javayun.a2:I}
  000c: string{"static code b1"}
  000d: string{"Log private static int a4"}
  000e: field{Javayun.a4:I}
  000f: string{"static code b2"}
  0010: type{Javayun}
  0011: type{java.lang.Object}
  0012: utf8{"a1"}
  0013: utf8{"I"}
  0014: utf8{"a2"}
  0015: utf8{"a3"}
  0016: utf8{"a4"}
  0017: utf8{"<init>"}
  0018: utf8{"()V"}
  0019: utf8{"Code"}
  001a: utf8{"LineNumberTable"}
  001b: utf8{"JavaPrintln"}
  001c: utf8{"(Ljava/lang/String;)I"}
  001d: utf8{"main"}
  001e: utf8{"([Ljava/lang/String;)V"}
  001f: utf8{"<clinit>"}
  0020: utf8{"SourceFile"}
  0021: utf8{"Javayun.java"}
  0022: nat{<init>:()V}
  0023: utf8{"Log private int a1"}
  0024: nat{JavaPrintln:(Ljava/lang/String;)I}
  0025: nat{a1:I}
  0026: utf8{"Log private int a3"}
  0027: nat{a3:I}
  0028: type{java.lang.System}
  0029: nat{out:Ljava/io/PrintStream;}
  002a: type{java.io.PrintStream}
  002b: nat{println:(Ljava/lang/String;)V}
  002c: utf8{"main fuction"}
  002d: utf8{"Log private static int a2"}
  002e: nat{a2:I}
  002f: utf8{"static code b1"}
  0030: utf8{"Log private static int a4"}
  0031: nat{a4:I}
  0032: utf8{"static code b2"}
  0033: utf8{"Javayun"}
  0034: utf8{"java/lang/Object"}
  0035: utf8{"java/lang/System"}
  0036: utf8{"out"}
  0037: utf8{"Ljava/io/PrintStream;"}
  0038: utf8{"java/io/PrintStream"}
  0039: utf8{"println"}
  003a: utf8{"(Ljava/lang/String;)V"}
end constant_pool
access_flags: public|super
this_class: type{Javayun}
super_class: type{java.lang.Object}
interfaces_count: 0000
fields_count: 0004

fields[0]:
  access_flags: private
  name: a1
  descriptor: I
  attributes_count: 0000
end fields[0]

fields[1]:
  access_flags: private|static
  name: a2
  descriptor: I
  attributes_count: 0000
end fields[1]

fields[2]:
  access_flags: private
  name: a3
  descriptor: I
  attributes_count: 0000
end fields[2]

fields[3]:
  access_flags: private|static
  name: a4
  descriptor: I
  attributes_count: 0000
end fields[3]
methods_count: 0004

methods[0]:
  access_flags: public
  name: <init>
  descriptor: ()V
  attributes_count: 0001
  
  attributes[0]:
    name: Code
    length: 00000037
    max_stack: 0002
    max_locals: 0001
    code_length: 00000017
    0000: aload_0 // 00
    0001: invokespecial method{java.lang.Object.<init>:()V}
    0004: aload_0 // 00
    0005: ldc string{"Log private int a1"}
    0007: invokestatic method{Javayun.JavaPrintln:(Ljava/lang/String;)I}
    000a: putfield field{Javayun.a1:I}
    000d: aload_0 // 00
    000e: ldc string{"Log private int a3"}
    0010: invokestatic method{Javayun.JavaPrintln:(Ljava/lang/String;)I}
    0013: putfield field{Javayun.a3:I}
    0016: return
    exception_table_length: 0000
    attributes_count: 0001
    
    attributes[0]:
      name: LineNumberTable
      length: 0000000e
      line_number_table_length: 0003
      0000 1
      0004 8
      000d 16
    end attributes[0]
  end attributes[0]
end methods[0]

methods[1]:
  access_flags: public|static
  name: JavaPrintln
  descriptor: (Ljava/lang/String;)I
  attributes_count: 0001
  
  attributes[0]:
    name: Code
    length: 00000027
    max_stack: 0002
    max_locals: 0001
    code_length: 0000000b
    0000: getstatic field{java.lang.System.out:Ljava/io/PrintStream;}
    0003: aload_0 // 00
    0004: invokevirtual method{java.io.PrintStream.println:(Ljava/lang/String;)
    V}
    0007: sipush #+01f4
    000a: ireturn
    exception_table_length: 0000
    attributes_count: 0001
    
    attributes[0]:
      name: LineNumberTable
      length: 0000000a
      line_number_table_length: 0002
      0000 4
      0007 5
    end attributes[0]
  end attributes[0]
end methods[1]

methods[2]:
  access_flags: public|static
  name: main
  descriptor: ([Ljava/lang/String;)V
  attributes_count: 0001
  
  attributes[0]:
    name: Code
    length: 00000025
    max_stack: 0002
    max_locals: 0001
    code_length: 00000009
    0000: getstatic field{java.lang.System.out:Ljava/io/PrintStream;}
    0003: ldc string{"main fuction"}
    0005: invokevirtual method{java.io.PrintStream.println:(Ljava/lang/String;)
    V}
    0008: return
    exception_table_length: 0000
    attributes_count: 0001
    
    attributes[0]:
      name: LineNumberTable
      length: 0000000a
      line_number_table_length: 0002
      0000 25
      0008 26
    end attributes[0]
  end attributes[0]
end methods[2]

methods[3]:
  access_flags: static
  name: <clinit>
  descriptor: ()V
  attributes_count: 0001
  
  attributes[0]:
    name: Code
    length: 00000049
    max_stack: 0002
    max_locals: 0000
    code_length: 00000021
    0000: ldc string{"Log private static int a2"}
    0002: invokestatic method{Javayun.JavaPrintln:(Ljava/lang/String;)I}
    0005: putstatic field{Javayun.a2:I}
    0008: getstatic field{java.lang.System.out:Ljava/io/PrintStream;}
    000b: ldc string{"static code b1"}
    000d: invokevirtual method{java.io.PrintStream.println:(Ljava/lang/String;)
    V}
    0010: ldc string{"Log private static int a4"}
    0012: invokestatic method{Javayun.JavaPrintln:(Ljava/lang/String;)I}
    0015: putstatic field{Javayun.a4:I}
    0018: getstatic field{java.lang.System.out:Ljava/io/PrintStream;}
    001b: ldc string{"static code b2"}
    001d: invokevirtual method{java.io.PrintStream.println:(Ljava/lang/String;)
    V}
    0020: return
    exception_table_length: 0000
    attributes_count: 0001
    
    attributes[0]:
      name: LineNumberTable
      length: 00000016
      line_number_table_length: 0005
      0000 10
      0008 13
      0010 18
      0018 21
      0020 22
    end attributes[0]
  end attributes[0]
end methods[3]
attributes_count: 0001

attributes[0]:
  name: SourceFile
  length: 00000002
  source: string{"Javayun.java"}
end attributes[0]
end classfile

4. 分析Javayun_dxdump文件

1、先来看一下<clinit>函数:类的初始化函数,只要使用这个类,都需要进入这个函数

methods[3]:
  //静态方法
  access_flags: static
  //名字叫<clinit>
  name: <clinit>
  //()括号里面代表参数,里面没有内容,代表没有参数,返回值是V也就是void,没有内容
  descriptor: ()V
  //这个字段里面有一个属性
  attributes_count: 0001
  
  //第一个属性
  attributes[0]:
    //代码
    name: Code
    length: 00000049
    max_stack: 0002
    max_locals: 0000
    code_length: 00000021
    //载入"Log private static int a2"到栈中
    //上面还有2句话,000a中string字段:对应CONSTANT_String_info的tag,指向002d(javap解析出来是#45)
    //000a: string{"Log private static int a2"} => #10 = String             #45
    //002d中utf8字段:对应CONSTANT_String_info的string_index,这里被初始化为Log private static int a2
    //002d: utf8{"Log private static int a2"} => #45 = Utf8               Log private static int a2
    0000: ldc string{"Log private static int a2"}
    //调用JavaPrintln方法,打印日志,传入的参数是上面载入栈中的"Log private static int a2"
    0002: invokestatic method{Javayun.JavaPrintln:(Ljava/lang/String;)I}
    //将返回值赋值给静态变量a2 => 到这里走完了private static int a2 = JavaPrintln("Log private static int a2");
    0005: putstatic field{Javayun.a2:I}

    //获取打印日志的静态方法System.out.println
    0008: getstatic field{java.lang.System.out:Ljava/io/PrintStream;}
    //将"static code b1"入栈
    000b: ldc string{"static code b1"}
    //打印日志 => 到这里执行完了System.out.println("static code b1");
    000d: invokevirtual method{java.io.PrintStream.println:(Ljava/lang/String;)
    V}

    //执行静态变量初始化private static int a2 = JavaPrintln("Log private static int a2");
    0010: ldc string{"Log private static int a4"}
    0012: invokestatic method{Javayun.JavaPrintln:(Ljava/lang/String;)I}
    0015: putstatic field{Javayun.a4:I}

    //执行静态代码块System.out.println("static code b2");
    0018: getstatic field{java.lang.System.out:Ljava/io/PrintStream;}
    001b: ldc string{"static code b2"}
    001d: invokevirtual method{java.io.PrintStream.println:(Ljava/lang/String;)
    V}

    //返回了
    0020: return
    exception_table_length: 0000
    attributes_count: 0001
    
    attributes[0]:
      name: LineNumberTable
      length: 00000016
      line_number_table_length: 0005
      0000 10
      0008 13
      0010 18
      0018 21
      0020 22
    end attributes[0]
  end attributes[0]
end methods[3]

那么按顺序目前跑了下面的代码

private static int a2 = JavaPrintln("Log private static int a2");

static {
    System.out.println("static code b1");
}

private static int a4 = JavaPrintln("Log private static int a4");

static {
    System.out.println("static code b2");
}

输出

Log private static int a2 //执行静态变量a2
static code b1 //执行静态代码块b1
Log private static int a4 //执行静态变量a4
static code b2 //执行静态代码块b2

2、接着看一下main,java程序的入口函数,运行java程序就会跑
(注意Android中不是每一个类都会有main函数,这里只有一个类,那就只能这个类跑main函数)

methods[2]:
  //静态方法,而且是public的
  access_flags: public|static
  //方法名字是main
  name: main
  //参数是"java/lang/String"字符串的"["数组,返回值是void
  descriptor: ([Ljava/lang/String;)V
  //这个字段里面有一个属性
  attributes_count: 0001
  
  attributes[0]:
    name: Code
    length: 00000025
    max_stack: 0002
    max_locals: 0001
    code_length: 00000009
    //打印日志System.out.println("main fuction");
    0000: getstatic field{java.lang.System.out:Ljava/io/PrintStream;}
    0003: ldc string{"main fuction"}
    0005: invokevirtual method{java.io.PrintStream.println:(Ljava/lang/String;)
    V}
    0008: return
    exception_table_length: 0000
    attributes_count: 0001
    
    attributes[0]:
      name: LineNumberTable
      length: 0000000a
      line_number_table_length: 0002
      0000 25
      0008 26
    end attributes[0]
  end attributes[0]
end methods[2]

到目前为止输出的是,程序已经执行完了,
可以看到确实没有跑类对象构造函数以及普通变量的初始化也没用进行,
静态变量和静态代码块的优先级是一样的,谁写在前面就谁先运行

Log private static int a2 //执行静态变量a2
static code b1 //执行静态代码块b1
Log private static int a4 //执行静态变量a4
static code b2 //执行静态代码块b2
main fuction //执行main函数

3、类中的其他方法

=> JavaPrintln静态函数

methods[1]:
  access_flags: public|static
  name: JavaPrintln
  descriptor: (Ljava/lang/String;)I
  attributes_count: 0001
  
  attributes[0]:
    name: Code
    length: 00000027
    max_stack: 0002
    max_locals: 0001
    code_length: 0000000b
    //获取print方法
    0000: getstatic field{java.lang.System.out:Ljava/io/PrintStream;}
    //aload_0在静态方法里面代表方法的第一个参数,这里是"String i"
    0003: aload_0 // 00
    //打印日志,传入的是操作数aload_0
    0004: invokevirtual method{java.io.PrintStream.println:(Ljava/lang/String;)
    V}
    //常量压入栈中,#+01f4是500
    //当int取值-1~5采用iconst指令,取值-128~127采用bipush指令,取值-32768~32767采用sipush指令,
    //取值-2147483648~2147483647采用 ldc 指令
    0007: sipush #+01f4
    //返回,返回值是i(int整型)
    000a: ireturn
    exception_table_length: 0000
    attributes_count: 0001
    
    attributes[0]:
      name: LineNumberTable
      length: 0000000a
      line_number_table_length: 0002
      0000 4
      0007 5
    end attributes[0]
  end attributes[0]
end methods[1]

=> 构造函数,如果使用javap会显示为”public Javayun()”,由于这里没有构造函数,会自动生成一个
看一下对象构造函数会做什么事情

methods[0]:
  access_flags: public
  name: <init>
  descriptor: ()V
  attributes_count: 0001
  
  attributes[0]:
    name: Code
    length: 00000037
    max_stack: 0002
    max_locals: 0001
    code_length: 00000017
    //在非静态函数里面aload_0代表:载入this到操作数栈aload_0中
    0000: aload_0 // 00
    //调用this的Object.<init>方法
    0001: invokespecial method{java.lang.Object.<init>:()V}

    //载入this到操作数栈aload_0中,不然找不到类普通变量a1
    0004: aload_0 // 00
    //将"Log private int a1"压入栈
    0005: ldc string{"Log private int a1"}
    //调用打印函数
    0007: invokestatic method{Javayun.JavaPrintln:(Ljava/lang/String;)I}
    //将返回值赋值给类普通变量a1 => 到这里执行完了private int a1 = JavaPrintln("Log private int a1");
    000a: putfield field{Javayun.a1:I}

    //执行private int a3 = JavaPrintln("Log private int a3");
    000d: aload_0 // 00
    000e: ldc string{"Log private int a3"}
    0010: invokestatic method{Javayun.JavaPrintln:(Ljava/lang/String;)I}
    0013: putfield field{Javayun.a3:I}

    //返回
    0016: return
    exception_table_length: 0000
    attributes_count: 0001
    
    attributes[0]:
      name: LineNumberTable
      length: 0000000e
      line_number_table_length: 0003
      0000 1
      0004 8
      000d 16
    end attributes[0]
  end attributes[0]
end methods[0]

5. 再来一个网上的例子JavaTest.java

1、JavaTest具体代码如下(网上的其实也是只公布了答案,没看到具体的代码运行过程,所以这里就挑选来讲一讲)

public class JavaTest {
 
    public static void main(String[] args){
        f1();
    }
 
    static JavaTest javaTest = new JavaTest();
 
    static {
        System.out.println("1");
    }
 
    {
        System.out.println("2");
    }
 
    JavaTest(){
        System.out.println("3");
        System.out.println("a=" + a + ", b=" + b);
    }
 
    public static void f1(){
        System.out.println("4");
    }
 
    int a = 100;
    static int b = 200;
}

从之前的例子我们其实可以大概知道这里的执行顺序,
<clinit>函数是先执行的,然后main函数最后也会执行

public class JavaTest {
 
    //8. main函数在<clinit>类初始化之后才会执行
    public static void main(String[] args){
        //9. main函数调用f1()
        f1();
    }

    //1. <clinit>第一个执行的地方,不过这里会调用构造函数<init>
    static JavaTest javaTest = new JavaTest();
 
    static {
        //6. <clinit>第二个执行的地方
        System.out.println("1");
    }
 
    //2. 属于对象构造,会先初始化,这里是非静态代码块优先级和非静态成员变量一样,谁在前面谁先执行
    {
        System.out.println("2");
    }
 
    JavaTest(){
        //4. 执行构造函数里面方法
        System.out.println("3");
        //5. a=100, b=0 (注意了,静态变量b还没有初始化,
        //目前还在<clinit>第一个执行的地方static JavaTest javaTest = new JavaTest())
        System.out.println("a=" + a + ", b=" + b);
    }
 
    public static void f1(){
        //10. f1()执行
        System.out.println("4");
    }
 
    //3. 属于对象构造,初始化非静态成员变量
    int a = 100;
    //7. <clinit>第三个执行的地方
    static int b = 200;

运行结果是,和上面描述的顺序是一致的,这里再得到一个信息,
类的构造函数会先将非静态成员初始化,然后再执行我们写入构造函数的方法(这里是由于本例子中重新写了构造函数,并在里面加了内容)

2
3
a=100, b=0
1
4

6. 查看一下JavaTest的反编译数据

1、一样是<clinit>开始

methods[3]:
  access_flags: static
  name: <clinit>
  descriptor: ()V
  attributes_count: 0001
  
  attributes[0]:
    name: Code
    length: 00000039
    max_stack: 0002
    max_locals: 0000
    code_length: 00000019
    //新建一个JavaTest对象
    0000: new type{JavaTest}
    //将JavaTest 2次压入栈中,dup复制栈顶。相当于把操作数栈顶元素pop出来,再把它push两次
    0003: dup
    //调用JavaTest的构造函数
    0004: invokespecial method{JavaTest.<init>:()V}
    //赋值给静态变量javaTest => 此处跑完了static JavaTest javaTest = new JavaTest();
    0007: putstatic field{JavaTest.javaTest:LJavaTest;}

    //初始化静态代码块System.out.println("1");
    000a: getstatic field{java.lang.System.out:Ljava/io/PrintStream;}
    000d: ldc string{"1"}
    000f: invokevirtual method{java.io.PrintStream.println:(Ljava/lang/String;)
    V}

    //初始化静态变量static int b = 200; sipush是将#+00c8(200)压入栈中
    0012: sipush #+00c8
    0015: putstatic field{JavaTest.b:I}

    //返回
    0018: return
    exception_table_length: 0000
    attributes_count: 0001
    
    attributes[0]:
      name: LineNumberTable
      length: 0000000e
      line_number_table_length: 0003
      0000 7
      000a 10
      0012 27
    end attributes[0]
  end attributes[0]
end methods[3]
attributes_count: 0001

2、<clinit>中调用了<init>构造函数

methods[1]:
  access_flags: 0000
  name: <init>
  descriptor: ()V
  attributes_count: 0001
  
  attributes[0]:
    name: Code
    length: 0000006e
    max_stack: 0003
    max_locals: 0001
    code_length: 00000042

    //Object.<init>初始化
    0000: aload_0 // 00
    0001: invokespecial method{java.lang.Object.<init>:()V}

    //初始化非静态代码块System.out.println("2");
    0004: getstatic field{java.lang.System.out:Ljava/io/PrintStream;}
    0007: ldc string{"2"}
    0009: invokevirtual method{java.io.PrintStream.println:(Ljava/lang/String;)
    V}

    //初始化非静态变量int a = 100;
    000c: aload_0 // 00
    000d: bipush #+64
    000f: putfield field{JavaTest.a:I}

    //运行自定义构造函数里面的内容System.out.println("3");
    0012: getstatic field{java.lang.System.out:Ljava/io/PrintStream;}
    0015: ldc string{"3"}
    0017: invokevirtual method{java.io.PrintStream.println:(Ljava/lang/String;)
    V}

    //运行自定义构造函数里面的内容System.out.println("a=" + a + ", b=" + b);
    //获取System.out.println方法
    001a: getstatic field{java.lang.System.out:Ljava/io/PrintStream;}
    //新建一个StringBuilder对象
    001d: new type{java.lang.StringBuilder}
    //StringBuilder对象在栈中一共压入了2次
    0020: dup
    //StringBuilder对象消耗了一次,用于StringBuilder.<init>
    0021: invokespecial method{java.lang.StringBuilder.<init>:()V}

    //载入"a="到栈中
    0024: ldc string{"a="}
    //将"a="放入StringBuilder.append
    0026: invokevirtual method{java.lang.StringBuilder.append:(Ljava/lang/Strin
    g;)Ljava/lang/StringBuilder;}

    //载入this到栈中
    0029: aload_0 // 00
    //获取非静态变量int a
    002a: getfield field{JavaTest.a:I}
    //将非静态变量int a放入StringBuilder.append
    002d: invokevirtual method{java.lang.StringBuilder.append:(I)Ljava/lang/Str
    ingBuilder;}

    //载入"b="到栈中,并放入StringBuilder.append
    0030: ldc string{", b="}
    0032: invokevirtual method{java.lang.StringBuilder.append:(Ljava/lang/Strin
    g;)Ljava/lang/StringBuilder;}

    //获取静态变量static int b(注意这里不需要this指针),并放入StringBuilder.append
    //到目前为止b是没有内容的,也就是默认是0,还没有初始化
    0035: getstatic field{JavaTest.b:I}
    0038: invokevirtual method{java.lang.StringBuilder.append:(I)Ljava/lang/Str
    ingBuilder;}

    //调用StringBuilder.toString并打印日志
    003b: invokevirtual method{java.lang.StringBuilder.toString:()Ljava/lang/St
    ring;}
    003e: invokevirtual method{java.io.PrintStream.println:(Ljava/lang/String;)
    V}

    //返回
    0041: return
    exception_table_length: 0000
    attributes_count: 0001
    
    attributes[0]:
      name: LineNumberTable
      length: 0000001a
      line_number_table_length: 0006
      0000 17
      0004 14
      000c 26
      0012 18
      001a 19
      0041 20
    end attributes[0]
  end attributes[0]
end methods[1]

3、在函数执行完后,会执行main函数,java程序入口函数

methods[0]:
  access_flags: public|static
  name: main
  descriptor: ([Ljava/lang/String;)V
  attributes_count: 0001
  
  attributes[0]:
    name: Code
    length: 00000020
    max_stack: 0000
    max_locals: 0001
    code_length: 00000004
    //调用静态函数public static void f1(),这里也不需要this
    0000: invokestatic method{JavaTest.f1:()V}

    0003: return
    exception_table_length: 0000
    attributes_count: 0001
    
    attributes[0]:
      name: LineNumberTable
      length: 0000000a
      line_number_table_length: 0002
      0000 4
      0003 5
    end attributes[0]
  end attributes[0]
end methods[0]

4、其它函数public static void f1()

methods[2]:
  access_flags: public|static
  name: f1
  descriptor: ()V
  attributes_count: 0001
  
  attributes[0]:
    name: Code
    length: 00000025
    max_stack: 0002
    max_locals: 0000
    code_length: 00000009
    //执行System.out.println("4");
    0000: getstatic field{java.lang.System.out:Ljava/io/PrintStream;}
    0003: ldc string{"4"}
    0005: invokevirtual method{java.io.PrintStream.println:(Ljava/lang/String;)
    V}

    0008: return
    exception_table_length: 0000
    attributes_count: 0001
    
    attributes[0]:
      name: LineNumberTable
      length: 0000000a
      line_number_table_length: 0002
      0000 23
      0008 24
    end attributes[0]
  end attributes[0]
end methods[2]

7. 总结一下

从上面我们知道的一些java的基础知识

1、 <clinit>是类初始化函数,会第一个运行(Android Zyogte的类提前加载preloaded-classes使用的方法就是<clinit>)
2、 静态变量和静态代码块会在<clinit>里面初始化,静态方法是按需调用,不是一定会跑的
3、 静态变量和静态代码块的优先级是一样的,谁写在前面就谁先运行
4、 <init>类对象构造函数,不一定会运行
5、<init>类对象构造函数第一个跑的是Object的init对象构造函数(注意了Object是父类)
6、 非静态变量和非静态代码块会在<init>里面初始化
7、 非静态变量和非静态代码块的优先级是一样的,谁写在前面就谁先运行
8、 非静态变量是在自定义构造函数代码之前会初始化
9、 main函数:java程序入口函数,运行java程序会执行(但是不是每个class都有,
Android的class如Activity.class就没有这个函数,程序入口只需要一个,不需要每个类都有)

使用的方法有:

1、使用java的原生指令:javap -v -p JavaTest.class > JavaTest_javap
2、使用Android SDK提供的工具:D:\tools\SDK\build-tools\30.0.0\dx.bat --dump JavaTest.class > JavaTest_dxdump

其它情况,如jar包和apk里面看class的内容,也是类似,这里将反编译方法写在下面:
如果你是jar包,可以反编译出来(效果类似于dx –dump)

java -jar apktool.jar d + 路径

如果是apk,可以使用下面反编译查看

./apktool d ***.apk

到这里大家应该清楚java代码的执行顺序了吧,不再是书本上的似是而非的东西,
这里只是入门指引,大家有兴趣可以基于此持续研究学习


版权声明:本文为yun_hen原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/yun_hen/article/details/123318543