面试题Java基础篇-常见面试题总结p3
Posted LL.LEBRON
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试题Java基础篇-常见面试题总结p3相关的知识,希望对你有一定的参考价值。
备战实习,会定期的总结常考的面试题,大家一起加油! 🎯
往期文章:
参考文章:
- https://blog.csdn.net/qq_45966440/category_10889559.html
- https://blog.csdn.net/weixin_43591980/category_10638797.html?spm=1001.2014.3001.5515
- https://javaguide.cn/java/basis/java%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E6%80%BB%E7%BB%93/
- https://pdai.tech/
注意:
如果本文中有错误的地方,欢迎评论区指正!🍭
1.说一下try-catch-finally各个部分的作用?
-
try块: 用于捕获异常。其后可接零个或多个
catch
块,如果没有catch
块,则必须跟一个finally
块 -
catch块: 用于处理
try
捕获到的异常 -
finally 块: 无论是否捕获或处理异常,
finally
块里的语句都会被执行。当在try
块或catch
块中遇到return
语句时,finally
语句块将在方法返回之前被执行
👨💻面试官又问:finally是不是一定会被执行到?
不一定的。在在以下 3 种特殊情况下,finally
块不会被执行:
- 在
try
或finally
块中用了System.exit(int)
退出程序。但是,如果System.exit(int)
在异常语句之后,finally
还是会被执行 - 程序所在的线程死亡
- 关闭 CPU
👨💻面试官继续追问:ftry-catch-finally中那个部分可以省略?
catch可以省略。
try
只适合处理运行时异常,try+catch
适合处理运行时异常+普通异常。
也就是说,如果你只用try去处理普通异常却不加以catch处理,编译是通不过的,因为编译器硬性规定,普通异常如果选择捕获,则必须用catch显示声明以便进一步处理。而运行时异常在编译时没有如此规定,所以catch可以省略
2.说说Error 和 Exception 的区别?
Error类和Exception类的父类都是Throwable类。主要区别如下︰
-
Error类
表示JVM 无法处理的错误,我们没办法通过
catch
来进行捕获 。例如,Java 虚拟机运行错误、虚拟机内存不够错误、类定义错误等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止 -
Exception类
程序本身可以处理的异常,可以通过
catch
来进行捕获。Exception
又可以分为 受检查异常(必须处理) 和 不受检查异常(可以不处理)
👨💻面试官追问:受检查异常和不受检查异常有什么不同?
-
受检查异常
Java 代码在编译过程中,如果受检查异常没有被
catch
/throw
处理的话,就没办法通过编译 。除了
RuntimeException
及其子类以外,其他的Exception
类及其子类都属于受检查异常 。常见的受检查异常有: IO 相关的异常、ClassNotFoundException
、SQLException
… -
不受检查异常
Java 代码在编译过程中 ,我们即使不处理不受检查异常也可以正常通过编译。
RuntimeException
及其子类都统称为非受检查异常,例如:NullPointerException
、NumberFormatException
(字符串转换为数字)、ArrayIndexOutOfBoundsException
(数组越界)、ClassCastException
(类型转换错误)、ArithmeticException
(算术错误)…
👨💻面试官继续追问:说一下你了解的常见异常类有哪些?
NullPointerException
:当应用程序试图访问空对象时,则抛出该异常。SQLException
:提供关于数据库访问错误或其他错误信息的异常。IndexOutOfBoundsException
︰指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。FileNotFoundException
︰当试图打开指定路径名表示的文件失败时,抛出此异常IOException
:当发生某种/O异常时,抛出此异常。此类是失败或中断的I/O操作生成的异常的通用类。ClassCastException
:当试图将对象强制转换为不是实例的子类时,抛出该异常IllegalArgumentException
:抛出的异常表明向方法传递了一个不合法或不正确的参数。
3.说说运行时异常和非运行时异常有什么区别?
-
运行时异常
都是
RuntimeException
类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
-
非运行时异常
也叫编译异常。是
RuntimeException
以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常
4.说一下throw 和 throws 的区别?
-
throw
异常的抛出,在方法体内部,表示抛出异常,由方法体内部的语句处理;throw是具体向外抛出异常的动作,所以它抛出的是一个异常实例
public static double method(int value) if(value == 0) throw new ArithmeticException("参数不能为0"); //抛出一个运行时异常 return 5.0 / value;
-
throws
异常的申明,在方法声明后面,表示如果抛出异常,由该方法的调用者来进行异常的处理;表示出现异常的可能性,并不一定会发生这种异常
public static void method() throws IOException, FileNotFoundException //something statements
5.知道try-with-resources吗?
如果你的资源实现了 AutoCloseable
接口,你可以使用这个语法。大多数的 Java 标准资源都继承了这个接口。当你在 try 子句中打开资源,资源会在 try 代码块执行后或异常处理后自动关闭。任何 catch 或 finally 块在声明的资源关闭后运行
Java 中类似于InputStream
、OutputStream
、Scanner
、PrintWriter
等的资源都需要我们调用close()
方法来手动关闭,一般情况下我们都是通过try-catch-finally
语句来实现这个需求,如下:
//读取文本文件的内容
Scanner scanner = null;
try
scanner = new Scanner(new File("D://read.txt"));
while (scanner.hasNext())
System.out.println(scanner.nextLine());
catch (FileNotFoundException e)
e.printStackTrace();
finally
if (scanner != null)
scanner.close();
使用 Java 7
之后的 try-with-resources
语句改造上面的代码:
try (Scanner scanner = new Scanner(new File("test.txt")))
while (scanner.hasNext())
System.out.println(scanner.nextLine());
catch (FileNotFoundException fnfe)
fnfe.printStackTrace();
通过使用分号
分隔,可以在try-with-resources
块中声明多个资源:
try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt"))))
int b;
while ((b = bin.read()) != -1)
bout.write(b);
catch (IOException e)
e.printStackTrace();
6.说说你知道的Throwable 类常用方法?
public String getMessage()
:返回异常发生时的简要描述public String toString()
:返回异常发生时的详细信息public String getLocalizedMessage()
:返回异常对象的本地化信息。使用Throwable
的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()
返回的结果相同public void printStackTrace()
:在控制台上打印Throwable
对象封装的异常信息
栗子:
/**
* @author xppll
* @date 2021/11/30 09:08
*/
public class testThrowable
public static void main(String[] args)
try
int a = 10 / 0;
catch (Exception e)
System.out.println("getMessage....."+e.getMessage());
System.out.println("toString....."+e.toString());
System.out.println("getLocalizedMessage....."+e.getLocalizedMessage());
System.out.print("printStackTrace.....");
e.printStackTrace();
输出:
getMessage...../ by zero
toString.....java.lang.ArithmeticException: / by zero
getLocalizedMessage...../ by zero
printStackTrace.....java.lang.ArithmeticException: / by zero
at com.atguigu.mybatisplus.testThrowable.main(testThrowable.java:10)
7.说说什么是序列化?什么是反序列化?
如果我们需要持久化 Java 对象比如将 Java 对象保存在文件中,或者在网络传输 Java 对象,这些场景都需要用到序列化。
简单来说:
- 序列化:将数据结构或对象转换成二进制字节流的过程
- 反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程
总的来说序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中
👨💻面试官追问:在Java序列化中如果有些字段不想进行序列化,怎么办?
对于不想进行序列化的变量,使用 transient
关键字修饰。它可以阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient
修饰的变量值也不会被持久化和恢复
例子:
创建实现Serializable
接口的Person
类:
/**
* @author xppll
* @date 2021/12/23 22:44
*/
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Person implements Serializable
private String name;
private transient Integer age;
测试类:
/**
* @author xppll
* @date 2021/12/23 22:32
*/
public class test
public static void main(String[] args)
File file = new File("D://out.txt");
//序列化对象
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(file)
))
Person person = new Person("dad", 13);
oos.writeObject(person);
catch (IOException e)
e.printStackTrace();
结果:
可以看出没有age属性
👨💻面试官继续追问:关于transient的使用有什么需要注意的?
transient
只能修饰变量,不能修饰类和方法。transient
修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰int
类型,那么反序列后结果就是0
。static
变量因为不属于任何对象(Object),所以无论有没有transient
关键字修饰,均不会被序列化
紧接上个测试案例,反序列化:
//反序列化
try (ObjectInputStream oos = new ObjectInputStream(
new FileInputStream(file)
))
Person res = (Person) oos.readObject();
System.out.println(res
);
catch (IOException e)
e.printStackTrace();
catch (ClassNotFoundException e)
e.printStackTrace();
控制台输出:
可以看出transient
修饰的变量,在反序列化后变量值将会被置成类型的默认值
8.说一下Java 中 IO 流分为几种?
- 按照流的流向分,可以分为输入流和输出流
- 按照操作单元划分,可以划分为字节流和字符流
- 按照流的角色划分为节点流和处理流
Java IO 流共涉及 40 多个类,这40 多个类都是从如下 4 个抽象类基类中派生出来的。
InputStream
/Reader
: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。OutputStream
/Writer
: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
按操作方式分类结构图:
按操作对象分类结构图:
👨💻面试官追问:既然有了字节流,为什么还要有字符流??
字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好
9.请你说一说BIO、NIO、AIO 有什么区别?
在对比这三者的区别之前,先了解一下什么是同步/异步、阻塞/非阻塞:
- 同步:一个任务的完成之前不能做其他操作,必须等待(相当于在打电话)
- 异步:一个任务的完成之前,可以进行其他操作(相当于在聊QQ)
- 阻塞:是相对于CPU来说的, 挂起当前线程,不能做其他操作只能等待
- 非阻塞:无须挂起当前线程,也可以去执行其他操作
-
BIO:Block IO 同步阻塞,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。每当有一个客户端向服务器发起请求时,服务器都要启动一个线程,无论客户端是否响应,线程都必须一直等待。如图所示:
-
NIO:New IO 同步非阻塞,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。服务器用一个线程来处理多个请求,客户端发送的请求会注册到多路复用器(selector选择器)上,有I/O请求的客户端分配线程处理,如图:
-
AIO:Asynchronous IO 异步非阻塞,是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的 操作。
10.Java中的深拷贝和浅拷贝有了解过吗?
-
浅拷贝
对于基础数据类型:直接复制数据值;对于引用数据类型:只是复制了对象的引用地址,新旧对象指向同一个内存地址,修改其中一个对象的值,另一个对象的值随之改变。
-
深拷贝
对于基础数据类型:直接复制数据值;对于引用数据类型:开辟新的内存空间,在新的内存空间里复制一个一模一样的对象,新老对象不共享内存,修改其中一个对象的值,不会影响另一个对象。
下面举个详细的例子:
浅拷贝
/**
* @author xppll
* @date 2021/12/21 12:27
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address implements Cloneable
private String name;
@Override
protected Object clone() throws CloneNotSupportedException
return super.clone();
/**
* @author xppll
* @date 2021/12/21 12:29
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person implements Cloneable
private Address address;
@Override
protected Object clone() throws CloneNotSupportedException
return super.clone();
测试:
/**
* @author xppll
* @date 2021/12/21 12:30
*/
public class Test
public static void main(String[] args) throws CloneNotSupportedException
Person person1 = new Person(new Address("长沙"));
Person person2 = (Person) person1.clone();
System.out.println(person1.getAddress()==person2.getAddress());//true
从结果可以看出, person1
的克隆对象和 person1
使用的仍然是同一个 Address
对象。
深拷贝
这里我们简单对 Person
类的 clone()
方法进行修改,连带着要把 Person
对象内部的 Address
对象一起复制。
/**
* @author xppll
* @date 2021/12/21 12:29
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person implements Cloneable
private Address address;
@Override
protected Object clone() throws CloneNotSupportedException
Person person = (Person) super.clone();
person.setAddress((Address) person.getAddress().clone());
return person;
再次测试:
/**
* @author xppll
* @date 2021/12/21 12:30
*/
public class Test
public static void main(String[] args) throws CloneNotSupportedException
Person person1 = new Person(new Address("长沙"));
Person person2 = (Person) person1.clone();
System.out.println(person1.getAddress()==person2.getAddress());//false
最后喜欢的小伙伴,记得三连哦!😏🍭😘
以上是关于面试题Java基础篇-常见面试题总结p3的主要内容,如果未能解决你的问题,请参考以下文章