String是不可变字符串
String定义字符串变量指向字符串对象
String是不可变字符串的原因:String变量每次修改其实都是产生并指向新的字符串对象,原来的字符串对象是没有改变的
String类是否可以被继承?为什么?
不能,因为String类是被final修饰的类型,final类是不能被继承的,String类是不可变类
加final或将String类设置成不可变类的原因
第一个原因是线程安全:不可变对象始终是线程安全的,因为线程必须完全构建它们,然后才能将它们传递给其他人——并且在构建之后,它们不能再被更改。因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。如果字符串是可变的,那么会引起很严重的安全问题。譬如,数据库的用户名、密码都是以字符串的形式传入来获得数据库的连接,或者在socket编程中,主机名和端口都是以字符串的形式传入。
第二个原因是速度:final 类无法被继承,这使得 JIT 在处理字符串时可以进行各种优化——永远不需要检查被覆盖的方法。
String类创建对象的方式
1. 直接双引号: String name = "hello world";
2. 构造器:
传入字符串 String s1 = new String("hello world");
传入字符数组 char[] chars = {'a','b','c','d'}; String s2 = new String(chars);
字符串常量池
通过构造器new的String对象,每new一次都会产生一个新的对象,放在堆内存中
而以双引号方式创建的字符串对象,在字符串常量池中存储,而且相同内容只会在期中存储一份(可以节约内存)。用双引号方式创建新的字符串时,会先查询字符串常量池中是否已经存在相同内容的字符串,若存在,在将字符串变量指向字符串常量池中已经存在的那一份字符串,不会创建新的对象。
字符串常量池在哪呢
在JDK1.8中,使用元空间代替永久代来实现方法区,但是方法区并没有改变,变动的只是方法区中内容的物理存放位置。类型信息(元数据信息)等其他信息被移动到了元空间中;但是运行时常量池和字符串常量池被移动到了堆中。但是不论它们物理上如何存放,逻辑上还是属于方法区的。
JDK1.8中字符串常量池和运行时常量池逻辑上属于方法区,但是实际存放在堆内存中,因此既可以说两者存放在堆中,也可以说两则存在于方法区中,这就是造成误解的地方。
字符串双等号比较辨析
双等号比较是比较地址
不是双引号直接给出来的,运算出来的结果时放在堆内存的
Java编译优化机制:在程序编译时,将"a" + "b" + "c" 转为 "abc"
intern()
String的intern()方法,检查当前字符串对象(在堆中)是否在常量池中有相同的字符串,若没有则在池中搞一个相同的字符串,并返回池中对象地址
字符串equals()比较
双等号比较是比较地址,而往往我们需要比较引用类型的内容,双等号不能满足这一需求
boolean | equals(String s) | String重写了Object的方法 |
boolean | equalsIgnoreCase(String anotherString) | 忽略大小写 |
不过基本数据类型仍然可以用双等号进行比较
常用的String类API
int | length() | |
char | charAt(int index) | |
String | substring(int beginIndex, int endIndex) | 包前不包后 |
String | substring(int beginIndex) | 从当前索引截取到末尾 |
char[] | toCharArray() | 把字符串转换成字符数组 |
String | replace(CharSequence target, CharSequence replacement) | |
boolean | contains(CharSequence s) | |
boolean | startWiths(String prefix) | |
String[] | split(String s) |