安排!Java易错面试题正在不断更新中......还不快跟上?

287 阅读16分钟

上次给大家整理了互联网技术面试常问问题汇总及回答技巧总结,相信大家看完之后收获都特别大。 所以今天就整理了这套Java易错面试题,分享出来与朋友们共勉。

在这里插入图片描述

一、易错面试题

1、final、finally、finalize的区别

final:用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,被其修饰的类不可继承。

finally:异常处理语句结构的一部分,表示总是执行。

finalize:Object类的一个方法,在垃圾回收器执行的时候会调用被回收此对象的方法,可以覆盖这个方法。

2、接口和抽象类的区别

(1)相同点:

①接口和抽象类都不能被实例化,它们都位于继承树的顶端,用于被其它类实现或继承。

② 可以将抽象类和接口作为引用类型

③ 借口和抽象类都包含抽象方法

④一个类如果继承了某个抽象类或者实现了某个接口,都需要对其中的抽象方法进行全部实现,否则该类仍然需要被声明为抽象类

(2)不同点

① 一个类可以实现多个接口,但只能继承一个抽象类

② 接口中不能定义构造器,抽象类中可以定义构造器;抽象类的构造器不是用来创建对象的,而是让其子类调用完成抽象类的初始化操作。

③ 接口中的方法全部都是抽象方法,抽象类中可以有抽象方法和具体方法。

④ 接口中的成员全部都是public,抽象类中的成员可以是private、默认,protected、public

⑤接口中定义的成员变量实际上都是常亮,抽象类中可以定义成员变量;

⑥接口中不能有静态方法,抽象类中可以包含静态方法;(jdk1.8之后接口中可以有静态方法)

3、&和&&的区别

① &:按位与,不管左边是true还是false,右边都会进行运算,不会短路

②&&:逻辑与,只看左边结果,如果左边为true,右边参与运算,如果左边为false,右边不参与运算,有短路功能

4、重载(Overloading)和重写(overriding)的区别

(1)重载(overloading):

①看是否是重载,一是看方法名相同,二是看参数是否相同。方法名相同,参数不同就是重载,否则不是。

②方法重载是让类以统一的方式处理不同类型数据的一种手段。多个同名函数同时存在,具有不同的参数类型。重载是一个类中多态的一种表现。

③ Java的方法重载,就是在类中可以创建多个方法,他们具体相同的名字,但具有不同的参数和不同的定义。调用方法是通过传递给他们不同的参数个数和类型来决定具体使用哪个方法,这就是多态。

④重载时,方法名一样,但是参数类型和个数不一样,返回值类型可以相同也可以不同。无法以返回类型作为重载函数的区分标准。

(2)重写(overriding):

①父类与子类之间的多态性,对父类的函数进行重新定义。如果子类中定义的某个方法与其父类有相同的名称和参数,我们说该方法被重写。在Java中,子类可以继承父类中的方法。而不需要重新编写相同的方法。但有时子类并不想原封不动的继承父类的方法,而是想做一定的修改,这就是采用方法的重写。方法重写也称之为方法覆盖。

②若子类中的方法与父类中的某一个方法具有相同的方法名,返回类型和参数表,则新方法将覆盖原有的方法。如需父类中原有的方法,可以使用super关键字,该关键字引用了当前的父类。

③子类函数的访问修改权限不能少于父类。

④重写需要遵循 ”两同两小一大“ 的原则:

  • 两同:子类重写的方法必须和父类被重写的方法名相同,参数列表相同
  • 两小:子类的返回值类型小于等于父类的返回值类型,子类抛出的异常小于等于父类抛出的异常
  • 一大:子类重写的方法使用的访问权限必须大于父类被重写的方法的访问权限

注意:子类不能重写父类中声明为private权限的方法

总结:

重写就是写在一边,重载就是再多一个

重写:父类里面有,子类就照猫画虎写一个

重写:自己类里面有,觉得不够就再写一个

5、==和equals的区别

① = =既可以比较基本类型也可以比较引用类型。对于基本类型比较的是值,对于引用类型比较的是内存地址。

