Java字符串

String类
1.Java中的字符串常量(如"abc"、“张三”)都可以看作是String类的数据体现。
字符串常量存在于方法区的常量池,程序中出现过的字符串常量都在常量池
创建,且同一个字符串常量只会创建一次。
String s1 = “abc”; //常量池中创建了 “abc”, s1指向它
s1 = “张三”; //常量池中创建了 “张三”, s1指向它
String s2 = “abc”; //常量池中已有 “abc”, s2直接指向它
2它很特殊,String类型的引用可以直接指向常量池,也可以先指向堆,堆再指向常量池
//字符串常量赋值
String s1 = “abc”; //s1指向常量池 “abc”
//new创建并初始化
String s2 = new String(“abc”); //s2指向堆 value属性地址, value属性地址指向常量池 “abc”

	s1、s2的字符串内容都是"abc", 但是两者存储的地址却不相同!
3.String数据存储采用final修饰的一维字符数组
	private final char[] value; //数组是引用类型,final修饰表示赋值后value的值不可变
	
	public String(String str){
		this.value = str.value; //将常量字符串所在常量池中的地址赋值给对象属性value
	}
4.String类型不可变性说明
	因String数据采用final修饰的一维字符数组存储,new创建后,实例中的字符数组value不可被改变
	即实例中的value属性的值不可再变。
	所以String类型变量进行 增、删、改 等操作后,其本身是不变的,一般要返回新值。
	但是String类型的变量只要不用final修饰,它是可以改变的。
	如:
		String s2 = new String("abc"); //s2指向堆中实例,实例中属性value指向 "abc"
		s2 = "def"; //s2改变指向"def"
		String s3 = s2.replace('d', 'D'); //s2中字符a替换为A,由于s2不可变且s2未改变指向,所以s2还是"def",s3是"Def"
5.String类构造器
	public String()
	public String(String original)
	public String(char value[])
	public String(char value[], int offset, int count)
	public String(int[] codePoints, int offset, int count)
	public String(byte ascii[], int hibyte, int offset, int count)
	public String(byte ascii[], int hibyte)
	public String(byte bytes[], int offset, int length, String charsetName) throws UnsupportedEncodingException
	public String(byte bytes[], int offset, int length, Charset charset)
	public String(byte bytes[], String charsetName) throws UnsupportedEncodingException
	public String(byte bytes[], Charset charset)
	public String(byte bytes[], int offset, int length)
	public String(byte bytes[])
	public String(StringBuffer buffer)
	public String(StringBuilder builder)
6.String类常用方法
	int length(): 返回字符串的长度(字符数量)
	char charAt(int index): 返回某索引处的字符
	boolean isEmpty(): 判断是否是空字符串(true-是|false-否)
	String toLowerCase(): 使用默认语言环境,将字符串所有字符改成小写并返回
	String toUpperCase(): 使用默认语言环境,将字符串所有字符改成大写并返回
	String trim(): 返回字符串的副本,忽略前导空白、尾部空白
	boolean equals(Object obj): 比较字符串的内容是否相同(true-是|false-否)
	boolean equalsIgnoreCase(String str): 忽略字符大小写比较字符串内容是否相同(true-是|false-否)
	String concat(String str): 将指定字符串连接到字符串的尾部,类似"+"
	int compareTo(String str): 比较两个字符串的大小
		从左往右逐个字符比较,当出现不相同则返回两个字符的差值(ascll差值)
		负数:表示字符串比指定字符串小
		等于0:表示字符串与指定字符串相同
		正数:表示字符串比指定字符串大
	String substring(int beginIndex): 截取字符串,从指定位置至最后一个字符。
	String substring(int beginIndex, int endIndex): 截取字符串,从指定位置至指定位置。[beginIndex,endIndex)
	boolean endsWith(String suffix): 判断字符串是否以指定的后缀结束(true-是|false-否)
	boolean startsWith(String prefix): 判断字符串是否以指定的前缀开始(true-是|false-否)
	boolean startsWith(String prefix, int toffset): 判断字符串从指定位置是否以指定的前缀开始(true-是|false-否)
	boolean contains(CharSequence s): 当且仅当此字符串包含指定的char至序列时,返回true
	int indexOf(String str): 返回指定字符串在字符串中第1次出现的位置(-1表示未找到)
	int indexOf(String str, int fromIndex): 返回指定字符串在字符串中从指定的位置开始第1次出现的位置(-1表示未找到)
	int lastIndexOf(String str): 返回指定字符串在字符串中最后1次出现的位置(-1表示未找到)
	int lastIndexOf(String str, int fromIndex): 返回指定字符串在字符串中从指定的位置开始最后1次出现的位置(-1表示未找到)
	String replace(char oldC, char newC): 字符替换,返回替换后的结果
	String replace(CharSequence oldSeq, CharSequence newSeq): 字符序列替换,返回替换后的结果
	String replaceFirst(String regex, String newStr): 字符串替换,替换第一个匹配的,返回替换后的结果,支持正则
	String replaceAll(String regex, String newStr): 字符串替换,替换所有匹配的,返回替换后的结果,支持正则
	boolean matches(String regex): 判断字符串是否符合某个正则表达式
	String[] split(String regex): 字符串分割, 以符合正则表达式的子串作为分割符。
	String[] split(String regex, int limit): 字符串分割, 以符合正则表达式的子串作为分割符。分割数量最多不超过limit个,超过时最后的所有为1个
