• StringTable


    1. String的基本特性

    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述

    2. String的内存分配

    在这里插入图片描述在这里插入图片描述
    使用String的intern方法可以把字符串加入到字符串常量池。同时可以使用该方法来证明JDK8中字符串常量池存储在堆里面。

    /**
     * jdk6中:
     * -XX:PermSize=6m -XX:MaxPermSize=6m -Xms6m -Xmx6m
     *
     * jdk8中:
     * -XX:MetaspaceSize=6m -XX:MaxMetaspaceSize=6m -Xms6m -Xmx6m
     * @author shkstart  shkstart@126.com
     * @create 2020  0:36
     */
    public class StringTest3 {
        public static void main(String[] args) {
            //使用Set保持着常量池引用,避免full gc回收常量池行为
            Set<String> set = new HashSet<String>();
            //在short可以取值的范围内足以让6MB的PermSize或heap产生OOM了。
            short i = 0;
            while(true){
                set.add(String.valueOf(i++).intern());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述

    3. String的基本操作

    在这里插入图片描述在这里插入图片描述在这里插入图片描述

    4.字符串的拼接操作

    在这里插入图片描述如下的s1 + s2 的执行细节:(变量s是我临时定义的)
    ① StringBuilder s = new StringBuilder();
    ② s.append(“a”)
    ③ s.append(“b”)
    ④ s.toString() --> 约等于 new String(“ab”)

        补充:在jdk5.0之后使用的是StringBuilder,在jdk5.0之前使用的是StringBuffer
    1) 字符串拼接操作不一定使用的是StringBuilder!
       如果拼接符号左右两边都是字符串常量或常量引用,则仍然使用编译期优化,即非StringBuilder的方式。
    2) 针对于final修饰类、方法、基本数据类型、引用数据类型的量的结构时,能使用上final的时候建议使用上。
    
    • 1
    • 2
    • 3
    • 4
    package com.atguigu.java1;
    
    import org.junit.Test;
    
    /**
     * 字符串拼接操作
     * @author shkstart  shkstart@126.com
     * @create 2020  0:59
     */
    public class StringTest5 {
        @Test
        public void test1(){
            String s1 = "a" + "b" + "c";//编译期优化:等同于"abc"
            String s2 = "abc"; //"abc"一定是放在字符串常量池中,将此地址赋给s2
            /*
             * 最终.java编译成.class,再执行.class
             * String s1 = "abc";
             * String s2 = "abc"
             */
            System.out.println(s1 == s2); //true
            System.out.println(s1.equals(s2)); //true
        }
    
        @Test
        public void test2(){
            String s1 = "javaEE";
            String s2 = "hadoop";
    
            String s3 = "javaEEhadoop";
            String s4 = "javaEE" + "hadoop";//编译期优化
            //如果拼接符号的前后出现了变量,则相当于在堆空间中new String(),具体的内容为拼接的结果:javaEEhadoop
            String s5 = s1 + "hadoop";
            String s6 = "javaEE" + s2;
            String s7 = s1 + s2;
    
            System.out.println(s3 == s4);//true
            System.out.println(s3 == s5);//false
            System.out.println(s3 == s6);//false
            System.out.println(s3 == s7);//false
            System.out.println(s5 == s6);//false
            System.out.println(s5 == s7);//false
            System.out.println(s6 == s7);//false
            //intern():判断字符串常量池中是否存在javaEEhadoop值,如果存在,则返回常量池中javaEEhadoop的地址;
            //如果字符串常量池中不存在javaEEhadoop,则在常量池中加载一份javaEEhadoop,并返回次对象的地址。
            String s8 = s6.intern();
            System.out.println(s3 == s8);//true
        }
    
        @Test
        public void test3(){
            String s1 = "a";
            String s2 = "b";
            String s3 = "ab";
            /*
            如下的s1 + s2 的执行细节:(变量s是我临时定义的)
            ① StringBuilder s = new StringBuilder();
            ② s.append("a")
            ③ s.append("b")
            ④ s.toString()  --> 约等于 new String("ab")
    
            补充:在jdk5.0之后使用的是StringBuilder,在jdk5.0之前使用的是StringBuffer
             */
            String s4 = s1 + s2;//
            System.out.println(s3 == s4);//false
        }
        /*
        1. 字符串拼接操作不一定使用的是StringBuilder!
           如果拼接符号左右两边都是字符串常量或常量引用,则仍然使用编译期优化,即非StringBuilder的方式。
        2. 针对于final修饰类、方法、基本数据类型、引用数据类型的量的结构时,能使用上final的时候建议使用上。
         */
        @Test
        public void test4(){
            final String s1 = "a";
            final String s2 = "b";
            String s3 = "ab";
            String s4 = s1 + s2;
            System.out.println(s3 == s4);//true
        }
        //练习:
        @Test
        public void test5(){
            String s1 = "javaEEhadoop";
            String s2 = "javaEE";
            String s3 = s2 + "hadoop";
            System.out.println(s1 == s3);//false
    
            final String s4 = "javaEE";//s4:常量
            String s5 = s4 + "hadoop";
            System.out.println(s1 == s5);//true
    
        }
    
        /*
        体会执行效率:通过StringBuilder的append()的方式添加字符串的效率要远高于使用String的字符串拼接方式!
        详情:① StringBuilder的append()的方式:自始至终中只创建过一个StringBuilder的对象
              使用String的字符串拼接方式:创建过多个StringBuilder和String的对象
             ② 使用String的字符串拼接方式:内存中由于创建了较多的StringBuilder和String的对象,内存占用更大;如果进行GC,需要花费额外的时间。
    
         改进的空间:在实际开发中,如果基本确定要前前后后添加的字符串长度不高于某个限定值highLevel的情况下,建议使用构造器实例化:
                   StringBuilder s = new StringBuilder(highLevel);//new char[highLevel]
         */
        @Test
        public void test6(){
    
            long start = System.currentTimeMillis();
    
    //        method1(100000);//4014
            method2(100000);//7
    
            long end = System.currentTimeMillis();
    
            System.out.println("花费的时间为:" + (end - start));
        }
    
        public void method1(int highLevel){
            String src = "";
            for(int i = 0;i < highLevel;i++){
                src = src + "a";//每次循环都会创建一个StringBuilder、String
            }
    //        System.out.println(src);
    
        }
    
        public void method2(int highLevel){
            //只需要创建一个StringBuilder
            StringBuilder src = new StringBuilder();
            for (int i = 0; i < highLevel; i++) {
                src.append("a");
            }
    //        System.out.println(src);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132

    5. intern()的使用

    在这里插入图片描述
    在这里插入图片描述

    /**
     * 题目:
     * new String("ab")会创建几个对象?看字节码,就知道是两个。
     *     一个对象是:new关键字在堆空间创建的
     *     另一个对象是:字符串常量池中的对象"ab"。 字节码指令:ldc
     *
     * 思考:
     * new String("a") + new String("b")呢?
     *  对象1:new StringBuilder()
     *  对象2: new String("a")
     *  对象3: 常量池中的"a"
     *  对象4: new String("b")
     *  对象5: 常量池中的"b"
     *
     *  深入剖析: StringBuilder的toString():
     *      对象6 :new String("ab")
     *       强调一下,toString()的调用,在字符串常量池中,没有生成"ab"
     *
     * @author shkstart  shkstart@126.com
     * @create 2020  20:38
     */
    public class StringNewTest {
        public static void main(String[] args) {
    //        String str = new String("ab");
    
            String str = new String("a") + new String("b");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    /**
     * 如何保证变量s指向的是字符串常量池中的数据呢?
     * 有两种方式:
     * 方式一: String s = "shkstart";//字面量定义的方式
     * 方式二: 调用intern()
     *         String s = new String("shkstart").intern();
     *         String s = new StringBuilder("shkstart").toString().intern();
     *
     * @author shkstart  shkstart@126.com
     * @create 2020  18:49
     */
    public class StringIntern {
        public static void main(String[] args) {
    
            String s = new String("1");
            s.intern();//调用此方法之前,字符串常量池中已经存在了"1"
            String s2 = "1";
            System.out.println(s == s2);//jdk6:false   jdk7/8:false
    
    
            String s3 = new String("1") + new String("1");//s3变量记录的地址为:new String("11")
            //执行完上一行代码以后,字符串常量池中,是否存在"11"呢?答案:不存在!!
            s3.intern();//在字符串常量池中生成"11"。如何理解:jdk6:创建了一个新的对象"11",也就有新的地址。
                                                //         jdk7:此时常量中并没有创建"11",而是创建一个指向堆空间中new String("11")的地址
            String s4 = "11";//s4变量记录的地址:使用的是上一行代码代码执行时,在常量池中生成的"11"的地址
            System.out.println(s3 == s4);//jdk6:false  jdk7/8:true
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    在这里插入图片描述在这里插入图片描述

    public class StringIntern1 {
        public static void main(String[] args) {
            //StringIntern.java中练习的拓展:
            String s3 = new String("1") + new String("1");//new String("11")
            //执行完上一行代码以后,字符串常量池中,是否存在"11"呢?答案:不存在!!
            String s4 = "11";//在字符串常量池中生成对象"11"
            String s5 = s3.intern();
            System.out.println(s3 == s4);//false
            System.out.println(s5 == s4);//true
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    /**
     * @author shkstart  shkstart@126.com
     * @create 2020  20:17
     */
    public class StringExer1 {
        public static void main(String[] args) {
            String s = new String("a") + new String("b");//new String("ab")
            //在上一行代码执行完以后,字符串常量池中并没有"ab"
    
            String s2 = s.intern();//jdk6中:在串池中创建一个字符串"ab"
                                   //jdk8中:串池中没有创建字符串"ab",而是创建一个引用,指向new String("ab"),将此引用返回
    
            System.out.println(s2 == "ab");//jdk6:true  jdk8:true
            System.out.println(s == "ab");//jdk6:false  jdk8:true
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    /**
     *
     * @author shkstart  shkstart@126.com
     * @create 2020  20:26
     */
    public class StringExer2 {
        public static void main(String[] args) {
            String s1 = new String("ab");//执行完以后,会在字符串常量池中会生成"ab"                         // false
    //        String s1 = new String("a") + new String("b");执行完以后,不会在字符串常量池中会生成"ab"  // true
            s1.intern();
            String s2 = "ab";
            System.out.println(s1 == s2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    intern的效率测试
    结论:对于程序中大量存在存在的字符串,尤其其中存在很多重复字符串时,使用intern()可以节省内存空间。

    /**
     * 使用intern()测试执行效率:空间使用上
     *
     * 结论:对于程序中大量存在存在的字符串,尤其其中存在很多重复字符串时,使用intern()可以节省内存空间。
     *
     *
     * @author shkstart  shkstart@126.com
     * @create 2020  21:17
     */
    public class StringIntern2 {
        static final int MAX_COUNT = 1000 * 10000;
        static final String[] arr = new String[MAX_COUNT];
    
        public static void main(String[] args) {
            Integer[] data = new Integer[]{1,2,3,4,5,6,7,8,9,10};
    
            long start = System.currentTimeMillis();
            for (int i = 0; i < MAX_COUNT; i++) {
    //            arr[i] = new String(String.valueOf(data[i % data.length]));
                arr[i] = new String(String.valueOf(data[i % data.length])).intern();
    
            }
            long end = System.currentTimeMillis();
            System.out.println("花费的时间为:" + (end - start));
    
            try {
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.gc();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在这里插入图片描述

    6. G1中的String去重操作

    在这里插入图片描述在这里插入图片描述在这里插入图片描述
    注:本文是学习 尚硅谷宋红康JVM全套教程(详解java虚拟机)所做笔记。

  • 相关阅读:
    从线上化走向智能化,数字办公助力企业实现“效率+安全”双提升|爱分析报告
    【数据结构】栈和队列&&OJ练习
    盘点 | 好用的开发者IDE工具
    设计模式:命令模式
    Nginx 快速入门
    log4j设置日志的时区
    RPG Maker MV-场所移动
    OEEL高阶图表——对比2000和2017年全球不同类型发电占比柱状图
    Java的Atomic原子类
    Java校招120道面试题目合集
  • 原文地址:https://blog.csdn.net/qq_44300280/article/details/128000954