String是一个字符串类型的类,使用“"”定义的内容都是字符串,咱们须要从类的角度和内存关系上分析这个类的做用。java
public class Hello{ public static void main(String args[]){ String str = "Hello World!!!"; System.out.println(str); } }
以上是字符串对象的直接赋值,该代码并无使用关键字new 进行,String类中其实也定义了一个构造方法web
public class Hello{ public static void main(String args[]){ String str = new String("Hello World!!!"); System.out.println(str); } }
int类型的数据可使用“==”进行大小的比较,字符串一样也能够.面试
public class Hello{ public static void main(String args[]){ String stra = "nihao"; String strb = new String("nihao"); String strc = strb; System.out.println(stra == strb); //false System.out.println(stra == strc); //false System.out.println(strc == strb); //true } }
以上三个String类对象的内容但是彻底同样的 ,获得的结果却不都是TRUE,下面从内存的角度分析:
设计模式
能够看出字符串若是用“==”进行比较的话,其实比较的是内存的地址,地址相同则相同,不一样则不一样。若是须要计较字符串的话须要用到String类中的比较方法:数组
public class Hello{ public static void main(String args[]){ String str1 = "nihao"; String str2 = new String("nihao"); String str3 = str2; System.out.println(str1.equals(str2)); //true System.out.println(str1.equals(str3)); //true System.out.println(str3.equals(str2)); //true } }
因此,之后在开发的过程当中,若是要进行字符串内容是否相等的比较,必定不要使用“==”,而是要使用stra.equals(strb)的这种方法。svg
**面试题:**请解释在字符串相等判断中“==”与equals()的区别?spa
实际上任何语言都没有字符串的概念,不少语言里面使用的是字符串数组,Java里面一样也没有字符串的概念。可是字符串是一个变成离不开的东西,Java本身创造了字符串的概念,可是此概念不是基本数据类型,他是将字符串做为了String类的匿名对象的形式存在的。设计
public class Hello{ public static void main(String[] args){ String str = "hello"; System.out.println("hello".equals(str)); //true } }
从上面的代码中能够看出,一个用引号括起来的字符串是能够直接调用String类中的方法的,从而证明了字符串常量其实就是一个String类的匿名对象,另外字符串字节复制的方法实际上就是至关于给一个字符串匿名对象给名字的过程。区别在于,String类的匿名对象不是用户建立的,而是系统自动生成的。3d
小小技巧:为了不空指向异常(使用了未实例化的对象,使用了未实例化的引用数据类型)的出现,能够将字符串写在前面调用方法。code
public class Hello{ public static void main(String[] args){ String str = null; System.out.println(str.equals("hello")); } } //输出结果 Exception in thread "main" java.lang.NullPointerException at Hello.main(Hello.java:4)
能够看出,若是用一个空的字符串去调用String类的方法的话会产生空指向异常,可是若是用下面的方法就不会出现错误。由于equals处理了空的状况
public class Hello{ public static void main(String[] args){ String str = null; System.out.println("hello".equals(str)); //false } }
也就是说,在实际开发的过程当中,若是须要对用户输入的数据跟一个字符串数据进行比较的话,请将字符串放在用户输入的内容前面。
直接赋值的实例化方法其实就是给一个匿名对上附一个名字。
// String 字符串名 = new String(字符串值) String str = "hello";
这样写会在内存中开辟一块栈内存,一块堆内存。
public class Hello{ public static void main(String[] args){ String stra = "hello"; String strb = "hello"; String strc = "hello"; String strd = "world"; System.out.println(stra.equals(strb)); //true System.out.println(stra.equals(strc)); //true System.out.println(strb.equals(strc)); //true System.out.println(stra.equals(strd)); //false } }
经过上面的代码咱们发现,给一个匿名对象赋值给多个不一样的名字,这些名字所指向的堆内存的地址是同样的。也就是说,采用直接赋值的String类对象的内存地址彻底相同。stra, strb, strc指向的内存地址是相同的
共享设计模式:
在JVM的底层实际上会有一个对象池,(不必定只保存String对象),当代码之中使用了直接赋值的方式定义了一个String类对象的时候,会将此字符串对象所使用的匿名对象入池保存,此后若是还有String类对象采用直接赋值的方式,而且设置了一样内容的时候,那么将不会开辟新的堆内存空间,而是使用已有的对象进行引用的分配,从而继续使用。
构造方法若是要使用,必定要用关键词new,一旦使用了关键词new,就意味着开辟一块堆内存。
String str = new String("hello");
这种方法赋值的内存分配以下:
语句是从右往左以此执行的,最右边是一个String的匿名对象,所以首先开辟一块堆内存,遇到关键字new 的时候又会开辟一个新的堆内存空间,里面存的也是hello,可是栈内存str保存的是new关键字开辟的堆内存,因此匿名对象开辟的堆内存就成为了垃圾。所以用构造方法实例化String对象的方式是很差的。
public class Hello{ public static void main(String[] args){ String str = new String("hello"); String stra = "hello"; System.out.println(str == stra); //false } }
从输出结果能够看出,由构造方法实例化的String对象是不会进入到对象池中,由于使用了关键词new。若是但愿new开辟的对象也入池的话,可使用手工的方法。public String intern();
public class Hello{ public static void main(String[] args){ String str = new String("hello").intern(); String stra = "hello"; System.out.println(str == stra); //true } }
public class Hello{ public static void main(String[] args){ String str = "hello "; str += "world"; str += "!!!"; System.out.println(str); } }
经过上面的操做,咱们改变了字符串str的值,内存分析如此下:
能够发现上面的代码其实不是改变了“hello”的堆内存,而是在一步步str 的堆内存指向,同时此种方法还产生了不少的垃圾。
String类的特色: