Java基础面试题(二)

📅 发布时间:2026/7/5 7:07:41 👁️ 浏览次数:
Java基础面试题(二)
一、String相关面试题1. 为什么 String 在 java 中是不可变的?如果不是不可变的这种情况根本不可能因为在字符串池的情况下一个字符串对象/文字例如 “Test” 已被许多参考变量引用因此如果其中任何一个更改了值其他参数将自动受到影响字符串被广泛作为参数使用例如为了打开网络连接你可以将主机名和端口号作为字符串传递你可以将数据库URL 作为字符串传递, 以打开数据库连接你可以通过将文件名作为参数传递给 File I/O 类来打开 Java 中的任何文件。如果String不是不可变的这将导致严重的安全威胁我的意思是有人可以访问他有权授权的任何文件然后可以故意或意外地更改文件名并获得对该文件的访问权限。由于不变性你无需担心这种威胁。这个原因也说明了为什么String 在 Java 中是最终的通过使java.lang.String finalJava设计者确保没有人覆盖 String 类的任何行为。线程间共享由于 String 是不可变的它可以安全地共享许多线程这对于多线程编程非常重要. 并且避免了 Java 中的同步问题不变性也使得String 实例在 Java 中是线程安全的这意味着你不需要从外部同步 String 操作。String 缓存其哈希码Java 中的不可变 String 缓存其哈希码并且不会在每次调用 String 的 hashCode 方法时重新计算这使得它在 Java 中的 HashMap 中使用的 HashMap 键非常快。简而言之因为 String 是不可变的所以没有人可以在创建后更改其内容这保证了 String 的 hashCode 在多次调用时是相同的。类加载机制使用如果 String 是可变的加载“java.io.Writer” 的请求可能已被更改为加载 “mil.vogoon.DiskErasingWriter”. 安全性和字符串池是使字符串不可变的主要原因2. 为什么 char 数组比 String 更适合存储密码String pass this.xxx(); char[] pass1 this.yyyy(); User user new User(); user.setPass(null); user.setPass1(null); Token tokne(user) ; 响应给前端由于字符串在 Java 中是不可变的如果你将密码存储为纯文本它将在内存中可用直到垃圾收集器清除它. 并且为了可重用性会存在 String 在字符串池中, 它很可能会保留在内存中持续很长时间从而构成安全威胁。由于任何有权访问内存转储的人都可以以明文形式找到密码这是另一个原因你应该始终使用加密密码而不是纯文本。由于字符串是不可变的所以不能更改字符串的内容因为任何更改都会产生新的字符串而如果你使用char[]你就可以将所有元素设置为空白或零。因此在字符数组中存储密码可以明显降低窃取密码的安全风险。使用 String 时总是存在在日志文件或控制台中打印纯文本的风险但如果使用 Array则不会打印数组的内容而是打印其内存位置。二、序列化相关问题1. 什么是 Java 序列化序列化是把对象改成可以存到磁盘或通过网络发送到其他运行中的Java 虚拟机的二进制格式的过程, 并可以通过反序列化恢复对象状态. Java 序列化API给开发人员提供了一个标准机制, 通过java.io.Serializable 和 java.io.Externalizable 接口, ObjectInputStream及ObjectOutputStream 处理对象序列化. Java 程序员可自由选择基于类结构的标准序列化或是他们自定义的二进制格式,通常认为后者才是最佳实践, 因为序列化的二进制文件格式成为类输出 API的一部分, 可能破坏 Java 中私有和包可见的属性的封装.2. 如何序列化让 Java中的类可以序列化很简单. 你的 Java 类只需要实现 java.io.Serializable 接口, JVM 就会把 Object对象按默认格式序列化. 让一个类是可序列化的需要有意为之. 类可序列会可能为是一个长期代价, 可能会因此而限制你修改或改变其实现.当你通过实现添加接口来更改类的结构时, 添加或删除任何字段可能会破坏默认序列化, 这可以通过自定义二进制格式使不兼容的可能性最小化,但仍需要大量的努力来确保向后兼容性。序列化如何限制你更改类的能力的一个示例是 SerialVersionUID。如果不显式声明SerialVersionUID, 则 JVM 会根据类结构生成其结构, 该结构依赖于类实现接口和可能更改的其他几个因素。假设你新版本的类文件实现的另一个接口, JVM 将生成一个不同的 SerialVersionUID 的,当你尝试加载旧版本的程序序列化的旧对象时, 你将获得无效类异常 InvalidClassException。3. Java 中的可序列化接口和可外部接口之间的区别是什么Externalizable给我们提供 writeExternal() 和 readExternal() 方法, 这让我们灵活地控制 Java 序列化机制, 而不是依赖于Java 的默认序列化。 正确实现 Externalizable 接口可以显著提高应用程序的性能。public interface Serializable { } ​ public interface Externalizable extends java.io.Serializable { /** * The object implements the writeExternal method to save its contents * by calling the methods of DataOutput for its primitive values or * calling the writeObject method of ObjectOutput for objects, strings, * and arrays. * * serialData Overriding methods should use this tag to describe * the data layout of this Externalizable object. * List the sequence of element types and, if possible, * relate the element to a public/protected field and/or * method of this Externalizable class. * * param out the stream to write the object to * exception IOException Includes any I/O exceptions that may occur */ void writeExternal(ObjectOutput out) throws IOException; ​ /** * The object implements the readExternal method to restore its * contents by calling the methods of DataInput for primitive * types and readObject for objects, strings and arrays. The * readExternal method must read the values in the same sequence * and with the same types as were written by writeExternal. * * param in the stream to read data from in order to restore the object * exception IOException if I/O errors occur * exception ClassNotFoundException If the class for an object being * restored cannot be found. */ void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; }4.serialVersionUID的作用SerialVersionUID是一个用来标识Serializable类版本的唯一标识符。它的作用是在序列化和反序列化过程中用来验证类的版本一致性确保反序列化过程中的类的版本和序列化时的版本是一致的以避免出现类版本不一致导致的问题。如果在反序列化时发现类的版本不一致就会抛出InvalidClassException异常。因此SerialVersionUID是用来确保类的版本一致性的重要机制。5. 序列化时,某些变量不希望被序列化怎么办?如果你不希望任何字段是对象的状态的一部分, 然后声明它静态或瞬态(trasient)根据你的需要, 这样就不会是在 Java 序列化过程中被包含在内。三、线程相关问题1. 创建线程的方式有哪些?直接继承Thread实现Runnable接口实现Callable接口(Runnable接口)通过线程池的方式获取2. Thread和Runnable的区别每一个线程其实就是一个Thread对象一个是继承和接口Runnable接口定义run方法的逻辑。多个Thread对象可以执行相关的逻辑3. 为什么wait和notify方法要写在同步块中?当一个线程需要调用对象的wait()方法的时候这个线程必须拥有该对象的锁接着它就会释放这个对象锁并进入等待状态直到其他线程调用这个对象上的notify()方法。同样的当一个线程需要调用对象的notify()方法时它会释放这个对象的锁以便其他在等待的线程就可以得到这个对象锁。由于所有的这些方法都需要线程持有对象的锁这样就只能通过同步来实现所以他们只能在同步方法或者同步块中被调用。如果你不这么做代码会抛出IllegalMonitorStateException异常。4.线程的生命周期是怎么样的生命周期对象从创建到销毁的全过程线程的生命周期线程对象(Thread)从开始到销毁的全过程5. 简述一下你对线程池的理解如果问到了这样的问题可以展开的说一下线程池如何用、线程池的好处、线程池的启动策略 合理利用线程池能够带来三个好处。 第一降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二提高响应速度。当任务到达时任务可以不需要等到线程创建就能立即执行。 第三提高线程的可管理性。线程是稀缺资源如果无限制的创建不仅会消耗系统资源还会降低系统的稳定性使用线程池可以进行统一的分配调优和监控。public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueRunnable workQueue, RejectedExecutionHandler handler) ​参数含义corePoolSize线程池核心线程数量 maximumPoolSize:线程池最大线程数量 keepAliverTime当活跃线程数大于核心线程数时空闲的多余线程最大存活时间 unit存活时间的单位 workQueue存放任务的队列 handler超出线程范围和队列容量的任务的处理程序线程池工作原理提交一个任务到线程池中线程池的处理流程如下判断线程池里的核心线程是否都在执行任务如果不是核心线程空闲或者还有核心线程没有被创建则创建一个新的工作线程来执行任务。如果核心线程都在执行任务则进入下个流程。线程池判断工作队列是否已满如果工作队列没有满则将新提交的任务存储在这个工作队列里。如果工作队列满了则进入下个流程。判断线程池里的线程是否都处于工作状态如果没有则创建一个新的工作线程来执行任务。如果已经满了则交给饱和策略来处理这个任务。四、IO相关面试题1.解释下同步、异步、阻塞、非阻塞同步和异步指的是当前线程是否需要等待方法调用执行完毕。阻塞和非阻塞指的是当前接口数据还未准备就绪时线程是否被阻塞挂起同步异步其实是处于框架这种高层次维度来看待的而阻塞非阻塞往往针对底层的系统调用方面来抉择也就是说两者是从不同维度来考虑的。这四个概念两两组合会形成4个新的概念如下同步阻塞客户端发送请求给服务端此时服务端处理任务时间很久则客户端则被服务端堵塞了所以客户端会一直等待服务端的响应此时客户端不能做其他任何事服务端也不会接受其他客户端的请求。这种通信机制比较简单粗暴但是效率不高。同步非阻塞客户端发送请求给服务端此时服务端处理任务时间很久这个时候虽然客户端会一直等待响应但是服务端可以处理其他的请求过一会回来处理原先的。这种方式很高效一个服务端可以处理很多请求不会在因为任务没有处理完而堵着所以这是非阻塞的。异步阻塞客户端发送请求给服务端此时服务端处理任务时间很久但是客户端不会等待服务器响应它可以做其他的任务等服务器处理完毕后再把结果响应给客户端客户端得到回调后再处理服务端的响应。这种方式可以避免客户端一直处于等待的状态优化了用户体验其实就是类似于网页里发起的ajax异步请求。异步非阻塞客户端发送请求给服务端此时服务端处理任务时间很久这个时候的任务虽然处理时间会很久但是客户端可以做其他的任务因为他是异步的可以在回调函数里处理响应同时服务端是非阻塞的所以服务端可以去处理其他的任务如此这个模式就显得非常的高效了。2.什么是BIO?BIO同步并阻塞服务器实现一个连接一个线程即客户端有连接请求时服务器端就需要启动一个线程进行处理没处理完之前此线程不能做其他操作如果是单线程的情况下我传输的文件很大呢当然可以通过线程池机制改善。BIO方式适用于连接数目比较小且固定的架构这种方式对服务器资源要求比较高并发局限于应用中JDK1.4以前的唯一选择但程序直观简单易理解。3.什么是NIONIO同步非阻塞服务器实现一个连接一个线程即客户端发送的连接请求都会注册到多路复用器上多复用器轮询到连接有I/O请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短轻操作的架构比如聊天服务器并发局限于应用中编程比较复杂JDK1.4之后开始支持。4.什么是AIOAIO异步非阻塞服务器实现模式为一个有效请求一个线程客户端的I/O请求都是由操作系统先完成了再通知服务器应用去启动线程进行处理AIO方式使用于连接数目多且连接比较长重操作的架构比如相册服务器充分调用操作系统参与并发操作编程比较复杂JDK1.7之后开始支持。AIO属于NIO包中的类实现其实IO主要分为BIO和NIOAIO只是附加品解决IO不能异步的实现在以前很少有Linux系统支持AIOWindows的IOCP就是该AIO模型。但是现在的服务器一般都是支持AIO操作5.字节流和字符流的介绍字节流继承inputStream和OutputStream字符流继承自InputSteamReader和OutputStreamWriter字符流和字节流的使用非常相似但是实际上字节流的操作不会经过缓冲区内存而是直接操作文本本身的而字符流的操作会先经过缓冲区内存然后通过缓冲区再操作文件在选择流类型时需要考虑到处理的数据类型。如果处理的是文本数据应选择字符流如果处理的是二进制数据或非文本数据应选择字节流。五、JavaWEB面试题1. 什么是网络编程网络编程的本质是多台计算机之间的数据交换。数据传递本身没有多大的难度不就是把一个设备中的数据发送给其他设备然后接受另外一个设备反馈的数据。现在的网络编程基本上都是基于请求/响应方式的也就是一个设备发送请求数据给另外一个然后接收另一个设备的反馈。在网络编程中发起连接程序也就是发送第一次请求的程序被称作客户端(Client)等待其他程序连接的程序被称作服务器(Server)。客户端程序可以在需要的时候启动而服务器为了能够时刻相应连接则需要一直启动。例如以打电话为例首先拨号的人类似于客户端接听电话的人必须保持电话畅通类似于服务器。连接一旦建立以后就客户端和服务器端就可以进行数据传递了而且两者的身份是等价的。在一些程序中程序既有客户端功能也有服务器端功能最常见的软件就是QQ、微信这类软件了。2. 网络编程中的两个主要问题是如何解决的一个是如何准确的定位网络上一台或多台主机另一个就是找到主机后如何可靠高效的进行数据传输,在TCP/IP协议中IP层主要负责网络主机的定位数据传输的路由由IP地址可以唯一地确定Internet上的一台主机。而TCP层则提供面向应用的可靠(TCP)的或非可靠(UDP)的数据传输机制这是网络编程的主要对象一般不需要关心!P层是如何处理数据的。 目前较为流行的网络编程模型是客户机/服务器(C/S)结构。即通信双方一方作为服务器等待客户提出请求并予以响应。客户则在雲要服务时向服务器提 出申请。服务器一般作为守护进程始终运行监听网络端口一旦有客户请求就会启动一个服务进程来响应该客户同时自己继续监听服务端口使后来的客户也 能及时得到服务。3. 网络协议是什么在计算机网络要做到井井有条的交换数据就必须遵守一些事先约定好的规则比如交换数据的格式、是否需要发送一个应答信息。这些规则被称为网络协议。4.介绍下OSI七层和TCP/IP四层的关系为了更好地促进互联网的研究和发展国际标准化组织ISO在1985 年指定了网络互联模型。OSI 参考模型Open System Interconnect Reference Model具有 7 层结构应用层各种应用程序协议比如HTTP、HTTPS、FTP、SOCKS安全套接字协议、DNS域名系统、GDP网关发现协议等等。表示层加密解密、转换翻译、压缩解压缩比如LPP轻量级表示协议。会话层不同机器上的用户建立和管理会话比如SSL安全套接字层协议、TLS传输层安全协议、RPC远程过程调用协议等等。传输层接受上一层的数据在必要的时候对数据进行分割并将这些数据交给网络层保证这些数据段有效到达对端比如TCP传输控制协议、UDP数据报协议。网络层控制子网的运行逻辑编址、分组传输、路由选择比如IP、IPV6、SLIP等等。数据链路层物理寻址同时将原始比特流转变为逻辑传输路线比如XTP压缩传输协议、PPTP点对点隧道协议等等。物理层机械、电子、定时接口通信信道上的原始比特流传输比如IEEE802.2等等。而且在消息通信的过程中具体的执行流程为网络传输的数据其实会通过这七层协议来进行数据的封装和拆解5.TCP原理三次握手1.第一次握手客户端将标志位syn重置为1随机产生seqa并将数据包发送给服务端 2.第二次握手服务端收到syn1知道客户端请求连接服务端将syn和ACK都重置为1acka1随机产一个值seqb并将数据包发送给客户端服务端进入syn_RCVD状态。 3.第三次握手客户端收到确认后检查ack是否为a1ACK是否为1若正确将ACK重置为1将ack改为b1然后将数据包发送给服务端服务端检查ack与ACK,若都正确就建立连接进入ESTABLISHEN.四次挥手1.开始双方都处于连接状态 2.客户端进程发出FIN报文并停止发送数据在报文中FIN结束标志为1seq为a连接状态下发送给服务器的最后一个字节的序号1报文发送结束后客户端进入FIN-WIT1状态。 3.服务端收到报文向客户端发送确认报文ACK1,seq为b服务端给客户端发送的最后字节的序号1acka1发送后客户端进入close-wait状态不再发送数据但服务端发送数据客户端一九可以收到城为半关闭状态。 4.客户端收到服务器的确认报文后客户端进入fin-wait2状态进行等待服务器发送第三次的挥手报文。 5.服务端向fin报文FIN1ACK1seqc服务器向客户端发送最后一个字节序号1ackb1发送结束后服务器进入last-ack状态等待最后的确认。 6.客户端收到是释放报文后向服务器发送确认报文进入time-wait状态后进入close 7.服务端收到确认报文进入close状态。