热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【转】javap-c命令详解

javap-c命令详解一直在学习Java,碰到了很多问题,碰到了很多关于i++和++i的难题,以及最经典的Stringstrabc共创建了几个对象的疑难杂症。知道有一日知道

javap -c命令详解

一直在学习Java,碰到了很多问题,碰到了很多关于i++和++i的难题,以及最经典的String str = "abc" 共创建了几个对象的疑难杂症。 知道有一日知道了java的反汇编 命令  javap。现将学习记录做一小结,以供自己以后翻看。如果有错误的地方,请指正

1.javap是什么:

 

 where options include:
-c Disassemble the code
-classpath Specify where to find user class files
-extdirs Override location of installed extensions
-help Print this usage message
-J Pass directly to the runtime system
-l Print line number and local variable tables
-public Show only public classes and members
-protected Show protected/public classes and members
-package Show package/protected/public classes
and members (default)
-private Show all classes and members
-s Print internal type signatures
-bootclasspath Override location of class files loaded
by the bootstrap class loader
-verbose Print stack size, number of locals and args for met
hods
If verifying, print reasons for failure 

以上为百度百科里对它的描述,只是介绍了javap的一些参数和使用方法,而我们要用的就是这一个:-c Disassemble the code。

明确一个问题:javap是什么?网上有人称之为 反汇编器,可以查看java编译器为我们生成的字节码。通过它,我们可以对照源代码和字节码,从而了解很多编译器内部的工作。

2.初步认识javap

从一个最简单的例子开始:

 

 这个例子中,我们只是简单的声明了两个int型变量并赋上初值。下面我们看看javap给我们带来了什么:(当然执行javap命令前,你得首先配置好自己的环境,能用javac编译通过了,即:javac TestJavap.java )

我们只看(方便起见,将注释写到每句后面)

  Code:
   0:   iconst_2
    //把2放到栈顶
   1:   istore_1    //把栈顶的值放到局部变量1中,即i中
   2:   iconst_3    //把3放到栈顶
   3:   istore_2    //把栈顶的值放到局部变量1中,即j中
   4:   return

 

是不是很简单?(当然,估计需要点数据结构的知识) ,那我们就补点java的关于堆栈的知识:

对于 int i = 2;首先它会在栈中创建一个变量为i的引用,然后查找有没有字面值为2的地址,没找到,就开辟一个存放2这个字面值的地址,然后将i指向2的地址。

看了这段话,再比较下上面的注释,是不是完全吻合?

为了验证上面这一说法,我们继续实验:

我们将 i 和 j的值都设为2。按照以上理论,在声明j的时候,会去栈中招有没有字面值为2的地址,由于在栈中已经有2这个字面值,便将j直接指向2的地址。这样,就出现了i与j同时均指向2的情况。

 拿出javap -c进行反编译:结果如下:

  Code:
   0:   iconst_2    //把2放到栈顶
   1:   istore_1    //把栈顶的值放到局部变量1中,即i中
   2:   iconst_2    //把2放到栈顶
   3:   istore_2    //把栈顶的值放到局部变量2中,即j中(i 和 j同时指向2)
   4:   return

 

虽然这里说i和j同时指向2,但这里不等于说i和j指向同一块地址(java是不允许程序员直接修改堆栈中的数据的,所以就不要想着,我是不是可以修改栈中的2,那样岂不是i和j的值都会变化。另:在编译器内部,遇到j=2;时,它就会重新搜索栈中是否有2的字面值,如果没有,重新开辟地址存放2的值;如果已经有了,则直接将j指向这个地址。因此,就算j另被赋值为其他值,如j=4,j值的改变不会影响到i的值。)

再来一个例子:

还是javap -c

  Code:
   0:   iconst_2    //把2放到栈顶
   1:   istore_1    //把栈顶的值放到局部变量1中,即i中
   2:   iload_1     //把i的值放到栈顶,也就是说此时栈顶的值是2
   3:   istore_2    //把栈顶的值放到局部变量2中,即j中
   4:   return

看到这里是不是有点明确了?

 

 

既然我们对javap有了一定的了解,那我们就开始用它来解决一些实际的问题:

1.i++和++i的问题

反编译结果为

 Code:
  0:   iconst_1
  1:   istore_1
  2:   iinc    1, 1 //这个个指令,把局部变量1,也就是i,增加1,这个指令不会导致栈的变化,i此时变成2了
  5:   iconst_1
  6:   istore_2
  7:   iinc    2, 1//这个个指令,把局部变量2,也就是j,增加1,这个指令不会导致栈的变化,j此时变成2了
  10:  return

 

可以看出,++在前在后,在这段代码中,没有任何不同。

我们再看另一段代码:

反编译结果:

  Code:
   0:   iconst_1
   1:   istore_1
   2:   iload_1
   3:   iinc    1, 1  //局部变量1(即i)加1变为2,注意这时栈中仍然是1,没有改变
   6:   istore_1    //把栈顶的值放到局部变量1中,即i这时候由2变成了1
   7:   iconst_1
   8:   istore_2
   9:   iinc    2, 1 //局部变量2(即j)加1变为2,注意这时栈中仍然是1,没有改变
   12:  iload_2    //把局部变量2(即j)的值放到栈顶,此时栈顶的值变为2
   13:  istore_2   //把栈顶的值放到局部变量2中,即j这时候真正由1变成了2
   14:  return

