java中表示字符串的类有String StringBuffer StringBuilder
String类被final修饰的 没有子类的
1.1 创建String类对象的方式
a.以字面量的形式赋值 创建String对象
String str = “bac”;
字面量 - 就是字符串常量 位于方法区的常量池中
int a = 10;b.根据构造方法创建字符串对象
String str = new String(“bcd”);
new 出来的对象是位于堆中的
a.以字面量形式创建的对象,位于java提供的字符串常量池中。
字符串常量池有一个特点:当定义对象时,先在常量池中查看是否有该对象值,如果有的话直接使用,如果没有先在常量池中添加,再使用
b.以字面量形式赋值的话 内存中只有一个对象
以构造方法赋值的话 内存中有两个对象
1.3 String对象是不可变的
一旦创建了String对象,对象的值将不再发生改变
源码分析String类对象为什么不可变
// The value is used for character storage.
private final char value[];
String字符串存放字符序列的数组
String str = "hello";
--》 char value[] = new char[]{'h','e','l','l', 'o'};
该数组使用的是final修饰的 表示该数组对象地址不可变
该数组前面的修饰符为private
只能在String本类中使用 外界获取不了的 --> 外界无法通过数组索引修改数组,所以数组的内容是固定的 而且地址是不可变的 导致String对象不可变
1.4 打印字符串变量的结果 【了解】
得到的结果是字符串序列值。
原因: 在String类中重写了toString方法 public String toString(){ retur this; } 以字面量形式赋值的对象 这个获取的常量池中字符序列值
以构造方法赋值的
public String(String original) { this.value = original.value; this.hash = original.hash; } String str = new String("abc"); 分析: str.value = "abc".value; str.hash = "abc".hash; "abc".hash ---> 获取的就是对应的字符序列 hash --> hashCode值 打印变量的时候 Object中toString方法 类名@hashCode的十六进制 this.hash = original.hash; 而方法区常量池的hash打印的是对应的字符序列 所以导致以构造方法形式创建的对象打印出来的也是字符序列
1.5 String中常见的操作
String的对象是不可变 如果下列方法中有操作String对象,其实是生成了一个新的String对象,对于原来的对象不受影响
获取
1.获取字符串字符序列的长度
int len=str.length()
2.根据索引位置获取对应的字符
char ch=str.charAt(index) index为索引值
3.根据字符或者字符串返回第一次或者是最后一次出现的索引位置根据字符返回第一次该字符出现的索引位置
int index=str.indexof('字符')
根据字符串返回第一次该字符串出现的索引位置
int index=str.indexof("字符串")
如果字符串中没有对应的字符或子串 返回值??? -1
根据字符返回最后一次出现的索引位置
int index=str.lastIndexof('字符')
根据字符串返回最后一次出现的索引位置
int index=str.lastIndexof("字符串")
从某个位置开始
int index=str.indexof('字符',fromindex)
判断
1.判断字符串是否包含某个子串
str.contains()
2.判断字符串中是否有内容
str.isEmpty()
3.判断字符串是否以指定内容开头
str.startsWith()
4.判断字符串是否以指定内容结尾
str.endsWith()
5.判断字符串内容是否相同[忽略大小写]
不忽略大小写 str.equals(str1)
忽略大小写 str.equalsIgnoreCase(str2)
转换
将字符串数组转化为字符串// 将字符串数组转化为字符串 char[] ch={'h','e','l','l','o'}; //1.通过构造方法转化 //a.全部转化 String str=new String(ch); //b.第二个参数表示从第几个开始转化 //第三个参数表示转化几位,注意数组越界问题 str=new String(ch,2,3); System.out.println(str); //2.通过静态方法转化 //a.String.copyValueOf String str1=String.copyValueOf(ch); System.out.println(str1); str1=String.copyValueOf(ch, 2, 3); System.out.println(str1); //b.String.valueOf str1=String.valueOf(ch); System.out.println(str1); str1=String.valueOf(ch, 2, 3);
将字符串转化为字符数组
char []ch1=str.toCharArray(); System.out.println(Arrays.toString(ch1));
将字节数组转化为字符串
将字符串转化为字节数组
将基本类型数据转化为字符串
字符串大小写转换
替换字符串
切割字符串
spilt
提取子串
substring
比较字符串大小
去除字符串两端空格
拼接字符串
字符串格式化
这个也是表示字符串的类
String和StringBuffer的区别
String是不可变的类 对象是不可变的
StringBuffer相当于是字符缓冲容器,对象是可变的。在对字符串做修改时,修改的是其本身
2.1 StringBuffer的初始化
创建StringBuffer对象 是通过构造方法创建的
a.public StringBufffer()
初始化的是一个不带字符的字符缓冲区,缓冲区初始容量为16个字符b. public StringBuffer(int a)
初始化的是一个不带字符的字符缓冲区,缓冲区初始容量为a个字符c. public StringBuffer(String str)
初始化的是一个带有字符的字符缓冲区,初始字符内容为String字符串内容,缓冲区初始容量为16 + str.length()个字符
StringBuffer对象可变的原因:
存放字符序列数组是可变的没有用final进行修饰
在StringBuffer中添加字符时,会先判断存放字符的数组长度是否充足,如果充足的话直接在数组添加添加一个新的内容,如果不充足,先改变存放字符数组长度,原数组中的内容进行拷贝,赋值到新的数组中
2.2 常用的方法
String中 获取的方法在StringBuffer也存在
获取字符串长度
获取某个位置上的字符
获取字符或者是字符串第一次/最后一次出现的位置
a. 获取StringBuffer的当前可存储字符的容量
b. 拼接字符串
StringBuffer append(基本数据类型的数据/String/StringBuffer/char[]/Object);c.插入字符串
StringBuffer insert(索引,基本数据类型的数据/String/StringBuffer/char[]/Object)d. 删除字符序列
删除多个字符[删除子串]
StringBuffer delete(int beginIndex, int endIndex);
[begin, end)删除单个字符
StringBuffer deleteCharAt(int index)e.替换字符串
替换子串
StringBuffer replace(int start, int end, String newStr)
替换单个字符
void setCharAt(int index, char newChar);f.反转字符串
StringBuffer reverse();
3.StringBuilder和StringBuffer
StringBuilder的使用和StringBuffer一样。区别在于:
StringBuffer是线程安全的 执行效率较慢
StringBuilder是线程非安全的 指向效率是偏快的
StringBuffer中的append方法
public synchronized StringBuffer append(int i) {
toStringCache = null;
super.append(i);
return this;
}
StringBuilder中的append方法
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
因为在StringBuffer所有的方法操作都是同步方法,被synchronized修饰,当多个线程来对方法同时进行操作时,线程顺序被同步化
简单而言就是一个线程一个线程的进行数据修改 StringBuilder的方法操作并没有同步化,当多条线程来进行方法访问时,线程是并发的,几乎是同时执行该方法 有可能会导致数据混乱的问题
4.String类中的“+”拼接符拼接字符串原理
String str = “abc”;
str = str + “ABC”;内部操作:
当使用+拼接符时:
StringBuffer sb = new StringBuffer(str);
sb.append(“ABC”);
str = sb.toString();文字描述:
String str = “abc”; 创建一个String对象"abc",str变量引用这个对象
再创建一个容量为str.length+16的StringBuffer对象
StringBuffer sb = new StringBuffer(str)
再调用StringBuffer中的append方法将“ABC”进行拼接
最后再调用StringBuffer中toString方法创建一个新的String对象将StringBuffer类型的数据"abcABC"
转换为String类型,最后将str再引用新的String对象
public String toString(){
return new String(value);
}
5.String和StringBuffer类型的转换
String ----》 StringBuffer
通过构造方法进行转换 String str = "abc"; StringBuffer sb =new StringBuffer(str);
StringBuffer ----》 String
调用StringBuffer的toString方法 String str1 =sb.toString();
6.包装类
包装类把基本数据类型的数据转化为对象
对于包装类来说,用途:
1.集合中只能存放引用类型的数据,如果想存放int数字等这个时候就需要使用包装类
2.对于每个类型的包装类都有对应的相关属性 和 每个类型的最大值与最小值以及对应的相关操作
如: Integer中对进制的转换
public Type(type value)
真实的 public Integer(int value)除了Character 都可将字符串类型转化为对应的数据包装类型
例如:将字符串类型转化为Integer类型
6.1 以Integer为例讲解一下常用的方法
1、基本数据类型 —> 引用类型
int a = 10;
Integer in = new Integer(a);2、将引用类型转化为基本数据类型 type typeValue()
int b = in.intValue();
6.2 装箱和拆箱
装箱:将基本数据类型使用其对应的引用类型包装起来
Integer in = new Integer(10);拆箱:将引用类型转化成对应的基本数据类型
int value = in.intValue();
JDK5.0之后支持自动装箱和自动拆箱
自动装箱:
Integer in = 10;
自动拆箱
int b = in;
自动装箱如何实现的?
Integer in = 10;
内部调用的方法为: static Integer valueOf(int i)
in = Integer.valueOf(10);
看一下valueOf这个方法:
public static Integer valueOf(int i) {
if (i >= -128 && i <= 127)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);}
6.3 面试中对于装箱拆箱会遇到的问题
1.Integer in = new Integer(10); 和 Integer in = 10; 两者的区别
a.前者不会触发自动装箱,但是后者会触发
b.前者一定会在堆中创建对象 但是后者可能不会【-128~127直接在数值缓冲池中取值】
2.自动装箱的弊端
Integer sum = 0;
for(int i = 0; i < 100; i++){
sum += i;
}Integer对象无法做算术运算,完成以上功能,得先对sum拆箱,进行加法运算,再进行装箱。如果数量过大,会降低程序的执行性能,加重了垃圾回收的工作
3.valueOf做了解