首页 > 编程语言 >Java方法链调用以及在JVM和安卓DalvikVM下的区别

Java方法链调用以及在JVM和安卓DalvikVM下的区别

时间:2024-12-27 22:21:26浏览次数:5  
标签:lang Ljava 调用 Java StringBuilder 安卓 v0 DalvikVM append

目录

方法链

方法链(Method Chaining),也被称为命名参数法,是在面向对象的编程语言中调用的调用多个方法的通用语法。每一个方法返回一个对象,在一个单一的声明里,方法链省去了中间变量的需要。

当需要构建一个对象或者设置其初始属性时,往往通过构造参数传入或者 setter 方法。比如:

User user = new User("张三", 34);
// 或者
User user = new User();
user.setName("张三");
user.setAge(34);

但这样存在一些缺点:

  • 参数数量:很多情况下我们可能只需要一两个参数,其余参数保持默认。使用构造方法的话需要为各种情况进行声明;
  • 中间变量:如果创建后的对象直接被作为其他方法的参数,如果不使用构造方法设定参数,而是只设定其中以一两个个参数,那么必须使用中间变量并通过 setter 实现。

方法链通过返回对象自身,便可以在设定一个参数后直接调用另一个参数而不必使用中间变量。方法链常与建造者模式(Builder)结合,比如 StringBuilder:

String str = new StringBuilder("a")
    .append(1)
    .append(3.14f)
    .toString();

字节码与 Smali 下的编译结果

Java 源代码通过编译为 JVM 可解释的字节码(Byte Code)以便在 JVM 上执行。为了适应移动设备的特点,Android 使用 DalvikVM 而不是 JVM,相应的字节码也需要转变。DalvikVM 字节码码反编译过后就是 Smali 代码。

JVM 是基于栈的虚拟机,而 DalvikVM 是基于寄存器的虚拟机。相比之下,基于寄存器的虚拟机效率更高,更适合在移动设备上运行。另外,JVM 通常使用 JIT(Just In Time, 即时编译)进行加速,而 DalvikVM 的继承者 ART 则采用 AOT(Ahead Of Time, 提前编译)加速。

对于以上 StringBuilder 的例子,使用 javac 编译成字节码后,通过 javap -c 查看,结果如下:

 0: new           #37                 // class java/lang/StringBuilder
 3: dup
 4: ldc           #50                 // String a
 6: invokespecial #52                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
 9: iconst_1
10: invokevirtual #54                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
13: ldc2_w        #57                 // float 3.14f
16: invokevirtual #59                 // Method java/lang/StringBuilder.append:(F)Ljava/lang/StringBuilder;
19: invokevirtual #46                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

可以看到,在第一条 invokevirtual 调用 append 方法之后会把其结果存到栈内,下一条 ldc2_w 则把常数 3.14 再存入栈内,此时栈顶有一个 StringBuilder 对象和一个 double。下一句 invokevirtual 则直接取栈顶两个作为参数进行调用,并再把结果放入栈内。可以看到链式调用不仅在 Java 源代码上可以简化写法,甚至在字节码上没有额外增加指令。

通过 d8 将该字节码编译为 .dex 文件,再通过安卓逆向工具得到 Smali 代码:

