随机名言

String



String是最常操作的引用类型了,但也是我最怕的地方(因为不熟悉),最怕还是String和Array同时出现,所以现在先写下一篇博客熟悉熟悉字符串


0. 字符串非空

if( str == null || str.length() == 0){
    System.out.println("11");
}

1.字符串的不可变性

我们常说String是不可变的,但的对应的变量为什么还是能"改"为不同的字符串呢?

来看一下String的部分源码

  • 在114行可以看出,String内部使用数组来存储,使用了private与final修饰,且内部没有修改value数组的方法,所以一旦定义就不能修改,即String的不可变性

**不可变性的好处**
  • 不可变才有字符串常量池,优化空间
  • 存储hashCode的,因为经常使用
  • 线程安全,因为不可变


2. 但为什么我们的变量还是能改为不同的字符串呢?

String a = new String("String不可变性");
a = new String("String确定不可变吗?");
System.out.println(a);

//输出
//String确定不可变吗?
  • 其实字符串都没有变,变的是a的引用地址,这样看起来貌似字符串改变了罢了


3. String Pool

创建字符串会放到字符串常量池中,下次创建相同的字符串会从常量池中拿取引用,所以相同字符串引用相同

String a = "String不可变性";	//字面量
String b = "String不可变性";
System.out.println(a == b);

//输出
//true

//但new关键字是在堆中复制一个副本,引用地址给了变量,所以指向的对象的地址不同
String a1 = new String("String不可变性");	//对象
String b1 = new String("String不可变性");
System.out.println(a1 == b1);

//输出
//false

对JVM不熟悉的同学可以看我另一篇 学不会的JVM



4. 连接符 “+”

//字符串常量,JVM会优化,在字符串常量池直接存放“123”
String a = "1" + "2" + "3";

//字符串变量,会在底层创建StringBuilder,然后append,最后toString返回
String a = new String("1") + new String("2");


### 5. 构造方法及常用方法
构造函数 解释
String(byte[] bytes, String charsetName) 构造一个新的String用指定的字节数组和解码
String(String original) 初始化新创建的String对象,新创建的字符串是参数字符串的副本
String(StringBuffer buffer) 其中包含当前包含在字符串缓冲区参数中的字符序列
String(StringBuilder builder) 其中包含当前包含在字符串构建器参数中的字符序列
返回值 方法名 解释
char charAt(int index) 返回指定索引处的字符
int compareTo(String anotherString) 按字典顺序比较两个字符串
int compareToIgnoreCase(String str) 按字典顺序比较两个字符串,忽略大小写
String concat(String str) 将指定的字符串连接到该字符串的末尾
boolean contains(Strin str) 判断字符串中是否有指定的子字符串
boolean equals(Object anObject) 判断值是否相同
byte[] getBytes(String charsetName) 使用命名的字符集将此String编码为字节序列
int length() 返回此字符串的长度
int indexOf(int ch,int fromIndex) 返回指定字符第一次出现的字符串内的索引,从指定索引开始
int lastIndexOf(int ch) 返回指定字符最后一次出现的字符串内的索引
String [] split(String regex) 将此字符串分割为给定的字符串
String substring(int beginIndex,int endIndex) 返回一个字符串,该字符串是此字符串的子字符串。
char[] toCharArray() 将此字符串转换为新的字符数组
String trim() 返回一个字符串,删除任何前导和尾随空格
String replace(old char/String, new char/String) 返回一个新字符串
boolean matches(String regex) 是否匹配正则

计算字串出现的次数

public int StrTimes(String a,String b){
    
    int count = 0
    int fromIndex = 0;
    
    while( (fromIndex = a.indexOf(b,fromIndex)) != -1 ){
        fromIndex += b.length();
        count++;
    }
    
    return count;
}


6. StringBuilder和StringBuffer

  • StringBuilder线程不安全,速度稍快
  • StringBuffer 线程安全,速度稍慢

他俩的实现方式是创建一个可变的底层数组,且提供各种改变数组序列的方法

