前言
错误的观点
== 是比较内存地址,equals是比较值
正确的观点
==
比较基本数据类型的时候,比较的是值,引用数据类型比较的是地址(new的对象,==
比较永远是false)equals
属于Object类的方法,如果我们没有重写过equals
方法,那么它就是==
,但是字符串里面的equals
被重写过了,比较的是值
equals
-
equals
方法没有重写还是比较对象地址 -
重写
equals
方法后要看是如何重写的(Object(地址)、Integer(值)、String(先地址后值、地址不同值相同返回true)、自定义类(可以自己定义)
)equals比较的时候如果是引用类型(除了String类型),那么使用的是object中的equals方法,比较的是地址是否一样; 如果是String类型的对象,那么String类重写了Object中的equals方法,首先比较地址是否一样,一样的话返回true,不一样再比较是否是为同种类型,不是同类型就返回false,同类型之后再看内容是否一样;内容一样返回true,不一样返回false 如果是基本数据类型对应的包装部类,使用的equals也不是Object中的equals方法,而是重写了这个方法,首先比较的是否是同类型,不是同类型,直接返回false,是同类型再比较内容是否一致,一致返回true
==
-
基本数据类型比较的是值
byte:8位,最大存储数据量是255,存放的数据范围是-128~127之间 short:16位,最大数据存储量是65536,数据范围是-32768~32767之间 int:32位,最大数据存储容量是2的32次方减1,数据范围是负的2的31次方到正的2的31次方减1 long:64位,最大数据存储容量是2的64次方减1,数据范围为负的2的63次方到正的2的63次方减1 float:32位,数据范围在3.4e-45~1.4e38,直接赋值时必须在数字后加上f或F double:64位,数据范围在4.9e-324~1.8e308,赋值时可以加d或D也可以不加 boolean:只有true和false两个取值 char:16位,存储Unicode码,用单引号赋值
-
引用类型比较的是地址值
==比较的是地址,引用类型(除了String类型)的时候,只要不是同一个对象那么不是一个地址,String类型的时候如果new出来两个对象,那么也是不同地址,如果不new,那么就是同一个地址,因为都是存放在字符串常量池中,如果是基本数据类型,都是放在常量池中,所以地址都是一样的 Object类中的equals方法中就是用的==进行判断
思考
其实==就是比较内存地址是否相同,那么为什么基本数据类型就是比较的值呢?因为基本数据类型没有对象,不能new,所以基本数据类型定义的值都在栈内存中,栈有一个很重要的特殊性,就是存在栈中的数据可以共享
基本数据类型
int a = 3;
int b = 3;
编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。这时,如果再令a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量
String类型
//用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象
String str = new String("abc");
//先在栈中创建一个对String类的对象引用变量str,然后查找堆中有没有存放"abc",如果没有,则将"abc"存放进堆,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”
String str = "abc";
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一个对象的
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的对象,每一次生成一个
因此用第一种方式创建多个”abc”字符串,在内存中其实只存在一个对象而已. 这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String(“abc”);的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担
另一方面, 要注意: 我们在使用诸如String str = “abc”;的格式定义类时,总是想当然地认为,创建了String类的对象str。担心陷阱!对象可能并没有被创建!而可能只是指向一个先前已经创建的对象。只有通过new()方法才能保证每次都创建一个新的对象。由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率
扩展概念
常量池
指的是在编译期确定,并被保存在已编译的字节码文件中的一些数据,它包括类、方法、接口等中的常量,存放字符串常量和基本类型常量(public static final)。
栈(stack)
主要保存基本数据类型(或者叫内置类型)(char、byte、short、int、long、float、double、boolean)和对象的引用,数据可以共享,速度仅次于寄存器(register),快于堆
栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄
堆(heap)
用于存储对象
例子
package demo;
import jdk.nashorn.internal.ir.CallNode;
public class Test {
public static void main(String[] args) {
Student a1 = new Student("a1");
Student a2 = new Student("a1");
Teacher b1 = new Teacher("a1");
String s1 = new String("a1");
String s2 = new String("a1");
String s6 = new String("1");
String s3 = "ccc";
String s4 = "ccc";
String s5 = "a1";
int i1 = 1;
int i2 = 1;
System.out.println(s1.equals("a1"));//true;因为s1是String类型的有自己的equals方法,先判断地址是否一样,不一一样再看参数是否为String,是的话比较的就是内容是否一样
System.out.println(a1.equals("a1"));//false;因为a1是Student类型的使用Object类的equals方法,比较的是地址
System.out.println(a1.equals(a2));//false;因为a1是Student类型的使用Object类的equals方法,比较的是地址,这里new了两个对象,显然地址不一样。
System.out.println(a1.equals(b1));//false;因为a1是Student类型的使用Object类的equals方法,比较的是地址,这里new了两个对象,显然地址不一样。
System.out.println(a2.equals(b1));//false;因为a2是Student类型的使用Object类的equals方法,比较的是地址,这里new了两个对象,显然地址不一样。
System.out.println(s1.equals(a1));//false;因为s1是String类型的使用Object类的equals方法,首先判断a1是否是s1同一个对象,不是的话判断这个参数a1是否为String类型的,如果不是那么返回false,如果是,那么就比较他们之间的内容是否一样
System.out.println(s1.equals(s1));//ture;因为s1是String类型的使用Object类的equals方法,首先判断s1是否是s1同一个对象;这里是同一个对象,所以返回true;
System.out.println(s1.equals(b1));//false;因为s1是String类型的使用Object类的equals方法,首先判断b1是否是s1同一个对象;这里不是同一个对象,继续判断参数是否为String类型,这里不是,所以返回false
System.out.println(s1.equals(s2));//true;因为s1是String类型的使用Object类的equals方法,首先判断s2是否是s1同一个对象;这里不是同一个对象,继续判断参数是否为String类型,这里是String类型,继续判断内容是否一样,内容一样所以是true;
System.out.println(s1.equals(s5));//true;因为S1是String类型的使用Object类的equals方法,首先判断S5是否是跟s1为同一个对象,不是,继续判读参数是否为String类型,是,然后比较内容一样返回true。
System.out.println(a1 == a1);//true;因为==比较的是地址,都是a1这个对象,只是值不同
System.out.println(a1 == a2);//false;new两个对象,所以地址不同
System.out.println(s1 == s2);//false;new两个对象,所以地址不同
System.out.println(s3 == s4);//true;因为没有new对象,都是保存在字符串常量池中,所以地址都是一样的。
System.out.println(i1 == i2);//true;因为是基本数据类型,保存在常量池中,所以地址都是一样的。
}
}