API(Application Programming Interface,应用程序接口
String:
lang:就是language的简称,所以是language语言包,系统默认的
一、字符串类型,String在java.lang.String中使用的final修饰,不能被继承
不需要导包的都是在Java.lang包中的,lang中放的是最最最常用的一些类
- java.lang.String使用的是final修饰,不能被继承
- 字符串底层封装了字符数组char[ ],这个字符数组也是final修饰的
String str = "Hello"; //在内存中占用的是10个字节
char[] chs = {'H','e','l','l','o'};
String str = "你好Hello"; //在内存中占用的是14个字节
char[] chs ={ '你','好','H','e','l','l','o' }
- String字符串在内存中采用的是Unicode编码方式,任何一个字符对应两个字节,不管它是中文还是英文(String到底占用多少个字节,要看它的长度,包含多少个字符)
- 字符串一旦创建,对象的内容永远无法改变,但字符串的引用可以重新赋值----不变对象
重新赋值就是重新创建新的对象
原因:底层是数组,数组不能改变
String s1 = "123abc";
String s2 = s1 + "!"; //s1="123abc!"重新赋值,又指向了另外一个对象
String str = "hello"; //相当于String str = new String("hello");但是java不推荐这种赋值方式
二、常量池
字符串常量池是只针对字符串的,只有字符串才有常量池
- java对字符串有一个优化的措施:专门提供了一个字符串常量池(常量池也是在堆中)
- java推荐我们使用字面量/直接量的方式来创建字符串,并且会缓存所有以字面量形式创建的字符串对象,当使用相同字面量再次创建字符串时会重用对象以减少内存开销,避免内存张堆积大量内容相同的字符串对象
- 字符串创建的字面量对象会存储到常量池中,且这个对象中存储的是它的地址,指向了这个字面量
注意:
- 只有字面量的形式才能存到常量池中,常量池中存储123abc这个对象的引用(地址),指向了堆中的字面量对象.当使用相同字面量再次创建对象时将复用常量池中的引用.它们指向的是同一个对象以减少内存开销,避免内存张堆积大量内容相同的字符串对象
- String s4 = s3+“abc”;这种形式在常量池中不会缓存123abc对象,因为他不是字面量.所以再次new相同字面量创建字符串时,内存中又分配了一个123abc对象
可以看到两个相同的地址指向了同一个对象
String s1 = "123abc"; //堆中创建一个123abc对象,常量池中存储这个对象的引用
//编译器在编译时,若发现是两个字面量连接,
//则直接运算好并将结果保存起来,如下代码相当于String s2="123abc";
String s2 = "123"+"abc"; //复用常量池中的123abc对象的引用
System.out.println(s1==s2); //true
String s3 = "123";
//因为s3不是字面量,所以并不会直接运算结果
String s4 = s3+"abc"; //会在堆中创建新的123abc对象,而不会重用常量池中的对象的引用
System.out.println(s1==s4); //false
String str1= "hello"; //推荐模式
String str2= "hello";
``
会把hello存到常量池中,使用相同字面量再次创建字符串时不会再创建一个hello,而是重复使用
使用字面量创建字符串对象时
1JVM会**先检查**常量池中是否有该对象:
1)没有,则创建该字符串对象并且存入到常量池中(常量池中存的是对象所对应的地址)
2)有,则直接将该对象返回而不再创建一个新的字符串对象(返回的是字符串常量池中的)
引用类型==,比较的是地址是否相同
常见面试题;
String s = new String("hello");
问创建了几个对象?
答:2个
第一个:字面量"hello"
——————java会创建一个String对象标识字面量"hello",并将其存入常量池中
第二个:new String()
new String()时会创建一个对象,只是恰好值相同,而栈中的地址是指向new出来对象
——————new String()时会再创建一个字符串,并且引用hello字符串的内容
```java
String s1 = new String("hello"); //s1装的是new String()对象的地址
System.out.println("s1"+s1);
String s2 = "hello"; //s2装的是字面量"hello"的地址
System.out.println("s2"+s2);
System.out.println(s1==s2); //false,因为s1与s2的地址不同
字符串实际开发中比较相等的需求是比较字符串的内容
因此我们应该使用字符串提供的equals()方法来比较两个字符串的内容
System.out.println(s1.equals(s2)); 比较s1和s2的值是否相同。
字符串重新赋值的时候(每次拼接字符串),就会重新创建一个新的对象
在java中,这有字面量才会存储在字符串常量池中,和变量拼接的不算
编译器在编译时,若发现一个计算的表达式可以在编译期间确定结果,则会直接运算好并将结果保存到表达式中,相当于String s2 = "123abc";
```java
String s1 = "123abc";
//编译器在编译时,若发现有一个计算表达式可以在编译期间确定结果,
//则直接运算好并将结果保存到表达式中,相当于String s2 = "123abc";
String s2 = "123"+"abc";
System.out.println(s1==s2); //true s1和s2引用的常量池相同
String s3 = "123";
//当变量与字符串拼接产生的内容与常量池是某内容相同时,也不会重用常量池的对象
String s4 = s3+"abc";
System.out.println(s4==s1); //创建一个新的对象存储123abc,而不会重用常量池中的对象
JVM不认识变量只要有变量参与的,
字符串内存分配
重点:
- 这在内存中不是把s1给改了,而是在堆中又创建了一个123abc!的对象,String是不可更改的
- 常量池中没有123abc! ,因为他不是字面量创建的
- s1也为新对象的地址,指向了123abc!对象
- JVM不认识变量只要有变量参与的,
- 字面量连接直接运算好,相当String s2 = 123abc;字面量还是会复用123abc的对象
同理:
byte = 5+6; //不会报错,因为直接量会直接计算好,等同 byte = 11;
String经典面试题
字面量的方式,就是双引号的方式,字符串对象的引用存储在常量池中
例题1:
equals方法的底层代码是==,比较的是地址相同.但如果是String类型则比较的是内容,因为String在底层重写了equals方法,重写之后比较的是每个字符是否相同.,所以下面的代码返回的true,两个字符串的内容相同
public class StringDemo {
public static void main(String[] args) {
String str1 = "www.jpc.com";
String str2 = new String("www.jpc.com");
System.out.println(str1.equals(str2)); //结果为true
}
}
例题2:
String s = new String("hello");
问:如上语句创建了几个对象?
答:2个
第一个:字面量"hello"
---java会创建一个String对象表示字面量"hello",并将其引用地址存入常量池
第二个:new String()
---new String()时会再创建一个字符串对象,并引用hello字符串的内容
String s = new String("hello");
String s1 = "hello";
System.out.println("s:"+s); //hello
System.out.println("s1:"+s1); //hello
System.out.println(s==s1); //false,==比较的是地址是否相同
//字符串实际开发中比较相等一般都是比较字符串的内容
//因此我们需要使用equals()方法来比较两个字符串的内容
System.out.println(s.equals(s1)); //equals()比较的是内容是否相同
三、String的常用方法
- 数组的长度是length属性
- Sting的length是方法,方法的后面是紧跟小括号的
1. int length() 获取字符串长度(字符个数)
没有参数,返回值类型是int
String str = "我爱java!";
int len = str.length(); //获取str字符串长度
System.out.println(len);
2. String trim() 去除当前字符串两边的空白字符
没有参数,返回值类型是String
String str = " Hello World ";
System.out.println(str);
str = str.trim();
System.out.println(str);
3. int indexOf(String str) 检索给定字符串在当前字符串的开始位置--根据字符串找开始位置
一个参数的情况:检索的字符串
两个参数的情况:检索的字符串,开始的位置
返回值类型是int,当字符串中没有给定字符,返回值即为-1
String str = "thinking in java";
int index = str.indexOf("in"); //检索in在字符串str中出现的开始位置
System.out.println(index); //2
index = str.indexOf("IN"); //当前字符串不包含给定内容IN,所以返回-1
System.out.println(index);
//重载的indexOf可以从指定位置开始检索第一次出现指定字符的位置
index = str.indexOf("in",3); //从第4个字符开始找in第一次出现的位置
System.out.println(index);//5
index = str.lastIndexOf("in"); //找in最后一次出现的位置
System.out.println(index);
4. String substring() 截取当前字符串中指定范围内的字符串
一个参数的情况:开始的下标 从开始的下标到末尾
两个参数的情况:开始的下标,结束的下标 截取指定范围的字符串
返回值类型是String:要头不要为尾--包含start,但不包含end
String str = "www.tedu.com";
String name = str.substring(4,8); //截取从第4个到第7个——————下标
System.out.println(name); //tedu
name = str.substring(4); //从第4个一直截取到字符串末尾——————下标
System.out.println(name); //tedu.cn
5. char charAt(int index) 返回当前字符串指定位置上的字符--根据位置找字符
一个参数:指定的下标
返回值类型是char
String str = "thinking in java";
char c = str.charAt(9); //获取位置9所对应的字符
System.out.println(c); //1
6. boolean startsWith(String str) 判断当前字符串是否是以给定的字符串开始
boolean endWith(String str)判断当前字符串是否是以给定的字符串结束的
一个参数:给定的字符串
返回值类型是boolean
String str = "thinking in java";
boolean starts = str.startsWith("think"); //判断str是否是以think开头的
System.out.println("starts:"+starts); //true
boolean ends = str.endsWith(".png"); //判断str是否是以.png结尾的
System.out.println("ends:"+ends); //false
7. String toUpperCase()将当前字符串中的英文部分转换为全大写
String toLowerCase()将当前字符串中的英文部分转换为全小写
没有参数
返回值类型是String
String str = "我爱Java";
String upper = str.toUpperCase(); //将str字符串的英文部分转为全大写
System.out.println(upper);
String lower = str.toLowerCase(); //将str字符串的英文部门转为全小写
System.out.println(lower);
通过引用打点是实例方法,静态方法类名打点
强转只能同类型转化,即基本只能转基本类型,引用只能转引用类型
8. String.valueOf(a)将其它数据类型转换为String
一个参数:其他类型的数据
返回值类型是String
是静态方法,
任何内容与字符串连接结果都是字符串
int a = 123;
String s1 = String.valueOf(a); //将int型变量a转换为String类型并赋值给s1
System.out.println("s1"+s1); //123
double dou = 123.456;
String s2 = String.valueOf(dou); //将double型变量dou转换为String类型并赋值为s2
System.out.println("s2:"+s2); //123.456
String s3 = a + ""; //任何内容与字符串连接结果都是字符串,效率低
System.out.println(s3); //123
补充:
- ASCII:美国标准编码,是美国最早的字符集,也是计算机最底层的字符集,一个字节
- GBK:国标编码,中国自己的编码,总共6万多个,一个汉字两个字节
- Unicode:万国码,装全世界所有符号,一个汉字两个字节
- UTF:在Unicode基础之上做的二次编码,里面加入了一个长度信息来标记是按一个字符解析还是按两个字符算
一个汉字三个字节
结论:互联网上真正使用的并不是unicode,真正传输出的是UTF这种带长度信息的编码,拿到UTF数据后再把长度去掉,还原成unicode编码。
用的是Unicode,但是真正传输的是UTF