这里来看StringBuffer ,StringBuilder类似,就不讲了


  • 二者都继承AbstractStringBuilder

  • 66行super()默认构造函数使用父类的,默认大小为16,底层也是字符数组
  • 会自动扩容,扩为原数组的2倍加2,这时是创建一个新的数组,并将原数组复制到新数组(与集合扩容类似)

  • 线程安全来源于synchronized

常见方法

构造函数

构造函数 解释
StringBuffer() 构造一个没有字符的字符串缓冲区,初始容量为16个字符
StringBuffer(String str) 构造一个初始化为指定字符串内容的字符串缓冲区,大小为str.length()+16
StringBuffer(int capacity) 构造一个没有字符的字符串缓冲区和指定的初始容量

常用方法

返回值 方法名 解释
StringBuffer append(String str) 将指定的字符串附加到此字符序列
int capacity() 返回当前容量
StringBuffer delete(int start, int end) 删除此序列的子字符串中的字符
StringBuffer insert(int offset, String str) 将字符串插入到此字符序列中
int length() 返回长度(字符数)
StringBuffer replace(int start, int end, String str) 用指定的String中的字符替换此序列的子字符串
StringBuffer reverse() 导致该字符序列被序列的相反代替
String toString() 返回表示此顺序中的数据的字符串
并且有String的方法

简单操作

可以链式操作,因为返回的是this本对象

StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("123")
    .append("abc")
    .append("-----")
    .delete(0, 3)
    .replace(3, 5, "dddd")
    .insert(7, "e");

System.out.println(stringBuffer.length());
System.out.println(stringBuffer.capacity());
System.out.println(stringBuffer.toString());
System.out.println(stringBuffer.reverse());
11
16
abcdddde---
---eddddcba


7. 总结

String适合操作少量数据

StringBuffer适合线程安全操作大量数据

StringBuilder适合单线程操作大量数据


没有地方写,补充一下日期的格式化

Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
System.out.println(sdf.format(date));

//2019-44-29 21:44:28


8 补充

java是按值传递,不是引用传递,下面举例


public class Pass {
	
	public static void test(int a, String b, User user){
		
		a = 2;
		b = "bb";
		user.name = "Change";
		
		System.out.println(a);
		System.out.println(b);
		System.out.println(user.name);
		
	}

	public static void main(String[] args) {
		
		int a = 1;
		String b = "b";
		User user = new User("Howl");
		
		test(a, b, user);
		System.out.println("--------------上面是传递改变,下面是未传递前----------------");
		
		System.out.println(a);
		System.out.println(b);
		System.out.println(user.name);
		
	}
}
2
bb
Change
--------------上面是传递改变,下面是未传递前----------------
1
b
Change

这里有个冲突分歧怪异点:为什么String和user对象只有user才能被改变?难道只有自定义对象才是按值传递?

非也,基本类型是按值传递这里大家没有意见把,主要在于引用类型是按值传递还是引用传递?其实这里应该在上面的test方法里加多一个语句才好理解


public static void test(int a, String b, User user){
		
		a = 2;
		b = "bb";
		user = new User("Change");	// 加的语句在这里
		// user.name = "Change";
		
		System.out.println(a);
		System.out.println(b);
		System.out.println(user.name);
}
2
bb
Change?
--------------上面是传递改变,下面是未传递前----------------
1
b
Howl

改变语句后,引用传递的效果就失去了,下面来分析一下:

public static void test(int a, String b, User user){
		
		a = 2;		// 参数a传递过来的是副本,即按值传递
		b = "bb";	// 这里String类型有点特殊,b = “bb” 相当于 b = new String(“bb”),新建了一个对象
		user = new User("Change");	// 把user的指向改为new User("XXX"),这里就明示出不是引用传递
		// user.name = "Change";	// test方法里和main方法里的user都指向堆内存同一个对象,当然可以改变
		
		System.out.println(a);
		System.out.println(b);
		System.out.println(user.name);
}

总结一下:基本类型按值传递,引用类型的传参传的是地址的副本,在给参数中的引用类型赋值时,改变的是参数的地址,即不属于引用传递,下面画图更好理解


没加入语句前


加入语句前



posted @ 2019-12-29 12:11  Howlet  阅读(246)  评论(0编辑  收藏  举报

Copyright © By Howl