String字符串
说在前面的话:
String类是我个人认为是Java设计当中一个特殊的,虽然String本身特殊引用数据类型 但是她却可以想基本数据类型那样直接赋值!!!
1.String类的两种对象实例化方式
String类之所以特殊,主要原因在于其有两种不同的对象的实例化方式.
-
采用直接赋值字符串的形式为String类对象实例化(推荐使用的写法)
package com.shxt.demo01;public class StringDemo01 { public static void main(String[] args) { String str = "直接方式实例化方式";//使用频率特别高 System.out.println(str); }}复制代码
-
采用String类的构造方法为String类进行实例化,String类重载了构造方法,在Java EE中我们会使用到字符串转码问题等会使用到
package com.shxt.demo01;public class StringDemo02 { public static void main(String[] args) { String str = new String("构造函数实例化String字符串"); System.out.println(str); }}复制代码
2.String是不可变的
对下面的代码进行分析说明
package com.shxt.demo01;public class StringDemo03 { public static void main(String[] args) { String s = "abcd"; s = "dbcdel"; }}复制代码
String不可变很简单,我们给一个已有的s字符串变量赋值为"abcd",第二次赋值成"abcdel",不是在原内存地址上修改,而是重新执行一个新对象,行地址.
为什么String就是不可变的呢? 我需要知道原因
开JDK源码,java.lang.String类起手前三行,是这样写的:
public final class String implements java.io.Serializable, Comparable, CharSequence { /* String的本质是一个char数组,并且使用了final关键修饰*/ /** The value is used for character storage. */ private final char value[]; ... ... ... ...} 复制代码
代码分析:
1.String类是用final关键字修饰,这说明String不可继承
2.String类的主要成员字段value是个char[ ]数组,而且是用 final 修饰的。
final修饰的字段创建以后就不可改变。
3.String类的用法说明
String类在java.lang包中,java使用String类创建一个字符串变量,字符串变量属于对象。java把String类声明的final类,不能有子类。String类对象创建后不能修改,由0或多个字符组成,包含在一对双引号之间,下面简单的熟悉一下其常用的API
java.lang.String char charAt (int index) 返回index所指定的字符 String concat(String str) 将两字符串连接 boolean endsWith(String str) 测试字符串是否以str结尾 boolean equals(Object obj) 比较两对象 char[] getBytes 将字符串转换成字符数组返回 char[] getBytes(String str) 将指定的字符串转成制服数组返回 boolean startsWith(String str) 测试字符串是否以str开始 int length() 返回字符串的长度 String replace(char old ,char new) 将old用new替代 char[] toCharArray 将字符串转换成字符数组 String toLowerCase() 将字符串内的字符改写成小写 String toUpperCase() 将字符串内的字符改写成大写 String valueOf(Boolean b) 将布尔方法b的内容用字符串表示 String valueOf(char ch) 将字符ch的内容用字符串表示 String valueOf(int index) 将数字index的内容用字符串表示 String valueOf(long l) 将长整数字l的内容用字符串表示 String substring(int1,int2) 取出字符串内第int1位置到int2的字符串复制代码
(1) 字符串比较
A.字符串上使用"=="比较
实际开发中字符的比较不会使用"=="进行比较,请回顾我们之前学习的内存地址方面的知识
package com.shxt.demo01;public class StringDemo04 { public static void main(String[] args) { String str1 = "hanpang" ; //直接赋值实例化对象 String str2 = new String("hanpang"); //构造方法实例化对象 String str3 = str2; //引用传递 System.out.println(str1==str2); // false System.out.println(str1==str3); // false System.out.println(str2==str3); // true }}复制代码
内存分析图如下:
代码分析:
使用"=="的确完成了相等的判断,但是最终判断的是两个对象是否相等,属于数值判断--判断两个对象的内存地址的数值,并没有判断起内容
如果想完成字符串内从的判断,必须要使用String类的操作方法
public boolean equals(String str)复制代码
B.使用equals()方法进行比较
package com.shxt.demo01;public class StringDemo05 { public static void main(String[] args) { String str1 = "hanpang" ; //直接赋值实例化对象 String str2 = new String("hanpang"); //构造方法实例化对象 String str3 = str2; //引用传递 System.out.println(str1.equals(str2)); //true System.out.println(str1.equals(str3)); //true System.out.println(str2.equals(str3)); //true }}复制代码
C.使用equals()方法的陷阱
以后要注意如何使用equals方法
package com.shxt.demo01;public class StringDemo06 { public static void main(String[] args) { String str1 = null ; if(str1.equals("Hello")){ System.out.println("成功"); } }}复制代码
运行后会在控制台报空指针的异常信息
Exception in thread "main" java.lang.NullPointerException at com.shxt.demo01.StringDemo06.main(StringDemo06.java:6) //错误的位置复制代码
修改后的代码为[重点!重点!重点]
package com.shxt.demo01;public class StringDemo06 { public static void main(String[] args) { String str1 = null ; if("Hello".equals(str1)){ //修改后的代码 System.out.println("成功"); } }}复制代码
常见面试题分析,请解释String类中"=="和"equals()"的区别?
- == : 比较两个字符串内存地址的数值是否相等,属于数值比较
- equals() : 比较两个字符串的内容,属于内容比较
简单的面试,这个属于常识
(2) 字符串与字符
编号 | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String(char[] value) | 构造方法 | 将字符数组内容变成字符串 |
2 | public String(char[] value,int offset,int count) | 构造方法 | 将部分字符数组变为字符串offset表示开始点count标识操作的长度 |
3 | public char charAt(int index) | 普通方法 | 取得指定索引位置的字符串,索引从0开始 |
4 | public char[] toCharArray() | 普通方法 | 将字符串转换为字符数组 |
验证charAt()方法
package com.shxt.demo01;public class StringDemo07 { public static void main(String[] args) { String str = "welcomeshxt" ; char c = str.charAt(0); System.out.println(c); }}复制代码
验证toCharArray()方法
package com.shxt.demo01;public class StringDemo08 { public static void main(String[] args) { String str = "welcomeshxt" ; char[] data = str.toCharArray(); for (int i = 0; i < data.length; i++) { System.out.print(data[i]+","); } }}复制代码
练习题
将全小写的英文字符串"welcomeshxt",变成大写的字符串,在控制台输出的内容为WELCOMESHXT 和 SHXT 两个字符串
package com.shxt.demo01;public class StringDemo09 { public static void main(String[] args) { String str = "welcomeshxt" ; }}复制代码
(4) 字符串和字节
编号 | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String(byte[] bytes) | 构造方法 | 将字节数组内容变成字符串 |
2 | public String(byte[] bytes,int offset,int length) | 构造方法 | 将部分字节数组变为字符串 |
3 | public byte[] getBytes() | 普通方法 | 将字符串变为字节数组 |
4 | public byte[] getBytes(String charsetName)throws UnsupportedEncodingException | 普通方法 | 字符串转码操作[重点] |
package com.shxt.demo01;public class StringDemo09 { public static void main(String[] args) { String str = "welcomeshxt" ; byte[] data = str.getBytes(); for (int i = 0; i < data.length; i++) { System.out.print(data[i]+","); data[i] -=32;//扩展赋值运算符,不会改变类型 } System.out.println(); System.out.println("全部字节转为字符串:"+new String(data)); System.out.println("全部部分字节转为字符串:"+new String(data,7,4)); }}复制代码
(5) 字符串查找
在String类中提供了从一个字符串查找指定字符串是否存在的操作,下面提供的说明必须要牢记
编号 | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public boolean contains(String s) | 普通方法 | 查找指定的字符串是否存在 |
2 | public int indexOf(String s) | 普通方法 | 从头查找指定的字符串位置,找不到返回-1 |
3 | public int indexOf(String s,int fromIndex) | 普通方法 | 由指定位置向后查找字符串的位置,找不到返回-1 |
4 | public int lastIndexOf(String s) | 普通方法 | 从后查找指定的字符串位置,找不到返回-1 |
5 | public int lastIndexOf(String s,int fromIndex) | 普通方法 | 从指定的位置由后向前查找 |
6 | public boolean startsWith(String prefix) | 普通方法 | 判断是否以指定的字符串开头 |
7 | public boolean startsWith(String prefix,int offset) | 普通方法 | 从指定的位置判断是否以指定的字符串开头 |
8 | public boolean endsWith(String suffix) | 普通方法 | 判断是否以指定字符串结尾 |
package com.shxt.demo01;public class StringDemo10 { public static void main(String[] args) { String str = "##pang@@sir**" ; //定义一个字符串 System.out.println(str.startsWith("##")); //判断开头 System.out.println(str.startsWith("sir",5)); // 从指定的位置开始判断开头 System.out.println(str.endsWith("**")); // 判断结尾 System.out.println(str.contains("sir")); //查找字符串是否存在 System.out.println(str.contains("AA")); System.out.println(str.indexOf("sir"));//查找字符串的位置 System.out.println(str.indexOf("AA")); System.out.println(str.lastIndexOf("sir"));//查找字符串的位置 System.out.println(str.lastIndexOf("AA")); }}复制代码
(6) 字符串截取
编号 | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String substring(int beginIndex) | 普通方法 | 从指定位置截取到结尾 |
2 | public String substring(int beginIndex,int endIndex) | 普通方法 | 截取部分字符串 |
package com.shxt.demo01;public class StringDemo12 { public static void main(String[] args) { String str = "Hello World" ; System.out.println(str.substring(2)); System.out.println(str.substring(2,8)); // [2,8) }}复制代码
练习题
- 已知一个文件名称为"西游记.悟空.docx"的字符串,请获取该文件的后缀名称 docx
(7) 字符串替换操作
编号 | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String replace(char oldChar,char newChar) | 普通方法 | 字符替换,不常用 |
2 | public String replaceAll(String regex,String s) | 普通方法 | 全部替换 |
3 | public String replaceFirst(String regex,String s) | 普通方法 | 替换首个 |
package com.shxt.demo01;public class StringDemo11 { public static void main(String[] args) { String str = "pangpang" ; System.out.println(str.replace('n','X')); System.out.println(str.replaceAll("an","*")); System.out.println(str.replaceFirst("an","#")); }}/**运行结果paXgpaXgp*gp*gp#gpang*/复制代码
(8) 字符串拆分
编号 | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public String[] split(String regx) | 普通方法 | 按照指定的字符串全拆分 |
2 | public String[] split(String regx,int limit) | 普通方法 | 拆分为指定的长度 |
package com.shxt.demo01;public class StringDemo13 { public static void main(String[] args) { String str = "Hello#World#!!!" ; String[] result = str.split("#"); for (int i = 0; i < result.length; i++) { System.out.println(result[i]); } }}复制代码
练习题:(进行拆分时也会出翔一些字符无法进行拆分,此时需要使用\\(表示一个\)
)进行转义
- 拆分IP地址(192.168.7.199),输出结果为 192 168 7 199
(9) 其他方法
编号 | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public boolean isEmpty() | 普通方法 | 判断是否为空的字符串("")无法进行null的判断 |
2 | public int length() | 普通方法 | 取得字符串长度 |
3 | public String trim() | 普通方法 | 去掉左右空格 |
4 | public String toLowerCase() | 普通方法 | 将全部字母转小写 |
5 | public String toUpperCase() | 普通方法 | 将全部字母转大写 |
6 | public String concat(String s) | 普通方法 | 字符串连接(+) |
package com.shxt.demo01;public class StringDemo14 { public static void main(String[] args) { String str = "Hello World" ; System.out.println(str.isEmpty());//false System.out.println("".isEmpty());//true System.out.println(str.length());//获取长度 System.out.println(" HAHA ".length()); System.out.println(" HAHA ".trim().length());//去空格的长度 System.out.println(str.toLowerCase()); System.out.println(str.toUpperCase()); System.out.println(str.concat("HAHA").concat("WOWO")); }}复制代码
练习题
- 设置字符串的首字母大写
user_name
→User_name
(10) 字符串与基本数据类型互转
A.基本数据类型转字符串
String类中提供了String valueOf()放法,用作基本类型转换为字符串类型
static String valueOf(char data[])static String valueOf(char data[], int offset, int count)static String valueOf(boolean b)static String valueOf(char c)static String valueOf(int i)static String valueOf(long l)static String valueOf(float f)static String valueOf(double d)复制代码
package com.shxt.demo01;public class StringDemo15 { public static void main(String[] args) { int num = 100; String s1 = String.valueOf(num); String s2 = ""+100; }}复制代码
B.字符串转基本数据类型
java.lang包中有Byte、Short、Integer、Float、Double类的调用方法:
public static byte parseByte(String s)public static short parseShort(String s)public static short parseInt(String s)public static long parseLong(String s)public static float parseFloat(String s)public static double parseDouble(String s)复制代码
package com.shxt.demo01;public class StringDemo16 { public static void main(String[] args) { String s = "999"; int num1 = Integer.parseInt(s); long num2 = Long.parseLong(s); float num3 = Float.parseFloat(s); System.out.println(num1+"-"+num2+"-"+num3); }}复制代码
StringBuffer和StringBuilder
我们来分析一下字符串的程序代码,看看这段代码为什么要在实际开发中尽量避免呢?
package com.shxt.demo02;public class Demo01 { public static void main(String[] args) { String str = ""; for (int i = 0; i < 10000; i++) { str += i; //字符串拼接 } System.out.println(str); }}复制代码
代码分析:
我们说过String是不可变的,上面的代码需要"断开-连接"String对象10000次,会产生大量垃圾,所以不推荐这种方式,那么我们如何改进代码呢?
1.StringBuffer类
StringBuffer字符串变量(线程安全)是一个容器,最终会通过toString方法变成字符串;
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, Appendable, CharSequence{ /** * Constructs a string buffer with no characters in it and an * initial capacity of 16 characters. */ public StringBuffer() { super(16); } public synchronized StringBuffer append(int i) { super.append(i); return this; } public synchronized StringBuffer delete(int start, int end) { super.delete(start, end); return this; }}复制代码
代码分析:
super(16)调用AbstractStringBuilder的抽象类的构造函数,对字符数组进行初识化操作
abstract class AbstractStringBuilder implements Appendable, CharSequence { char[] value; AbstractStringBuilder(int capacity) { value = new char[capacity]; }}复制代码我们发现字符数组不是final修饰,意味着是可变的!
A.StringBuffer的内容是可以改变的,引用传递
package com.shxt.demo02;public class Demo02 { public static void main(String[] args) { StringBuffer buf = new StringBuffer(); //定义StringBuffer对象 buf.append("Han ").append(" Pang"); //连接字符串 fun(buf); //引用传递 System.out.println(buf.toString()); //将buf转为字符串 } public static void fun(StringBuffer temp){ temp.append(" Welcome ").append("shxt"); }}复制代码
编号 | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public StringBuffer append(数据类型 b) | 普通方法 | 追加内容到当前StringBuffer对象的末尾,类似于字符串的连接。调用该方法以后,StringBuffer对象的内容也发生改变 |
2 | public StringBuffer deleteCharAt(int index) | 普通方法 | 删除指定位置的字符,然后将剩余的内容形成新的字符串 |
3 | public StringBuffer insert(int offset, 数据类型 b) | 普通方法 | 插入内容,然后形成新的字符串 |
4 | public StringBuffer reverse() | 普通方法 | 内容反转 |
5 | public void setCharAt(int index, char ch) | 普通方法 | 修改对象中索引值为index位置的字符为新的字符ch |
6 | public void trimToSize() | 普通方法 | 将StringBuffer对象的中存储空间缩小到和字符串长度一样的长度,减少空间的浪费 |
package com.shxt.demo02;public class Demo03 { public static void main(String[] args) { StringBuffer buf = new StringBuffer("西游记"); //定义StringBuffer对象,并初始化数据 buf.append(",悟空").append(999); //append追加内容 System.out.println("结果1"+buf.toString()); //删除数据 buf = buf.deleteCharAt(buf.length()-1); buf = buf.deleteCharAt(buf.length()-1); buf = buf.deleteCharAt(buf.length()-1); System.out.println("删除后的结果:"+buf.toString()); //插入数据 buf.insert(3,"====>"); System.out.println("插入后的结果:"+buf.toString()); buf.replace(3,8,"");//替换数据 System.out.println("替换后的结果:"+buf.toString()); //反转数据 buf.reverse(); System.out.println("反转的数据为:"+buf.toString()); }}/*结果1西游记,悟空999删除后的结果:西游记,悟空插入后的结果:西游记====>,悟空替换后的结果:西游记,悟空反转的数据为:空悟,记游西*/复制代码
StringBuffer sb=new StringBuffer();
sb.delete(0, sb.length());
sb.setLength(0);
StringBuffer通过使用sb.setLength(0)来清空StringBuffer对象中的内容效率最高
2.StringBuilder类
StringBuilder 字符串变量(非线程安全),使用方式跟StringBuffer一样
3.区别
String
继承于CharSequence
,也就是说String
也是CharSequence
类型StringBuilder
和StringBuffer
都是可变的字符序列。它们都继承于AbstractStringBuilder
,实现了CharSequence
接口String
是不可变的,StringBuffer
、StringBuilder
是可变的StringBuilder
是非线程安全的,而String
(不可变对象)、StringBuffer
(对方法加了同步锁或者对调用的方法加了同步锁)是线程安全的StringBuilder
适用于单线程环境,StringBuffer
适用于多个线程操作同一个字符串- 大部分情况下:
StringBuilder
>StringBuffer
>String
参考资料: https://juejin.im/entry/59082ab5a0bb9f006510683a