String
在学习Java前期时候,会学到Java八大基本数据类型和String。在这之后,总会下意识的将String放入八大基本数据类型的行列中。当然,这是不对的,但是也是不可避免的。
为什么这样说呢?因为教材中八大基本数据类型和String是放在一起的,不免有一种联想记忆,而初学Java,对这种概念的理解非常低,但是在越学越深的时候,对Java有一定的了解后,就应该对前边似是而非的概念进行一定的巩固与复习和了解。[这是一种非常好的习惯。]
变量的声明
Java是一种强类型的编程语言,对变量的数据类型有严格的限定。在定义变量时必须声明变量的类型,并且赋值时必须赋予与变量类型一致的值。
显然是很枯燥的定义,但是换一种方式去理解,就会很简单:
变量类型 变量名 = 赋予值 ; 变量的声明规则
int i = 2;
我要一个int类型
我声明int类型
赋予变量名以及变量的值
川菜馆 菜单1.1= 麻婆豆腐 ;
我要一道川菜
我去川菜馆
点餐麻婆豆腐 [我可以直接说要麻婆豆腐,或者把麻婆豆腐在菜单上的位置给服务员 ]
key:你想吃哪种类型的菜,就要去相应的菜馆,并且点的菜是该类型的菜。所以说,你想要哪种类型,这个很重要,也就是声明变量类型很重要;不能说我去川菜馆点豫菜,这就是赋予只类型和声明类型一致。
变量的数据类型
既然变量的声明类型很重要,那么变量的声明(数据)类型有哪些嘞?
数据类型分为:基本数据类型和引用数据类型
基本数据类型分为:八大基本数据类型--byte 、 short 、 int 、 long 、 float 、 double 、 char 、 boolean
引用数据类型分为:类、接口、数组、枚举、注解
看到这里,String既然不是基本数据类型,那就是引用数据类型了,而且只能是类。
恍然大悟,String是一个类,而且是一个不可变的类。最最最重要的是,String本质是一个字符数组。
public final class String{ private final char value[]; public String(){this.value="".value;} public String(char value[]){this.value = Arrays.copyOf(value, value.length);} public String(String original){this.value=original.value} }
看到源码的构造方法,就知道怎么去new一个String类型的变量。
//1.无参构造方法 String str1=new String(); str1="str1"; //2.char[]数组构造方法 String str2=new String(new char[]{'s','t','r','2'}); //3.传入参数为String类型的构造方法,和方法一基本一样 String str3=new String("str3"); //4.不new,直接赋值 String str4="str4";
当然,最最最常用的就是第四种。为什么,简单呗,效果一样,为什么要选择复杂的呢?
可能有人问了,第四种确实是最常用的,但是String是一个类啊,你不new怎么出来的这个对象呢?
对啊!一个类不new怎么出来对象?Java虚拟机的运行机制是关键,下边会简述一下
[注:在JDK1.7及以后,字符串常量池是存在于堆中的一个独立特殊空间]
new一个String
编译程序会先去JVM字符串常量池中进行查找,是否存在该字面值
如果存在,则在堆中new一个String类型的实例对象,栈中的变量名指向堆中的原实例对象
如果不存在,则在JVM字符串常量池中新建一个该字面值的常量对象,然后在堆中new一个String类型的实例对象,栈中变量名指向堆中原实例对象
所以new一个String对象的话,就可能创建了两个对象。1是常量池中的对象,2是堆中的原实例对象
直接赋值
编译程序会先去JVM字符串常量池中进行查找,是否存在该字面值
如果存在,在栈中创建变量名指向常量池中的对象
如果不存在,先在字符串常量池中新建该字面值的常量对象,在栈中创建变量名指向常量池中的对象
直接赋值的话,就可能只创建了一个对象。就是常量池中的对象
两者的区别:
对机器来说,new需要在堆中创建实例对象,肯定是不如直接赋值快,当然,这个速度差是可以忽略的
对程序员来说,便捷以及需要一个新的对象[每new一次就会在堆中产生一个新的实例对象,如果字面值相同的话,指向的还是常量池中的同一个常量对象]。
日常的使用中,自然是推荐这两种方法来创建String对象,其他的了解就好。
equals和==
equals: 比较字面值
== : 如果是基本类型,比较的是字面值;如果是引用类型,比较的是对象的内存地址
String str1="str"; String str2="str"; System.out.println(str1==str2);//true System.out.println(str1.equals(str2);//true String str3=new String("str"); String str4=new String("str"); System.out.println(str3==str4);//true System.out.println(str3.equals(str4);//false
str3和str4的值都是一样的("str"),为什么equals为false呢?
答案很简单,new,结合上文的JVM机制,每new一次,就会在堆中创建一个新的实例对象,然后指向字符串常量池中的常量对象,那么栈再指向堆中的实例对象。
public boolean equals(Object anObject) {//传入的参数为Object if (this == anObject) {//用==比较字面值是否相同,也就是常量池中的对象是否相同,相同就为true return true; } if (anObject instanceof String) {//参数是否为String类型 String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) {//比较两个String变量的长度 char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) {//开始遍历比较每个字符是否相同,String的本质就是字符数组 if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
这一看源码,就明白equals其实可以理解为==的重写。