new-instance v0, Ljava/lang/StringBuilder;
const-string v1, "a"
invoke-direct {v0, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
.line 28
const/4 v1, 0x1
invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
move-result-object v0
.line 29
const v1, 0x4048f5c3    # 3.14f
invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(F)Ljava/lang/StringBuilder;
move-result-object v0
.line 30
invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v0

Smali 代码中可以看到很多寄存器,有一种看 MIPS 或者 ARM 汇编的感觉。可以看到每次通过 invoke-virtual 调用 append 方法后需要把返回值移动到寄存器 v0,之后再添加参数,调用下一个 append 方法。注意,每次调用 invoke-virtual 时,第一个参数都是寄存器 v0,而 v0 都来自于上次调用 append 后的返回值。而实际上在第一句 new-instance 之后,v0 始终为同一个 StringBuilder 对象。也就是说,此处的 move-result-object v0 是多余的(除了最后一个调用 toString 之后的)。每一次调用 append 时,需要指定除 v0 外的另一个参数 v1(通过 const 等语句),而调用之后则通过 move-result-object 将结果返回给 v0,当 append 调用很多时,冗余率达 1/3

所以对 DalvikVM 好一点儿的写法是:

StringBuilder bld = new StringBuilder("a");
bld.append(1);
bld.append(3.14f);
String str = bld.toString();

另外,对于字符串拼接,源代码中使的用加号(+)实际上是语法糖,会被编译成 StringBuilder 或者 StringBuffer,而每次至少调用两次 append (因为脱糖之后不会在构造方法内传入第一个字符串,而是不带参构造,两次调用 append 进行拼接)和一次 toString。通过 StringBuilder 在处理大量的拼接操作时固然有效,但是对于仅两个字符串的拼接,建议直接用 String.concat 方法以减少参数调用次数,同时效率也不打折扣。

总结

方法链固然带来了很大的方便,但是对于安卓来说会在一定程度上带来冗余的 Dalvik 字节码,因此在做安卓开发时需要慎用。


原文地址:https://www.cnblogs.com/RainbowC0/p/18636744 ,未经作者许可禁止转载

标签:lang,Ljava,调用,Java,StringBuilder,安卓,v0,DalvikVM,append
From: https://www.cnblogs.com/RainbowC0/p/18636744

相关文章

  • 基于java的SpringBoot/SSM+Vue+uniapp的员工日志管理信息系统的详细设计和实现(源码+l
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 【Java毕业设计】基于SpringBoot+Vue的社区维修平台
    源码获取:https://download.csdn.net/download/u011832806/89432062基于SpringBoot+Vue的社区维修平台开发语言:Java数据库:MySQL技术:SpringBoot+MyBatis+Vue.js工具:IDEA/Ecilpse、Navicat、Maven系统演示视频:链接:https://pan.baidu.com/s/1PcACXotF1Z3-elv1RWDZ4w?pwd=xs......
  • 【Java毕业设计】基于SpringBoot+Vue的藏区特产销售平台
    源码获取:https://download.csdn.net/download/u011832806/89456836基于SpringBoot+Vue的藏区特产销售平台开发语言:Java数据库:MySQL技术:SpringBoot+MyBatis+Vue.js工具:IDEA/Ecilpse、Navicat、Maven系统演示视频:链接:https://pan.baidu.com/s/18HNgFVJ_GyLH8QE5uRJghg?pw......
  • JAVA-Day 03:数据类型
    数据类型Java的数据类型分为两大类,分别是基本类型(primitivetype)和引用类型(referencetype)。基本数据类型(PrimitiveType)分为数值类型和boolean(布尔)类型数值类型整数类型byte型占1个字节范围:-128~127如图所示:short型占2个字节范围:-32768~32767如图所示:......
  • 基于Java+Springboot+MySQL新闻资讯网站系统设计与实现
     博主介绍:黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育、辅导。所有项目都配有从入门到精通的基础知识视频课程,学习后应对毕业设计答辩,提供核心代码讲解,答辩指导。项目配有对应开发......
  • 前段时间将一个项目代码从java转为鸿蒙,总结了其中java转ArkTS语法的一些经验
    前段时间将一个项目代码从java转为鸿蒙,总结了其中java转ArkTS语法的一些经验。整个项目代码花了10多天时间,转完后运行bug还是较少的。以下是记录下来的一些,部分未经常出现的可能没有记录//向下取整(int)->Math.floor()Integer.parseInt("10")->parseInt("10")//字符串st......
  • 谷歌Pixel 2 刷安卓10系统 APatch获取Root权限
    事前准备手机需要解Bootloader锁,打开OEM解锁,开启USB调试链接电脑下载Platform-Tools#地址https://developer.android.google.cn/tools/releases/platform-tools?hl=zh-cn检查是否需要需要安装Android驱动刷机下载先刷机包https://developers.google.cn/......
  • Java+Vue构建物流仓储管理系统,源码文档完备
    前言:物流仓储管理系统是供应链管理中至关重要的组成部分,它负责优化仓库作业流程,提高库存准确性,降低运营成本,并提升客户满意度。以下是对系统的八大模块的详细解释:一、车辆管理车辆管理模块负责跟踪、调度和优化物流运输车辆。这包括:车辆追踪:实时获取车辆位置、行驶路线和预......
  • 【华为OD-E卷 - 猜字谜100分(python、java、c++、js、c)】
    【华为OD-E卷-猜字谜100分(python、java、c++、js、c)】题目小王设计了一个简单的猜字谜游戏,游戏的谜面是一个错误的单词,比如nesw,玩家需要猜出谜底库中正确的单词。猜中的要求如下:对于某个谜面和谜底单词,满足下面任一条件都表示猜中:变换顺序以后一样的,比如通过变换w和e......
  • Java多线程处理文件详解与代码示例
    在Java编程中,文件处理是一项常见的任务。当需要处理大量文件或处理文件的时间较长时,单线程的处理方式可能会显得效率低下。为了提高文件处理的效率,我们可以使用多线程技术。本文将详细介绍如何使用Java多线程来处理文件,并提供一个详细的代码示例,该示例可以直接运行。一、多线......