最最最常见的Java面试题总结(一)

发布 : 2019-02-20 分类 : JAVA 浏览 :

一、为什么JAVA中只有值传递?

按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。

Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。

例一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;

swap(num1, num2);
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
}

public static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.println("a = " + a);
System.out.println("b = " + b);
}

结果

1
2
3
4
a = 20
b = 10
num1 = 10
num2 = 20

在swap方法中,a、了的值进行交换,并不会影响到 num1、num2。因为 a、b中的值,只是从 num1、num2中复制过来的。也就是说 a、b相当于 num1、num2的副本,副本的内容无论怎么修改,都不会影响到原件本身。

通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样

例二
1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
int[] arr = {1, 3, 5, 7, 9};
System.out.println(arr[0]);
change(arr);
System.out.println(arr[0]);
}
public static void change(int[] array) {
// 将数组的第一个元素变为0
array[0] = 0;
}

结果

1
2
1
0

array被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr指向的是同一个数组对象。因此,外部对引用对象的改变会反映到所对应的对象上。

通过例二我们已经看到,实现一个改变对象参数状态的方法并不是一件难事,理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象

例三
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test {

public static void main(String[] args) {
Student s1 = new Student("张三");
Student s2 = new Student("李四");
Test.swap(s1, s2);
System.out.println("s1:" + s1.getName());
System.out.println("s2:" + s2.getName());
}

public static void swap(Student x, Student y) {
Student temp = x;
x = y;
y = temp;
System.out.println("x:" + x.getName());
System.out.println("y:" + y.getName());
}
}

结果

1
2
3
4
x:李四
y:张三
s1:张三
s2:李四

可以看出方法并没有改变存储在变量 s1 和 s2 中的对象引用。swap 方法的参数 x 和 y 被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝。

总结

Java程序设计语言对对象采用的不是引用调用,实际上,对象引用是按值传递的。
下面再总结一下Java中方法参数的使用情况:

  • 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)
  • 一个方法可以改变一个对象参数的状态。
  • 一个方法不能让对象参数引用一个新的对象。

二、== 与 equals(重要)

==: 它的作用是判断两个对象的地址是不是相等。即判断两个对象是不是同一个对象。(基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址)

equals():它的作用也是判断两个对象是否相等。但它一般有两种使用情况:

  • 情况1:类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过 == 比较两个对象。
  • 情况2:类覆盖了equals()方法。一般,我们都覆盖 equals() 方法来判断两个对象的内容是否相等。若它们的内容相等,则返回true(即,认为这两个对象相等)。
    例子
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class test {
    public static void main(String[] args) {
    String a = new String("ab"); // a 为一个引用
    String b = new String("ab"); // b为另一个引用,对象的内容一样
    String aa = "ab"; // 放在常量池中
    String bb = "ab"; // 从常量池中查找
    if (aa == bb) // true
    System.out.println("aa==bb");
    if (a == b) // false,非同一对象
    System.out.println("a==b");
    if (a.equals(b)) // true
    System.out.println("aEQb");
    if (42 == 42.0) { // true
    System.out.println("true");
    }
    }
    }

说明:

  • String中的 equals() 方法是被重写过的,因为 Objectequals() 方法是比较的对象的内存地址,而 Stringequals() 方法比较的是对象的值。
  • 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。

三、hashCode 与 equals(重要)

面试官可能经常会问你:你重写过hashCode和equals方法么,为什么重写equals时必须重写hashCode方法?

hashCode() 介绍

hashCode()的作用是获取哈希码,也称为散列码。它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode()定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。另外需要注意的是:Object 的 hashCode() 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法通常来将对象的内存地址转换为整数之后返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
   /**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* {@link java.util.HashMap}.
* <p>
* As much as is reasonably practical, the hashCode method defined by
* class {@code Object} does return distinct integers for distinct
* objects. (This is typically implemented by converting the internal
* address of the object into an integer, but this implementation
* technique is not required by the
* Java&trade; programming language.)
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.System#identityHashCode
*/
public native int hashCode();

散列表存储的是键值对(key-value),它的特点是:能根据 键 快速的检索出对应的值。这其中就利用到了散列码!(可以快速找到所需要的对象)

为什么要有hashCode

以 HashSet 如果检查重复为例子来说明为什么要有hashCode
当你把对象加入 HashSet 时, HashSet 会先计算对象的 hashCode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashCode 的值作比较,如果没有相符的 hashCodeHashSet会假设对象没有重复出现。但是如果发现有相同 hashCode 值的对象,这时会调用 equals() 方法来检查 hashCode 相等的对象是否真的相同。如果两者相同, HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 equals() 的次数,相应就大大提高了执行速度。

hashCode() 与 equals() 相关规定

1、如果两个对象相等,则 hashCode 一定也是相同的
2、两个对象相等,对两个对象分别调用 equals 方法都返回 true
3、两个对象有相同的 hashCode 值,它们也不一定是相等的
4、因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
5、hashCode() 的默认行为是对堆上的对象产生独特值,如果没有重写 hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

为什么两个对象有相同的 hashCode 值,它们也不一定是相等的?

因为 hashCode() 所使用的杂凑算法也许刚好会让多个对象传回相同的杂凑值。越糟糕的杂凑算法越容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的 hashCode )。

上面提到了 HashSet 如果 HashSet 在对比的时候,同样的 hashCode 有多个对象,它会使用 equals() 来判断是否真的相同。也就是说 hashCode 只是用来缩小查找成本。
参考:
https://blog.csdn.net/zhzhao999/article/details/53449504
https://www.cnblogs.com/skywang12345/p/3324958.html
https://www.cnblogs.com/skywang12345/p/3324958.html
https://www.cnblogs.com/Eason-S/p/5524837.html

本文作者 : KYRIECAO
原文链接 : https://caozongpeng.github.io/2019/02/20/最最最常见的Java面试题总结-一/
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

知识 & 情怀 | 二者兼得

微信扫一扫, 向我投食

微信扫一扫, 向我投食

支付宝扫一扫, 向我投食

支付宝扫一扫, 向我投食

留下足迹