关于java string 的面试题有很多。。。网上的说法也有很多中。。。。
在这我解析一下string 的用法的字节码。。。。
public class C{ public C(){ } public C(String a){ this.str=a; } String str0 = "min0"; static String str = "min"; static String str2 = "min2"; final String str3 = "minmin2"; final String str4 = "min3"; public void t(int b){ System.out.println("min"+str2); System.out.println("t--"+str3); System.out.println(str4+str3); System.out.println(str0+str); } public static void main(String[] args) { new A().t(2); } }
通过javap -verbose 查看一下字节码的存储信息如下
Compiled from "C.java" public class C extends java.lang.Object SourceFile: "C.java" minor version: 0 //class文件的次版本号 major version: 49//class文件的主版本号 Constant pool://常量池 //Method:方法 //Field:字段 //String:字符串 //Asciz:这个Asciz表示是CONSTANT_Utf8吧,因为C ONSTANT_Utf8可以是存储四种基本信息类型:文字字符串、被定义的类和接口描述、对其他类或接口的符号引用以及属性相关的字符串。 //NameAndType:变量名的类型 //Class:类 const #1 = Method #24.#44; // java/lang/Object."<init>":()V //CONSTANT_Methodref_info (10) const #2 = String #45; // min0 //字符串常量池中的常量 CONSTANT_String_info (8) const #3 = Field #23.#46; // C.str0:Ljava/lang/String;//没有static修饰的字段记录字段信息 const #4 = String #47; // minmin2 //字符串常量池中的常量 CONSTANT_String_info (8) const #5 = Field #23.#48; // C.str3:Ljava/lang/String;//没有static修饰的字段 const #6 = String #49; // min3 //字符串常量池中的常量 const #7 = Field #23.#50; // C.str4:Ljava/lang/String;//没有static修饰的字段 const #8 = Field #23.#51; // C.str:Ljava/lang/String;//static修饰的字段 const #9 = Field #52.#53; // java/lang/System.out:Ljava/io/PrintS tream; const #10 = class #54; // java/lang/StringBuilder 用于记录类或接口名CONSTANT_Class_info const #11 = Method #10.#44; // java/lang/StringBuilder."<init>":()V//用于记录方法信息 CONSTANT_Methodref_info const #12 = String #55; // min //字符串常量池中的常量CONSTANT_String_info (8) const #13 = Method #10.#56; // java/lang/StringBuilder.append:(Ljav a/lang/String;)Ljava/lang/StringBuilder;////用于记录方法信息 CONSTANT_Methodref_info const #14 = Field #23.#57; // C.str2:Ljava/lang/String;//static修饰的字段 const #15 = Method #10.#58; // java/lang/StringBuilder.toString:()L java/lang/String; const #16 = Method #59.#60; // java/io/PrintStream.println:(Ljava/l ang/String;)V const #17 = String #61; // t--minmin2 //字符串常量池中的常量 const #18 = String #62; // min3minmin2 //字符串常量池中的常量 const #19 = class #63; // A const #20 = Method #19.#44; // A."<init>":()V const #21 = Method #19.#64; // A.t:(I)V const #22 = String #65; // min2 const #23 = class #66; // C const #24 = class #67; // java/lang/Object const #25 = Asciz str0; const #26 = Asciz Ljava/lang/String;; const #27 = Asciz str; const #28 = Asciz str2; const #29 = Asciz str3; const #30 = Asciz ConstantValue; const #31 = Asciz str4; const #32 = Asciz <init>; const #33 = Asciz ()V; const #34 = Asciz Code; const #35 = Asciz LineNumberTable; const #36 = Asciz (Ljava/lang/String;)V; const #37 = Asciz t; const #38 = Asciz (I)V; const #39 = Asciz main; const #40 = Asciz ([Ljava/lang/String;)V; const #41 = Asciz <clinit>; const #42 = Asciz SourceFile; const #43 = Asciz C.java; const #44 = NameAndType #32:#33;// "<init>":()V//记录方法或字段的名称(name)和描述符(descriptor) const #45 = Asciz min0; const #46 = NameAndType #25:#26;// str0:Ljava/lang/String;// CONSTANT_NameAndType_info const #47 = Asciz minmin2; const #48 = NameAndType #29:#26;// str3:Ljava/lang/String; const #49 = Asciz min3; const #50 = NameAndType #31:#26;// str4:Ljava/lang/String; const #51 = NameAndType #27:#26;// str:Ljava/lang/String; const #52 = class #68; // java/lang/System const #53 = NameAndType #69:#70;// out:Ljava/io/PrintStream; const #54 = Asciz java/lang/StringBuilder; const #55 = Asciz min; const #56 = NameAndType #71:#72;// append:(Ljava/lang/String;)Ljava/lang/String Builder; const #57 = NameAndType #28:#26;// str2:Ljava/lang/String; const #58 = NameAndType #73:#74;// toString:()Ljava/lang/String; const #59 = class #75; // java/io/PrintStream const #60 = NameAndType #76:#36;// println:(Ljava/lang/String;)V const #61 = Asciz t--minmin2; const #62 = Asciz min3minmin2; const #63 = Asciz A; const #64 = NameAndType #37:#38;// t:(I)V const #65 = Asciz min2; const #66 = Asciz C; const #67 = Asciz java/lang/Object; const #68 = Asciz java/lang/System; const #69 = Asciz out; const #70 = Asciz Ljava/io/PrintStream;; const #71 = Asciz append; const #72 = Asciz (Ljava/lang/String;)Ljava/lang/StringBuilder;; const #73 = Asciz toString; const #74 = Asciz ()Ljava/lang/String;; const #75 = Asciz java/io/PrintStream; const #76 = Asciz println; { java.lang.String str0; static java.lang.String str; static java.lang.String str2; final java.lang.String str3; Constant value: String minmin2 final java.lang.String str4; Constant value: String min3 //如下的Locals表示方法内局部变量个数,该例中是1,有些人疑惑的是Dog()中明明没有参数啊,应该是0啊! //当线程调用一个方法的时候,jvm会开辟一个帧出来,这个帧包括操作栈、局部变量列表、常量池的引用 //非static方法,在调用的时候都会给方法默认加上一个当前对象(this)类型的参数,不需要在方法中定义, //这个时候局部变量列表中index为0的位置保存的是this,其他索引号按变量定义顺序累加 //static方法不依赖对象,所以不用传this //Args_size表示参数个数,public C();会传一个this进去,所以value是1 public C(); Code: Stack=2, Locals=1, Args_size=1 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V //调用父类的构造函数 4: aload_0 5: ldc #2; //String min0 7: putfield #3; //Field str0:Ljava/lang/String; 10: aload_0 11: ldc #4; //String minmin2 13: putfield #5; //Field str3:Ljava/lang/String; 16: aload_0 17: ldc #6; //String min3 19: putfield #7; //Field str4:Ljava/lang/String; 22: return LineNumberTable: line 6: 0 line 14: 4 line 17: 10 line 18: 16 line 8: 22 //invokespecial //调用构造方法、父类方法 invokevirtual //调用普通方法(非构造方法、static方法) invokestatic //调用static方法 public C(java.lang.String); Code: Stack=2, Locals=2, Args_size=2 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V //调用父类构造方法 4: aload_0 5: ldc #2; //String min0 //将将常量池中的常量压入方法栈 7: putfield #3; //Field str0:Ljava/lang/String; 10: aload_0 11: ldc #4; //String minmin2 13: putfield #5; //Field str3:Ljava/lang/String; 16: aload_0 17: ldc #6; //String min3 19: putfield #7; //Field str4:Ljava/lang/String; 22: aload_0 23: pop 24: aload_1 25: putstatic #8; //Field str:Ljava/lang/String; 28: return LineNumberTable: line 10: 0 line 14: 4 line 17: 10 line 18: 16 line 11: 22 line 12: 28 public void t(int); Code: Stack=3, Locals=2, Args_size=2 0: getstatic #9; //Field java/lang/System.out:Ljava/io/PrintStream; 3: new #10; //class java/lang/StringBuilder //在堆中分配内存,返回对象引用,压入操作数栈 6: dup //复制引用到stack(栈) 7: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V //调用构造方法 10: ldc #12; //String min //将将常量池中的常量压入栈 12: invokevirtual #13; //Method java/lang/StringBuilder.append:(Ljava/lang /String;)Ljava/lang/StringBuilder; //调用方法 15: getstatic #14; //Field str2:Ljava/lang/String;//访问static字段 18: invokevirtual #13; //Method java/lang/StringBuilder.append:(Ljava/lang /String;)Ljava/lang/StringBuilder;//调用方法 21: invokevirtual #15; //Method java/lang/StringBuilder.toString:()Ljava/l ang/String;//调用方法 24: invokevirtual #16; //Method java/io/PrintStream.println:(Ljava/lang/St ring;)V//调用方法 27: getstatic #9; //Field java/lang/System.out:Ljava/io/PrintStream;//访问static字段 30: ldc #17; //String t--minmin2 //将常量池中的常量压入栈 32: invokevirtual #16; //Method java/io/PrintStream.println:(Ljava/lang/St ring;)V//调用方法 35: getstatic #9; //Field java/lang/System.out:Ljava/io/PrintStream; 38: ldc #18; //String min3minmin2 //将常量池中的常量压入栈 40: invokevirtual #16; //Method java/io/PrintStream.println:(Ljava/lang/St ring;)V//调用方法 43: getstatic #9; //Field java/lang/System.out:Ljava/io/PrintStream;//访问static字段 46: new #10; //class java/lang/StringBuilder //在堆中分配内存,返回对象引用,压入操作数栈 49: dup 50: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V//调用方法 53: aload_0 54: getfield #3; //Field str0:Ljava/lang/String;//访问str0字段 57: invokevirtual #13; //Method java/lang/StringBuilder.append:(Ljava/lang /String;)Ljava/lang/StringBuilder;//调用方法 60: getstatic #8; //Field str:Ljava/lang/String; 63: invokevirtual #13; //Method java/lang/StringBuilder.append:(Ljava/lang /String;)Ljava/lang/StringBuilder; //调用方法 66: invokevirtual #15; //Method java/lang/StringBuilder.toString:()Ljava/l ang/String; //调用方法 69: invokevirtual #16; //Method java/io/PrintStream.println:(Ljava/lang/St ring;)V //调用方法 72: return LineNumberTable: line 20: 0 line 21: 27 line 22: 35 line 23: 43 line 24: 72 public static void main(java.lang.String[]); Code: Stack=2, Locals=1, Args_size=1 0: new #19; //class A 3: dup 4: invokespecial #20; //Method A."<init>":()V 7: iconst_2 8: invokevirtual #21; //Method A.t:(I)V 11: return LineNumberTable: line 26: 0 line 28: 11 static {}; Code: Stack=1, Locals=0, Args_size=0 0: ldc #12; //String min 2: putstatic #8; //Field str:Ljava/lang/String; 5: ldc #22; //String min2 7: putstatic #14; //Field str2:Ljava/lang/String; 10: return LineNumberTable: line 15: 0 line 16: 5 }
通过字节码信息我们可以发现
system.out.print("min"+str2);Stringbuilder.append(String).append(I)toString;常量池一个对象2个对象
0: getstatic #9; //Field java/lang/System.out:Ljava/io/PrintStream; 3: new #10; //class java/lang/StringBuilder//StringBuilder对象 6: dup 7: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V 10: ldc #12; //String min 12: invokevirtual #13; //Method java/lang/StringBuilder.append:(Ljava/lang /String;)Ljava/lang/StringBuilder; 15: getstatic #14; //Field str2:Ljava/lang/String; 18: invokevirtual #13; //Method java/lang/StringBuilder.append:(Ljava/lang /String;)Ljava/lang/StringBuilder; 21: invokevirtual #15; //Method java/lang/StringBuilder.toString:()Ljava/l ang/String;//调用stringbuilder.toString()方法。产生对象 24: invokevirtual #16; //Method java/io/PrintStream.println:(Ljava/lang/St ring;)V
System.out.println(str0+str);
字符串拼接。。。。结果同上
System.out.println("t--"+str3);
由于str3是final修饰的在编译期间就在常量池中了 t--minmin2
const #17 = String #61; // t--minmin2
System.out.println(str4+str3);
由于str4.str3都是final编译常量所以同上
const #62 = Asciz min3minmin2;
System.out.println(“你好”+“吗”);
由于编译期间(“你好”+“吗”);已经在String常量池中了。同上
const #19 = String #64; // 你好吗
由于时间问题。写的比较毛糙。先写到这了。。下午还要上班。。先小睡会。。。。。下次在写