② equals 是属于java Object的一个方法,如果该方法没有被重写默认也是”= =“ ;通常情况下 重写equals方***比较类中的相应属性是否相等。

6、error和exception的区别

(1)相同点

  • Error类和Exception的父类都是Throwable类

(2)不同点

  • Error类一般是指Java虚拟机无法为解决的严重错误,如系统崩溃,虚拟机错误,内存不足,方法调用栈溢出等。
  • Exception类表示其他因编程错误或偶然存在的外在因素导致的问题,程序可以处理,可以捕获且能修复。

7、throw和throws的区别

(1)throw:

  • throw语句用在方法体内,表示抛出异常,由方法体内的语句处理
  • throw是具体向外抛出异常的动作,它抛出的是一个异常实例,执行throw一定是抛出了某中异常。

(2)throws:

  • throws语句是在方法声明后面,表示如果抛出一床,由该方法的调用者来进行异常处理。
  • throws主要是声明这个方***抛出某种类型的异常,让它的使用者要知道需要捕获的异常的类型。
  • throws表示出现异常的一种可能性,并不一定会发生这种异常。

8、String、StringBuilder、StringBuffer的区别

(1)String 有final修饰,是不可变字符序列,一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可被改变的,直至这个对象被销毁。 即:

String a="张三";
a="123"//打印为123
System.out.println(a);

可以看出来,再次给a赋值时,并不是对原来堆中实例对象进行重新赋值,而是生成了一个新的实例对象,并且指向456这个字符串。a则指向最新生产的实例对象,之前的实例对象仍然存在,如果没有被再次引用,则会被垃圾回收。

(2)StringBuilder类也代表可以变字符串对象。实际上,StringBudiler和StringBuffer基本相似,不同的是:StringBuffer是线程安全的,而StringBuilder则没有实现线程安全功能所以性能略高

(3)StingBuffer对象则代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()等方法可以修改这个字符串对象的字符序列。一旦通过StringBuffer生了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象。

StringBuffer b= new StringBuffer("123");
b.append("456");
//b打印结果为:123456
System.out.println(b);

  • StringBuffer对象是与i个字符序列可以变的字符串,它没有重新生成一个对象;而且在原来的对象中可以连接新的字符串。

总结:String有final修饰,不可变字符序列;StringBuffer 可变字符序列,其效率低但是线程安全;StringBuilder 可变字符序列其效率高 但是线程不安全。

9、StringBuffer是如何实现线程安全的

StringBuffer类中实现的方法:

由此可见,StringBuffer类中的方法都添加了synchronized关键字,也就是给这个方法添加了一个锁,用来保证线程安全。

Java9的改进

  • Java9改进了字符串(包括String、StringBuffer、StringBuilder)的实现。在Java9以前字符串采用char[]数组来保存字符,字符串的每个字符占2字节;而Java9的字符串采用byte[]数组再加一个encoding-flag字段来保存字符,因此字符串的每个字符只占1字节。所以Java9的字符串更加节省空间,字符串的功能方法也没有受到影响。

10、ArrayList和LinkedList的区别

两者线程都不安全,但是效率比Vector的高

  • ArrayList底层是以数组形式保存的,随机访问集合中的元素比LinkedList快原因是因为LinkedList要移动指针
  • LinkedList内部是以链表的形式博爱村集合里面的数据的,它所及访问集合中的元素性能慢,但是新增和删除时速度比ArrayLIst快 原因时ArrayList要移动数据

11、equals和hashcode的区别

  • 如果两个对象equals相等,那么它们的hashcode一定相等。
  • 如果两个对象equals不相等,那么它们的hashcode有可能相等
  • 如果两个对象hashcode相等,他们的equals不一定相等
  • 如果两个对象hachcode不相等,他们的equals不一定相等

12、Java类成员的访问控制权限

public > protected > default(同包) > private

13、Hashtable和hashmap的区别

(1)相同点

  • Hashtable和Hashmap都是Map的子接口

(2)不同点

  • Hashtable线程安全,不支持key和value为空,key不能重复,但value可以重复,不支持key()和value为null
  • Hashmap线程不安全,支持key和value为空,key不能重复,但是value可以重复,支持key和value为null

