miracle just wanna be better

String类型为什么不可变

2019-11-01
miracle

String常用的api

  • int length
  • int indexOf(int ch) 查找ch字符在该字符中第一次出现的位置
  • int indexOf(String str) 查找str字符串第一次出现的位置
  • int lastIndexOf(int ch) 查找ch字符串最后一次出现的位置
  • String substring(int beginIndex) 获取从beginIndex位置开始到结束的子字符串
  • string substring(int beginIndex,int endIndex)
  • String trim() 去除了前后空格的字符串
  • boolean equals()
  • String toLowerCase()
  • char charAt()
  • String[] split(String regex)
  • byte getBytes()

String特性

String类是不可变对象:

  • 类内部所有的字段都是final修饰的
  • 类内部所有字段都是私有的,private
  • 类不能继承和拓展
  • 没有对外提供修改内部状态的方法,比如sette方法
  • 类内部的字段如果是引用,不能获取这个引用

String原理

String str="张三";
str="李四";

上面代码并没有改变str,是重新创建了一个String对象李四,把str的引用指向地址改变而已

源码

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0
    ...

String其实就是一个字符数组,一个个字符存储在数组中,但是value没有setter方法,所以不能在外部改变,真正改变的是value的地址

疑惑

内部的方法substring,replace等方法是怎么操作的?
每次使用这些操作,其实是在堆内存中创建了一个新的对象,然后value指向不同的对象


    public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }



public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= value.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }

在判断没有outofbound后,用构造方法new一个新的String对象

怎么改变string

反射机制,违反语言设计原则

public class TestString {
	@Test
	public void testString() {
		String str = "张三";
		System.out.println(str);
		try {
			Field field = String.class.getDeclaredField("value");
			field.setAccessible(true);//私有属性必须写这句
			char[] value;
			value=(char[]) field.get(str);//获取对象的值
			//更改字符
			value[0]='老';
			System.out.println(str);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

这样就把这个字符串给修改了

为什么把String设置为不可变

  1. String的类型使用是最多的,在进行增删改查时,jvm需要检查这个String对象的安全性,通过hashcode确定String的唯一性,提高效率
  2. 字符串保留在常量池中,如果允许改变将产生各种错误

上一篇 注解

下一篇 java内存结构

Comments

Content