zoukankan      html  css  js  c++  java
  • String到底在内存中是如何存储的

    String会出现在哪些地方

    • 方法内的局部string
    • 类内的字段String
    • static string
    • 容器中存储的string
    • String数组
      那么String的位置会影响其存储方式吗?
      显然是不会的,类永远只会储存在堆上。
      但是实际上类的字段并不是一直在堆上的。

    String的构造方法

    以下来自String类 源码,一些无关紧要的实现被我省略了:

     private final char value[];
        private int hash; // Default to 0
        private static final long serialVersionUID = -6849794470754667710L;
        private static final ObjectStreamField[] serialPersistentFields =
            new ObjectStreamField[0];
       public String() {
            this.value = "".value;
        }
      public String(String original) {
            this.value = original.value;//底层char[]指向了同一位置!
            this.hash = original.hash;
        }
        public String(char value[]) {
            this.value = Arrays.copyOf(value, value.length);//底层char[]指向不同位置!下面的截取也是如此
        }
        public String(char value[], int offset, int count) { ... }
    
        public String(int[] codePoints, int offset, int count) {...}
    
        public String(byte ascii[], int hibyte, int offset, int count) {...}
    
        public String(byte ascii[], int hibyte) {...}
    

    我们可以发现String的构造器并不关注是否将char[]指向同一位置,之所以有一些没有指向同一位置完全是为了保证char[]是immutable的。
    这并不能说明调用构造器构造的String的内存位置有什么特别之处。

    String另一种构造方式--为包装类型提供的专有构造方式

    运行如下代码:

    class StringPointerTest{
        String g="gh",h="gh";
    }
    public class StringTest {
        public static void main(String[] args) {
            String a="abc";
            String b="abc";
            System.out.println(a==b);//true使用简写的构造能够复用创建的string类
            String c=new String("abc");
            System.out.println(a==c);//false是用构造器则不能
            String d=new String("def");
            String e="def";
            System.out.println(d==e);//false即使先构造器再使用简化构造也不行。
            StringPointerTest spt=new StringPointerTest();
            System.out.println(spt.g==spt.h); //true与String的属于方法局部变量还是类字段也无关
        }
    }
    
    class IntegerPointTest1{
        Integer a=1;
    
    }
    class IntegerPointTest2{
        Integer b=1;
    }
    public class IntegerTest {
        public static void main(String[] args) {
           System.out.println(new IntegerPointTest1().a==new IntegerPointTest2().b);//true!
        }
    }
    

    原理

    需要存储的代码元素有:

    • Class类文件
    • 方法,类的所以实例应该共用一段方法
    • static字段
    • 字符串常量
    • 值常量
    • 类实例
    • 各种引用
    • 基本变量
      他们各自有各自的存储位置,方法内的引用存在方法栈,类内的引用存在堆,类存储在堆上,方法中的局部基本变量存于栈但是类字段的基本变量存在堆上(方法区内)。
      值得一提的是方法区(又叫静态区),其存储值常量、字符串常量、方法、静态字段、.class文件,等只用一个备份的数据。
      栈和方法区都有共享数据的功能。
      因此使用简化方法构造String类的时候,在方法内和类内会将字符串存于栈/方法区,这无关紧要,重要的是,
      使用这种构造方法,如果没有所构造的字符串常量存在于内存中,那么会在栈/方法区中存上一份,然后再堆中新建一个String类,把String类的char[]引用指向在栈/方法区中的字符串常量;
      如果所构造的字符串常量已经存在于内存中,那么则会检索关联与之对应的堆中String实例,并直接使用这个String类实例。
      只有使用简化方法构造才能被栈/方法区记录下来,如果使用new则不行,这也是为什么上例即使先new,再使用简化构造相同字符串也不会引用相同。
      因为new出来的String实例的字符串常量存储在堆上,和栈/方法区无关。

    拓展到所有包装类型---可以吗?

    完全不可以。
    他们都没有像String那样实现缓存。
    但是实现了类似的缓存,它们的自动包装机制也提供缓存功能,
    但是是基于valueof方法的,该方法会对一定范围内进行缓存。
    而且实现方式非常暴力,是在对应类里存一个静态的类数组,并静态初始化全部填充。

    可以缓存的范围:
    byte Byte -128–127
    short Short -128–127
    int Integer -128—127
    long Long -128—127
    char Character 0–127
    boolean Boolean TURE,FALSE
    String
    不缓存的:
    float Float
    double Double

    可见不是所有的自动包装机制都实现了全缓存。

  • 相关阅读:
    移动APP的开发需求分析
    我心中的理想团队和对软件开发流程的理解
    Git 命令
    在Visio2010中修改默认字体的大小
    Remove @Override annotation错误提示
    SPRING框架中ModelAndView、Model、ModelMap区别
    select count(*)和select count(1)的区别
    eclipse 关键字高亮显示
    MyEclipse下安装MyBatis Generator代码反向生成工具
    关于 log4j.additivity的说明
  • 原文地址:https://www.cnblogs.com/gjl-blog/p/8600738.html
Copyright © 2011-2022 走看看