14、Java中Object类的常用方法

(1)clone();

  • 创建并返回此对象的一个副本。

(2)equals();

  • 指示其他某个对象是否与此对象”相等“

(3)finalize();

  • 当垃圾回收器确定不存在对该对象引用时,由对象的垃圾回收器调用此方法

(4)getClass();

  • 返回此Object的运行时类

(5)notify(),notifyAll();

  • 唤醒在此对象监视器上等待的单个线程notify();
  • 唤醒在此对象监视器上等待的所有线程 notifyAll();

(6)toString();

  • 返回该对象的字符串表示。

15、Java编译时异常和运行时异常(RuntimeExecption)的区别,并举出几种运行时异常

(1)编译时异常

  • 在编译阶段不通过:(如图)
  • 将String类型赋值给int类型的数组出现编译异常

(2)运行时异常

  • 编译阶段无异常,但是在运行时报错(数组越界异常和空指针异常): 在这里插入图片描述

常见的RuntimeException类型的异常

  • ArithmeticException 数学计算异常
  • NullPointerException 空指针异常
  • NegativeArraySizeException 负数组长度异常
  • ClssCastException 类型强制转换异常
  • SecurityException 违背安全原则异常
  • ArrayIndexOutOfBoundsException 数组越界异常

面试题种类太多,一篇文章写不完?我的问题在这篇文章里没有解答?不用担心,我整理了多家互联网大厂的多年面试题,多位大佬的面经资料,以及Java学习资料,还有更多的面试技巧。有需要的朋友可以点击进入获取。暗号:掘金。一起来学习吧!

16、说一下Java中反射的概念和它的优缺点

反射指的是在运行时能够分析类的能力的程序,首先说到反射我们需要知道在正常情况下,如果使用一个类,必须要经过一下几个步骤:

  • 使用important导入类所在的包。
  • 通过关键字new进行类对象实例化
  • 产生对象可以使用“对象.属性” 来进行类中属性的调用
  • 通过“对象.方法()”调用类中的方法

在反射中,使用一个类并不需要导入类所在的包,只要知道类的完整的路径就可以知道该类中的信息。反射并不需要有明确的类型对象,所有的对象使用Objiect表示。可以直接通过Object的与反射机制的混合调用类中的方法。简单来说,反射机制时程序在运行时能狗获取自身的信息。

在Java中,只要给定类的名字就可以通过反射机制来获取类的所有信息。

(1) 为什么要使用反射?它在实际编译中有什么应用?

  • 静态编译: 在编译时确定类型,绑定对象即通过。
  • 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了Java的灵活性,体现了多态的应用,降低类之间的耦合性。

(2)反射的优点和缺点

  • 优点:可以实现动态创建对象和编译,体现出很大的灵活性
  • 缺点:对性能有影响。使用反射基本上是一种操作解释,我们告诉jvm我们希望做什么并且它满足我们的需求。这类操作总是慢于直接执行相同的操作。

从这道题可以看出:

①Filed、Method Constructor分别分别用与描述类的与、方法和构造器。 在Java.lang.reflet包中而Class而是在Java.lang中

②是正确的。动态代理就是实现接口的 ,然后我们可以通过类的路径可以的到类的全部信息。(可以看下面的动态代理的原理);

反射常见的作用有:动态加载类、动态获取类的信息(属性、方法、构造器);

动态构造对象;动态调用类和对象的任意方法、构造器;

动态调用和处理属性;

获取泛型信息;处理注解

③反射的概念中提到过 反射可以通过类的路径来得到该类的全部信息。

④反射可以动态的调用类和对象的任意方法

⑤反射的缺点是对性能有影响

⑥反射会降低效率,可是设置禁止安全检查,来提高反射的运行速度;

17、动态代理的原理

由于通过上面反射是可以动态实现接口的,我们扯到了动态代理,在明白动态代理之前我们要明白什么是代理。

什么是代理模式(Proxy): 给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用

