Java面试总结

Posted 路上阡陌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java面试总结相关的知识,希望对你有一定的参考价值。

说说你对面向对象三大特征的理解

封装:使用抽象数据类型将数据和基于数据的操作封装在一起,数据被保护在抽象数据类型内部,只保留一些对外接口和外界联系,也就是说用户不用关心对象的内部细节,只需要通过对象的对外接口访问对象。

继承:在已经定义的类的基础上建立新的类,子类可以新增自己的数据或者功能,但不能选择性继承父类的数据或功能。通过继承可以实现代码复用,提高靠开发效率。

需要注意的地方:

  1. 子类拥有父类的所有属性和方法,包括私有属性和方法,但是私有属性和方法不能被访问,只是拥有;
  2. 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展;
  3. 子类可以重写父类的方法(重写)。

多态:一个引用变量到底指向哪个类的实例对象,这个引用变量调用的方法到底是哪个类实现的方法,只有在程序运行期间才能知道。

实现多态的三个必要条件:

  1. 继承:再多态中必须存在有继承关系的子类和父类;
  2. 重写:子类需要重写父类的方法,这样在调用这些方法时就会调用子类重写的方法;
  3. 向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才既能调用父类方法,有能调用子类方法。

重载(Overload)和重写(Override)的区别是什么?

重载:在一个类或者子类父类之间,多个同名方法根据不同的传参来执行不同的逻辑处理。

重写:重写就是子类按照自己的需求对继承自父类的方法进行重新实现。

重载的核心就是:方法名相同,参数列表不同(参数个数、参数顺序和参数类型)。

重写的核心就是:子类对父类的方法进行重新实习,外部样子不可以改变,内部逻辑可以改变。

深拷贝和浅拷贝区别了解吗?什么是引用拷贝?

  1. 浅拷贝: 浅拷贝会在堆上创建一个新的对象(区别于引用拷贝),但如果对象内部有有引用类型的属性,会直接复制内部对象的引用地址,即原对象和拷贝对象共用一个内部对象;
  2. 深拷贝: 深拷贝会完全复制整个对象,包括这个对象的内部对象。

浅拷贝

浅拷贝的示例代码如下,我们这里实现了 Cloneable 接口,并重写了 clone() 方法。

clone() 方法的实现很简单,直接调用的是父类 Object 的 clone() 方法。

public class School implements Cloneable

    private String schoolName;
    
    // 省略构造函数、Getter&Setter方法
    
    @Override
    protected Object clone() throws CloneNotSupportedException 

        try 
            return (School)super.clone();
         catch (CloneNotSupportedException e) 
            throw new AssertionError();
        
    


public class Student implements Cloneable

    private School school;

	// 省略构造函数、Getter&Setter方法
	
    @Override
    protected Object clone() throws CloneNotSupportedException 
        try 
            Student student = (Student) super.clone();
            return student;
         catch (CloneNotSupportedException e) 
            throw new AssertionError();
        
    


测试 :

 Student student1 = new Student(new School("第一中学"));

 Student student2 = (Student) student1.clone();

 //true
 System.out.println(student1.getSchool() == student2.getSchool());

从输出结果就可以看出, student1 的克隆对象和 student1 使用的仍然是同一个 School 对象。

深拷贝

这里我们简单对 Student类的 clone() 方法进行修改,连带着要把 Student 对象内部的 School 对象一起复制。

@Override
    protected Object clone() throws CloneNotSupportedException 
        try 
            Student student = (Student) super.clone();
            student.setSchool((School) student.getSchool().clone());
            return student;
         catch (CloneNotSupportedException e) 
            throw new AssertionError();
        
    

测试 :

 Student student1 = new Student(new School("第一中学"));

 Student student2 = (Student) student1.clone();

 //false
 System.out.println(student1.getSchool() == student2.getSchool());

从输出结果就可以看出,student1 的克隆对象和 student1 包含的 School 对象已经是不同的了。

那什么是引用拷贝呢?

简单来说,引用拷贝就是两个不同的引用指向同一个对象。也就是说引用拷贝不会再堆上创建新的对象,而是直接复制原对象的对象引用。

什么是不可变对象?好处是什么?

