javaSE---string

目录

字符串类型

定义字符串

String

1、String类被final修饰,这个类不能被继承​

2、String的底层实现

常量池

1.Class文件常量池《在磁盘上》

2.运行时常量池《在方法区》

3.字符串常量池《在堆区》

哈希表

底层实现

直接赋值:创建一次对象

 使用new关键字:创建两次对象

 +拼接

手动入池---intern()方法

StringBuilder

StringBuffer

区别


字符串类型

c语言中没有字符串类型,而java中有字符串类型

c语言中字符串的结束标志是'\0',而java中字符串没有结束标志

定义字符串

直接赋值

String str="qwe";

使用关键字new,调用构造方法

String dest=new String("uiy");

 字符数组转化为字符串,调用构造方法

char[]str3={'r','t','o'};
String str4=new String(str3);

String

1、String类被final修饰,这个类不能被继承javaSE---string_第1张图片

2、String的底层实现

常量池

1.Class文件常量池《在磁盘上》

Class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池用于存放编译器生成的各种字面量和符号引用

字面量:例如 int a=10,10就是字面量

符号引用包括:类和接口的全限定名 、 字段的名称和描述符、 方法的名称和描述符

2.运行时常量池《在方法区》

是方法区的一部分,编译好的字节码文件通过java虚拟机先加载到内存,Class文件常量池中各种字面量和符号引用也将加载到运行时常量池中

3.字符串常量池《在堆区》

主要存放字符串常量本质是一个哈希表

哈希表

一种数据结构,按照某一种映射关系(设计哈希函数)来存放元素

下列使用除留余数法构造哈希函数,使用链地址法解决冲突

javaSE---string_第2张图片

底层实现

public class text {
    public static void main(String[] args) {
        String str1="hello";
        String str2=new String("hello");
    }
}

打开String的源码

 发现String包含有许多的构造方法,并且使用成员value和hash

javaSE---string_第3张图片

javaSE---string_第4张图片

 value:该值用于字符存储

hash:缓存字符串的哈希代码,默认0

直接赋值:创建一次对象

public class text {
    public static void main(String[] args) {
        String str1="hello";
    }
}

程序编译之后,生成.class文件,.class文件常量池中存放字面值和符号引用,hello存放在里面

程序运行:.class文件通过JVM的类加载器加载到内存,JVM的解析器对加载到内存的java类进行解析

当.class文件通过JVM的类加载器加载到内存时,class文件常量池信息会被加载到运行时常量池。"Hello"会在堆区中创建一个对象,同时会在字符串常量池(堆上)存放一个它的引用

javaSE---string_第5张图片javaSE---string_第6张图片

 如果这时有一个"hi"对象,和"hello"的存储位置冲突,那么next域存放冲突结点的地址,否则为NULL

javaSE---string_第7张图片

 然后,主函数的str1变量开始在栈上创建,虚拟机会去字符串常量池中找是否有equals(“Hello”)的String,如果相等就把在字符串池中“Hello”的引用复制给str1。如果找不到相等的字符串,就会在堆中新建一个对象,再把引用赋给str1。

javaSE---string_第8张图片

当用字面量赋值的方法创建字符串时,无论创建多少次,只要字符串的值相同,它们所指向的都是堆中的同一个对象。(指向同一个字符串)

javaSE---string_第9张图片

 public static void main(String[] args) {
        String str1="hello";
        String str="hello";
        System.out.println(str==str1);
    }

 结果:true,两个都是0x666

 使用new关键字:创建两次对象

 当利用new关键字去创建字符串时,前面加载的过程是一样的,只是在运行时无论字符串常量池中有没有与当前值相等的对象引用,都会在堆中新开辟一块内存,创建一个对象

  public static void main(String[] args) {
        String str1="hello";
        String str2=new String("hello");
    }

str1中,Hello会在堆区中创建一个对象,同时会在字符串常量池存放一个它的引用

str2中,会在堆上new一个String,虚拟机会去字符串常量池中找是否有equals(“Hello”)的String,如果相等就把在字符串池中“Hello”的引用的value复制给创建的String的value

