写点什么

Java 岗大厂面试百日冲刺 - 日积月累,每日三题【Day5,docker 教程学习

作者:Java高工P7
  • 2021 年 11 月 10 日
  • 本文字数:3918 字

    阅读完需:约 13 分钟

追问 2:用抽象类实现一个接口,和普通类实现接口会有什么不同么?

一般来说我们使用普通类来实现接口,这个普通类就必须实现接口中所有的方法,这样的结果就是普通类中就需要实现多余的方法,造成代码冗余。但是如果我们使用的是抽象类来实现接口,那么就可以只实现接口中的部分方法,并且当其他类继承这个抽象类时,仍然可以实现接口中有但抽象类并未实现的方法。


如以下代码,抽象类只是实现了接口 A 中的方法 a,方法 b,但是当类 C 继承抽象类 B 时,可以直接实现接口 A 中的 c 方法,有一点需要注意的是,类 C 中的方法 a,方法 b 都是调用的父类 B 的方法 a,方法 b,不是直接实现接口的方法 a 和 b。


/**


*接口


*/


interface A{


public void aaa();


public void bbb();


public void ccc();


}


/**


*抽象类


*/


abstract class B implements A{


public void aaa(){}


public void bbb(){}


}


/**


  • 实现类


*/


public class C extends B{


public void aaa(){}


public void bbb(){}


public void ccc(){}


}

追问 3:抽象类能使用 final 修饰吗?

不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类。






课间休息,欣赏一下来自咱们SQL大腿群美女同学的搬砖工地,坐标:西安。




面试题 2:final 在 Java 中有什么作用?


========================================================================================


正经回答:




用于修饰类、方法和属性;


1、修饰类


当用 final 修饰类的时,表明该类不能被其他类所继承。需要注意的是:final类中所有的成员方法都会隐式的定义为final方法。


2、修饰方法


使用 final 方法的原因主要是把方法锁定,以防止继承类对其进行更改或重写。


若父类中 final 方法的访问权限为 private,将导致子类中不能直接继承该方法,因此,此时可以在子类中定义相同方法名的函数,此时不会与重写 final 的矛盾,而是在子类中重新地定义了新方法。


class A{


private final void getName(){


System.out.println("getName - A");


}


}


public class B extends A{


public void getName(){


System.out.println("getName - B");


}


public void main(String[]args){


this.getName(); // 日志输出:getName - B


}


}


3、修饰变量


当 final 修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果 final 修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。本质上是一回事,因为引用的值是一个地址,final 要求值,即地址的值不发生变化。


final 修饰一个成员变量(属性),必须要显示初始化。这里有两种初始化方式,一种是在变量声明的时候初始化;第二种方法是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。


当函数的参数类型声明为 final 时,说明该参数是只读型的。即你可以读取使用该参数,但是无法改变该参数的值。


深入追问:



追问 1:能分别说一下 final、finally、finalize 的区别么?

  • final 可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。

  • finally 一般作用在 try-catch 代码块中,在处理异常的时候,通常我们将一定要执行的代码方法 finally 代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。当然,还有多种情况走不了finally~

  • finalize 是一个方法,属于 Object 类的一个方法,而 Object 类是所有类的父类,该方法一般由垃圾回收器来调用,当我们调用 System.gc() 方法的时候,由垃圾回收器调用 finalize(),回收垃圾,一个对象是否可回收的最后判断。





课间休息,欣赏一下来自咱们`S


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


QL 大腿群`同学的搬砖工地,坐标:北京。


北漂们猜猜具体位置是哪?




面试题 3:你对 Java 序列化了解么?


=================================================================================


正经回答:




序列化过程:


是指把一个 Java 对象变成二进制内容,实质上就是一个 byte[]数组。


因为序列化后可以把 byte[]保存到文件中,或者把 byte[]通过网络传输到远程(IO),这样,就相当于把 Java 对象存储到文件或者通过网络传输出去了。


反序列化过程:


把一个二进制内容(也就是 byte[]数组)变回 Java 对象。有了反序列化,保存到文件中的 byte[]数组又可以“变回”Java 对象,或者从网络上读取 byte[]并把它“变回”Java 对象。


以下是一些使用序列化的示例:



  • 以面向对象的方式将数据存储到磁盘上的文件,例如,Redis 存储 Student 对象的列表。


  • 将程序的状态保存在磁盘上,例如,保存游戏状态。


  • 通过网络以表单对象形式发送数据,例如,在聊天应用程序中以对象形式发送消息。


一个 Java 对象要能序列化,必须实现一个特殊的java.io.Serializable接口,它的定义如下:


public interface Serializable {


}


