== 和 equals 的区别

常见面试题分析

== 和 equals 的区别是什么?

命题:java中其实只有 == 比较,没有equals比较,其实底层还是 ==。只不过equals是很多类定义个方法而已,而这个方法把内存地址的比较改成基础数据类型的比较。

  • 就算String ,Integer,Long 等都回归到基础数据类型的比较。

1、==

java中的数据类型,可分为两类:

1.基本数据类型,也称原始数据类型

 byte,short,char,int,long,float,double,boolean   他们之间的比较,应用双等号(==),比较的是他们的值。 
  • 在基础数据类型,就是字面值的比较,和数据类型无关,只要相同就是true
  • 在封装数据类型中,它内存地址比较。在java中Short,Integer,Long都Byte范围的长度进来缓存,只要在-128-127都是相等的,其他的都是内存地址比较都是false

数据结构int 基本数据结构 不是类 只是比较字面值
1、基本数据类型:

  • 整形:byte 1 short 2 int 4 long 8

  • 浮点型:float 4 double 8

  • 布尔型:boolean 1

  • 字符型:char 2

  • 为什么java要定义这么多数据类型?

  • 内存分配

在开发中定义任何一个类,都可以当前数据类型使用。

1、为什么要出现引用数据类型,面向对象的数据类型,Integer类型

​ 答案:String -->Integer -->int

在基础数据类型,就是字面值的比较,和数据类型无关,只要相同就是true

在封装数据类型中,它内存地址比较。Java中Short,Integer,Long都是Byte范围的长度进来缓存,只要在-128~127都是相等的,其他的都是内存地址比较都是false

判断一个字符串是否为空,一定是先判断null,不是“”,否则会报空指针异常。

equals方式是来自Object方法,

  • 如果我们定义一个类,如果没有覆盖equals方法的话,全部都是内存地址比较。

    Parent parent1 = new parent(1,2);
    Parent parent2 = new parent(1,2);
    parent1.equals(parent2);==相当于  parent1==parent2 == false
    
  • 因为 String,Integer ,Long、Double等是它们都重写了Object的equals方法,把equals方法中的内存地址比较改成了基础数据类型的字面值比较。根据基础数据类型的比较规则,只要字面值相同就是true。

.引用类型(类、接口、数组)

public class testDay {
    public static void main(String[] args) {
        String s1 = new String("11");
        String s2 = new String("11");
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
    }
}
结果是:false  true

在这里插入图片描述

s1和s2都分别存储的是相应对象的地址。所以如果用 s1== s2时,比较的是两个对象的地址值(即比较引用是否相同),为false。而调用equals方向的时候比较的是相应地址里面的值,所以值为true。这里就需要详细描述下equals()了。

2、equals()方法详解
equals()方法是用来判断其他的对象是否和该对象相等。其再Object里面就有定义,所以任何一个对象都有equals()方法。区别在于是否重写了该方法。

先看下源码:

public boolean equals(Object obj) {
    return (this == obj);
}

很明显Object定义的是对两个对象的地址值进行的比较(即比较引用是否相同)。但是为什么String里面调用equals()却是比较的不是地址而是堆内存地址里面的值呢。这里就是个重点了,像String 、Math、Integer、Double等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法。看下String里面重写的equals():

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

重写了之后就是这是进行的内容比较,而已经不再是之前地址的比较。依次类推Math、Integer、Double等这些类都是重写了equals()方法的,从而都会回归到基础数据类型的字面值比较。

需要注意的是当equals()方法被override时,hashCode()也要被override。按照一般hashCode()方法的实现来说,相等的对象,它们的hashcode一定相等。为什么会这样,这里又要简单提一下hashcode了。

3. 两个对象的 hashCode() 相同, 那么 equals() 也一定为 true吗?

不对,两个对象的 hashCode() 相同,equals() 不一定 true。

代码示例:

String str1 = "keep";
String str2 = "brother";
System. out. println(String. format("str1:%d | str2:%d",  str1. hashCode(),str2. hashCode()));
System. out. println(str1. equals(str2));

执行的结果:

str1:1179395 | str2:1179395

false

代码解读:很显然“keep”和“brother”的 hashCode() 相同,然而 equals() 则为 false,因为在散列表中,hashCode() 相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等。

3、hashcode()浅谈

Hashcode覆盖的作用其实:未来这个对象要添加到java中数据结构比如:链表,map中,这个时候提供的数字。比如

明明是java中和equals和hashCode的区别问题,怎么一下子又扯到hashcode()上面去了。你肯定很郁闷,好了,我打个简单的例子你就知道为什么或者equals的时候会涉及到hashCode。

举例说明下:如果你想查找一个集合中是否包含某个对象,那么程序应该怎么写呢?不要用indexOf方法的话,就是从集合中去遍历然后比较是否想到。万一集合中有10000个元素呢,累屎了是吧。所以为了提高效率,哈希算法也就产生了。核心思想就是将集合分成若干个存储区域(可以看成一个个桶),每个对象可以计算出一个哈希码,可以根据哈希码分组,每组分别对应某个存储区域,这样一个对象根据它的哈希码就可以分到不同的存储区域(不同的区域)。

