Java面试黄金宝典3
1. 什么是 NIO
- 原理
- 缓冲区(Buffer):
- 它是一个线性的、有限的基本数据元素序列,本质上是一块内存区域,被包装成了一个对象,以便于进行高效的数据读写操作。不同类型的基本数据都有对应的
Buffer
子类,如ByteBuffer
、IntBuffer
等。 Buffer
有三个重要属性:capacity
(容量)、position
(位置)和limit
(界限)。capacity
表示缓冲区的最大容量;position
指示下一个要读写的元素的位置;limit
表示缓冲区中可以读写的元素的上限。在读写操作过程中,这些属性会动态变化。
- 它是一个线性的、有限的基本数据元素序列,本质上是一块内存区域,被包装成了一个对象,以便于进行高效的数据读写操作。不同类型的基本数据都有对应的
- 通道(Channel):
- 通道是双向的,既可以进行读操作,也可以进行写操作,这与传统的流(
Stream
)不同,流通常是单向的(输入流或输出流)。 - 通道可以与多种数据源进行连接,如文件、网络套接字等。例如,
FileChannel
用于文件的读写,SocketChannel
用于网络套接字的读写。
- 通道是双向的,既可以进行读操作,也可以进行写操作,这与传统的流(
- 选择器(Selector):
- 选择器可以让一个线程同时监听多个通道的事件。它通过
Selector
对象的select()
方法来检查是否有通道发生了感兴趣的事件(如读就绪、写就绪等)。 - 当有事件发生时,
select()
方法会返回发生事件的通道的数量,然后可以通过selectedKeys()
方法获取发生事件的通道的SelectionKey
集合,进而处理这些通道的事件。
- 选择器可以让一个线程同时监听多个通道的事件。它通过
- 要点
- 非阻塞 I/O 模型使得一个线程可以处理多个连接,大大提高了系统的并发处理能力和资源利用率。
- 基于缓冲区和通道的操作方式,减少了数据的复制次数,提高了数据传输效率。
- 适用于构建高性能的网络服务器,如 HTTP 服务器、即时通讯服务器等。
- 应用
- NIO.2 引入了异步 I/O 操作,通过
AsynchronousFileChannel
和AsynchronousSocketChannel
等类,实现了真正的异步文件和网络 I/O,进一步提高了系统的性能和响应速度。 - 在实际应用中,Netty 框架是基于 Java NIO 构建的高性能网络编程框架,它简化了 NIO 编程的复杂性,提供了更强大的功能和更好的性能。
2. 什么是 ThreadLocal
- 原理
ThreadLocal
内部维护的ThreadLocalMap
是一个自定义的哈希表,它的键是ThreadLocal
对象的弱引用。当ThreadLocal
对象没有其他强引用时,垃圾回收器会自动回收该对象,从而避免了内存泄漏的风险。- 每个
Thread
对象都有一个ThreadLocalMap
实例,当调用ThreadLocal
的set()
方法时,会首先获取当前线程的ThreadLocalMap
,如果ThreadLocalMap
为空,则会创建一个新的ThreadLocalMap
,并将当前ThreadLocal
对象作为键,要存储的值作为值存储到ThreadLocalMap
中;当调用get()
方法时,会从当前线程的ThreadLocalMap
中获取对应的值。
- 要点
- 为每个线程提供独立的变量副本,避免了多线程之间的变量共享问题,提高了代码的线程安全性。
- 常用于存储与线程相关的上下文信息,如数据库连接、用户会话信息等。
- 应用
- 在使用
ThreadLocal
时,需要注意内存泄漏问题。除了及时调用remove()
方法清除数据外,还可以使用弱引用的ThreadLocal
对象,让垃圾回收器自动回收不再使用的ThreadLocal
对象。 - 在 Java 8 中,
ThreadLocal
提供了withInitial()
方法,用于创建一个带有初始值的ThreadLocal
对象,简化了ThreadLocal
的使用。
3. 什么是 finalize, finalization, finally,有什么区别
- 原理
- finalize:
Object
类中的finalize()
方法是一个受保护的方法,当垃圾回收器确定对象没有任何引用时,会在回收对象之前调用该对象的finalize()
方法。finalize()
方法的目的是让对象在被回收之前有机会进行一些资源清理工作,如关闭文件、释放数据库连接等。 - finalization:指的是对象的终结过程,包括垃圾回收器标记对象为可回收对象、调用对象的
finalize()
方法(如果有的话)以及回收对象占用的内存。 - finally:
finally
是 Java 中的一个关键字,用于try-catch
语句块中。无论try
块中的代码是否抛出异常,finally
块中的代码都会被执行。这是因为finally
块的执行是由 Java 虚拟机(JVM)保证的,即使在try
或catch
块中使用了return
、break
或continue
语句,finally
块中的代码也会在这些语句执行之前被执行。
- 要点
finalize
方法的执行时间不确定,且不保证一定会被调用,因此不建议依赖它进行资源清理。finalization
是对象的整个终结过程,包括多个步骤。finally
用于确保某些代码一定会被执行,常用于资源的释放,如关闭文件、释放数据库连接等。
- 应用
- 在 Java 7 及以后的版本中,引入了
try-with-resources
语句,它可以自动关闭实现了AutoCloseable
接口的资源,进一步简化了资源管理的代码。 - 由于
finalize()
方法的性能开销较大,且可能导致内存泄漏,从 Java 9 开始,finalize()
方法被标记为@Deprecated
,建议使用其他方式进行资源清理。
4. 什么是 Object
- 原理
- 在 Java 中,所有的类都直接或间接地继承自
Object
类。这意味着所有的类都可以使用Object
类中定义的方法,如equals()
、hashCode()
、toString()
、clone()
等。 - 当创建一个对象时,该对象会自动继承
Object
类的所有方法和属性。如果子类没有重写这些方法,则会使用Object
类中默认的实现。
- 要点
Object
类是 Java 类层次结构的根,为所有类提供了一些通用的方法,这些方法是 Java 面向对象编程的基础。- 可以将任何类型的对象赋值给
Object
类型的变量,这体现了 Java 的多态性。
- 应用
- 在实际开发中,经常需要重写
Object
类的一些方法,以满足特定的业务需求。例如,重写equals()
方法可以实现对象内容的比较,重写toString()
方法可以方便地输出对象的信息。 Object
类的clone()
方法用于创建对象的副本,但默认的clone()
方法是浅克隆,即只复制对象的基本数据类型和引用,而不复制引用指向的对象。如果需要深克隆,则需要在子类中重写clone()
方法。
5. equals 和 == 的区别
- 原理
- ==:对于基本数据类型,
==
比较的是两个变量的值是否相等;对于引用数据类型,==
比较的是两个对象的引用是否相等,即它们是否指向同一个内存地址。 - equals:在
Object
类中,equals()
方法的默认实现是比较两个对象的引用是否相等,与==
的作用相同。但很多类(如String
、Integer
等)会重写equals()
方法,以实现自定义的内容比较逻辑。例如,String
类的equals()
方法会比较两个字符串的内容是否相等。
- 要点
==
比较的是对象的引用或基本数据类型的值,而equals()
比较的是对象的内容。- 在比较对象时,应该根据具体需求选择使用
==
还是equals()
方法。
- 应用
- 在重写
equals()
方法时,通常需要遵循以下几个原则:自反性、对称性、传递性、一致性和非空性。 - 重写
equals()
方法时,通常也需要重写hashCode()
方法,以保证相等的对象具有相同的哈希码,这在使用哈希表(如HashMap
、HashSet
等)时非常重要。
6. 什么是 public, private, default, protected,有什么区别
- 原理
- public:被
public
修饰的类、方法和变量可以被任何其他类访问,无论这些类是否在同一个包中。 - private:被
private
修饰的类、方法和变量只能在其所在的类内部访问,其他类无法直接访问。 - default:当没有使用任何访问修饰符时,默认就是
default
权限。被default
修饰的类、方法和变量只能在其所在的包内访问,不同包的类无法访问。 - protected:被
protected
修饰的类、方法和变量可以在其所在的包内访问,也可以在不同包的子类中访问。在不同包的子类中,可以通过子类对象访问父类的protected
成员。
- 要点
public
的访问权限最大,private
的访问权限最小。default
和protected
的区别在于,protected
允许不同包的子类访问。
- 应用
- 合理使用访问修饰符可以提高代码的封装性和安全性,隐藏类的实现细节,只暴露必要的接口给外部使用。
- 在设计类和接口时,应该根据类和成员的使用场景和安全性要求,选择合适的访问修饰符。
7. 什么是异常
- 原理
- 在 Java 中,异常是指程序在运行过程中出现的错误或意外情况,它会导致程序的正常执行流程被打断。Java 中的异常是通过异常类来表示的,所有的异常类都继承自
Throwable
类。 Throwable
类有两个重要的子类:Error
和Exception
。Error
表示系统级的错误,如内存溢出(OutOfMemoryError
)、栈溢出(StackOverflowError
)等,这些错误通常是不可恢复的,程序无法处理;Exception
表示程序可以处理的异常,又分为检查异常(Checked Exception)和非检查异常(Unchecked Exception)。
- 要点
- 异常分为检查异常和非检查异常。检查异常必须在方法的声明中使用
throws
关键字声明,或者在方法内部使用try-catch
语句进行处理;非检查异常(如RuntimeException
及其子类)不需要进行声明或处理。 - 异常处理可以提高程序的健壮性,避免程序因意外情况而崩溃。
- 应用
- Java 中提供了
try-catch
、try-with-resources
、throws
和throw
等关键字来进行异常的捕获、处理和抛出。在实际开发中,应该根据具体情况选择合适的异常处理方式。 - 自定义异常类可以继承自
Exception
或RuntimeException
,用于表示特定的业务异常,提高代码的可读性和可维护性。
8. 什么是 Comparable 接口和 Comparator 接口
- 原理
- Comparable 接口:该接口只有一个
compareTo()
方法,用于定义对象的自然排序规则。实现了Comparable
接口的类的对象可以进行比较和排序。compareTo()
方法返回一个整数值,如果返回值小于 0,表示当前对象小于参数对象;如果返回值等于 0,表示当前对象等于参数对象;如果返回值大于 0,表示当前对象大于参数对象。 - Comparator 接口:该接口有多个方法,常用的是
compare()
方法,用于定义对象的自定义排序规则。Comparator
接口可以在不修改对象类的情况下,为对象提供不同的排序方式。compare()
方法的返回值规则与compareTo()
方法相同。
- 要点
Comparable
接口是类内部的排序规则,而Comparator
接口是类外部的排序规则。- 当需要对对象进行排序时,可以根据具体情况选择使用
Comparable
接口或Comparator
接口。
- 应用
- 在使用
Collections.sort()
或Arrays.sort()
方法对对象进行排序时,如果对象类实现了Comparable
接口,则可以直接进行排序;如果需要自定义排序规则,则可以传入一个Comparator
对象。 Comparator
接口还提供了一些静态方法,如comparing()
、thenComparing()
等,用于方便地创建和组合比较器。
9. 什么是接口和抽象类
- 原理
- 接口:是一种抽象类型,它定义了一组方法的签名,但没有提供方法的实现。接口中的方法默认是
public abstract
的,变量默认是public static final
的。一个类可以实现多个接口,通过实现接口中的方法来满足接口的规范。 - 抽象类:是一种不能被实例化的类,它可以包含抽象方法(只有方法签名,没有方法体)和具体方法。抽象类的主要作用是为子类提供一个通用的模板,子类必须实现抽象类中的抽象方法,同时可以继承抽象类中的具体方法。
- 要点
- 接口强调的是行为的规范,而抽象类强调的是类的抽象和继承。
- 接口用于实现多继承的效果,而抽象类用于代码的复用和扩展。
- 应用
- 在 Java 8 及以后的版本中,接口可以包含默认方法和静态方法,默认方法提供了方法的默认实现,静态方法可以通过接口名直接调用。
- 在设计类和接口时,应该根据具体需求选择使用接口还是抽象类。如果需要定义一组规范,让不同的类去实现,并且类之间没有太多的代码复用,可以使用接口;如果需要对一些类进行抽象和复用代码,可以使用抽象类。
10. 什么是 Socket
- 原理
- 客户端(Socket):通过创建
Socket
对象,指定服务器的 IP 地址和端口号,与服务器建立连接。连接建立后,就可以通过Socket
的输入输出流进行数据的读写操作。Socket
的输入流用于接收服务器发送的数据,输出流用于向服务器发送数据。 - 服务器端(ServerSocket):通过创建
ServerSocket
对象,指定监听的端口号,等待客户端的连接请求。当有客户端连接时,ServerSocket
会返回一个Socket
对象,用于与客户端进行通信。服务器端可以通过该Socket
对象的输入输出流与客户端进行数据的读写操作。
- 要点
Socket
是基于 TCP 协议的,提供可靠的、面向连接的通信。- 适用于需要确保数据准确传输的场景,如文件传输、邮件发送等。
- 应用
- 除了基于 TCP 协议的
Socket
,Java 还提供了基于 UDP 协议的DatagramSocket
和DatagramPacket
类,用于实现无连接的、不可靠的网络通信,适用于对实时性要求较高、对数据准确性要求较低的场景,如视频直播、实时游戏等。 - 在实际应用中,为了提高网络通信的性能和可扩展性,可以使用线程池来处理多个客户端的连接请求,避免创建过多的线程导致系统资源耗尽。
友情提示:本文已经整理成文档,可以到如下链接免积分下载阅读
https://download.csdn.net/download/ylfhpy/90496058