一、十天速通Java面试(第三天)

目录

[1. ThreadLocal](#1. ThreadLocal)

[2. finalize、finalization、finally](#2. finalize、finalization、finally)

[2.1 finalize用途](#2.1 finalize用途)

[2.2 finally](#2.2 finally)

[2.3 finally代码块和finalize()方法区别](#2.3 finally代码块和finalize()方法区别)

3.修饰权限

4.Object

5.equls和==的区别

6.异常

[6.1 问题一](#6.1 问题一)

[6.2 问题二](#6.2 问题二)

7.序列化

[7.1 定义](#7.1 定义)

[7.2 作用](#7.2 作用)

[7.3 序列化和反序列化](#7.3 序列化和反序列化)

8.comparable接口和comparator接口

[8.1 定义](#8.1 定义)

[8.2 区别](#8.2 区别)

9.接口和抽象类

10.Socket

[10.1 网络分层](#10.1 网络分层)

[10.2 通信](#10.2 通信)

[10.3 UDP通信特点](#10.3 UDP通信特点)

11.Runtime类

[11.1 作用](#11.1 作用)

[11.2 实例获取](#11.2 实例获取)

[11.3 设计模式](#11.3 设计模式)

12.值的传递与引用传递

[12.1 值传递](#12.1 值传递)

[12.2 引用传递](#12.2 引用传递)

13.泛型中的"T"与"?(通配符)"

14.枚举类Enum

15.Java注解

16.字节流,字符流

[16.1 字节流](#16.1 字节流)

[16.2 字符流](#16.2 字符流)

[16.3 转换桥梁](#16.3 转换桥梁)

[16.4 主要区别](#16.4 主要区别)

17.静态内部类

18.匿名类


1. ThreadLocal

ThreadLocal不是解决对象的共享访问问题,通过***ThreadLocal.set()***到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。

各个线程独立的对象不是通过***ThreadLocal.set()***创建的,而是线程新New的对象。

ThreadLocal.set()将新建对象的引用保存到线程独有的map中,每当有其他线程对这个引用指向的对象做出修改时,其他线程Thread对象中保存的值也会发生变化。

java 复制代码
//ThreadLocal源码
//Thread类中有threadlocalmap对象
public void set(T value){
    Thread t=Thread.currentThread();
    ThreadLocalMap map=getMap(t);
    if(map!=null){
        map.set(this,value);
    }else {createMap(t,value);}
}


public T get(){
    Thread t=Thread.currentThread();
    ThreadLocalMap map=getmap(t);
    if(map!=null){ return (T)map.get(this);}
    T value=initialValue();
    createMap(t,value);
    return value;
}

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。

2. finalize、finalization、finally

2.1 finalize用途

(一)垃圾回收器:garbage colector 决定回收某对象时,就会运行该对象的finalize()方法--但在Java中很不幸,如果内存总是充足的,垃圾回收可能永远不会执行。

(二)主要用途:回收特殊渠道申请的内存--如:JNI调用non-Java程序,它的工作就是回收这部分内存。

2.2 finally

常用于Try--catch--finally中释放资源,回收内存,因为finally块中的内容一定会被执行。

需要注意:

**--**在try中return之前会执行finally,如果finally中有return则直接return,值为finally中修改的。

**--**如果finally中没有return,则执行try中return,数值仍是try中的。

2.3 finally代码块和finalize()方法区别

(一)无论是否抛出异常,finally代码块都会执行,主要用于释放占用内存;

(二)finalize()是Object中的一个protected方法,在对象被垃圾回收之前由Java虚拟机调用的。

3.修饰权限

4.Object

了解常用方法:hashcode()、equals()、toString()、getCalss()、wait()、noitfy()、notifyAll()、finalize()。

5.equls和==的区别

基本数据类型:==进行比较,比的是实际值;

复合数据类型:==进行比较,比的是内存地址;

复合数据类型:equal进行比较:(String、Ingeter、date)等重写了equal比较的是内容,没有重写equal的,比较的还是内存地址

StringBuffer和StringBuilder特殊,==和equal都是比较地址

6.异常

6.1 问题一

Java中的两种异常类型是什么?有什么区别?

运行时异常(不受检查)和编译时异常(受检查)。

运行时异常不必抛出,编译时异常必须修改或抛出。

6.2 问题二

throw和throws有什么区别?

throw关键字用来在程序中明确的抛出异常;

throws语句用来表明方法不能处理的异常。

7.序列化

7.1 定义

将那些实现了Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象,序列化可以弥补不同操作系统之间的差异。

7.2 作用

Java远程方法调用(RMI);

对JavaBeans进行序列化。

7.3 序列化和反序列化

(一)实现方式:

实现Serializable接口:该接口仅为可序列化的标志,无实际属性和方法。若不添加readObject()和writeObject()方法,采用默认序列化机制;

实现Externalizable接口:能自主控制序列化内容,决定那些属性可被序列化。

(二)反序列化:

实现Serializable接口的对象,反序列化时不调用类的构造方法,完全基于字节操作;

实现Externalizable接口的对象,反序列化时会调用构造方法。

(三)注意事项:

被static修饰的属性不参与序列化;

对象的类名、属性会被序列化,方法不会;

确保序列化对象所在类的属性也可被序列化;

通过网络、文件序列化时,需按写入顺序读取对象;

反序列化必须有对应类的class文件。

(四)常见序列化协议

COM、CORBA、XML&SOAP、JSON、Thirft、Protobuf、Avro。

协议对比:XML序列化性能和简洁性差,Thrift与Protobuf相比,在时空开销方面有劣势;Protobuf和Avro在性能和简洁性等方面表现优越。

8.comparable接口和comparator接口

8.1 定义

comparable接口(自然排序):使用Array或者Collection的排序方法时,自定义类需要实现该接口的compareTo(TOBJ)方法。

comparator接口(比较器排序):可以实现两个对象的特定字段比较(如比较两个员工年龄),该接口的compare(Object o1,Object o2)方法可以传递两个对象。

8.2 区别

自然排序用于单逻辑:比如我一直需要按照年龄从小到大排序。

比较器排序适用于多逻辑:比如我现在需要从小到大排,后面我又需要从大到小排,此时只需要修改传入的比较器对象即可完成这一部分改变。

9.接口和抽象类

10.Socket

Socket(套接字)是在传输层提供的一种抽象,它使得不同主机之间的进程能够进行通信,以下是 Socket 通信原理的详细介绍:

10.1 网络分层

常见的是 TCP/IP 四层模型(应用层、传输层、网络层、数据链路层);

Socket 位于应用层和传输层之间,为应用程序提供了一个访问传输层服务的接口,应用程序通过 Socket 来发送和接收数据,无需关心底层网络协议的复杂细节。

10.2 通信

(一)准备

创建Socket对象:

java 复制代码
//创建客户端Socket
Socket socket=new Socket();

绑定端口:

java 复制代码
//在服务器端创建监听指定接口:port的sserverSocket
ServerSocket serverSocket=new ServerSocket(port);

监听连接(服务器端):

java 复制代码
serverSocket.listen();

(二)建立连接(以TCP为例)

TCP 是一种面向连接的可靠传输协议,建立连接的过程通常被称为三次握手:

第一次握手:客户端向服务器发送一个带有 SYN(同步序列号)标志的 TCP 报文段,该报文段包含客户端的初始序列号(client_isn),表示客户端请求建立连接。

第二次握手:服务器接收到客户端的 SYN 报文后,向客户端发送一个带有 SYN 和 ACK(确认)标志的 TCP 报文段。其中 ACK 标志位用于确认收到客户端的 SYN,确认号为client_isn + 1,同时服务器也发送自己的初始序列号(server_isn) 。

第三次握手:客户端收到服务器的 SYN + ACK 报文后,向服务器发送一个带有 ACK 标志的 TCP 报文,确认号为server_isn + 1,表示客户端确认收到服务器的 SYN + ACK 报文,至此连接建立完成。

(三)数据传输

**发送数据:**连接建立后,应用程序可以通过 Socket 发送数据。数据会被传递给传输层,传输层根据选择的协议(如 TCP 或 UDP)对数据进行封装。以 TCP 为例,数据会被分割成合适大小的 TCP 报文段,添加 TCP 头部信息(包含源端口、目的端口、序列号、确认号等),然后交给网络层。网络层再添加 IP 头部信息(包含源 IP 地址、目的 IP 地址等),继续向下传递,最终通过物理网络发送出去。

**接收数据:**当数据到达目标主机时,从物理层开始逐层向上解封装。网络层去掉 IP 头部信息,将数据交给传输层;传输层检查 TCP 头部信息,进行确认、排序等操作后,去掉 TCP 头部信息,将数据交给应用层的 Socket,应用程序就可以从 Socket 中读取接收到的数据。

(四)关闭连接(以TCP为例)

TCP 连接的关闭通常需要四次挥手:

第一次挥手:客户端向服务器发送一个带有 FIN(结束)标志的 TCP 报文段,表示客户端数据发送完毕,请求关闭连接。

第二次挥手:服务器收到客户端的 FIN 报文后,向客户端发送一个带有 ACK 标志的 TCP 报文,确认收到客户端的 FIN,此时客户端到服务器的连接关闭,但服务器到客户端的连接仍然保持。

第三次挥手:服务器处理完剩余数据后,向客户端发送一个带有 FIN 标志的 TCP 报文,表示服务器也准备关闭连接。

第四次挥手:客户端收到服务器的 FIN 报文后,向服务器发送一个带有 ACK 标志的 TCP 报文,确认收到服务器的 FIN,此时服务器到客户端的连接也关闭,整个 TCP 连接完全关闭。

10.3 UDP通信特点

UDP 是一种无连接的不可靠传输协议,与 TCP 不同,它不需要建立连接和进行挥手操作。应用程序直接通过 UDP Socket 发送数据,数据被封装成 UDP 数据报,添加 UDP 头部信息(包含源端口、目的端口、长度等)后交给网络层发送。UDP 不保证数据的可靠传输,可能会出现数据丢失、乱序等情况,但它的传输效率高,适合对实时性要求高、对数据准确性要求相对较低的应用场景,如视频直播、网络游戏等。

11.Runtime类

11.1 作用

Runtime类封装了JVM(Java虚拟机),每个Java程序启动会对应一个JVM进程,每个进程对应一个由JVM实例化的Runtime实例。

11.2 实例获取

由于Runtime类的构造方法被私有化,不能直接实例化,应用通过public static Runtime getRuntime()这个静态方法获取当前Runtime运行时对象的引用。

11.3 设计模式

这种通过静态方法获取唯一实例的方式,符合单例设计模式。获取实例后,可调用Runtime对象的方法控制Java虚拟机的状态和行为。

12.值的传递与引用传递

12.1 值传递

针对基本型变量,传递的是变量的一个副本,修改副本不会对原变量产生影响。

12.2 引用传递

通常针对对象型变量,传递的是对象地址的一个副本,并非原对象本身。

一般观点认为,Java 里的传递都属于值传递,但 Java 中实例对象的传递是引用传递。

13.泛型中的"T"与"?(通配符)"

T(类型参数):在使用泛型方法(如:show1方法)时,T是一个具体的类型占位符,调用方法时会确定T代表的实际类型。

如List<Student> list1,调用此方法T就为Student类型,若添加其他类型就会报错。

?(通配符):表示集合可以是任意类型的泛型集合。

14.枚举类Enum

定义的枚举类(如Day)会被编译为一个继承Enum类的最终类(final class),这限制了枚举类不能被继承。

编译器会为枚举类自动添加一些方法:

  1. value()方法:返回包含所有枚举实例的数组,方便遍历枚举值,其内部是返回枚举实例数组的克隆。
  2. valueOf(String s)方法:根据字符串参数获取对应的枚举实例,内部间接调用了Enum类的valueOf方法。

枚举类有私有构造函数,确保枚举类只能在类内部创建。

枚举类中定义的每个枚举实例(如:MONDAY,TUESDAY等),都会被编译成public static final修饰的枚举类类型变量,且在静态代码块中完成这些枚举实例的初始化,同时还会创建一个包含所有枚举实例的数组(如$VALUES),用于支持values()方法等操作。

15.Java注解

@Override:用于标明方法覆盖了父类的方法(重写)。

@Deprecated:用于标明已过时的方法或类--传递废弃信息--保持旧代码兼容,平滑过渡。

@SuppressWarnings:用于有选择地关闭编译器对类、方法、成员变量、变量初始化等的警告。其作用目标涵盖类型、字段、方法、参数、构造方法、局部变量等,保留策略为源码级别,且有String[] value()方法,用于指定要抑制的警告类型。

16.字节流,字符流

16.1 字节流

基类:InputStream(输入)和OutputStream(输出);

文件:FileInputStream(文件字节输入)和FileOutputStream(文件字节输出);

数组:ByteArrayInputStream(字节数组输入)和ByteArrayOutputStream(字节数组输出);

操作时不使用缓冲区,直接与文件交互,若不关闭资源(close方法),文件也能输出。

16.2 字符流

基类:Reader(输入)和Writer(输出);

缓冲:BufferedReader(带缓冲的字符输入)和BufferedWriter(带缓冲字符输出);

文件:FileReader(文件字符输入)和FileWriter(文件字符输出);

用于处理字符、字符数组或字符串,操作时使用缓冲区,若不使用close方法且不强制刷新(flush方法),则不会输出内容。

16.3 转换桥梁

InputStreamReader类是字节流到字符流的桥梁,能读入字节并根据指定编码转换为字符流。

16.4 主要区别

(一)缓冲区使用:字节流操作不使用缓冲区,字符流使用缓冲区。

(二)资源关闭与输出:字节流不关闭资源也可能输出,字符流不关闭且不刷新则无输出。

(三)读取返回值范围:Reader的read()方法返回0-65535范围的字符(占2字节),InputStream的read()方法返回0-255范围的字节,对于像汉字这类不能用0-255表示的值,需用字符流读取。

(四)处理对象:字节流处理字节、字节数组或二进制对象;字符流处理字符、字符数组或字符串。

17.静态内部类

java 复制代码
// 外部类
class Outer {
    // 外部类静态成员
    static int staticVar = 10;
    // 外部类非静态成员
    int nonStaticVar = 20;

    // 静态内部类(嵌套类)
    static class Inner {
        // 静态内部类静态成员
        static int innerStaticVar = 30;
        // 静态内部类非静态成员
        int innerNonStaticVar = 40;

        // 静态内部类方法
        void innerMethod() {
            // 可访问外部类静态成员
            System.out.println("外部类静态变量:" + staticVar);
            // 不可访问外部类非静态成员(编译报错)
            // System.out.println("外部类非静态变量:" + nonStaticVar);
            
            // 访问自身静态和非静态成员
            System.out.println("内部类静态变量:" + innerStaticVar);
            System.out.println("内部类非静态变量:" + innerNonStaticVar);
        }
    }

    // 外部类方法
    void outerMethod() {
        // 访问静态内部类静态成员(直接通过内部类访问)
        System.out.println("内部类静态变量:" + Inner.innerStaticVar);
        
        // 访问静态内部类非静态成员(需实例化内部类)
        Inner inner = new Inner();
        System.out.println("内部类非静态变量:" + inner.innerNonStaticVar);
    }
}

// 测试类
public class Test {
    public static void main(String[] args) {
        // 实例化静态内部类(无需外部类实例)
        Outer.Inner inner = new Outer.Inner();
        inner.innerMethod();

        // 实例化外部类并调用其方法
        Outer outer = new Outer();
        outer.outerMethod();
    }
}

代码展示了Java中静态内部类的使用规则:

定义与实例化: 静态内部类被声明为static,可独立于外部类实例化,无需依赖外部类对象。普通内部类需外部类实例化后才能实例化,而静态内部类可直接通过外部类.内部类 方式实例化。

成员访问规则:

静态内部类内部:不能访问外部类的非静态成员(变量和方法),但可访问外部类的静态成员;自身可定义静态或非静态成员。

外部类访问静态内部类成员:不能直接访问,需通过静态内部类的实例来访问其非静态成员,可直接通过内部类.静态成员访问其静态成员

与非静态内部类的区别:

静态内部类可拥有静态成员,非静态内部类不能有静态成员。

静态内部类的非静态成员可访问外部类静态变量,不可访问外部类非静态变量;非静态内部类的非静态成员可访问外部类非静态变量。

非静态内部类可随意访问外部类成员(包括私有成员),静态内部类对象无法访问外部类非静态成员。

作用域:

静态内部类(如Person类)仅在其外部类(如StaticTest类)范围可见,其他类中引用或初始化会报错。

18.匿名类

**基本定义:**没有名字的内部类。

**成员与方法限制:**不能定义任何静态成员、方法;其中的方法不能是抽象的。

**实现要求:**必须实现接口或抽象父类的所有抽象方法,且因为创建时会立即实例化,所以不能是抽象类。

**继承与实现:**会继承一个父类(有且只有一个)或实现一个接口(有且只有一个),可实现父类或接口中所有抽象方法,也能改写父类方法、添加自定义方法。

**外部类成员访问:**访问的外部类成员变量或成员方法必须用static修饰

**构造器与同名成员:**因为没有类名,不能定义构造器;当与外部类有同名变量(方法)时,默认访问匿名内部类的,要访问外部类的需加上外部类的类名。

相关推荐
迎風吹頭髮3 小时前
UNIX下C语言编程与实践63-UNIX 并发 Socket 编程:非阻塞套接字与轮询模型
java·c语言·unix
我是华为OD~HR~栗栗呀3 小时前
23届考研-Java面经(华为OD)
java·c++·python·华为od·华为·面试
Javatutouhouduan3 小时前
Java程序员如何深入学习JVM底层原理?
java·jvm·java面试·后端开发·java架构师·java程序员·互联网大厂
王嘉俊9253 小时前
设计模式--享元模式:优化内存使用的轻量级设计
java·设计模式·享元模式
2301_803554524 小时前
C++联合体(Union)详解:与结构体的区别、联系与深度解析
java·c++·算法
EnCi Zheng4 小时前
SpringBoot 配置文件完全指南-从入门到精通
java·spring boot·后端
烙印6014 小时前
Spring容器的心脏:深度解析refresh()方法(上)
java·后端·spring
为什么我不是源代码5 小时前
JPA读取数据库离谱问题-No property ‘selectClassByName‘ found-Not a managed type
java·sql
Lisonseekpan5 小时前
Guava Cache 高性能本地缓存库详解与使用案例
java·spring boot·后端·缓存·guava