7.String类型与其他类型的转换
	[1-1]String -> 基本数据类型/包装类:调用包装类的静态方法 Xxx.parseXxx(字符串)
		int n1 = Integer.parseInt("123");
		float n2 = Float.parseFloat("123.2");
		double n3 = Double.parseDouble("123.2");
		boolean b = Boolean.parseBoolean("true");
		char c = "123".charAt(0);
	[1-2]基本数据类型/包装类 -> String:调用String类的静态方法 String.valueOf(值)
		String str = String.valueOf(123);
	
	[2-1]String -> char[] 调用String类的方法 字符串.toCharArray()
		char[] cs = "abcs".toCharArray();
	[2-2]char[] -> String 调用String类的构造器
		String str = new String(new char[]{'a','b','c'});
	
	[3-1]String -> byte[] 调用String类的方法 字符串.getBytes();
		byte[] bs = "abcs".getBytes();//使用默认字符集编码
		byte[] bs = "abcs".getBytes("gbk");//使用指定的字符集编码
		utf-8字符集中,一个汉字占3个byte,一个字母占1个byte
		gbk字符集中,一个汉字占2个byte,一个字母占1个byte
	[3-2]byte[] -> String 调用String类的构造器
		byte[] bs = "abcs".getBytes();//使用默认字符集编码
		String str = new String(bs);  //使用默认字符集解码
		byte[] bs = "abcs".getBytes("gbk");//使用指定的字符集编码
		String str = new String(bs,"gbk"); //使用指定的字符集解码

StringBuffer类、StringBuilder类
这两个类也都是可变字符序列的字符串类型,且这两个类中的属性、方法基本一致。
区别在于:StringBuffer类中的方法都是synchronized修饰的同步方法,线程安全但效率低。
StringBuilder类中的方法不是同步方法,未处理线程安全问题但效率高,效率一般是StringBuffer的2倍。
StringBuilder类为JDK5.0新增。
执行效率:StringBuilder > StringBuffer >> String
1.数据存储采用一维字符数组
private char[] value; //数组是引用类型,没有final修饰表示value可被改变

2.数据可变性说明
	因数据存储采用的一维字符数组没有被final修饰,new创建后,实例中的字符数组value可被改变
	即实例中的value属性的值可再变。
	所以StringBuffer/StringBuilder类型变量进行 增、删、改 等操作后,其本身是可变的。
	如:
		StringBuffer s2 = new StringBuffer("def"); //s2指向堆中实例,实例中属性value指向 "abc"
		StringBuffer s3 = s2.replace(0,1,"D"); //s2中字符a替换为A,由于s2所以s2是"Def",s3是"Def"
3.StringBuffer类、StringBuilder类常用方法
	StringBuffer append(形参): 提供多个重载方法,用于添加内容到字符串
	StringBuffer delete(int start, int end): 删除指定位置的内容
	StringBuffer replace(int start, int end, String str): 把[start,end)位置的内容替换为str
	StringBuffer insert(int offset, xxx): 在指定位置插入xxx
	StringBuffer reverse(): 把字符序列逆转
	int indexOf(String str): 指定字符串首次出现的位置
	String substring(int start, int end): 截取[start,end)位置的内容
	int length(): 返回字符串实际字符数量
	void setCharAt(int n, char c): 将指定位置的字符修改为c

面试题:
1.对于String型变量,字符串常量赋值 与 new创建赋值的区别?
字符串常量赋值:String s1 = “abc”;
此方式赋值变量s1指向的是"abc"所在常量池的内存地址,内存中只会创建一个对象
new创建:String s2 = new String(“abc”);
此方式创建变量s2指向的是堆内存的实例对象,实例中属性value指向"abc"所在常量池的内存地址
内存中会创建2个对象,一个在堆内存中,一个在常量池中。

