Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义类,叫做String。每个用双引号括起来的字符串都是String类的一个实例。
Java里面预定义是指,已经定义好的类,例如String,Integer等等;这些预定义的类都在java.lang包里面,jvm默认会加载这个包里面的所有类到JVM。
对于其他预定义类,例如java.util包里面的,要是的就必须引入才可以。例如,你要使用ArrayList,你就必须在程序前面import java.util.ArrayList; 对于String这些在java.lang包里面的就不用引入。例如下面就是多余的:import java.lang.String;
这里会用到Java虚拟机的知识,关于此部分不做赘述。
字符串的初始化有两种方式: String s1 = "abc"; String s3 = new String("abc");
(1)以字面量形式创建字符串对象
(字面量:文本字符串、声明为final的常量值等)
String s1 = "abc"; 对象引用存放在Java虚拟机栈的局部变量表中,而字符串对象”abc“存储在方法区中的运行时常量池中。当代码中出现以字面量形式创建字符串对象时,JVM首先会对这个字面量进行检查,如果字符串常量池中存在相同内容的字符串对象,判断依据String.equals(Object obj),也是就判断的内容是否相同,如果存在则将这个字符串的引用返。如果不存在则创建该字符串对象,然后把它加入到字符串常量池中,并返回它的引用。
String s1 = "abc"; // 1
String s2= "abc"; // 2
当执行第1条语句时,JVM检测“abc”这个字面量,这里我们认为没有内容为“abc”的对象存在。JVM通过字符串常量池查找不到内容为“abc”的字符串对象存在,那么就创建这个字符串对象,然后将刚创建的对象放入到字符串常量池中,并且将引用返回给变量s1。当执行第2条语句时,JVM还是要检测这个字面量,JVM通过查找字符串常量池,发现内容为“abc”字符串对象存在,于是将已经存在的字符串对象的引用返回给变量s2,这里不会重新创建新的字符串对象。
(2)new一个字符串对象
String s3 = new String("abc"); 相当于"abc" 和 new String()两个操作。先在虚拟机栈中创建一个对String类的对象引用变量s3,然后new()操作会在Java堆中产生一个新的对象"abc",并将s3指向堆中的"abc",同时检查字符串常量池中是否有对象"abc",如果没有则产生一个字符串常量"abc",并将它添加到字符串常量池中;如果有则不产生。
new String("abc")创建了几个对象?
答案:一个或两个。如果常量池中原来有"abc",那么只需要在Java堆中创建新的对象;如果常量池中没有,那么除了在Java堆中创建对象,还需在字符串常量池中创建”abc“.
String s1 = "abc"; //在字符串常量池中创建一个“abc”字符串对象
String s2 = "abc"; //“abc”已经在常量池中存在,只需要将引用s2指向“abc”
String s3 = new String("abc"); //在堆中创建新对象
String s4 = new String("abc"); //在堆中又创建一个新对象
对应的以上代码存储位置为
(1)”==“运算符用来比较两个变量的值是否相等。
如果两个变量是基本数据类型,可以直接使用”==“来比较其对应的值是否相等。
如果变量是引用类型,使用”==“只能判断两个变量是否指向同一块存储空间。
想要比较这两个对象的内容是否相等,就需要使用equals方法。
(2)equals方法
equals方法是Object类提供的方法之一。Object类中定义的equals(Object)方法是直接使用”==“运算符比较的两个对象,所以在没有覆盖equals(Object)方法的情况下,equals(Object)与”==“一样,比较的是引用。
在字符串中,覆盖了Object的equals方法,比较是两个对象的内容是否相等。
我们来看下面这一段代码:
public class StringTest{
public static void main(String[] args) {
String s1 = "abc"; //在字符串常量池中创建一个“abc”字符串对象
String s2 = "abc"; //“abc”已经在常量池中存在,只需要将引用s2指向“abc”
String s3 = new String("abc"); //在堆中创建新对象
String s4 = new String("abc"); //在堆中又创建一个新对象
System.out.println(s1==s2); //true
System.out.println(s1==s3); //false
System.out.println(s3==s4); //false
System.out.println(s1.equals(s2)); //true
System.out.println(s1.equals(s3)); //true
System.out.println(s3.equals(s4)); //true
}
}
运行结果
true
false
false
true
true
true
s1和s2都指向字符串常量池中abc,因此这两个变量所对应内存中的数值(即地址)相等,“s1==s2”返回true;
而s3指向堆中的new String(“abc”),因此返回false; 而s4指向的是堆中另一个new String(“abc”),“s1==s3”和"s3==s4"都返回fasle
equals比较的是字符串内容是否相同,所有后面三个都返回true.
再添几行代码:
String s5 = s1;
String s6 = s1 + "";
String s7 = "ab"+"c";
System.out.println(s1==s5); //true
System.out.println(s1==s6); //false
System.out.println(s1==s7); //true
运行结果:
true
false
true
语句“String s5 = s1;”直接将s1的引用赋给s5; 所以“s1==s5”返回true;
而语句“String s6 = s1 +"" ”;采用拼接的方式将变量s1和空字符串拼接起来,虽然没有看到new关键词,但是由于String类“+”操作符被重载之后,重载的方法之中一定会重新new一个String,所以s6存储在堆中。 “s1==s6”返回false;
语句“String s7 = "ab" +"c" ”;采用拼接的方式将常量“ab”和常量“c”拼接起来,在编译器就转换为“abc”,由于常量池中已经存在“abc”,所以直接将引用赋给s7。 “s1==s7”返回true;
此外,字符串还提供了intern()方法,String的intern()方法是一个本地方法,定义为public native String intern(); intern()方法的价值在于让开发者能将注意力集中到String池上。当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并且返回此 String 对象的引用。
再添加代码
System.out.println(s3.intern() == s1); //true
System.out.println(s3.intern() == s4.intern()); //true
运行结果:
true
true
s3.intern( ) 和 s4.intern( ) 都指向字符串常量池中的“abc”,所以都返回true.
(3) hashCode()方法
hashCode()方法也是从Object类中继承过来的,Object类中的hashCode()方法返回对象在内存中地址转换成的一个int值,所以如果没有重写hashCode()方法,任何对象的hashCode()方法都是不相等的。
一般来讲,equals()方法是给用户调用的,如果需要判断两个对象是否相等,可以重写equals()方法,然后在代码中调用。对于hashCode()方法用户一般不去调用它,例如在hashmap中,由于key是不可以重复的,它在判断key是否重复时就判断了hashCode()方法,而且也用到了equals()方法。此处“不可重复”指的是equals()和hashCode()只要有一个不等就可以了。
一般覆盖equals()方法的同时也要覆盖hashCode()方法,否则,会违反Object.hashCode的通用约定。
x.equals(y)返回true ——> x.hashCode() 等于 y.hashCode();
x.equals(y)返回false ——> x.hashCode() 有可能等于 y.hashCode(); 也有可能不等。
x.hashCode() 不等于 y.hashCode(); ——> x.equals(y)一定返回false;
x.hashCode() 等于 y.hashCode(); ——> x.equals(y)可能返回true,也可能返回false。
(1) 创建并初始化字符串:
1). 使用字符串常量直接初始化 String s="hello!";
2). 使用构造方法创建并初始化
new String();//初始化一个对象,表示空字符序列
new String(value);//利用已存在的字符串常量创建一个新的对象
new String (char[] value);//利用一个字符数组创建一个字符串
new String(char[] value,int offset,int count);//截取字符数组offset到count的字符创建一个非空串
new String(StringBuffer buffer);//利用StringBuffer对象初始化String对象
(2) 获取字符串信息:
下标:int indexOf(String str); int lastIndexOf(String str);
字符:char charAt(int index)
字节数组:byte[ ] getBytes()
长度:int length()
(3)判断字符串
相等:bool equals(String str)
前缀:startsWith(前缀)
后缀:endsWith(后缀)
大小:int compareTo()
(4)替换字符串
去掉前后空格:String trim()
替换:String replace(CharSequence oldString, CharSequence newString)
(5)截取字符串
单点截取:subString(int beginIndex)
双点截取:subString(int beginIndex,int endIndex)
(6)转换
转换为字符数组:char[ ] toCharArray()
分割为字符串数组:String[] split(分隔符)
基本数据型态转换为字符串: String.valueOf();
(1)空串是一个Java对象,是长度为0的字符串,有自己的串长度(0)和内容(空)。
判断一个字符串是否为空:
if(str.length() == 0) 或 if(str.equals(“”))
(2)String s = null; 表示目前没有任何对象与变量s关联。
判断一个字符串是否为空:
if(str == null)
检查一个字符串既不是null也不是空串: if(str != null && str.length() != 0)