使用的场景有两种:

  • 客户端不想直接访问实际对象,或者访问实际的对象存在技术上的障碍, 因而通过代理对象作为桥梁来完成简介访问;
  • 在不改变目标对象方法的情况下对方法进行增强。

代理又分为静态代理和动态代理

①静态代理

  • 在不破坏接口实现类的同时又想新增一些功能 怎么办 ? 可以引出动态代理,其实就是创建一个实现接口类,且内部维护一个接口的成员变量指向原始接口的对象,通过代理对象的方法内部调用原始的接口实例的方法;
  • 代码如下:创建一个接口里面有一个方法。
public interface IDveloper {
public void writeCode();
}
public class Developer  implements  IDveloper{
private  String name;
public Developer(String name){
this.name = name;
}
@Override
public void writeCode() {
System.out.print("Developer " +name + " wrters code");
}
}
public class DeveloperTest {
public static void main(String[] args) {
IDveloper wrto = new Developer("wtao");
wrto.writeCode();
}
}

  • 静态代理的优点:

容易理解和实现。代理类和真实类的关系是编译期静态决定的,和动态代理比较起来,执行时没有任何额外开销

  • 静态代理的缺点:

接口和代理时1对1的,有多个接口需要代理或接口中有多个方法,就需要创建多个代理类或者实现接口中的方法,繁琐

②动态代理

静态代理受限于接口的实现。而动态代理就是通过反射,动态的获取抽象接口的类型,从而获取相关特性进行代理。

使用动态的代理有三个步骤:

  • 创建一个代理执行类,,必须实现InvocationHandler 接口,表面该类是一个动态代理执行类。
  • 代理类需要实现InvocationHandler接口中的invoke(Object[] proxy,Method,Object[] args)方法【增强被代理类的功能或逻辑的方法】
  • 获取代理类,增强了功能,关键是要获取到代理类的对象,使用静态方法Proxy.newProxylnstance去获取代理的对象。

代码如下:

获取代理类对象的方法,要传入的三个参数:第一个参数是类加载器;第二个参数是委托类的接口类型,代理类返回的是同一个实现接口下的类型,保持代理类返回的类型;

第三个参数就是代理类本身,就是告诉代理类,代理类遇到某个原视类的方法时该调用哪个代理执行下的invoke方法;

也可以在定义代理执行类时,直接在类中写出一个public方法来获取代理类的对象

代码如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DeveloperProxy implements InvocationHandler {
private  Object target; //被代理的类
public Object getProxyInstance(Object target){
this.target = target;
Object object = Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
return  object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.print("还没睡呢? ");
Object result = method.invoke(target,args);
System.out.print("你还没睡呢? ");
return result;
}
}
public class DeveloperTest {
public static void main(String[] args) {
//创建被代理的对象
IDveloper wtao = new Developer("wato");
//创建被代理执行的对象
DeveloperProxy jdkProxy = new DeveloperProxy();
//将被代理对象交给代理类维护且获取代理类对象
IDveloper WtaoProxy = (IDveloper) jdkProxy.getProxyInstance(wtao);
WtaoProxy.writeCode();
}
}

③静态代理类和动态代理类的区别

  • 静态代理类需要自己写代理类并一一实现目标方法,且代理类必须实现目标对象相同的接口。
  • 动态代理不需要自己实现代理类,它是利用JDKAPI,动态地子啊内存中构建代理对象需要我们传入被代理类,并且默认实现所有的目标方法。

二、写在最后

以上就是整理的一些在面试Java技术岗时的易错题,受限于水平,我整理的难免有所不足,如果有不当之处,或者可以有补充的,欢迎指正!

学习是一个终身的过程。瞬息万变的时代带来的技术快速革新要求我们每一个人不断学习。

本文整理的也只是沧海一粟,最初的想法也是希望能对大家有帮助。

生也有涯,而学也无涯,希望大家都能不断学习,不断进步,成为更好的自己。谢谢各位的观看!

在这里插入图片描述

最后,不要忘了点赞,收藏,转发一键三连点一下(暗示~暗示( ̄︶ ̄)↙)。谢谢各位啦!