不可变对象指对象一旦被创建,状态就不能再改变,任何修改都会创建一个新的对象,如 String、Integer及其它包装类。不可变对象最大的好处是线程安全。

能否创建一个包含可变对象的不可变对象?

当然可以,比如final Person[] persons = new Persion[]。persons是不可变对象的引用,但其数组中的Person实例却是可变的。这种情况下需要特别谨慎,不要共享可变对象的引用。这种情况下,如果数据需要变化时,就返回原对象的一个拷贝。

值传递和引用传递的区别的什么?为什么说Java中只有值传递?

值传递(pass by value)是指在调用方法时将实参复制一份传递到方法中,这样当方法对形参进行修改时不会影响到实参。

引用传递(pass by reference)是指在调用方法时将实参的地址直接传递到方法中,那么在方法中对形参所进行的修改,将影响到实参。

值传递和引用传递的关键区别有两点:

  1. 调用方法时有没有对实参进行复制。

  2. 方法内对形参的修改会不会影响到实参。

基本类型作为参数被传递时肯定是值传递;引用类型作为参数被传递时也是值传递,只不过“值”为对应的引用。Java 的确是值传递的。只不过,引用类型在调用有参方法的时候,传递的是对象的引用,并不是对象本身。而对象的引用在传递的过程中并没有发生改变,虽然对象本身发生了变化。

== 和 equals 区别是什么?

==常用于相同的基本数据类型之间的比较,也可用于相同类型的对象之间的比较;

如果 == 比较的是基本数据类型,那么比较的是两个基本数据类型的值是否相等;
如果 == 比较的是引用类型数据,那么比较的是两个引用是否指向了同一块内存区域;

equals方法主要用于两个对象之间,检测一个对象是否等于另一个对象;

看一看Object类中equals方法的源码:

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

它的作用也是判断两个对象是否相等,般有两种使用情况:

  • 情况1,类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。

  • 情况2,类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。

Java语言规范要求equals方法具有以下特性:

  • 自反性。对于任意不为null的引用值x,x.equals(x)一定是true。
  • 对称性)。对于任意不为null的引用值x和y,当且仅当x.equals(y)是true时,y.equals(x)也是true。
  • 传递性。对于任意不为null的引用值x、y和z,如果x.equals(y)是true,同时y.equals(z)是true,那么x.equals(z)一定是true。
  • 一致性。对于任意不为null的引用值x和y,如果用于equals比较的对象信息没有被修改的话,多次调用时x.equals(y)要么一致地返回true要么一致地返回false。
  • 对于任意不为null的引用值x,x.equals(null)返回false。

介绍下hashCode()?

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode()函数。

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

为什么要有 hashCode?

以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode:

当把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。

但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。

equals方法和hashcode的关系?

  1. 如果两个对象equals相等,那么这两个对象的HashCode一定也相同;

  2. 如果两个对象的HashCode相同,不代表两个对象就相同,只能说明这两个对象在散列存储结构中,存放于同一个位置。hashCode()只表示对象的哈希码,哈希码相同的对象不一定相等,反之,没有重写equals方法的前提下,两个对象相等,则hashCode一定相同。

  3. 两个对象相等,对两个对象分别调用equals方法都返回true;

为什么重写 equals 方法必须重写 hashcode 方法 ?

判断的时候先根据hashcode进行的判断,相同的情况下再根据equals()方法进行判断。如果只重写了equals方法,而不重写hashcode的方法,会造成hashcode的值不同,而equals()方法判断出来的结果为true。

在Java中的一些容器中,不允许有两个完全相同的对象,插入的时候,如果判断相同则会进行覆盖。这时候如果只重写了equals()的方法,而不重写hashcode的方法,Object中hashcode是根据对象的存储地址转换而形成的一个哈希值。这时候就有可能因为没有重写hashcode方法,造成相同的对象散列到不同的位置而造成对象的不能覆盖的问题。

以上是关于Java面试总结的主要内容,如果未能解决你的问题,请参考以下文章

JavaEE面试总结

阿里JAVA开发面试常问问题总结4

Java面试题总结

Java开发社招面试总结!java开发地图可视化

Java开发社招面试总结!dockerubuntu镜像端口

毕业之后所有面试总结