- 浏览: 532326 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
c__海棠依旧:
很强,对于我这个新手很容易理解,准们登录来给你点赞的!
BeanFactory和FactoryBean -
hudazheng:
很清晰!
X86、X64和X86_64区别 -
hugh.wang:
...
BeanFactory和FactoryBean -
CB00J:
...
Executor框架和线程池 -
Arbow:
请教一个问题。现在互联网业务的数据库通常用分片方式来连接一组数 ...
BoneCP源码——概述
常量池(constant_pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量和符号引用。运行时常量池是方法区的一部分。
在Class文件结构中,最头的4个字节用于存储魔数Magic Number,用于确定一个文件是否能被JVM接受,再接着4个字节用于存储版本号,前2个字节存储次版本号,后2个存储主版本号,再接着是用于存放常量的常量池,由于常量的数量是不固定的,所以常量池的入口放置一个U2类型的数据(constant_pool_count)存储常量池容量计数值。常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References),字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:
类和接口的全限定名
字段名称和描述符
方法名称和描述符
Java中八种基本类型的包装类的大部分都实现了常量池技术,它们是Byte、Short、Integer、Long、Character、Boolean,另外两种浮点数类型的包装类(Float、Double)则没有实现。另外Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值在-128到127时才可使用对象池。
看下面代码:
/** * Huisou.com Inc. * Copyright (c) 2011-2012 All Rights Reserved. */ /** * @description * @package * @title Test.java * @author chenzehe * @email * @version * @updateUser * @create 2011-12-21 上午11:27:48 * @update 2011-12-21 上午11:27:48 */ public class Test { public static void main(String[] args) throws Exception { Integer a = 127; Integer b = 127; Integer c = 128; Integer d = 128; System.out.println(a == b);// 输出true System.out.println(c == d);// 输出false } }
使用javap查看生成的字节码:
E:\chenzehe\workspace_b2b_3th\test\src>javap -verbose Test Compiled from "Test.java" public class Test extends java.lang.Object SourceFile: "Test.java" minor version: 0 major version: 50 Constant pool: const #1 = Method #6.#21; // java/lang/Object."<init>":()V const #2 = Method #22.#23; // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; const #3 = Field #24.#25; // java/lang/System.out:Ljava/io/PrintStream; const #4 = Method #26.#27; // java/io/PrintStream.println:(Z)V const #5 = class #28; // Test const #6 = class #29; // java/lang/Object const #7 = Asciz <init>; const #8 = Asciz ()V; const #9 = Asciz Code; const #10 = Asciz LineNumberTable; const #11 = Asciz main; const #12 = Asciz ([Ljava/lang/String;)V; const #13 = Asciz StackMapTable; const #14 = class #30; // "[Ljava/lang/String;" const #15 = class #31; // java/lang/Integer const #16 = class #32; // java/io/PrintStream const #17 = Asciz Exceptions; const #18 = class #33; // java/lang/Exception const #19 = Asciz SourceFile; const #20 = Asciz Test.java; const #21 = NameAndType #7:#8;// "<init>":()V const #22 = class #31; // java/lang/Integer const #23 = NameAndType #34:#35;// valueOf:(I)Ljava/lang/Integer; const #24 = class #36; // java/lang/System const #25 = NameAndType #37:#38;// out:Ljava/io/PrintStream; const #26 = class #32; // java/io/PrintStream const #27 = NameAndType #39:#40;// println:(Z)V const #28 = Asciz Test; const #29 = Asciz java/lang/Object; const #30 = Asciz [Ljava/lang/String;; const #31 = Asciz java/lang/Integer; const #32 = Asciz java/io/PrintStream; const #33 = Asciz java/lang/Exception; const #34 = Asciz valueOf; const #35 = Asciz (I)Ljava/lang/Integer;; const #36 = Asciz java/lang/System; const #37 = Asciz out; const #38 = Asciz Ljava/io/PrintStream;; const #39 = Asciz println; const #40 = Asciz (Z)V; { public Test(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 18: 0 public static void main(java.lang.String[]) throws java.lang.Exception; Code: Stack=3, Locals=5, Args_size=1 0: bipush 127 2: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 5: astore_1 6: bipush 127 8: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 11: astore_2 12: sipush 128 15: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 18: astore_3 19: sipush 128 22: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 25: astore 4 27: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream; 30: aload_1 31: aload_2 32: if_acmpne 39 35: iconst_1 36: goto 40 39: iconst_0 40: invokevirtual #4; //Method java/io/PrintStream.println:(Z)V 43: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream; 46: aload_3 47: aload 4 49: if_acmpne 56 52: iconst_1 53: goto 57 56: iconst_0 57: invokevirtual #4; //Method java/io/PrintStream.println:(Z)V 60: return LineNumberTable: line 20: 0 line 21: 6 line 22: 12 line 23: 19 line 24: 27 line 25: 43 line 26: 60 StackMapTable: number_of_entries = 4 frame_type = 255 /* full_frame */ offset_delta = 39 locals = [ class "[Ljava/lang/String;", class java/lang/Integer, class java/lang/Integer, class java/lang/Inte ger, class java/lang/Integer ] stack = [ class java/io/PrintStream ] frame_type = 255 /* full_frame */ offset_delta = 0 locals = [ class "[Ljava/lang/String;", class java/lang/Integer, class java/lang/Integer, class java/lang/Inte ger, class java/lang/Integer ] stack = [ class java/io/PrintStream, int ] frame_type = 79 /* same_locals_1_stack_item */ stack = [ class java/io/PrintStream ] frame_type = 255 /* full_frame */ offset_delta = 0 locals = [ class "[Ljava/lang/String;", class java/lang/Integer, class java/lang/Integer, class java/lang/Inte ger, class java/lang/Integer ] stack = [ class java/io/PrintStream, int ] Exceptions: throws java.lang.Exception }
Integer a = 127;对应的指令为:
0: bipush 127 2: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
Integer c = 128;对应的指令为:
12: sipush 128 15: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
bipush指令意思是将单字节的常量值(-128~127)推送到栈顶
sipush指令意思是将短型的常量值(-32768~32767)推送到栈顶
invokestatic指令意思是调用静态方法,这里调用的是常量池中#2指向的方法java/lang/Integer.valueOf,查看Integer.valueOf方法:
public static Integer valueOf(int i) { final int offset = 128; if (i >= -128 && i <= 127) { // must cache return IntegerCache.cache[i + offset]; } return new Integer(i); }
IntegerCache代码:
private static class IntegerCache { private IntegerCache(){} static final Integer cache[] = new Integer[-(-128) + 127 + 1]; static { for(int i = 0; i < cache.length; i++) cache[i] = new Integer(i - 128); } }
其它封装类如下:
//Boolean类也实现了常量池技术 Boolean bool1=true; Boolean bool2=true; System.out.println(bool1==bool2); //输出true //浮点类型的包装类没有实现常量池技术 Double d1=1.0; Double d2=1.0; System.out.println(d1==d2); //输出false
String s = new String( "xyz" ); 在运行时涉及 几个String实例?
两个,一个是字符串字面量"xyz"所对应的、驻留(intern)在一个全局共享的字符串常量池中的实例,另一个是通过new String(String)创建并初始化的、内容与"xyz"相同的实例。
String中的final用法和理解:
final只对引用的"值"(即内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象的变化,final是不负责的。
final StringBuffer a = new StringBuffer("111"); final StringBuffer b = new StringBuffer("222"); a=b;//此句编译不通过 final StringBuffer a = new StringBuffer("111"); a.append("222");//编译通过
String a = "a1"; String b = "a" + 1; System.out.println((a == b)); //result = true String a = "atrue"; String b = "a" + "true"; System.out.println((a == b)); //result = true String a = "a3.4"; String b = "a" + 3.4; System.out.println((a == b)); //result = true
JVM对于字符串常量的"+"号连接,将程序编译期,JVM就将常量字符串的"+"连接优化为连接后的值,拿"a" + 1来说,经编译器优化后在class中就已经是a1。在编译期其字符串常量的值就确定下来,故上面程序最终的结果都为true。
String a = "ab"; String bb = "b"; String b = "a" + bb; System.out.println((a == b)); //result = false
JVM对于字符串引用,由于在字符串的"+"连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即"a" + bb无法被编译器优化,只有在程序运行期来动态分配并将连接后的新地址赋给b。所以上面程序的结果也就为false。
String a = "ab"; final String bb = "b"; String b = "a" + bb; System.out.println((a == b)); //result = true
和上面唯一不同的是bb字符串加了final修饰,对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量池中或嵌入到它的字节码流中。所以此时的"a" + bb和"a" + "b"效果是一样的。故上面程序的结果为true。
String a = "ab"; final String bb = getBB(); String b = "a" + bb; System.out.println((a == b)); //result = false private static String getBB() { return "b"; }
JVM对于字符串引用bb,它的值在编译期无法确定,只有在程序运行期调用方法后,将方法的返回值和"a"来动态连接并分配地址为b,故上面程序的结果为false。
通过上面4个例子可以得出得知:
String s = "a" + "b" + "c";
就等价于String s = "abc"; int i = 1+2+3;也等价于int i = 6;
String a = "a"; String b = "b"; String c = "c"; String s = a + b + c;
这个就不一样了,最终结果等于:
StringBuffer temp = new StringBuffer(); temp.append(a).append(b).append(c); String s = temp.toString();
由上面的分析结果,可就不难推断出String 采用连接运算符(+)效率低下原因分析,形如这样的代码:
public class Test { public static void main(String args[]) { String s = null; for(int i = 0; i < 100; i++) { s += "a"; } } }
每做一次 + 就产生个StringBuilder对象,然后append后就扔掉。下次循环再到达时重新产生个StringBuilder对象,然后 append 字符串,如此循环直至结束。 如果我们直接采用 StringBuilder 对象进行 append 的话,我们可以节省 N - 1 次创建和销毁对象的时间。所以对于在循环中要进行字符串连接的应用,一般都是用StringBuffer或StringBulider对象来进行 append操作。
String.intern()解析
Java语言并不要求常量一定只能在编译期产生,运行时也可能将新的常量放入常量池中,这种特性用的最多的就是String.intern()方法。
String的intern()方法就是扩充常量池的一个方法;当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加 一个Unicode等于str的字符串并返回它的引用。
String s0= "xyz"; String s1=new String("xyz"); String s2=new String("xyz"); System.out.println(s0==s1); s1.intern(); s2=s2.intern(); //把常量池中“pku”的引用赋给s2 System.out.println( s0==s1); System.out.println( s0==s1.intern() ); System.out.println( s0==s2 );
输出为:
false
false //虽然执行了s1.intern(),但它的返回值没有赋给s1
true //说明s1.intern()返回的是常量池中”pku”的引用
true
有人说,“使用String.intern()方法则可以将一个String类的保存到一个全局String表中,如果具有相同值的Unicode字符串 已经在这个表中,那么该方法返回表中已有字符串的地址,如果在表中没有相同值的字符串,则将自己的地址注册到表中“如果我把他说的这个全局的String 表理解为常量池的话,他的最后一句话,“如果在表中没有相同值的字符串,则将自己的地址注册到表中”是错的:
String s1=new String("xyz"); String s2=s1.intern(); System.out.println( s1==s1.intern() ); System.out.println( s1+" "+s2 ); System.out.println( s2==s1.intern() );
输出为:
false
xyz xyz
true
发表评论
-
Java异常机制Error类和Exception类
2015-01-23 20:39 0Error类和Exception类都继承自Throwabl ... -
RPC框架简单实现
2014-11-24 21:47 1435/* * Copyright 2011 A ... -
Java读取文件中单词进行排序并写到另一个文件中
2013-12-04 11:12 4704支持 http://ifeve.com/tao-code-m ... -
Java级联调用方法的类设计
2013-11-13 14:10 3167在Java方法设计时返回当前对象的引用(thi ... -
用反射解析jar文件并执行里面Java代码
2013-10-30 23:25 114161、使用JarFile类读取jar包MANIFEST.MF ... -
Hadoop IPC RPC类中对请求的客户端缓存类ClientCache问题
2013-09-24 19:52 2021Hadoop IPC RPC类中对请求的客 ... -
Java NIO 使用实例
2013-09-23 20:47 7033在JDK1.4之前,Java Output ... -
Java 远程接口调用 RMI
2013-09-06 12:00 0Java RMI 指的是远程方法调用 (Remo ... -
Comparable Comparator 的区别
2013-09-03 14:34 1204注:本文为转载 当需要排序的集合或数组不是单纯的数字型时 ... -
Java 枚举使用实例
2013-07-01 15:30 1926Lucene Field类中使用枚举如下: 声明抽象方法 ... -
Java垃圾回收:GC在什么时候对什么做了什么
2013-02-27 20:23 5814GC在什么时候对什么做了什么? ... -
Java虚拟机工具
2013-02-26 18:38 25471、jdk工具在linux中的安装,有些系统只安装了jre ... -
Java集合框架 Map接口
2013-01-30 18:34 15861、HashMap HashMap是Map接口最常见的实 ... -
Java集合框架 Collection接口
2013-01-29 17:49 11621、ArrayList ArrayList是List接口 ... -
Java集合框架
2013-01-25 20:19 1112集合是Java中是最常用的包,最常见的有Colle ... -
携带结果的任务Callable和Future
2013-01-21 21:37 1664Executor框架使用Runnable作为基本的 ... -
线程池的使用
2013-01-17 18:23 1873线程池实现类ThreadPoolExecutor ... -
Executor框架和线程池
2013-01-15 21:08 6557简介 在JDK5后主 ... -
同步工具类之Exchanger
2013-01-11 17:11 1107Exchanger<V>,java.uti ... -
同步工具类之CyclicBarrier循环的barrier
2013-01-11 15:39 1411CyclicBarrier在java.util.c ...
相关推荐
JVM常量池 Class常量池(静态常量池) 运行时常量池 字符串常量池(全局常量池) 包装类型缓存池 JVM常量池 Jvm常量池分为: Class常量池(静态常量池) 运行时常量池 字符串常量池(全局常量池) 包装类型缓存池 Class常量...
1. 字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建 2. JVM为了提高性能和减少内存开销,在实例化字符串
1. 字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建 2. JVM为了提高性能和减少内存开销,在实例化字符串
JavaFocus:hammer: Java学习重点 ...success和isSuccessequals 和 ==String为什么是不可变的字符串常量池为什么直接定义的字符串可以调用String对象的各种方法JDK6 和 JDK7 substring原理的改变字符串拼接的几种方式
[*]了解Java内存机制:栈、堆、常量池等,理解垃圾回收机制。 第3章 面向过程(数组和方法) 4课时 理解如何声明数组、构造数组、初始化数组以及使用数组中的各个元素。 清楚数组作为...
Big-Data-InterviewJava开发、大数据...封装、继承和多态Java语言数据类型Java的自动类型转换,强制类型转换String的不可变性、虚拟机的常量池中的String字符串、String.intern()的底层原理Java语言中的关键字:finalJa
6.3 特殊字符串 6.3.1 全限定名 6.3.2 简单名称 6.3.3 描述符 6.4 常量池 6.4.1 constant_utf8_info表 6.4.2 constant_integer_info表 6.4.3 constant_float_info表 6.4.4 constant_long_...
int和Integer有什么区别? 答:int是java的原始数据类型,Integer是java为int提供的封装类... 答:基本数据类型8种:int、short、byte、long、float、double、char、boolean String不是基本数据类型,引用数据类型。 ...
实例91 流标记分割和统计字符串 234 实例92 Java操作Excel文件 237 第11章 Java高级特性 245 实例93 自动装箱与拆箱 245 实例94 for/in循环 247 实例95 参数不确定(可变长参数) 249 实例96 方法改变(协变式...
实例42 字符串索引越界异常(StringIndexOutBounds) 60 实例43 操作错误(UnsupportedOperationException) 60 4.2 运行时异常 61 实例44 找不到指定类时发生的异常(ClassNotFoundException) 62 ...
实例42 字符串索引越界异常(StringIndexOutBounds) 60 实例43 操作错误(UnsupportedOperationException) 60 4.2 运行时异常 61 实例44 找不到指定类时发生的异常(ClassNotFoundException) 62 实例45 请求的...
实例42 字符串索引越界异常(StringIndexOutBounds) 60 实例43 操作错误(UnsupportedOperationException) 60 4.2 运行时异常 61 实例44 找不到指定类时发生的异常(ClassNotFoundException) 62 ...
实例42 字符串索引越界异常(StringIndexOutBounds) 60 实例43 操作错误(UnsupportedOperationException) 60 4.2 运行时异常 61 实例44 找不到指定类时发生的异常(ClassNotFoundException) 62 实例45 请求的...
实例42 字符串索引越界异常(StringIndexOutBounds) 60 实例43 操作错误(UnsupportedOperationException) 60 4.2 运行时异常 61 实例44 找不到指定类时发生的异常(ClassNotFoundException) 62 实例45 请求...
JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要...
JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变...