Java基础——字符串

1. 字符串不是基本数据类型,而是一个类

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;

2. 字符串的存储机制 

这里会用到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"); //在堆中又创建一个新对象

 对应的以上代码存储位置为

Java基础——字符串_第1张图片

 

  3. 字符串的"=="、equals 和 hashCode的区别

 (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。

4. 字符串的常用方法

(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();

5. 空串与null串 

(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)

你可能感兴趣的:(Java)