1 相等性规则
== 比较运算符
- ==比较基本数据类型,判断的是值是否相等(基本数据类型只能用“==”比较)
- ==比较引用类型,判断的是内存地址是否相等
equals()方法
- Object类的equals()方法,比较的内存地址是否想等;
- String类的equals()方法,比较的是值是否相等;
- 按照自己的相等性规则进行比较,需要自定义equals方法;
2 String类型
String类型是一个引用类型,即栈中存放对象名和对象的引用,堆中存放String对象;
String类型的对象,底层实际是一个final修饰的char类型的数组。
2.1 创建String类型的对象
String a = "abc";
/*
这种创建方式,会把值存放到字符串常量池中
字符串常量池,可以理解为一个String的数据集,它是在堆内存中开辟的一块特殊内存
*/
String b = new String("abc");
/*
这种创建方式,就是创建一个String类型的对象,是在内存中新申请一块内存
*/
boolean tar = (a == b);//tar的值为false,因为==比较的是内存地址
2.2 常用方法
方法 |
返回值 |
作用 |
示例 |
equals() |
boolean |
比较字符串是否值相等 |
String a = “hi”; String b = ”hello”; boolean tar = a.equals(b); |
length() |
int |
返回字符串的长度 |
int len = a.length(); |
trim() |
String |
去除字符串两端的空格 |
String b = a.trim(); |
replace() |
String |
替换字符串中的指定字符(串) |
a.replace(s1,s2); s1-被替换的字符串 s2-替换字符串 |
concat() |
String |
拼接字符串 |
a.concat(“123”); |
toLowerCase() |
String |
字符串转小写 |
a.toLowerCase(); |
toUpperCase() |
String |
字符串转大写 |
a.toUpperCase(); |
split(",") |
String[] |
根据指定字符把字符串切分成数组,返回String[] |
String a = “a,b,c”; char[] arr = a.split(“,”); |
contains() |
boolean |
判断字符串中是否包含指定字符 |
a.contains(“a”); |
substring(1) |
String |
根据下标截取字符串,从指定下标到字符串末尾 |
substring(int startIndex); String b = a.substring(1); |
substring(1,2) |
String |
截取下标范围内的字符串,从开始下标到结束下标 |
substring(int s, int e); s-开始下标 e-结束下标 |
indexOf() |
int |
获取指定字符在字符串中的下标 |
int index = a.indexOf(“a”); |
charAt() |
char |
获取字符串中指定下标的字符 |
char c = a.charAt(1); |
isEmpty() |
boolean |
判断字符串是否为空 |
boolean tar = a.isEmpty(); |
toCharArray() |
char[] |
将字符串转成字符数组 |
char[] d = a.toCharArray(); |
String.valueOf() |
String |
将变量转为String类型 |
int a = 1; String b = null; b = String.valueOf(a); |
//14 String类的静态方法 format() 能用来创建可复用的格式化字符串
float f1 = 1.1f;
int i2 = 10;
String s3 = "hello";
String str = String.format("浮点型%f 整型%d 字符串%s", f1, i2, s3);
System.out.println(str); //打印:浮点型1.100000 整型10 字符串hello
3 字符串常量池
3.1 字符串常量池的设计思想
字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序的性能
3.2 常量池工作原理
JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化
为字符串开辟一个字符串常量池,类似于缓存区
创建字符串常量时,首先检查字符串常量池是否存在该字符串
存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中
3.3 实现的基础
实现该优化的基础是因为字符串是不可变的(final),可以不用担心数据冲突进行共享
运行时实例创建的全局字符串常量池中有一个表,总是为池中每个唯一的字符串对象维护一个引用,这就意味着它们一直引用着字符串常量池中的对象,所以,在常量池中的这些字符串不会被垃圾收集器回收
4 StringBuffer与StringBuilder
String和StringBuffer、StringBuilder他们都可以存储和操作字符串,即包含多个字符的字符串数据。
String类是字符串常量,是不可更改的常量。而StringBuffer/StringBuilder是字符串变量,它的对象是可以扩充和修改的。
StringBuffer和StringBuilder的默认大小为16。
1) StringBuffer和StringBuilder的默认容量长度为16。
* 2) 如果在声明StringBuffer对象的同时,通过构造方法进行value[]初始化(即给变量赋值),
* 则容量长度是是(value[]的长度+16)
* 3) 如果调用append方法拼接字符串,字符串的长度超出了容量长度,则对容量长度进行扩容,扩容大小为(原长度<<1+2)
*
特点:
- 修改String对象的值,内存地址会发生改变;
- StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的对象,即内存地址不变;
- StringBuffer是线程安全的,StringBuilder是线程不安全的;
- StringBuilder的执行速度比StringBuffer快;
常用方法:
方法 |
返回值 |
作用 |
示例 |
append() |
StringBuffer |
拼接字符串到末尾 |
StringBuffer a; a.append("abc"); |
reverse() |
StringBuffer |
将字符串中的字符顺序反转 |
|
delete() |
StringBuffer |
删除字符串中指定下标范围的字符 |
a.delete(1,3); |
insert() |
在字符串指定下标位置插入字符 |
a.insert(1, "efd"); |
|
replace() |
替换指定索引区间内的字符串 |
a.replace(1,3,"hul"); |
|
length() |
int |
字符串长度 |
a.length(); |
capacity() |
int |
容量长度 |
//初始化容量长度 x StringBuffer s1 = new StringBuffer(3); s1.append("abc"); int y = s1.length(); int z = s1.capacity(); // x<y<2*x+2 z = 2*x+2 // y<x z = x // y>2*x+2 z = y |
2 String, StringBuffer, StringBuilder的区别是什么?String为什么是不可变的?
1、String是字符串常量,StringBuffer和StringBuilder都是字符串变量。后两者的字符内容可变,而前者创建后内容不可变。
2、String不可变是因为String类被声明为一个final类。
3、StringBuffer是线程安全的,而StringBuilder是非线程安全的。
4、线程安全会带来额外的系统开销,所以StringBuilder的执行效率比StringBuffer高。
5 值传递与引用传递
5.1 值传递pass by value
- 基本数据数据类型遵循值传递;
- 将原来的值,复制一份,传给方法
5.2 引用传递pass by reference
- 引用类型遵循引用传递;
- 将原来的引用,复制一份,传给方法
注意:
String是一个final类,它的值不能被改变;
一旦给String对象,在方法中重新赋值,等于在内存中重新开辟一块内存,引用会发生改变;
String遵循引用传递,会将引用复制一份,传给方法,但是在方法中重新赋值,等于给复制的引用重新赋值一个新的内存地址;
所以将一个String变量传给方法,原来的值不会发生改变;
示例:
public class Test {
String name;
int age;
public void write() {
System.out.println("name:"+name+",age="+age);
}
public Test() {
name = "张三";
age = 20;
}
public static void update(int a, String b, Test c) {
a = 18;
b = "cba";
c.name = "李四";
}
public static void main(String[] args) {
int a = 1;
String b = "abc";
Test c = new Test();
update(a, b, c);
System.out.println(a);//1
System.out.println(b);//abc
c.write();//name:李四,age=20
}
}