创建String
常见的构造方法:
String str1="hello";
String str2=new String("world");
在官方文档上 (https://docs.oracle.com/javase/8/docs/api/index.html) 我们可以看到 String 还支持很多其他的构造方式, 我们用到的时候去查就可以了
字符串比较
大部分编程语言中,比较两个字符串内容的相等,主要都是使用==
但是有两个语言例外:C,Java
C是因为字符串是字符数组,用 是比较字符数组的地址,所以C语言用strcmp函数来解决
**Java使用是比较引用的地址是否相等**
public class Test1126 {
public static void main(String[] args) {
String str1="hello";
String str2="hello";
System.out.println(str1==str2);
}
}
输出的结果为true
那为什么和前面说的结论不一致呢?不应该是false嘛?
Java的字符串常量会被保存到“字符串常量池”中,此时字符串常量只需要保存一份即可
所以后面创建的str直接去字符串常量池中引用的是相同的地址,所以打印true,正确的方法如下:
但实际上只有字符串字面量是共享的,而+和substring等操作得到的字符串并不共享
public class Test1126 {
public static void main(String[] args) {
String str1=new String("hello");
String str2=new String("hello");
System.out.println(str1==str2);
}
}
打印false
因此,如果要想比较两个字符串的内容,就需要使用**.equals**方法
public class Test1126 {
public static void main(String[] args) {
String str1=new String("hello");
String str2=new String("hello");
System.out.println(str1.equals(str2));
}
}
equals注意事项:
//不介意这样写,一旦str1是null,就会抛出空指针异常
if(str1.equals("hello")){
}
//建议使用,如果str1是null,不会抛异常,返回false
if("hello".equals(str1)){
}
字符串常量池
直接赋值
String str1 = "hello" ;
String str2 = "hello" ;
String str3 = "hello" ;
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // true
System.out.println(str2 == str3); // true
为什么现在并没有开辟新的堆内存空间呢?
String类的设计使用了共享设计模式
在JVM底层实际上会自动维护一个对象池(字符串常量池)
如果现在采用了直接赋值的模式进行String类的对象实例化操作,那么该实例化对象(字符串内容)将自动保存 到这个对象池之中.
如果下次继续使用直接赋值的模式声明String类对象,此时对象池之中如若有指定内容,将直接进行引用
如若没有,则开辟新的字符串对象而后将其保存在对象池之中以供下次使用
构造方法
我们可以使用 String 的 intern 方法来手动把 String 对象加入到字符串常量池中
String str=new String("hello").insert();
面试题:请解释String类中两种对象实例化的区别
1. 直接赋值:只会开辟一块堆内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用。
2. 构造方法:会开辟两块堆内存空间,不会自动保存在对象池中,可以使用intern()方法手工入池。
理解字符串不可变
字符串是一种不可变对象,它的内容不可改变
String类的内部实现也是基于char[]来实现的,但是String类并没有提供set方法来修改内部的字符数组
为什么Java的String设计成不可变?
1.方便放到池中
2.对象的内容不可变,则对象的hashCode也不可变,方便和Hash结构搭配使用
3.对象不可变,线程安全更有保证
Java中为了方便修改,也提供了StringBuilder和StringBuffer这样的类
反射—特殊手段
Java中的”不可变对象“不是绝对的 ,只是常规手段 不能改
还有特殊手段~~
反射和“封装”是背道而驰的
1.使用反射往往可能打破封装
2.反射的代码比较复杂,容易出错
3.反射牺牲了编译器自身的一些检查校验机制,更需要程序员人工保证代码的正确性
String str="hello";
//通过反射的方式修改“hello”的内容
//特数手段,不是常规手段
//1.获取到String的类对象
//2.根据“value”这个字段名,在类对象中拿到对应的字段
Field valueField=String.class.getDeclaredField("value");
//让value这个private成员也能被访问到
valueField.setAccessible(true);
//3.根据图纸,把str这个对象给拆开,取出零件
char[] value=(char[])valueField.get(str);
//4.修改零件内容
value[4]='a';
System.out.println(str);
输出打印hella
char[]和String
String str="hedenghui";
//获取指定位置的字符
System.out.println(str.charAt(0));
//将字符串变成字符数组返回
System.out.println(str.toCharArray());
StringBuffer和StringBuilder
面试题:请解释String、StringBuffer、StringBuilder的区别:
String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.
StringBuffer与StringBuilder大部分功能是相似的
StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作
String API
char charAt(int index)
返回给定未知的代码单元
int codePointAt(int index)
返回从给定位置开始的码点
int offsetByCodePoints(int startIndex, int cpCount)
返回从startIndex码点开始,cpCount个码点后的码点索引
int compareTo(String other)
按照字典顺序,如果字符串位于other之前,返回一个负数,如果位于other之后返回一个正数,两个字符串相等则返回0
IntStream codePoints()
将这个字符串的码点作为一个流返回,调用toArray将他们放在一个数组中
new String(int[] codePoints,int offset, int count)
用数组中从offset开始的count个码点构造一个字符串
boolean empty()
判断字符串为空
boolean blank()
如果字符串由空格组成返回true
boolean equals(Object other)
如果字符串与other相等,返回true
boolean equalsIgnore(String other)
忽略大小写字符串相等返回true
boolean startsWith(String prefix)
boolean endsWith(String suffix)
如果字符串以prefix开头或者以suffix结尾,返回true
int indexOf(String str)
int indexOf(String str, int fromIndex)
int indexOf(int cp)
int indexOf(int cp, int fromIndex)
返回与字符串str或码点cp匹配的第一个字串的开始位置,从原始字符串的索引0或者fromIndex开始匹配,不存在返回-1
int lastIndex(String str)
int lastIndexOf(String str, int fromIndex)
int lastindexOf(int cp)
int lastindexOf(int cp, int fromIndex)
返回与字符串str或码点cp匹配的最后一个字串的开始位置,从原始字符串的末尾或者fromIndex开始匹配
int length()
字符串代码单元的个数
int codePointCount(int startIndex, int endIndex)
返回startIndex到endIndex-1的码点个数
String replace(CharSequence oldString, CharSequence newString)
将newString替换oldString
String subString(int beginIndex)
返回一个新字符串,从beginIndex到结尾的所有代码单元
String subString(int beginIndex, int endIndex)
返回一个新字符串,从beginIndex到endIndex-1的所有代码单元
String toLowerCase()
大写改小写
String toUpperCase()
小写改大写
String trim()
返回一个新字符串,这个字符串将删除原始字符串头部和尾部小于等于U+0020的字符
String strip()
返回一个新字符串,这个字符串将删除原始字符串头部和尾部的空格,不是所有的空格
String join(CharSequence delimiter, CharSequence... elements)
返回一个新字符串,用给定的定界符连接所有元素
String repeat(int count)
返回一个字符串,将当前字符串重复count次