• Java之字节码(1)


    转载地址

    一:Java字节代码的组织形式

      类文件{

      OxCAFEBABE,小版本号,大版本号,常量池大小,常量池数组,访问控制标记,当前类信息,父类信息,实现的接口个数,实现的接口信息数组,域个数,域信息数组,方法个数,方法信息数组,属性个数,属性信息数组

      }

    二:查看方法 --- javap命令

      例子:有一个Java类Demo.java

     1 public class Demo { 
     2     private String str1; 
     3     private String str2; 
     4     private int num1; 
     5     private int num2; 
     6     public static final String STATIC_DATA = "hello world"; 
     7      
     8     private void sayHello1(){ 
     9         System.out.println("this is method1..."); 
    10     } 
    11     private void sayHello2(){ 
    12         System.out.println("this is method2..."); 
    13     } 
    14     public void sayHello3(){ 
    15         System.out.println("this is method3..."); 
    16     } 
    17 }

    通过jdk自带的反编译工具命令 javap 可以查看class文件的字节码信息

    1 D:>javap -verbose Demo >> Demo.txt

    Demo.txt:

     1 Compiled from "Demo.java" 
     2 public class Demo extends java.lang.Object 
     3   SourceFile: "Demo.java" 
     4   minor version: 0 
     5   major version: 49   
     6   
     7   Constant pool: 
     8 const #1 = class      #2;   //  Demo 
     9 const #2 = Asciz     Demo; 
    10 const #3 = class      #4;   //  java/lang/Object 
    11 const #4 = Asciz     java/lang/Object; 
    12 const #5 = Asciz     str1; 
    13 const #6 = Asciz     Ljava/lang/String;; 
    14 const #7 = Asciz     str2; 
    15 const #8 = Asciz     num1; 
    16 const #9 = Asciz     I; 
    17 const #10 = Asciz   num2; 
    18 const #11 = Asciz   STATIC_DATA; 
    19 const #12 = Asciz   ConstantValue; 
    20 const #13 = String  #14; //  hello world 
    21 const #14 = Asciz   hello world; 
    22 const #15 = Asciz   <init>; 
    23 const #16 = Asciz   ()V; 
    24 const #17 = Asciz   Code; 
    25 const #18 = Method       #3.#19;   //  java/lang/Object."<init>":()V 
    26 const #19 = NameAndType    #15:#16;//  "<init>":()V 
    27 const #20 = Asciz   LineNumberTable; 
    28 const #21 = Asciz   LocalVariableTable; 
    29 const #22 = Asciz   this; 
    30 const #23 = Asciz   LDemo;; 
    31 const #24 = Asciz   sayHello1; 
    32 const #25 = Field   #26.#28;  //  java/lang/System.out:Ljava/io/PrintStream; 
    33 const #26 = class    #27; //  java/lang/System 
    34 const #27 = Asciz   java/lang/System; 
    35 const #28 = NameAndType    #29:#30;//  out:Ljava/io/PrintStream; 
    36 const #29 = Asciz   out; 
    37 const #30 = Asciz   Ljava/io/PrintStream;; 
    38 const #31 = String  #32; //  this is method1... 
    39 const #32 = Asciz   this is method1...; 
    40 const #33 = Method       #34.#36;  //  java/io/PrintStream.println:(Ljava/lang/String;)V 
    41 const #34 = class    #35; //  java/io/PrintStream 
    42 const #35 = Asciz   java/io/PrintStream; 
    43 const #36 = NameAndType    #37:#38;//  println:(Ljava/lang/String;)V 
    44 const #37 = Asciz   println; 
    45 const #38 = Asciz   (Ljava/lang/String;)V; 
    46 const #39 = Asciz   sayHello2; 
    47 const #40 = String  #41; //  this is method2... 
    48 const #41 = Asciz   this is method2...; 
    49 const #42 = Asciz   sayHello3; 
    50 const #43 = String  #44; //  this is method3... 
    51 const #44 = Asciz   this is method3...; 
    52 const #45 = Asciz   SourceFile; 
    53 const #46 = Asciz   Demo.java; 
    54   
    55 { 
    56 public static final java.lang.String STATIC_DATA; 
    57   Constant value: String hello world 
    58 public Demo(); 
    59   Code: 
    60    Stack=1, Locals=1, Args_size=1 
    61    0:      aload_0 
    62    1:      invokespecial  #18; //Method java/lang/Object."<init>":()V 
    63    4:      return 
    64   LineNumberTable: 
    65    line 2: 0 
    66   LocalVariableTable: 
    67    Start  Length  Slot  Name   Signature 
    68    0      5      0    this       LDemo; 
    69   
    70 public void sayHello3(); 
    71   Code: 
    72    Stack=2, Locals=1, Args_size=1 
    73    0:      getstatic   #25; //Field java/lang/System.out:Ljava/io/PrintStream; 
    74    3:      ldc   #43; //String this is method3... 
    75    5:      invokevirtual  #33; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 
    76    8:      return 
    77   LineNumberTable: 
    78    line 17: 0 
    79    line 18: 8 
    80   LocalVariableTable: 
    81    Start  Length  Slot  Name   Signature 
    82    0      9      0    this       LDemo; 
    83 }

    解析:

      1、版本号 major version: 49 //java版本 jdk1.6显示的是50, jdk1.5显示的是49,jdk1.4显示的是58 , 高版本能执行低版本的class文件

      2、常量池Constant pool

      Method:方法

      Field:字段

      String:字符串

      Asciz:签名如<init>由jvm调用,其他是不能够去调用它的

      NameAndType:变量名的类型

      Class:类

      通过字节码,我们可以看到Demo类 继承于java.lang.Object,如果类中没有显式声明构造函数的话,编译器会插入一个缺省无参的构造函数(构造函数在JVM级别是显示成<init>的普通函数)。

      三:检测代码的效率问题

      学习Java的过程中,都会了解到字符串合并时要用到StringBuffer 来代替String,那下面就来通过Java字节码来验证两种方式的效率性。

      例子:一个Java类 TestString.java

    1 <strong>public class TestString { 
    2     public String testString(String str1, String str2){ 
    3        return str1 + str2; 
    4     } 
    5     public String testStringBuffer(StringBuffer sb, String str){ 
    6        return sb.append(str).toString(); 
    7     } 
    8 } 
    9  </strong>

    javap –c TestString 后字节码信息:

    Compiled from "TestString.java" 
    public class TestString extends java.lang.Object{ 
    public TestString(); 
      Code: 
       0:      aload_0 
       1:      invokespecial  #8; //Method java/lang/Object."<init>":()V 
       4:      return 
      
    public java.lang.String testString(java.lang.String, java.lang.String); 
      Code: 
       0:      new #16; //class java/lang/StringBuilder 
       3:      dup 
       4:      aload_1 
       5:      invokestatic    #18; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String; 
       8:      invokespecial  #24; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 
       11:     aload_2 
       12:    invokevirtual  #27; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
       15:    invokevirtual  #31; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
       18:    areturn 
      
    public java.lang.String testStringBuffer(java.lang.StringBuffer, java.lang.String); 
      Code: 
       0:      aload_1 
       1:      aload_2 
       2:      invokevirtual  #40; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; 
       5:      invokevirtual  #45; //Method java/lang/StringBuffer.toString:()Ljava/lang/String; 
       8:      areturn 
    }

    从 上面编译后的字节码信息可以看出来,方法testString 调用了五个方法:new 、invokestatic 、invokespecial 和两个invokevirtual ; 而testStringBuffer 方法只调用了两个invokevirtual 方法。第一个方法比第二个方法多做了好多工作,其效率当然是要低的。而且我们从java/lang/StringBuilder.append: (Ljava/lang/String;)Ljava/lang/StringBuilder;

       可以看出来其实对于String字符串合并,内部还是转化为StringBuilder的方法调用,这是因为String是长度不可变的,所以不如直接 采用StringBuilder(与StringBuffer 长度都是可变的,只不过前者是非线程安全,后者是线程安全)进行字符串合并。

     
     
  • 相关阅读:
    Array 对象-sort()
    vue安装
    前端面试题
    JavaScript对象原型
    CSS如何水平垂直居中?
    块格式化上下文(Block Formatting Context,BFC)
    盒子模型
    前端基础
    Markdown语法
    浏览器 滚动条 占据 y轴宽度的解决方案
  • 原文地址:https://www.cnblogs.com/royi123/p/3569926.html
Copyright © 2020-2023  润新知