文档章节

通过Java字节码发现有趣的内幕之String篇(二)

jaffa
 jaffa
发布于 2015/08/31 17:46
字数 907
阅读 763
收藏 5

1、字面量相加

首先来看两个字面量字符串相加发生了什么情况,Java代码:

package com.jaffa.test.string;

public class StringOptTest {
	public static void main(String[] args){
	        //假设下面不符合常理的写法发生了,看聪明的编译器为我们做什么
		String str1 = "abc"+"def";
	}
}

编译后常量池列表和指令截图如下:

   

   

通过编译后常量池列表和指令我们发现"abc"和"def"在编译阶段被自动合成一个字符串常量了,减少运行时的指令运算。对于两个字面量的操作还适用于基本类型,如int、float、long、double等,在编译时都会对两个字面量进行运算。但是如果是一个错误的运算结果,那么编译保留原生的字节码指令,运行时报异常,如下面测试代码:

int iNum = 1/0;

2、声明变量的方式相加

String str1 = "abc";
String str2 = "def";
String str3 = str1 + str2;

   常量池列表:  

   

   字节码指令:

 0: ldc           #16
 2: astore_1
 3: ldc           #18
 5: astore_2
 6: new           #20
 9: dup
10: aload_1
11: invokestatic  #22
14: invokespecial #28
17: aload_2
18: invokevirtual #31
21: invokevirtual #35
24: astore_3
25: return

在字节码指令第6个是先new #20,即声明了一个java.lang.StringBuilder的对象,接着执行aload_1将"abc"从栈帧的局部变量表加载到操作数栈中,执行invokestatic #22调用String.valueOf(Ljava/lang/Object)得字符串,并调用invokespecial #28来完成StringBuilder的初始化工作,即new StringBuilder("abc")。第17,18个执行指令完成了append("def")的操作,最后执行21、24指令将数据存储第3个局部变量中。所以可以得到被编译优化后的代码应该如下:

String str1 = "abc";
String str2 = "def";
//String str3 = str1 + str2;
String str3 = new StringBuilder(String.valueOf(str1)).append(str2).toString();

通过验证是否可以说明在开发时可以直接使用String来操作字符相加呢,当然不是,如果只是简单的两个短字符串相加在性能上影响不大,但是如果所要操作的是一个大内容的字符串时,在使用上是建议通过new StringBuilder(capacity)来显示声明对象,因为大量的内容相加StringBuilder内部会不断扩展存放空间和复制数据来满足操作,这样会带来额外的消耗,下面这段代码两个情况执行效率上存在差距。

long time1 = System.currentTimeMillis();
long index = 0;
String str1 = "abc";
String str2 = "def";
while(index<=1000000){
    //cost time 3383
	StringBuilder str3 = new StringBuilder(200);
	//cost time 9821
	//String str3 = null;
	for(int i=0;i<100;i++){
		str3.append(str1).append(str2);
		//str3 += str1+str2;
	}	
	index ++;
}
System.out.printf("cost time %s",System.currentTimeMillis()-time1);

3、类成员属性

package com.jaffa.test.string;

public class StringOptTest {
	private String str1 = "abc";
	private String str2 = "def";
	private String str3 = str1+str2;
}

   

从编译后地字节码我们发现默认生成一个不带参数的构造函数,所有的成员初始化操作在构造函数中完成,注意这里和静态方法不太一样,默认会有aload_0表示实例本身,即this对象是默认参数传入。如果我们显示的编写一个构造函数时,构造函数中的代码会放在初始化成员属性后面执行。如下截图。

© 著作权归作者所有

jaffa
粉丝 27
博文 10
码字总数 9735
作品 0
福州
程序员
私信 提问
加载中

评论(3)

烫不了大卷
烫不了大卷
图挂了 请博主重新更新一下
jaffa
jaffa 博主

引用来自“YanbinQ”的评论

编译优化, 这要体现编译器的智能, 例如有些编译器能把 a * 9 优化成 a << 3 + a, 移位和加法性能优于乘法.
对于 final static 的变量的引用会内联常量值, 所以有些常量值变了, 光替换修改编译后的 class 文件是没用的.

