面经-基础篇-三
本文最后更新于:2021年3月30日 晚上
以下内容并非博主亲身经历的面试问题,只是对网络中的面经做一个总结、记录。
1.21 如何在父类中为子类自动完成所有的hashcode和equals实现?这么做有何优劣?
- 优点:子类中我们不用再写。
- 缺点:有时候父类中equals和hashcode方法不满足我们的需求,需要重写。
1.22 说一说你对java.lang.Object对象中hashCode和equals方法的理解。在什么场景下需要重新实现这两个方法。
- hashCode与equals方法都是Java Object对象中的方法,也就是说Java的一切对象都提供这两个方法。
- 当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。
如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;
如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。
所以这里存在一个冲突解决的问题,这个时候就需要重写。
1.23 有没有可能2个不相等的对象有相同的hashcode。
- hashCode是所有java对象的固有方法;
- 如果不重载的话,返回的实际上是 该对象在jvm的堆上的内存地址 ,而不同对象的内存地址肯定不同,所以这个hashCode也就肯定不同了。
- 如果重载了的话,由于采用的算法的问题,有可能导致两个不同对象的hashCode相同。
1.24 这样的a.hashcode() 有什么用,与a.equals(b)有什么关系。
- hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。
- equals() 主要是用于比对两者的对象时候相同,相对于hashCode()是比较简单的。
- 两者主要在数据的存储方面有联系;
- equals()的时间复杂度为O(n),而hashCode()判断是否有相同元素的代价,只是一次哈希计算,时间复杂度为O(1),这极大地提高了数据的存储性能。
- 当hashcode有冲突时,容器就能判断:这个新加入的元素已经存在,需要另作处理:覆盖掉原来的元素(key)或舍弃。
- 此时就需要再进行一个equals()的比对,只有当equals()也返回true的时候,才会认为元素重复,舍弃存储
1.25 数组和链表数据结构描述,各自的时间复杂度。
1.25.1 各自的特点:
两种数据结构都是线性表,在排序和查找等算法中都有广泛的应用
数组:
数组是将元素在内存中连续存放,由于每个元素占用内存相同,可以通过下标迅速访问数组中任何元素。但是如果要在数组中增加一个元素,需要移动大量元素,在内存中空出一个元素的空间,然后将要增加的元素放在其中。同样的道理,如果想删除一个元素,同样需要移动大量元素去填掉被移动的元素。如果应用需要快速访问数据,很少或不插入和删除元素,就应该用数组。链表:
链表恰好相反,链表中的元素在内存中不是顺序存储的,而是通过存在元素中的指针联系到一起。比如:上一个元素有个指针指到下一个元素,以此类推,直到最后一个元素。如果要访问链表中一个元素,需要从第一个元素开始,一直找到需要的元素位置。但是增加和删除一个元素对于链表数据结构就非常简单了,只要修改元素中的指针就可以了。如果应用需要经常插入和删除元素你就需要用链表数据结构了。
1.25.2 数组和链表的区别:
- 从逻辑结构角度来看:
- 数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费。
- 链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。(数组中插入、删除数据项时,需要移动其它数据项)
- 数组元素在栈区,链表元素在堆区;
- 从内存存储角度来看:
- (静态)数组从栈中分配空间, 对于程序员方便快速,但自由度小。
- 链表从堆中分配空间, 自由度大但申请管理比较麻烦。
- 数组利用下标定位,时间复杂度为O(1),链表定位元素时间复杂度O(n);
- 数组插入或删除元素的时间复杂度O(n),链表的时间复杂度O(1)。
1.26 error和exception的区别,CheckedException,RuntimeException的区别。
首先Exception和Error都是继承于Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。
Exception 是程序正常运行过程中可以预料到的意外情况,并且应该被开发者捕获,进行相应的处理。
Error是java程序运行中不可预料的异常情况(正常情况下不大可能出现的情况),这种异常发生以后,会直接导致JVM不可处理或者不可恢复的情况。所以这种异常不可能抓取到,比如OutOfMemoryError、NoClassDefFoundError等。【表示由JVM所侦测到的无法预期的错误,由于这是属于JVM层次的严重错误 ,导致JVM无法继续执行,因此,这是不可捕捉到的,无法采取任何恢复的操作,顶多只能显示错误信息。 Error类体系描述了Java运行系统中的内部错误以及资源耗尽的情形。应用程序不应该抛出这种类型的对象(一般是由虚拟机抛出)。假如出现这种错误,除了尽力使程序安全退出外,在其他方面是无能为力的。】
其中的Exception又分为检查性异常(checked)和非检查性异常(unchecked)(也叫RuntimeException)。
两个根本的区别在于:
- 检查性异常 必须在编写代码时,使用try catch捕获(比如:IOException异常)。
- 非检查性异常 在代码编写使,可以忽略捕获操作(比如:ArrayIndexOutOfBoundsException),这种异常是在代码编写或者使用过程中通过规范可以避免发生的,具体根据需要来判断是否需要捕获,并不会在编译器强制要求。
1.27 常见运行时异常 (RuntimeException)
- NullPointerException (空指针异常)
- IllegalArgumentException (传递非法参数异常)
- ClassCastException (类转换异常)
- IndexOutOfBoundsException (下标越界异常)
- ArrayIndexOutOfBoundsException (数组越界异常)
- ArrayStoreException (数据存储异常,操作数组时类型不一致)
- NumberFormatException (数字格式异常)
- BufferOverflowException (缓冲区溢出异常)
- AruthmeticException (算术异常)
1.28 常见非运行时异常 (CheckedException)
- IOException (IO 操作异常)
- ClassNotFoundException (找不到指定 class 的异常)
- FileNotFoundException (文件不存在异常)
- SQLException (SQL语句异常)
- InterruptedException (中断异常-调用线程睡眠时候)
1.29 常见错误 (Error)
- OutOfMemoryError (内存溢出错误)
- NoClassDefFoundError (找不到 class 定义错误)
- StackOverflowError (深递归导致栈被耗尽而抛出的错误)
1.30 在自己的代码中,如果创建一个java.lang.String类,这个类是否可以被类加载器加载?为什么?
不能
Java使用的是 双亲委托机制 :如果一个类加载器收到了类加载的请求,它首先不会自己尝试去加载这个类,而是把这个请求委派给父类加载器,每一个层次的类加载器都是加此,因此所有的加载请求最终到达顶层的启动类加载器,只有当父类加载器反馈自己无法完成加载请求时(指它的搜索范围没有找到所需的类),子类加载器才会尝试自己去加载。
因此,当你创建一个java.lang.String类时,启动类加载器首先加载的是java.lang.String本类,加载你自己的java.lang.String类时,就会由虚拟机抛出的 java.lang.SecurityException:Prohibited package name:java.lang 异常。
00. 参考链接
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!