2.以下代码,在内存中创建了几个对象?
String s = new String(“abc”);
会创建2个对象,一个在堆内存,一个在常量池中。
3.以下代码输出的结果分别是什么?
String s1 = “javaEE”;
String s2 = “hadoop”;

String s3 = "javaEEhadoop";
String s4 = "javaEE" + "hadoop";
String s5 = s1 + "hadoop";
String s6 = "hadoop" + s2;
String s7 = s1 + s2;

System.out.println(s3 == s4);//true
System.out.println(s3 == s5);//false
System.out.println(s3 == s6);//false
System.out.println(s3 == s7);//false
System.out.println(s5 == s6);//false
System.out.println(s5 == s7);//false
System.out.println(s6 == s7);//false
结论:常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
	  常量与变量的拼接结果在堆中。
	  字符串调用intern()方法表示取字符串所在常量池的地址。

4.以下代码输出结果是什么?
public class StringTest{
String str = new String(“good”);
char[] ch = {‘t’,‘e’,‘s’,‘t’};
public void change(String str,char[] ch){
str = “tesk ok”; //字符串虽然是引用类型,但是是不可变的,地址传进来后即时修改值也不改变原值
ch[0] = ‘b’;//数组是引用类型,地址传进来后进行操作会影响原值
}

	public static void main(String[] args){
		StringTest ex = new StringTest();
		ex.change(ex.str, ex.ch);
		System.out.println(ex.str);//good
		System.out.println(ex.ch);//best
	}
}

5.常见算法题
模拟一个trim方法, 去除字符串两端的空格。

将一个字符串进行反转。将字符串中指定部分进行反转。

获取一个字符串在另一个字符串中出现的次数。

获取两个字符串中最大相同子串。比如 str1="abcwerthelloyuiodef";str2="cvhellobnm"

对字符串中字符进行自然顺序排序。

6.String、StringBuffer、StringBuilder的异同?(高频面试题)
相同点:
三者都是字符串类型,底层存储都是采用字符数组。
不同点:
String类型是不可变字符序列,StringBuffer/StringBuilder类型是可变字符序列。
String类型除了可以new创建对象,还可以字符串常量直接赋值,StringBuffer/StringBuilder则只能new创建
StringBuffer类中方法都是synchronized修饰的同步方法,线程安全但效率低。
StringBuilder类中的方法不是同步方法,未处理线程安全问题但效率高。不存在线程安全问题时优先用它。
执行效率不同:StringBuilder > StringBuffer >> String
源码层面:
String s1 = new String(); //final char[] value = new char[0];
String s2 = new String(“abc”); //final char[] value = new char[]{‘a’,‘b’,‘c’};
System.out.println(s2.length());//3

	StringBuffer s3 = new StringBuffer(); //char[] value = new char[16];
	StringBuffer s4 = new StringBuffer("abc"); //char[] value = new char["abc".length+16];
	System.out.println(s4.length());//3
	1.String通过new创建对象,调用空参构造器时,底层字符数组长度为0
	  调用有参构造器时,传入的常量是几个字符,底层字符数组长度就为多少
	2.StringBuffer/StringBuilder创建对象调用空参构造器,底层字符数组长度为16
	  调用有参构造器时,传入的常量是几个字符,底层字符数组长度就为:传入的字符数量+16
	3.StringBuffer/StringBuilder对象实例中的字符数组value,存储了实际字符及预留的容量
	  调用length()方法返回的是实际字符数量。
	4.StringBuffer/StringBuilder扩容问题:比如当前对象实例中的属性value容量为16个字符
	  此时通过append方法添加的字符数量超过16,则添加前会进行扩容,扩容逻辑为:
			if (当前已有字符数+将要添加的字符数)>value的总容量 {
				新容量 = value的总容量*2+2
				if (当前已有字符数+将要添加的字符数)>新容量 {
					新容量 = (当前已有字符数+将要添加的字符数)
				}
			}
			char[] tmp = new char[新容量];
			将原有字符复制到新字符数组
			将需要添加的字符追加到新字符数组
			将属性value赋值为新字符数组(value指向新数组)
	由于扩容过程涉及字符数组创建、数组复制等操作耗时,编写代码时尽量做到不用出现扩容
	方法是,new创建对象时,提前指定一个足够的容量:new StringBuffer(长度);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值