Java String

1.String 对象创建以及存储

先简单介绍下相关概念

栈:运行时存放基本数据类型的变量数据和对应引用

堆:存放所有new 对象

常量池:输入堆中分配出来的一块内存区域,存放String常量和基本数据类型,或一些static final 数据。常量池数据是可以共享的。

按图分析

这里写图片描述

new 过程分析

String ns4 = new String("早上好");
String s4 = "早上好";
//返回false 表示创建了两个对象,常量池中的和new 堆中的
System.out.println(s4 == ns4);

String s5 = "中午好";
String ns5 = new String("中午好");
//返回false 表示创建了两个对象,常量池中的和new 堆中的
System.out.println(s5 == ns5);

//那么问题是,new的过程到底是怎么样的呢?

try {
    Field valueField = String.class.getDeclaredField("value");
    valueField.setAccessible(true);
    char[] valueChar = (char[]) valueField.get(ns4);
    valueChar[2]='?';
    //输出 早上?
    System.out.println(ns4);
    //输出 早上?,s4 和ns4 相等,表示表示常量池和堆中的对象内部引用同一个char[]
    //所以常量池的对象 肯定是在new 之后就产生了的,而不是到String s4 = "早上好" 定义时才创建对象
    //否则引用的char[] 不能是同一个
    System.out.println(s4);

    char[] valueChar5 = (char[]) valueField.get(ns5);
    valueChar5[2]='?';
    //输出 中午?
    System.out.println(ns5);
    //输出 中午?,s5 和ns5 相等,表示表示常量池和堆中的对象内部引用同一个char[]
    //所以在new Sting的时候,回去判断常量池是否存在,如果存在,怎使用已有的char[]创建。
    System.out.println(s5);

    //综上所述,new String()时,会先在常量池里判断是否有等值对象,如果有则使引用其底层char[]创建对象;若没有,则
    //创建对象,并在常量池中也创建一个等值对象,引用同一个char[]存储

} catch (Exception e) {
    e.printStackTrace();
}

两种初始化分析:

//创建1个对象,1个引用,在常量池中创建了一个"hello"对象,在栈中分配了一个引用s1指向常量池"hello"对象
String s1 = "hello";
//创建 0个对象,1个引用,因为常量池中已经存在"hello" 对象,在栈中分配了一个引用s2指向常量池"hello"对象
String s2 = "hello";

//创建了2个对象,1个引用,在常量池中创建了一个"hi"对象,在堆中使用new 创建了一个"hi"对象,在栈中分配了一个引用ns1指向堆中"hi"对象
String ns1 = new String("hi");
//创建了1个对象,1个引用,因为常量池中已经存在"hi" 对象,在堆中使用new 创建了一个"hi"对象,在栈中分配了一个引用ns2指向堆中"hi"对象
String ns2 = new String("hi");

//创建1个对象,1个引用,在常量池中创建了一个"你好"对象,在栈中分配了一个引用s3指向常量池"你好"对象
String s3 = "你好";
//创建了1个对象,1个引用,因为常量池中已经存在"你好" 对象,在堆中使用new 创建了一个"你好"对象,在栈中分配了一个引用ns3指向堆中"你好"对象
String ns3 = new String("你好");
2.String 类为什么是finnal

String源码注释上表述:Strings 是常量,一旦被创建则值不可改变,String 对象是不可变的共享对象,我想如果是共享对象,那么不使用线程安全策略,那么会有极大的编程风险,其次String对象不可变性指的是值的不可变,String对象的值是存储在常量池中的,也就是堆中,String对象是基于字符数组实现的,当我们改变String对象的值时,不是在原数组上操作,而是新建了一个数组存储新值,原数组并没有改变,这就是String 类的不变性。

3.String、Stringbuilder、Stringbuffer区别

源码如下阐述:java语言提供了对字符串连接符(“+”),其他对象转为字符串对象的特别支持。字符串的连接是通过 StringBuilder 或者 StringBuffer 这两个类及它们的append 方法实现的。StringBuffer 和StringBuilder 几乎拥有相同的对外方法,StringBuffer对几乎所有的方法做了synchronized 同步,考虑到线程安全,多线程环境则使用StringBuffer。

看下StringBuilder 的append方法是如何优化字符串连接的

StringBuilder

public StringBuilder append(String str) {
    	//调用了抽象类AbstractStringBuilder的append 方法
        super.append(str);
        return this;
    }

AbstractStringBuilder

public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        //调用String 中的getChars方法将str中的数组copy到value数组中
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

String

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        //调用数组copy
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }

哦,已经明白了吧,StringBuilder通过底层数组copy减少了一次,中间字符串对像的分配操作。

4.String 常用方法

length:返回字符串长度

public int length() {
    //返回内部数组的长度
    return value.length;
}

empty:是否为空数组

public boolean isEmpty() {
    //返回内部数组长度是否为0
    return value.length == 0;
}

charAt:返回字符串中的第n个字符

public char charAt(int index) {
    if ((index < 0) || (index >= value.length)) {
        throw new StringIndexOutOfBoundsException(index);
    }
    //返回内部数据相应索引位置的字符
    return value[index];
}

equals:比较值是否相等

public boolean equals(Object anObject) {
    if (this == anObject) {
        //引用相同,则为true
        return true;
    }
    //String 实例比较
    //length相等,char数组遍历,同一个位置的char 必须相等
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    //不是String 实例,则返回false
    return false;
}

intern: 返回字符串常量池对象,在重复对象上使用该方法可以大量减少内存消耗

/* 本地方法;当调用此方法时,如果常量池已包含和当前字符相同的对象,则返回常量池字符串引用
 * 否则,这个对象会被加入常量池,返回该常量池对象引用
 * 可以提高程序效率或者减少内存占用的情况
 */
public native String intern();

trim: 去除两端特殊字符,32(" ") 以下

split: 按正则切分成字符串数组

replace: 字符替换

contains: 是否包含

substring: 截取子串,左毕右开

indexOf : 子串存在当前字符串的开始位置索引

startWith: 是否以匹配的字符串开始

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值