Serializable 接口没有定义任何方法,它是一个空接口。我们把这样的空接口称为“标记接口”(Marker Interface),实现了标记接口的类仅仅是给自身贴了个“标记”,并没有增加任何方法。


深入追问:



追问 1:Java 序列化是如何工作的?

当且仅当对象的类实现java.io.Serializable接口时,该对象才有资格进行序列化。可序列化 是一个标记接口(不包含任何方法),该接口告诉 Java 虚拟机(JVM)该类的对象已准备好写入持久性存储或通过网络进行读取。


默认情况下,JVM 负责编写和读取可序列化对象的过程。序列化/反序列化功能通过对象流类的以下两种方法公开:


  • ObjectOutputStream。writeObject(Object):将可序列化的对象写入输出流。如果要序列化的某些对象未实现 Serializable 接口,则此方法将引发NotSerializableException

  • ObjectInputStream。readObject():从输入流读取,构造并返回一个对象。如果找不到序列化对象的类,则此方法将引发ClassNotFoundException


如果序列化使用的类有问题,则这两种方法都将引发InvalidClassException,如果发生 I / O 错误,则将引发IOException。无论NotSerializableExceptionInvalidClassException是子类IOException异常。


让我们来看一个简单的例子。以下代码将 String 对象序列化为名为“ data.ser”的文件。字符串对象是可序列化的,因为 String 类实现了 Serializable 接口:


String filePath = "data.ser";


String message = "Java Serialization is Cool";


try (


FileOutputStream fos = new FileOutputStream(filePath);


ObjectOutputStream outputStream = new ObjectOutputStream(fos);


) {


outputStream.writeObject(message);


} catch (IOException ex) {


System.err.println(ex);


}


以下代码反序列化文件“ data.ser”中的 String 对象:


String filePath = "data.ser";


try (


FileInputStream fis = new FileInputStream(filePath);


ObjectInputStream inputStream = new ObjectInputStream(fis);


) {


String message = (String) inputStream.readObject();


System.out.println("Message: " + message);


} catch (ClassNotFoundException ex) {


System.err.println("Class not found: " + ex);


} catch (IOException ex) {


System.err.println("IO error: " + ex);


}


请注意,readObject()返回一个 Object 类型的对象,因此您需要将其强制转换为可序列化的类,在这种情况下为 String 类。


让我们看一个涉及使用自定义类的更复杂的示例。


给定以下学生班:


import java.io.*;


import java.util.*;


/**


  • Student.java

  • @author chenhh


*/


public class Student extends Person implements Serializable {


public static final long serialVersionUID = 1234L;


private long studentId;


private String name;


private transient int age;


public Student(long studentId, String name, int age) {


super();


this.studentId = studentId;


this.name = name;


this.age = age;


System.out.println("Constructor");


}


public String toString() {


return String.format("%d - %s - %d", studentId, name, age);


}


}


如上面代码,你会发现两点:


  • long serialVersionUID 类型的常量。


  • 成员变量 age 被标记为 transient。



下面两个问题让我们搞明白它们。

追问 2:什么是 serialVersionUID 常数

serialVersionUID是一个常数,用于唯一标识可序列化类的版本。从输入流构造对象时,JVM 在反序列化过程中检查此常数。如果正在读取的对象的serialVersionUID与类中指定的序列号不同,则 JVM 抛出InvalidClassException。这是为了确保正在构造的对象与具有相同serialVersionUID的类兼容。


请注意,serialVersionUID是可选的。这意味着如果您不显式声明 Java 编译器,它将生成一个。


那么,为什么要显式声明serialVersionUID呢?


原因是:自动生成的serialVersionUID是基于类的元素(成员变量,方法,构造函数等)计算的。如果这些元素之一发生更改,serialVersionUID也将更改。想象一下这种情况:


  1. 您编写了一个程序,将 Student 类的某些对象存储到文件中。Student 类没有显式声明的 serialVersionUID。

  2. 有时,您更新了 Student 类(例如,添加了一个新的私有方法),现在自动生成的 serialVersionUID 也被更改了。

  3. 您的程序无法反序列化先前编写的 Student 对象,因为那里的 serialVersionUID 不同。JVM 抛出 InvalidClassException。


这就是为什么建议为可序列化类显式添加 serialVersionUID 的原因。

追问 3、那你知道什么是瞬时变量么?

在上面的 Student 类中,您看到成员变量 age 被标记为 transient,对吗?JVM 在序列化过程中跳过瞬态变量。这意味着在序列化对象时不会存储 age 变量的值。


因此,如果成员变量不需要序列化,则可以将其标记为瞬态。


以下代码将 Student 对象序列化为名为“ students.ser”的文件:

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
Java岗大厂面试百日冲刺 - 日积月累,每日三题【Day5,docker教程学习