是否看明白了? 如果这个看明白了,那么下面的一个问题应该就是迎刃而解了:

m = m ++;这句话,java虚拟机执行时是这样的: m的值加了1,但这是栈中的值还是0, 马上栈中的值覆盖了m,即m变成0,因此不管循环多少次,m都等于0。

如果改为m = ++m; 程序运行结果就是100了。。。


推荐阅读
  • Netty框架中运用Protobuf实现高效通信协议
    在Netty框架中,通过引入Protobuf来实现高效的通信协议。为了使用Protobuf,需要先准备好环境,包括下载并安装Protobuf的代码生成器`protoc`以及相应的源码包。具体资源可从官方下载页面获取,确保版本兼容性以充分发挥其性能优势。此外,配置好开发环境后,可以通过定义`.proto`文件来自动生成Java类,从而简化数据序列化和反序列化的操作,提高通信效率。 ... [详细]
  • 本文将带你快速了解 SpringMVC 框架的基本使用方法,通过实现一个简单的 Controller 并在浏览器中访问,展示 SpringMVC 的强大与简便。 ... [详细]
  • Java中不同类型的常量池(字符串常量池、Class常量池和运行时常量池)的对比与关联分析
    在研究Java虚拟机的过程中,笔者发现存在多种类型的常量池,包括字符串常量池、Class常量池和运行时常量池。通过查阅CSDN、博客园等相关资料,对这些常量池的特性、用途及其相互关系进行了详细探讨。本文将深入分析这三种常量池的差异与联系,帮助读者更好地理解Java虚拟机的内部机制。 ... [详细]
  • 分布式开源任务调度框架 TBSchedule 深度解析与应用实践
    本文深入解析了分布式开源任务调度框架 TBSchedule 的核心原理与应用场景,并通过实际案例详细介绍了其部署与使用方法。首先,从源码下载开始,详细阐述了 TBSchedule 的安装步骤和配置要点。接着,探讨了该框架在大规模分布式环境中的性能优化策略,以及如何通过灵活的任务调度机制提升系统效率。最后,结合具体实例,展示了 TBSchedule 在实际项目中的应用效果,为开发者提供了宝贵的实践经验。 ... [详细]
  • 如何使用 net.sf.extjwnl.data.Word 类及其代码示例详解 ... [详细]
  • Spring Boot 实战(一):基础的CRUD操作详解
    在《Spring Boot 实战(一)》中,详细介绍了基础的CRUD操作,涵盖创建、读取、更新和删除等核心功能,适合初学者快速掌握Spring Boot框架的应用开发技巧。 ... [详细]
  • 本文探讨了如何在 Java 中将多参数方法通过 Lambda 表达式传递给一个接受 List 的 Function。具体分析了 `OrderUtil` 类中的 `runInBatches` 方法及其使用场景。 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • 深入解析 Lifecycle 的实现原理
    本文将详细介绍 Android Jetpack 中 Lifecycle 组件的实现原理,帮助开发者更好地理解和使用 Lifecycle,避免常见的内存泄漏问题。 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 浏览器作为我们日常不可或缺的软件工具,其背后的运作机制却鲜为人知。本文将深入探讨浏览器内核及其版本的演变历程,帮助读者更好地理解这一关键技术组件,揭示其内部运作的奥秘。 ... [详细]
  • 深入解析:Synchronized 关键字在 Java 中对 int 和 Integer 对象的作用与影响
    深入探讨了 `Synchronized` 关键字在 Java 中对 `int` 和 `Integer` 对象的影响。尽管初看此题似乎简单,但其实质在于理解对象的概念。根据《Java编程思想》第二章的观点,一切皆为对象。本文详细分析了 `Synchronized` 关键字在不同数据类型上的作用机制,特别是对基本数据类型 `int` 和包装类 `Integer` 的区别处理,帮助读者深入理解 Java 中的同步机制及其在多线程环境中的应用。 ... [详细]
  • 字节码开发笔记:深入解析与应用技巧 ... [详细]
  • 在处理大图片时,PHP 常常会遇到内存溢出的问题。为了避免这种情况,建议避免使用 `setImageBitmap`、`setImageResource` 或 `BitmapFactory.decodeResource` 等方法直接加载大图。这些函数在处理大图片时会消耗大量内存,导致应用崩溃。推荐采用分块处理、图像压缩和缓存机制等策略,以优化内存使用并提高处理效率。此外,可以考虑使用第三方库如 ImageMagick 或 GD 库来处理大图片,这些库提供了更高效的内存管理和图像处理功能。 ... [详细]
  • 如何使用和示例代码解析 org.semanticweb.owlapi.model.OWLSubPropertyChainOfAxiom.getPropertyChain() 方法 ... [详细]
author-avatar
freezing-wing
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有