String类
- 先看一些源码提提神
public final class String //final修饰说明不可变性,不能被继承 实现了 可序列化比较 等规范
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[]; //底层还是字符数组
/** Cache the hash code for the string */
private int hash; // Default to 0
- String类:代表字符串.Java程序中所有的字符串字面值(如"abc")都作为此类的实例实现.
- String是一个final类,代表不可变的字符序列
- 字符串是常量,用双引号引起来表示,他的值在创建之后不能更改
- String类对象的字符内容是存储在一个字符数组value[]中的
/*
* 1.String实现了Serializable:表示字符串是支持序列化的
* 实现了Comparable接口:表示String可以比较大小
* 2.String内部定义了final char[] value用于存储字符串数据
* 3.String:代表不可变的字符序列.简称:不可变性
* 体现:1.当对字符串重新赋值时,需要重新指定内存区域值,不能使用原有的value进行赋值
* 2.当对现有字符连接操作时候,也需要重写指定内存区域值,不能使用原有的value进行赋值
* 3.当调用String的replace()方法修改指定字符或字符串时,也需要重写指定内存区域值,不能使用原有的value进行赋值
* 4.通过字面量的方式(区别于new) 给一个字符串赋值,此时的字符串值声明在字符串常量池中
* 5.字符串常量池是不会存储相同内容的字符串的
* */
@Test
public void test() {
String s1="abc"; //字面量的定义方式
String s2="abc";
String s3="abc";
System.out.println(s1 == s2); //true
s1="hello";
System.out.println(s1); //hello
System.out.println(s2); //abc
System.out.println("****");
s3+="abc";
System.out.println(s3);
System.out.println(s2);
String s4="abc";
String s5=s4.replace('a','m');
System.out.println(s5);
}
结果:显而易见
String对象的创建
方式一:通过字面量定义的方式
方式二:通过new的方式
@Test
public void test2(){
String s1="javaEE";
String s2="javaEE";
//通过new+构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值.
String s3=new String("javaEE");
String s4=new String("javaEE");
System.out.println(s1==s2); //true
System.out.println(s1==s3); //false
System.out.println(s3==s4); // false
}
- 结果:
true
false
false
- 练习
JVM内存结构
规范的时候把永久存储区(方法区)分到堆里面,实施的时候没把放进堆里面
- JDK1.6
-
JDK1.7
-
JDK1.8
这里宋老师四应该说错了 jdk1.8常量池应该在堆中,他的jvm虚拟中讲解的时候说的是对的(后期可以看看)
方法区具体实现已经不是永久代了,而是元空间
String的常用方法1
- 转化大小写 去空格 求长度 取索引位置元素
public static void main(String[] args) {
String s="HelloWord";
System.out.println(s.length()); //9
System.out.println(s.charAt(0)); //H
// System.out.println(s.charAt(10)); //StringIndexOutOfBoundsException
System.out.println(s.isEmpty());
String s1 = s.toLowerCase();
System.out.println(s); //s没变 HelloWord
System.out.println(s1); // helloword
String s2=" hel l o worl d ";
String trim = s2.trim(); //去除原来字符串的收尾空格,中间的还在
System.out.println("---"+s2+"---"); //s2没变
System.out.println("---"+trim+"---"); //去除首尾空格
}
结果:
false
HelloWord
helloword
--- hel l o worl d ---
---hel l o worl d---
- 比较字符串内容忽略大小写 连接concat()
public static void main(String[] args) {
String s="HelloWord";
String s1="helloword";
System.out.println(s.equals(s1)); //false
System.out.println(s.equalsIgnoreCase(s1)); //true 忽略大小写
//连接方法concat()就不写了,我们一般用+就行
}
public static void main(String[] args) {
String s="abc";
String s1 = new String("ad"); //比较时候和创建的方式没关系
System.out.println(s.compareTo(s1)); //-2 b的ASCII值减去c的ASCII值
}
结果:
-2
public static void main(String[] args) {
String s="北京尚硅谷宋老师教育";
String s1 = s.substring(2);
System.out.println(s);
System.out.println(s1);
String s2 = s.substring(2, 5); //左闭右开 一般都是左闭右开, 注意有的方法是从哪里开始取几个
System.out.println(s2);
}
结果:
北京尚硅谷宋老师教育
尚硅谷宋老师教育
尚硅谷
String的常用方法2
boolean | startsWith(String prefix) 测试此字符串是否以指定的前缀开头。 |
---|---|
boolean | startsWith(String prefix, int toffset) 测试在指定索引处开始的此字符串的子字符串是否以指定的前缀开头。 |
int | hashCode() 返回此字符串的哈希码。 |
---|---|
int | indexOf(int ch) 返回指定字符第一次出现的字符串内的索引。 |
String | replace(char oldChar, char newChar) 返回从替换所有出现的导致一个字符串 oldChar 在此字符串 newChar 。 |
---|---|
String | replace(CharSequence target, CharSequence replacement) 将与字面目标序列匹配的字符串的每个子字符串替换为指定的字面替换序列。 |
String | replaceAll(String regex, String replacement) 用给定的替换替换与给定的 regular expression匹配的此字符串的每个子字符串。 |
String | replaceFirst(String regex, String replacement) 用给定的替换替换与给定的 regular expression匹配的此字符串的第一个子字符串。 |
String[] | split(String regex) 将此字符串分割为给定的 regular expression的匹配。 |
String[] | split(String regex, int limit) 将这个字符串拆分为给定的 regular expression的匹配。 |
String类与其他结构之间转换
- 与基本数据类型之间的转换
String str1="132";
//String转基本数据类型
//int num=(int)str1 错误的
int i = Integer.parseInt(str1);
int j = new Integer(str1);
System.out.println(i);
System.out.println(j);
//基本数据类型转String
String s = String.valueOf(i);
String d = String.valueOf(i);
String s2 = i+"";
System.out.println(s);
System.out.println(d);
System.out.println(s2==s); //false
132
132
132
132
false
- String和char[]之间转换
@Test
public void test1(){
String str1="abc123";
//String----->char[]:调用toCharArray()
char[] chars = str1.toCharArray();
System.out.println(chars);
for (char aChar : chars) {
System.out.println(aChar);
}
//char[]---->String:调用String的构造器
char[] chars1={'a','b','1','2','d'};
String string = new String(chars1);
System.out.println(string);
}
abc123
a
b
c
1
2
3
ab12d
Process finished with exit code 0
- String与byte[]之间的转换
String --->byte[]:调用String的getBytes() 编码
byte[]---->String:解码
//编码:字符串--->字节 (能看的懂的-->看不懂的)
//解码:编码的逆过程,字节-->字符串
@Test
public void test1() throws UnsupportedEncodingException {
String str1="abc123中";
//String --->byte[]:调用String的getBytes() 编码
byte[] bytes = str1.getBytes(); //使用默认的字符集,进行转换
System.out.println(bytes);
String s3 = Arrays.toString(bytes);
System.out.println(s3+"dd"); //数组按字符串的样式打印出来[97, 98, 99, 49, 50, 51, -28, -72, -83]dd
System.out.println(Arrays.toString(bytes));
byte[] gbks = str1.getBytes("gbk"); //使用gbk字符集进行编码
String s = Arrays.toString(gbks); //转字符串
System.out.println(s);
String s1 = new String(bytes); //使用默认的字符集(我的utf8)进行解码
System.out.println(s1);
String s2 = new String(gbks); //使用默认的字符集进行解码
System.out.println(s2); //乱码 abc123��,原因:编码集和解码集不一样
String gbk = new String(gbks, "gbk");
System.out.println(gbk);
}
[B@4ee285c6
[97, 98, 99, 49, 50, 51, -28, -72, -83]dd
[97, 98, 99, 49, 50, 51, -28, -72, -83]
[97, 98, 99, 49, 50, 51, -42, -48]
abc123中
abc123��
abc123中
Process finished with exit code 0
说明:解码是,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码.
面试题:
-
String s=new String(“abc”);方式创建对象,在内存中创建了几个对象?
两个:一个是堆空间中new结构,另一个是char[]对应字符串常量池中的数据.
“abc” 如果字符串常量池中有abc那么就只会创建一个.
-
结果是什么?
@Test
public void test3(){
String s1="javaEE";
String s2="hadoop";
String s3="javaEEhadoop";
String s4="javaEE"+"hadoop";
String s5=s1+"hadoop";
String s6="javaEE"+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
String s8=s5.intern(); //此时返回得到的s8使用的常量值中已经存在的"javaEEhadoop"
System.out.println(s3 == s8); //true
System.out.println(s8);//javaEEhadoop
}
结论:
- 常量与常量拼接结果在字符串常量池,且字符串常量池中不会存在相同内容的常量
- 只要其中有一个是变量,结果就在堆中
- 如果拼接的结果调用intern()方法,返回值就在常量池中
- intern()返回的是常量池中地址内容
- 结果?
public class StringTest {
String str=new String("good");
char[] ch={'t','e','s','t'};
public void change(String str,char ch[]){
str="test ok";
ch[0]='b';
System.out.println("形参中str"+str);
};
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
}
}
结果:
形参中strtest ok
good
best
考察的是值传递
基本类型传的是你存的数据,引用类型传的是地址值
引用类型具体变了没要具体看,String是个不可变的,数组不是不可变的,可以被修改.
- 和变量+在一起不一定会是在堆中重新创建个对象
@Test
public void test4(){
String s1="javaEEhadoop";
String s2="javaEE";
String s3=s2+"hadoop";
System.out.println(s1==s3); //false
// final String s4=new String("javaEE"); //是这个时候下面结果是false
final String s4="javaEE";
String s5=s4+"hadoop";
System.out.println(s1==s5); //true
}