2018.9.6 Java常考知识点总结

2021年11月21日 阅读数:3
这篇文章主要向大家介绍2018.9.6 Java常考知识点总结,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

一 Java中的值传递和引用传递(很是重要)

首先要明确的是:“对象传递(数组、类、接口)是引用传递,原始类型数据(整型、浮点型、字符型、布尔型)传递是值传递。”html

那么什么是值传递和应用传递呢?

值传递是指对象被值传递,意味着传递了对象的一个副本,即便副本被改变,也不会影响源对象。(由于值传递的时候,其实是将实参的值复制一份给形参。)java

引用传递是指对象被引用传递,意味着传递的并非实际的对象,而是对象的引用。所以,外部对引用对象的改变会反映到全部的对象上。(由于引用传递的时候,其实是将实参的地址值复制一份给形参。)c++

有时候面试官不是单纯问你“Java中是值传递仍是引用传递”是什么啊,骚年?而是给出一个例子,而后让你写出答案,这种也常见在笔试题目中!因此,很是重要了,请看下面的例子:面试

值传递和应用传递实例

1. 值传递

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);
}

结果:算法

a = 20
b = 10
num1 = 10
num2 = 20

解析:编程

在swap方法中,a、b的值进行交换,并不会影响到num一、num2。由于,a、b中的值,只是从num一、num2的复制过来的。 也就是说,a、b至关于num一、num2的副本,副本的内容不管怎么修改,都不会影响到原件自己。数组

2. 引用传递

public static void main(String[] args) {
    int[] arr = {1,2,3,4,5};

    change(arr);

    System.out.println(arr[0]);
}

public static void change(int[] array) {
//将数组的第一个元素变为0
    array[0] = 0;
}

结果:app

1
0

解析:函数

不管是主函数,仍是change方法,操做的都是同一个地址值对应的数组。 。所以,外部对引用对象的改变会反映到全部的对象上。测试

一些特殊的例子

1. StringBuffer类型传递

	// 测试引用传递:StringBuffer
	@org.junit.Test
	public void method1() {
		StringBuffer str = new StringBuffer("java");
		System.out.println(str);
		change1(str);
		System.out.println(str);
	}

	public static void change1(StringBuffer str) {
		str = new StringBuffer("abc");//
		//str.append("我喜欢");//
		//str.insert(3, "(编程)");
		
	}

结果:

java我喜欢

解析:

不少要这个时候要问了:StringBuffer建立的明明也是对象,那为何输出结果依然是原来的值呢?

由于在change1方法内部咱们是新建了一个StringBuffer对象,因此str指向了另一个地址,相应的操做也一样是指向另外的地址的。

那么,若是将change1方法改为以下图所示,想必你们应该知道输出什么了,若是你还不知道,那可能就是我讲的有问题了,我反思(开个玩笑,上面程序中已经给出答案):

	public static void change1(StringBuffer str) {

		str.append("aa");
		str.insert(3, "(bb)");
		
	}

2. String类型传递

	// 测试引用传递:Sring
	@org.junit.Test
	public void method2() {
		String str = new String("aaa");
		System.out.println(str);
		change2(str);
		System.out.println(str);
	}

	public static void change2(String str) {
		// str="abc"; //aaa
		str = new String("abc"); //aaa
	}

结果:

aaa
aaa

能够看到不管是执行str="abc;"仍是str = new String("abc");str的输出的值都不变。 按照咱们上面讲“StringBuffer类型传递”的时候说的,str="abc;"应该会让str的输出的值都不变。为何呢?由于String在建立以后是不可变的。

3. 一道相似的题目

下面的程序输出是什么?

public class Demo {
	public static void main(String[] args) {
		Person p = new Person("张三");

		change(p);

		System.out.println(p.name);
	}

	public static void change(Person p) {
		Person person = new Person("李四");
		p = person;
	}
}

class Person {
	String name;

	public Person(String name) {
		this.name = name;
	}
}

很明显仍然会输出张三。由于change方法中从新建立了一个Person对象。

那么,若是把 change方法改成下图所示,输出结果又是什么呢?

	public static void change(Person p) {
		p.name="李四";
	}

答案我就不说了,我以为你们若是认真看完上面的内容以后应该很很清楚了。

二 ==与equals(重要)

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

equals() : 它的做用也是判断两个对象是否相等。但它通常有两种使用状况:

  • 状况1:类没有覆盖equals()方法。则经过equals()比较该类的两个对象时,等价于经过“==”比较这两个对象。
  • 状况2:类覆盖了equals()方法。通常,咱们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。

举个例子:

public class test1 {
    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方法是被重写过的,由于object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。
  • 当建立String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要建立的值相同的对象,若是有就把它赋给当前引用。若是没有就在常量池中从新建立一个String对象。

三 hashCode与equals(重要)

面试官可能会问你:“你重写过 hashcode 和 equals 么,为何重写equals时必须重写hashCode方法?”

hashCode()介绍

hashCode() 的做用是获取哈希码,也称为散列码;它其实是返回一个int整数。这个哈希码的做用是肯定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。另外须要注意的是: Object 的 hashcode 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法直接返回对象的 内存地址。

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

为何要有hashCode

咱们以“HashSet如何检查重复”为例子来讲明为何要有hashCode:

当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其余已经加入的对象的hashcode值做比较,若是没有相符的hashcode,HashSet会假设对象没有重复出现。可是若是发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。若是二者相同,HashSet就不会让其加入操做成功。若是不一样的话,就会从新散列到其余位置。(摘自个人Java启蒙书《Head fist java》第二版)。这样咱们就大大减小了equals的次数,相应就大大提升了执行速度。

hashCode()与equals()的相关规定

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

为何两个对象有相同的hashcode值,它们也不必定是相等的?

在这里解释一位小伙伴的问题。如下内容摘自《Head Fisrt Java》。

由于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