DAY9.1 Java核心基础
String
String 开发使用的频率高
String 实例化有两种方式
1、直接赋值
String str1 ="Word";
2、通过构造函数创建对象
String str2 =new String("Word");
字符串对象底层的基本数据类型是char
比如Word,是char[] str ={'W','o','r','d'};
两种实例化的区别
先看一个代码示例:
public static void main(String[] args) {String str1 ="Word";String str2 ="Word";String str3 =new String("Word");String str4 =new String("Word");System.out.println(str1 == str2);System.out.println(str1 == str3);System.out.println(str3 == str4);
}
输出:
可以看见虽然这四个对象的值都是一样的,但是str1和str2是相等的,而str1和ster3以及str3和str4是不等的,为什么呢?
这里要提到一个String对象的字符串常量池了
当使用String直接赋值创建实例化的时候,会先去字符串常量池寻找是否有这个对象
如果有这个对象则直接返回找到的字符串常量的地址,无需额外在堆内存里面开辟空间
所以在创建str2的时候先去字符串常量池找是否有”Word“的对象,找到了str1对象的值匹配,则返回的str1的地址,所以str1 == str2
具体的 JMM 内存
由上图可以清晰得出这几个String对象的关系
基于上述原因,我们在比较两个字符串是否相等的时候一般调用equels方法,这个方法是String包装类来重写父类Object的equels方法,这样就可以比较值是否相等
public static void main(String[] args) {// 直接赋值String str1 ="Word";String str2 ="Word";// 构造器创建String str3 =new String("Word");String str4 =new String("Word");System.out.println(str1.equals(str2));System.out.println(str1.equals(str3));System.out.println(str3.equals(str4));
}
我们来看看String类里面的equals方法
public boolean equals(Object anObject) {if (this == anObject) {return true;}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;}}return false;
}
有几个关键节点
- this == anObject:比较地址是否相等,如果相等肯定值相等,返回true
- anObject instanceof String :如果不是String类型,则返回false
- n == anotherString.value.length:比较长度是否相等,如果不相等直接返回false
- while (n-- != 0):从后往前遍历比较,如果有一位不相等则返回false
String常用方法
方法 | 描述 |
---|---|
public String() | 创建一个值为空的对象 |
public String(String value) | 创建一个值为 value 的对象 |
public String(char[] value) | 将一个 char 类型数组转为字符串对象 |
public String(char[] value,int offset,int count) | 将一个指定范围的 char 类型数组转为字符串对象 |
public String(byte[] bytes) | 将一个 byte 类型数组转为字符串对象 |
public String(byte[] bytes,int offset,int count) | 将一个指定范围的 byte 类型数组转为字符串对象 |
public int length() | 返回字符串的长度 |
public boolean isEmpty() | 判断字符串是否为空 |
public char charAt(int index) | 返回字符串中指定位置的字符 |
public byte[] getBytes() | 将字符串转为 byte 类型数组 |
public boolean equals(Object obj) | 判断两个字符串是否相等 |
public boolean equalsIgnoreCase(String str) | 判断两个字符串是否相等并且忽略大小写 |
public int compareTo(String str) | 对两个字符串进行排序 |
public boolean startsWith(String str) | 判断是否以指定的值开头 |
public boolean endsWith(String str) | 判断是否以指定的值结尾 |
public int hashCode() | 获取字符串的散列值 |
public int indexOf(String str) | 从头开始查找指定字符的位置 |
public int indexOf(String str,int index) | 从指定的位置开始查找指定字符的位置 |
public String substring(int index) | 截取字符串从指定位置开始到结尾 |
public String substring(int index1,int index2) | 截取字符串从指定位置开始到指定位置结束 |
public String concat(String str) | 追加字符串 |
public String replaceAll(String str1,String str2) | 替换字符串 |
public String[] split(String str) | 用指定字符串对目标字符串进行分割,返回数组 |
public String toLowerCase() | 将字符串转为小写 |
public String toUpperCase() | 将字符串转为大写 |
public char[] toCharArray() | 将字符串转为 char 数组 |
StringBuffer
实际开发中使用 String 会存在一个问题,String 对象一旦创建,值是不能修改的
诶,那我这样不就是修改了吗
public static void main(String[] args) {String str = "Hello, World!";System.out.println(str);str = "Word";System.out.println(str);
}
NoNoNo,这样不是在String对象修改
str = "Word";的效果是在堆内存里面创建一个新对象,然后把新对象的地址赋值给str变量,所以知识str变量的地址改变了
String 的值一旦修改,等于重新创建了一个对象进行赋值,而不能在原对象上进行修改
为什么 String 对象一旦修改,不能在原数据上进行修改,而是创建新对象重新赋值?
数组的特点:一旦创建,长度不能修改
因为 String 底层是一个字符数组,所以新的字符串的值无法追加到老字符串的后面,所以只能重新创建一个新的数组来存储新的字符串,所以说 String 的值不能修改(无法在原数据基础上进行修改)
创建的 String 对象,实际上是把字符串的内容存储到一个 char 数组中,String 底层是一个 char 数组
如果开发中需要对某个字符串进行频繁的修改,使用 String 就不合适了,会造成内存空间浪费的问题,如何解决?
使用 StringBuffer 来解决,StringBuffer 和 String 类似,底层也是用一个数组来存储字符串的值,并且默认长度是 16,既一个空的 StringBuffer 对象,数组长度为 16
当我们调用构造器创建一个 StringBuffer 对象时,数组长度就不是 16 了,而是根据当前对象的值来决定数组的长度,值的长度 + 16 作为数组的长度
无论创建的 String Buffer 对象是什么内容,都会预留 16 个长度,就是用来进行修改的
StringBuffer常用方法(和String差不多,实际就是比String更加节约内存)
方法 | 描述 |
---|---|
public StringBuffer() | 无参构造,创建一个空的 StringBuffer 对象 |
public StringBuffer(String str) | 有参构造 |
public synchronized int length() | 返回 StringBuffer 的长度 |
public synchronized char charAt(int index) | 返回字符串中指定位置的字符 |
public synchronized StringBuffer append(String str) | 追加字符 |
public synchronized StringBuffer delete(int start,int end) | 删除指定区间内的字符 |
public synchronized StringBuffer deleteCharAt(int index) | 删除指定位置的字符 |
public synchronized StringBuffer replace(int start,int end,String str) | 将指定区间内的值替换为 str |
public synchronized String substring(int start) | 截取字符串从指定位置开始到结尾 |
public synchronized String substring(int start,int end) | 截取字符串从指定位置开始到指定位置结束 |
public synchronized StringBuffer insert(int offset,String str) | 向指定位置插入 str |
public int indexOf(String str) | 从头开始查找指定字符的位置 |
public int indexOf(String str,int index) | 从指定位置开始查找指定字符的位置 |
public synchronized StringBuffer reverse() | 进行反转 |
public synchronized String toString() | 返回 StringBuffer 对应的 String |
但是注意StringBuffer的方法很多有 synchronized 锁,因为StringBuffer具有可操作性
如果一个线程在读的时候另外一个线程在写,那么会导致数据错乱的安全问题
部分方法使用以及反馈:
public static void main(String[] args) {StringBuffer stringBuffer = new StringBuffer("Hello");// 添加stringBuffer.append(" World");System.out.println(stringBuffer);// 获取长度System.out.println(stringBuffer.length());// 获取字符System.out.println(stringBuffer.charAt(1));// 删除stringBuffer.delete(0, 5);System.out.println(stringBuffer);// 插入stringBuffer.insert(0, "Hello");System.out.println(stringBuffer);// 替换stringBuffer.replace(0, 5, "World");System.out.println(stringBuffer);// 截取字符串System.out.println(stringBuffer.substring(0, 5));// StringBuffer的容量System.out.println(stringBuffer.capacity());// 获取对应单词的起始位置System.out.println(stringBuffer.indexOf("d"));// 反转System.out.println(stringBuffer.reverse());
}