一、创建字符串
常见的构造String方法
String str = "XXXX";
//方式一
String str2=new String("Xxxx");
//方式二
char[] array = {'a','b','c'};
String str3 =new String(array);
//方式三
String 为引用类型
复习:Java中两大类型
1. 8种内置类型:byte、short、int、long、float、double、char、boolean
2.引用类型(除了以上8种内置类型,其他均为引用类型)如:数组,String类
String str1 = "hello";
String str2 = str1;
System.out.println(str2);
//输出结果为 hello
而当改变str1时,并不会改变str2中的值
str1="word";
System.out.println(str2);
//输出结果为 hello
这种操作并不是在修改字符串,而是让str1这个引用指向了一个新的String对象。
二、字符串比较相等
String类对象中使用“==”不是在比较字符串内容而是在比较两个引用是否指向同一个对象
String str1 = "hello";
String str2 = "hello";
System.out.println(str1==str2);
//输出结果为 true
String str3 = new String("hello");
String str4 = new String("hello");
System.out.println(str3==str4);
//输出结果为 false
内存布局如下图所示
String类对象实质是在比较是否为同一个“储物柜”(是在比较身份,身份相同则其中放的东西肯定相同),比较“储物柜”中存放物品时需使用“equals”
String str3 = new String("hello");
String str4 = new String("hello");
System.out.println(str3==str4);
//输出结果为 false
System.out.println(str3.equals(str4));
//输出结果为true
equals使用注意事项:
String str1 =new String("hello");
// 方式一
System.out.println(str1.equals("hello"));
//方式二
System.out.println("hello".equals(str1));
在上述方式中,常选择方式二
若str1为null,代码就会抛出异常,而方式二不会。
三、字符串常量池
常量:和变量相比,“hello”这就是常量(字符串的字面值常量)
池:计算机中一个重要术语 内存池、线程池、进程池、数据库连接池、对象池
池的目的就是为了降低开销,提高效率,本质是把频繁使用的东西提前保存好,以备用到的时候,随时就能用。
Java会把一些字符串常量放到内置的“字符串常量池”中。
String str1 = "hello" ;
String str2 = "hello" ;
String str3 = "hello" ;
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // true
System.out.println(str2 == str3); // true
内存布局如下:
以上代码并没有开辟新的堆内存空间
原因在于String类的设计采用了共享设计模式
在JVM底层实际上会自动维护一个对象池(字符串常量池)
1.如果现在采用了直接赋值的模式进行String类的对象实例化操作,那么该实例化对象(字符串内容)将自动保存到这个对象池之中;
2.如果下次继续使用直接赋值的模式声明String类对象,此时对象池之中如果有指定内容,将直接进行引用;
3.如果没有,则开辟新的字符串对象而后将其保存在对象池之中以供下次使用。
类对象在实例化时常采用构造方法实例化
面试题:
String str1 = new String("hello");
String str2 = "hello";
System.out.println(str1==str2);
// 采用String构造方法直接实例化
//输出结果为false
String str3 = new String("hello").intern();
//直接使用String str3 = "hello";直接赋值也可
String str4 = "hello";
System.out.println(str3==str4);
//采用intern方法手动将hello放入字符串常量池中
//输出结果为true
两种方法的区别:
(1)构造方法:
缺点一:在new String执行完之后,发现并没有引用指向0x100(举个例子)这个对象,于是该对象就会被JVM垃圾回收站给回收掉;
缺点二:字符串共享问题,同一个字符串可能会被存储多次,比较浪费空间。
直接采用String构造方法就会开辟两块堆内存空间,不会自动保存在对象池中,可以使用intern()方法手动入池。(拿着当前这个字符串里的内容在字符串常量池中找,直接返回该池中的地址,若不存在,则把当前字符串的内容加到常量池中,返回池中地址)
(2)直接赋值
只会开辟一块堆内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用。
四、Java中为何要设计成字符串不可变
1.方便放入池中,如果是可变的对象,一旦池中的内容发生改变,就会影响到所有引用这个池对象的结果;
2.对象内容不可变,则对象的hashCode(简单粗暴的理解成对象的“签名”用来识别对象身份的方式)也不可变,方便和hash表这样的结构配合使用。
3.对象不可变,线程安全更有保证
String s = "hello"; 即将其变为“hella”此种操作,在Java中,只能通过构造一个新的对象来完成。
关于反射:
• 反射是面向对象编程的一种重要特性, 有些编程语言也称为 “自省”.
• 指的是程序运行过程中, 获取/修改某个对象的详细信息(类型信息, 属性信息等), 相当于让一个对象更好的 “认清自己” .
• Java 中使用反射比较麻烦一些. 我们后面的课程中会详细介绍反射的具体用法。
五、字符、字节、字符串
1. 字符与字符串
2. 字节与字符串
字节常用于数据传输以及编码转换的处理之中,String 也能方便的和 byte[] 相互转换。
3. 小结
何时使用byte[],何时使用char[]呢?
(1)byte[]是把String按照一个字节一个字节的方式处理,这种适合在网络传输,数据存储这样的场景下使用,更适合针对二进制数据来操作;
(2)char[]是把String按照一个字符一个字符的方式处理,更适合针对文本数据来操作,尤其是包含中文的时候。
六、字符串常见操作
1. 字符串比较
2.字符串查找
在使用indexOf()需要注意的是,如果内容重复,它只能返回查找的第一个位置
String str = "helloworld";
System.out.println(str.indexOf("l"));//2
System.out.println(str.indexOf("l",5));//8
System.out.println(str.lastIndexOf("l"));//8
3. 字符串替换
注意:由于字符串是不可变对象,替换不修改当前字符串,而是产生一个新的字符串
字符串拆分
注意:
拆分是特别常用的操作. 一定要重点掌握. 另外有些特殊字符作为分割符可能无法正确切分, 需要加上转义。
• 字符"|","*","+“都得加上转义字符,前面加上”".
• 而如果是"",那么就得写成"\".
• 如果一个字符串中有多个分隔符,可以用"|"作为连字符
5.字符串截取
注意:
• 索引从0开始
• 注意前闭后开区间的写法, substring(0, 5) 表示包含 0 号下标的字符, 不包含 5 号下标
6. 其他
七、StringBuffer和StringBulider
String是不可变对象,不能直接修改内容,如果我们需要使用可变版本的String,就需使用StringBuffer和StringBulider,两个类的用法基本一摸一样
StringBuffer类的一些方法:
注意:
String 和 StringBuffer 类不能直接转换,想互相转换可以采用如下原则:
• String 变为 StringBuffer:利用StringBuffer的构造方法或append()方法;
• StringBuffer 变为 String:调用toString()方法。
经典面试题:String、StringBuffer、StringBuilder的区别:
• String 的内容 不可修改 , StringBuffer 与 StringBuilder 的内容 可以修改 ;
• StringBuffer 与 StringBuilder 大部分功能是 相似的 ;
• StringBuffer采用同步处理,属于线程安全操作 ;而 StringBuilder未采用同步处理,属于线程不安全操作(核心区别) 。