说说String
来源:互联网 发布:数据概况英文翻译 编辑:程序博客网 时间:2024/06/07 06:33
String它是一个引用数据类型,不是一个基础数据类型。
先思考一个问题:String为什么是不可更改的。
查看String类的签名如下:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {}
然后再看看String到底是怎么存储字符串的:
/** The value is used for character storage. */ private final char value[];
String类的签名,和存储String的char数组都被final修饰,它们确保了String对象是永远不会被修改的。
一、内存存储
下面我们继续String之旅吧,先贴一段学习代码。
public class LearnString { public static void main(String args[]) { String a = "abc"; String b = "abc"; String c = new String("abc"); System.out.println(a == b); System.out.println(a == c); System.out.println(a.equals(c)); }}
运行结果也如大家所想:
truefalsetrue
我们先看看String的内存分布情况吧
public class LearnString { String b = "abc"; public static void main(String args[]) { String a = "abc"; }}
编译后,我们通过javap -v LearnString来查看内存的分布情况,可以看到在字节码的Constant pool区有如下一行:
Constant pool: #1 = Methodref #6.#23 // java/lang/Object."<init>":()V #2 = String #24 // ab #3 = Fieldref #5.#25 // com/example/learn/LearnString.d:Ljava/lang/String; #4 = String #26 // abc
可见不管是成员变量还是局部变量,只要String一开始就被赋值了,那么它的值就会被保存在字节码的常量池中。其实你会发现如果把之前的:
String d = "ab" + "d";
重新编译后再javap后如下:
Constant pool: #1 = Methodref #6.#23 // java/lang/Object."<init>":()V #2 = String #24 // ab #3 = Fieldref #5.#25 // com/example/learn/LearnString.d:Ljava/lang/String; #4 = String #26 // abc
这时候我们可以发现结果是一样的,也就是说compiler发现这些”+”操作完全可以在编译阶段优化掉,compiler就会进行一定的优化操作。
接下来我们可以讨论开篇的那个例子了。“abc”字符串按照上面所说,在编译的时候就被保存在字节码的常量池中了,所以a 和 b都是拿的常量池中的“abc”值(指向了同一个堆地址),故a==b为true;c = new String(“abc”) ,它其实创建了两个对象,一个new出来的另一个就是常量“abc”,c的引用指向了new出来的对象,故a!=c。
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
这是String重写Object的equals方法,由此可以发现equals方法是比较的是两个String对象里头的字符数组(char[]),比较的是堆中值而非堆地址,故a.equals(c)是相等。
二、注意的问题
先看看下面代码
String s = ""; for(int i = 0; !"end".equals(args[i]);){ s+=args[i]; }
同样用javap反编译字节码
Classfile /F:/workspaces/JavaLearn/out/production/JavaLearn/com/example/learn/LearnString.class Last modified 2017-4-25; size 779 bytes MD5 checksum 646b7bf02cd674af0bc5e55605ee3713 Compiled from "LearnString.java"public class com.example.learn.LearnString minor version: 0 major version: 50 flags: ACC_PUBLIC, ACC_SUPERConstant pool: #1 = Methodref #10.#30 // java/lang/Object."<init>":()V #2 = String #31 // #3 = String #32 // end #4 = Methodref #33.#34 // java/lang/String.equals:(Ljava/lang/Object;)Z #5 = Class #35 // java/lang/StringBuilder #6 = Methodref #5.#30 // java/lang/StringBuilder."<init>":()V #7 = Methodref #5.#36 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #8 = Methodref #5.#37 // java/lang/StringBuilder.toString:()Ljava/lang/String; #9 = Class #38 // com/example/learn/LearnString #10 = Class #39 // java/lang/Object #11 = Utf8 <init> #12 = Utf8 ()V #13 = Utf8 Code #14 = Utf8 LineNumberTable #15 = Utf8 LocalVariableTable #16 = Utf8 this #17 = Utf8 Lcom/example/learn/LearnString; #18 = Utf8 main #19 = Utf8 ([Ljava/lang/String;)V #20 = Utf8 i #21 = Utf8 I #22 = Utf8 args #23 = Utf8 [Ljava/lang/String; #24 = Utf8 s #25 = Utf8 Ljava/lang/String; #26 = Utf8 StackMapTable #27 = Class #40 // java/lang/String #28 = Utf8 SourceFile #29 = Utf8 LearnString.java #30 = NameAndType #11:#12 // "<init>":()V #31 = Utf8 #32 = Utf8 end #33 = Class #40 // java/lang/String #34 = NameAndType #41:#42 // equals:(Ljava/lang/Object;)Z #35 = Utf8 java/lang/StringBuilder #36 = NameAndType #43:#44 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #37 = NameAndType #45:#46 // toString:()Ljava/lang/String; #38 = Utf8 com/example/learn/LearnString #39 = Utf8 java/lang/Object #40 = Utf8 java/lang/String #41 = Utf8 equals #42 = Utf8 (Ljava/lang/Object;)Z #43 = Utf8 append #44 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #45 = Utf8 toString #46 = Utf8 ()Ljava/lang/String;{ public com.example.learn.LearnString(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 6: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/example/learn/LearnString; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=3, args_size=1 0: ldc #2 // String 2: astore_1 3: iconst_0 4: istore_2 5: ldc #3 // String end 7: aload_0 8: iload_2 9: aaload 10: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 13: ifne 40 16: new #5 // class java/lang/StringBuilder 19: dup 20: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V 23: aload_1 24: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 27: aload_0 28: iload_2 29: aaload 30: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 33: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 36: astore_1 37: goto 5 40: return LineNumberTable: line 8: 0 line 9: 3 line 10: 16 line 12: 40 LocalVariableTable: Start Length Slot Name Signature 5 35 2 i I 0 41 0 args [Ljava/lang/String; 3 38 1 s Ljava/lang/String; StackMapTable: number_of_entries = 2 frame_type = 253 /* append */ offset_delta = 5 locals = [ class java/lang/String, int ] frame_type = 250 /* chop */ offset_delta = 34}SourceFile: "LearnString.java"
在执行”+”时候
13: ifne 40 16: new #5 // class java/lang/StringBuilder 19: dup 20: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V 23: aload_1
在这个循环里每一次对String执行”+”操作,都会创建一个StringBuilder对象,可见这多么消耗性能。为了避免这种事情发生只要你在执行循环之前创建一个StringBuilder对象然后将之后的”+”操作换成StringBuilder.append()就可以。
- 说说String
- 说说string和int,byte
- 说说String和StringBuffer的那些事
- 说说
- 说说
- 说说
- 说说
- 说说
- 说说
- 说说
- 说说
- 说说
- 说说
- 说说
- 说说
- 简单说说JAVA的String和byte[]的关系
- 简单说说JAVA的String和byte[]的关系
- 说说JDK中的String.valueOf()传null的诡异处理
- python中的三个读read(),readline()和readlines()
- (iOS-基本知识)Category VS Extension 原理详解
- Java生成UUID
- 可变参数
- NYOJ 开心的小明
- 说说String
- Java Web 开发过程中的问题
- Fire Net HDU
- MySQL Administrator 管理/备份/还原 My SQL 数据库
- Spring源码解读:Envrionment接口
- php实现伪静态
- eclipse转Android studio的小差异
- Navicat 2003-can't connect to MYSQL server on 'localhost'(10061)
- 输入字符串的格式不正确。