所以再比较元素的时候,实际上是先比较hashcode,如果相等了之后才去比较equal方法。

看下hashcode图解:

在这里插入图片描述

 一个对象一般有key和value,可以根据key来计算它的hashCode值,再根据其hashCode值存储在不同的存储区域中,如上图。不同区域能存储多个值是因为会涉及到hash冲突的问题。简单如果两个不同对象的hashCode相同,这种现象称为hash冲突。简单来说就是hashCode相同但是equals不同的值。对于比较10000个元素就不需要遍历整个集合了,只需要计算要查找对象的key的hashCode,然后找到该hashCode对应的存储区域查找就over了。

所以判断相等的流程如图所示:

大概可以知道,先通过hashcode来比较,如果hashcode相等,那么就用equals方法来比较两个对象是否相等。再重写了equals最好把hashCode也重写。其实这是一条规范,如果不这样做程序也可以执行,只不过会隐藏bug。一般一个类的对象如果会存储在HashTable,HashSet,HashMap等散列存储结构中,那么重写equals后最好也重写hashCode。

总结:

   1.hashCode是为了提高在散列结构存储中查找的效率,在线性表中没有作用。
   2.equals重写的时候hashCode也跟着重写
   3.两对象equals如果相等那么hashCode也一定相等,反之不一定。

5. Java 中的 Math. round(-1. 5) 等于多少?

Math.round(-1.5)的返回值是-1。四舍五入的原理是在参数上加0.5然后做向下取整。

我们可以通过大量实验看下结果

public class test {
	public static void main(String[] args){
		System.out.println(Math.round(1.3));   //1
		System.out.println(Math.round(1.4));   //1
		System.out.println(Math.round(1.5));   //2
		System.out.println(Math.round(1.6));   //2
		System.out.println(Math.round(1.7));   //2
		System.out.println(Math.round(-1.3));  //-1
		System.out.println(Math.round(-1.4));  //-1
       
		System.out.println(Math.round(-1.5));  //-1 --------这里要注意
        
		System.out.println(Math.round(-1.6));  //-2
		System.out.println(Math.round(-1.7));  //-2
	}
}

6. String 属于基础的数据类型吗?

String是final修饰的java类,java中的基本类型一共有8个,它们分别为:

  • 字符类型:byte,char
  • 基本整型:short,int,long
  • 浮点型:float,double
  • 布尔类型:boolean

此外需要说明 有的文章中吧void也算是一种基本的数据类型

7. Java 中操作字符串都有哪些类?它们之间有什么区别?

操作字符串的类有:String、StringBuffer、StringBuilder。

String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。

StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

8. String str="i"与 String str=new String(“i”)一样吗?

不一样,因为内存的分配方式不一样。String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。

代码示例:

String x = "叶痕秋";
String y = "叶痕秋";
String z = new String("叶痕秋");
System.out.println(x == y); // true
System.out.println(x == z); // false

String x = “叶痕秋” 的方式,Java 虚拟机会将其分配到常量池中,而常量池中没有重复的元素,比如当执行“叶痕秋”时,java虚拟机会先在常量池中检索是否已经有“叶痕秋”,如果有那么就将“叶痕秋”的地址赋给变量,如果没有就创建一个,然后在赋给变量;而 String z = new String(“叶痕秋”) 则会被分到堆内存中,即使内容一样还是会创建新的对象。

11. 抽象类必须要有抽象方法吗?

不需要,抽象类不一定非要有抽象方法。

示例代码:

abstract class Cat {
    public static void sayHi() {
        System. out. println("hi~");
    }
}

上面代码,抽象类并没有抽象方法但完全可以正常运行。

12. 普通类和抽象类有哪些区别?

  • 普通类不能包含抽象方法,抽象类可以包含抽象方法。
  • 抽象类不能直接实例化,普通类可以直接实例化。

13. 抽象类能使用 final 修饰吗?

不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类,如下图所示,编辑器也会提示错误信息:

14. 接口和抽象类有什么区别?

  • 实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。
  • 构造函数:抽象类可以有构造函数;接口不能有。
  • 实现数量:类可以实现很多个接口;但是只能继承一个抽象类。
  • 访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。

在这里插入图片描述

静态变量和实例变量的区别?

静态变量存储在方法区,属于类所有.实例变量存储在堆当中,其引用存在当前线程栈.需要注意的是从JDK1.8开始用于实现方法区的PermSpace被MetaSpace取代了.

Object中有哪些公共方法?

equals()`,`clone()`,`getClass()`,`notify(),notifyAll(),wait()`,`toString

java 创建对象的几种方式

java中提供了以下四种创建对象的方式:

  • new创建新对象
  • 通过反射机制
  • 采用clone机制
  • 通过序列化机制
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值