1、String的常见创建方式:
//方式一
String str1="hello"; //这种用的最多
//方式二
String str2=new String("hello");
//方式三
char[] ch={'h','e','l','l','o'};
String str3=new String(ch);
//方式四
String str4=new String("hello").intern();//intern()方法先在常量池里查找,
要是有值为hello的字符串,//则不新建,将地址指向。要是没有则在常量池里新建一个。所以相等。
2、字符串比较:
==比较内存。equals比较内容
1、==比较
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2);
// 执行结果
true
俩都在字符串常量池的,str1 和str2指向同一个对象,是==
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2);
// 执行结果
false
俩都是在堆上开辟内存,所以是指向不同的堆地址,false
String str3=new String("hello");
String str4=new String("hello").intern();//intern()方法先在常量池里查找,要是有值为hello的字符串,//则不新建,将地址指向。要是没有则在常量池里新建一个。所以相等。
System.out.println(str1 == str2);
// 执行结果
true
// 执行结果 true 反之,要是先str4,后str3,则不相等
2、equals比较
String的equals实际上是重写了Object的equals方法,才实现了String的内容的比较
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1.equals(str2));
// 执行结果
true
String的equals的注意事项:
调用.equals的部分不能为null,不然抛出异常
String str = null;
// 方式一
System.out.println(str.equals("Hello"));
// 执行结果 抛出 java.lang.NullPointerException 异
常
// 方式二
System.out.println("Hello".equals(str)); // 执行结果 false
3、字符串常量池
String类的设计使用了共享设计模式(可以理解为现在的共享单车,如果我们想用的话就在外面直接扫一辆单车直接用就行了(从常量池中取),不用我们再买一辆新的自行车(自己new一个新的对象赋值),大大方便了我们的使用)
在JVM底层实际上会自动维护一个对象池(字符串常量池)
- 如果现在采用了直接赋值的模式进行String类的对象实例化操作,那么该实例化对象(字符串内容)将自动保存到这个对象池之中.
- 如果下次继续使用直接赋值的模式声明String类对象,此时对象池之中如若有指定内容,将直接进行引用。
- 如若没有,则开辟新的字符串对象而后将其保存在对象池之中以供下次使用
我们一般采取直接赋值的方式创建 String 对象
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2);
的结果为true。
这块也就解释了,第一次的时候,在常量池维护了“hello”,第二次直接“拿”了。所以是同一份“hello”
4、字符串不可变
String类底层是char[]实现的,String 类的内部实现也是基于 char[] 来实现的, 但是 String 类并没有提供 set 方法之类的来修改内部的字符数组。
String str = "hello" ;
System.out.println(str.hashCode());
str = str + " world" ;
str += "!!!" ;
System.out.println(str);
System.out.println(str.hashCode());
执行结果
99162322
hello world!!!
1640396477
表象上看,str是执行了 +=的操作。但其实是生成了新的String,其地址已经发生改变,hashCode值发生改变,底层原理是在字符串常量池新申请了一块内存,其值为+=操作后的值,引用到str。
如果必须实现修改字符串
1)常见办法: 借助原字符串, 创建新的字符串
String str = "Hello";
str = "h" + str.substring(1);
System.out.println(str);
// 执行结果
hello
2)反射
使用 “反射” 这样的操作可以破坏封装, 访问一个类内部的 private 成员
为什么 String 要不可变?(不可变对象的好处是什么?)
- 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑何时深拷贝字符串的问题了.
- 不可变对象是线程安全的.
- 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中
5、字符、字节与字符串
1、字符串内部包含一个字符数组,String可以和char[]相互转换
常用方法
-
- toCahrArray()将字符串转换为char数组并返回
- charAt(index)截取一个字符
- String()数组转为字符串
- valuesOf(char[])数组转字符串
- copyValueOf(char[])数组转字符串
2、字节与字符串
字节常用于数据传输以及编码转换的处理之中,String 也能方便的和 byte[] 相互转换(一般在网络方面使用)
常用的一些方法
- getBytes()将字符串以字节数组的形式返回
- getBytes(String charsetName)throws UnsupportedEncodingException编码转换处理
6、字符串常见的操作
1、字符串比较
字符串比较
- equals()和equalsIgnoreCase() 比较两个字符串(ignore不区分大小写)
- compareTo()和compareToIgnoreCase() 比较字符串
字符串查找
- 字符串查找,最好用最方便的就是contains()
- startsWith()方法判断是否以指定字符开头
- endWith()方法决定是否以指定字符结尾
- indexOf() 查找字符或者子串第一次出现的地方。
- lastIndexOf() 查找字符或者子串是后一次出现的地方
字符串替换
- replaceAll() 替换所有指定内容
- replaceFirst()替换首个内容
注意事项: 由于字符串是不可变对象 , 替换不修改当前字符串, 而是产生一个新的字符串
字符串拆分
- split(String regex)将字符串全部拆分
- split(String regex,int limit)将字符串部分拆分,切分该数组长度的就是limit的最大值
拆分是特别常用的操作. 一定要重点掌握. 另外有些特殊字符作为分割符可能无法正确切分, 需要加上转义.
String str = "192.168.1.1" ;
String[] result = str.split("\\.") ;
for(String s: result) {
System.out.println(s);
}
注意事项:
- 字符" | “,” * “,”+“都得加上转义字符,前面加上”\ ".
- 而如果是"\ “,那么就得写成”\".
- 如果一个字符串中有多个分隔符,可以用"|"作为连字符.
多次拆分
String str = "name=zhangsan&age=18" ;
String[] result = str.split("&") ;
for (int i = 0; i < result.length; i++) {
String[] temp = result[i].split("=") ;
System.out.println(temp[1]);
}
运行结果
zahngsan
split(String regex,int limit)
String str = "aaaaaa=bbbbbb=cccccc=ddddd" ;
String[] strings=str.split("=",2);
for(int i=0;i<strings.length;i++) {
System.out.println(strings[i]);
}
运行结果为
aaaaaa
bbbbbb=cccccc=ddddd
limit给2的话数组的长度便会是2(会切分为两部分
字符串截取
从一个完整的字符串之中截取出部分内容。可用方法如下
- String substring(int beginIndex)从指定索引截取到结尾 String
- substring(int beginIndex,int endInx)截取部分内容
String str = "helloworld" ;
System.out.println(str.substring(5));
System.out.println(str.substring(0, 5));
输出结果
world
hello
注意事项:
- 索引从0开始
- 注意左闭右开的写法, substring(0, 5) 表示包含 0 号下标的字符, 不包含 5 号下标
其他常用的方法
- String trim()去掉字符串中的左右空格,保留中间的空格
- String toUpperCase()字符串转大写
- String toLowerCase()字符串转小写
- public native String intern()字符串入池
- String concat(String str)字符串连接,等同于“+‘’ 但不入池
- int length()取字符串的长度
- boolean isEmpty()判断是否为空字符串,但不是null,而是长度为0
7、String、StringBuffer 和 StringBuilder
String类的特点:
任何的字符串常量都是String对象,而且String的常量一旦声明不可改变,如果改变对象内容,改变的是其引用的指向而已。通常来讲String的操作比较简单,但是由于String的不可更改特性,为了方便字符串的修改,提供StringBuffer和StringBuilder类。
1、String、StringBuffer 和 StringBuilder之间的转换
注意:String和StringBuffer类不能直接转换。如果要想互相转换,可以采用如下原则:(String和StringBuilder类似)
- String变为StringBuffer:利用StringBuffer的构造方法或append()方法
StringBuffer定义了三个构造函数:
StringBuffer()
StringBuffer(int size)
StringBuffer(String str)
StringBuffer(CharSequence chars)
- StringBuffer变为String:调用toString()方法
2、String、StringBuffer 和 StringBuilder之间的区别
String 和 StringBuilder StringBuffer 区别:
- 后两者包含了一些String没有的方法 比如reverse方法
- 后两者是可变的,String是不可变的。String的每次拼接,都会产生新的对象,后两者每次的拼接都返回的是this
- StringBuilder 之间 StringBuffer 区别:
StringBuilder和String 出现在单线程情况下
StringBuffer因为有synchronized关键字 所以一般出现多线程情况下。
- StringBuilder和 String之间有啥区别!!
String的拼接 + 会被优化 优化为StringBuilder . append了 !!!
在循环当中 不可以使用String直接进行拼接 这样会产生大量的临时对象(包括优化之后StringBuilder对象)
感谢大佬详细总结 【抱拳】