类的变量初始化或 {} 中的代码会放到 <init> 方法中, 相应的 static{} 中的代码会放到 <cinit> 方法中, 这就是我们双大括号的写法 new HashMap(){{this.put("a", "b");}}
对于 final static 的变量的引用会内联常量值, 所以有些常量值变了, 光替换修改编译后的 class 文件是没用的. 学习了,验证一下确实是的,如果final static在父类常量中,编译优化后通过子类去使用这个常量时甚至不会引发父类的静态初始化
YanbinQ
YanbinQ
编译优化, 这要体现编译器的智能, 例如有些编译器能把 a * 9 优化成 a << 3 + a, 移位和加法性能优于乘法.
对于 final static 的变量的引用会内联常量值, 所以有些常量值变了, 光替换修改编译后的 class 文件是没用的.

类的变量初始化或 {} 中的代码会放到 <init> 方法中, 相应的 static{} 中的代码会放到 <cinit> 方法中, 这就是我们双大括号的写法 new HashMap(){{this.put("a", "b");}}
通过Java字节码发现有趣的内幕之String篇(一)

很多时候我们在编写Java代码时,判断和猜测代码问题时主要是通过运行结果来得到答案,本博文主要是想通过Java字节码的方式来进一步求证我们已知的东西。这里没有对Java字节码知识进行介绍,如...

jaffa
2015/08/28
3K
7
【JVM系列】一步步解析java执行内幕

对于任何一门语言,要想达到精通的水平,研究它的执行原理(或者叫底层机制)不失为一种良好的方式。在本篇文章中,将重点研究java源代码的执行原理,即从程 序员编写JAVA源代码,到最终形成产...

java菜分享
03/02
21
0
深入理解JVM内幕:从基本结构到Java 7新特

摘要:许多没有深入理解JVM的开发者也开发出了很多非常好的应用和类库。不过,如果你更加理解JVM的话,你就会更加理解Java,这样你会有助于你处理类似于我们前面的案例中的问题。 每个Java开...

开源中国驻成都办事处
2012/12/06
296
1
深入Java虚拟机之 -- 总结面试篇

系列文章: 深入Java虚拟机之 -- 总结面试篇 深入Java虚拟机之 --- JVM的爱恨情仇 JAVA 垃圾回收机制(一) --- 对象回收与算法初识 JAVA 垃圾回收机制(二) --- GC回收具体实现 深入Java虚拟机...

夏至的稻穗
05/06
0
0
大概优秀的java程序员都要会分析class文件吧

相信大家在学java的时候都会听到这样的一些结论: enum 是一个类 泛型的实现使用了类型擦除技术 非静态内部类持有外部类的引用 需要将自由变量声明成final才能给匿名内部类访问 ... 初学的时候...

嘉伟咯
03/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周六乱弹 —— 早上儿子问我他是怎么来的

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @凉小生 :#今日歌曲推荐# 少点戾气,愿你和这个世界温柔以待。中岛美嘉的单曲《僕が死のうと思ったのは (曾经我也想过一了百了)》 《僕が死の...

小小编辑
32分钟前
29
0
Excption与Error包结构,OOM 你遇到过哪些情况,SOF 你遇到过哪些情况

Throwable 是 Java 中所有错误与异常的超类,Throwable 包含两个子类,Error 与 Exception 。用于指示发生了异常情况。 Java 抛出的 Throwable 可以分成三种类型。 被检查异常(checked Exc...

Garphy
今天
9
0
计算机实现原理专题--二进制减法器(二)

在计算机实现原理专题--二进制减法器(一)中说明了基本原理,现准备说明如何来实现。 首先第一步255-b运算相当于对b进行按位取反,因此可将8个非门组成如下图的形式: 由于每次做减法时,我...

FAT_mt
昨天
6
0
好程序员大数据学习路线分享函数+map映射+元祖

好程序员大数据学习路线分享函数+map映射+元祖,大数据各个平台上的语言实现 hadoop 由java实现,2003年至今,三大块:数据处理,数据存储,数据计算 存储: hbase --> 数据成表 处理: hive --> 数...

好程序员官方
昨天
7
0
tabel 中含有复选框的列 数据理解

1、el-ui中实现某一列为复选框 实现多选非常简单: 手动添加一个el-table-column,设type属性为selction即可; 2、@selection-change事件:选项发生勾选状态变化时触发该事件 <el-table @sel...

everthing
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部