value定义就是对数组的引用,存放的是数组的地址

javaSE---string_第10张图片

 栈上创建str2,引用创建的String类

javaSE---string_第11张图片

 +拼接

 public static void main(String[] args) {
        String str1="hello";
        String str="hel"+"lo";
        System.out.println(str==str1);
    }

结果:true

   String str="hel"+"lo";两个常量的拼接,在编译时已经转化为了"hello"

javaSE---string_第12张图片

public static void main(String[] args) {
        String str1="hello";
        String str2="hel";
        String str=str2+"lo";
        System.out.println(str==str1);
    }

结果:false

编译期间str2是变量,会创建hel对象,str2引用这个对象, 会创建lo对象,然后str指向了两个对象相拼接的对象

手动入池---intern()方法

 public static void main(String[] args) {
        String str1="11";
      String str2=new String("1")+new String("1");
        System.out.println(str2==str1);
    }

结果:false

javaSE---string_第13张图片

   String str2=new String("1")+new String("1");字符串常量池没有1,先将1加载到字符串常量池中,并会在堆区new String两个对象

javaSE---string_第14张图片

 两个对象拼接产生新的数组"11",字符串拼接底层优化为StingBuilder,产生新的对象StingBuilder,StingBuilder的val指向了"11"这个数组,并调用StingBuilder.toString方法产生新的String类,这个新的String类的val指向了"11"这个数组

javaSE---string_第15张图片

javaSE---string_第16张图片

 在这个过程中,拼接产生的”11“没有入字符串常量池

javaSE---string_第17张图片

 public static void main(String[] args) {
        String str2=new String("1")+new String("1");
        str2.intern();
        String str1="11";
        System.out.println(str2==str1);
    }

结果true

  str2.intern():str2指向的对象被手动入池:11在字符串常量池中

  String str1="11";发现11在字符串常量池中,得到和str2一样的地址

StringBuilder

在一个没有字符串拼接的代码中,不会调用StringBuilder

 public static void main(String[] args) {
        String str = "123";
        System.out.println(str);
    }

javaSE---string_第18张图片

  但是:如果出现字符串拼接,那么会被优化为StringBuider

  public static void main(String[] args) {
        String str="123";
        str+="890";
        System.out.println(str);
    }

javaSE---string_第19张图片

 StringBuider的toString方法:new String返回javaSE---string_第20张图片

那么:代码相当于

 public static void main(String[] args) {
        String str = "123";
        //第一步:创建StringBuilder对象,调用无参数的构造方法
        StringBuilder s = new StringBuilder();
        //第二步:加载123
        s.append("123");

        str += "890";
        //第三步:加载”890“
        s.append("890");

        System.out.println(str);
        //第四步:调用StringBuider的toString方法进行类型转化
        String ss = s.toString();
        System.out.println(ss);
    }

StringBuffer

javaSE---string_第21张图片

javaSE---string_第22张图片

  StringBuffer的方法相比于 StringBuilder多了个synchronized(意为:同步的)保证线程安全

所谓同步就是指你要等待一个过程(调用,任务,事件等)执行完毕后才能进行下一步操作

多线程处理可以同时运行多个过程,单线程只能等一个过程结束了才能执行下一个过程

区别

1.String是不可以更改的,而StringBuffer和StringBuilder的内容可以更改

 public static void main(String[] args) {
        String s = "789";
        s = "90";
    }

 实际上创建了两个对象:0x777和0x888,s指向了新的对象

javaSE---string_第23张图片

 String类是被final修饰的,字符串的所有方法都不改变字符串本身,而是返回一个新的字符串

但是StringBuffer和StringBuilder的内容可以更改,所有方法都改变本身

javaSE---string_第24张图片

 2.StringBuffer和StringBuilder有许多相似的方法

 3.StringBuffer保证线程安全,适用于多线程,StringBuilder不保证线程安全,适用于单线程,相对来说StringBuilder效率升高

 StringBuffer的方法相比于 StringBuilder多了个synchronized(意为:同步的)保证线程安全

javaSE---string_第25张图片

javaSE---string_第26张图片

 

你可能感兴趣的:(android,java,apache)