JAVA八股文
一、JAVA基础
1.面向对象:
面向对象编程是一种以对象为核心的编程,通过封装、继承、多态和抽象管理代码。
1.封装:将数据(属性)和行为(方法)绑定在一个对象中,隐藏内部细节,仅通过公开接口与外界交互。
目的:保护数据完整性、降低模块耦合度
2.继承:从已有的类得到继承信息创建新类的过程,子类继承父类的属性和方法,实现代码复用和层次化设计
单继承:一个类只能继承一个父类(接口可以多继承)
3.多态:允许不同类型子类的对象对同一消息做出不同的响应
编译时多态:方法重载
运行时多态:方法重写、向上转型
4.抽象:将同一类对象的共同特征总结出来构造类的过程,数据抽象(属性)、行为抽象(方法)
2.重载重写
重载:在同一个类中,多个方法方法名相同,但参数列表不同(参数类型、个数或顺序不同)。重载与返回值类型和访问修饰符无关,是编译时多态的体现。
重写:子类重新定义父类中已有的方法,要求方法名、参数列表、返回类型完全相同(或返回类型为父类方法返回类型的子类),是运行时多态的体现。重写方法的权限大于父类的权限,构造方法不能重写。
3.接口、抽象类
接口:被类实现,可多继承,不能有构造器,只能用public修饰,只能声明变量
抽象类:被类继承,单继承,可以有构造器,可以有public、protected和default这些修饰符,可以有成员变量,不能被实例化
4.深拷贝、浅拷贝
深拷贝:既会拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行复制,深拷贝出来的对象,内部的类执行指向的不是同一个对象
浅拷贝:拷贝基本数据类型的值,实例对象的引用地址,并不会复制一份引用地址所指向的对象,也就是浅拷贝出来的对象,内部的类属性指向的是同一个对象
-
浅拷贝:只复制表面,内部的东西还是原来的(共享)。只复制表面一层
数据简单,或者你希望共享内部对象(比如多个配置共用同一个默认参数)
-
深拷贝:里里外外全部复制一份(完全独立)。彻底复制所有东西
数据复杂(比如多层嵌套的字典/列表),且需要完全独立修改。
5.sleep、wait
sleep(休眠):
作用:让当前线程(或程序)暂停一段时间,不释放任何资源(比如锁)
特点:
不参与线程间协作:单纯地等待时间流逝,时间到了自动恢复。
不释放锁:如果线程持有锁,其他线程会一直等待。
适用场景:
模拟耗时操作(如下载延迟)。
定时任务(每隔一段时间执行一次)。
wait(等待):
作用:让当前线程主动让出资源并等待,直到被其他线程唤醒。会释放锁。
特点:
用于线程间协作:通常和 notify()
/notify_all()
配合使用。
释放锁:其他线程可以获取锁并执行任务。
适用场景:
生产者-消费者模型(消费者等待生产者生产数据)。
多线程任务协调(如等待某个条件达成)。
-
sleep
:暂停一段时间,不关心其他线程,自己到点继续执行。 -
wait
:主动让出资源,等待其他线程唤醒自己(协作必备)。
6.自动装箱、自动拆箱, int和Integer
装箱:将基本类型转换成包装类对象
拆箱:将包装类对象转换成基本类型的值
基本数据类型和包装类之间的自动互相转换
1.Integer是int的包装类,int是java的一种基本数据类型
2.Integer必须实例化后才能使用,int不需要
3.Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象,而int则是直接存储数据值
4.Integer默认值是null,int默认值是0
7.==、equals
==:
基本数据类型:比较的是变量的值
引用数据类型:比较的是地址值(两个对象是否指向同一块内存)
equals(equals方法是从Object类中继承的,默认的实现就是使用==):
没有重写:比较的是两个对象的地址值
重写:比较的是两个对象中的属性内容
8.Strin为什么不能被继承 为什么用final修饰
不能被继承,因为String类有final修饰符,而final修饰的类是不能被继承的。
为什么String要用final修饰?
保证不可变性:
一旦创建,其值不能被修改
final防止子类破坏不可变性:如果允许继承,子类可能通过重写方法改变内部字符数组
安全性:
String广泛用于敏感操作:如网络连接、文件路径、密码存储等。如果允许子类化,恶意代码可能伪造一个“看起来正常”的String,实际在内部窃取数据。
防止哈希漏洞:String
是HashMap
的常用键。若子类重写hashCode()
或equals()
,可能导致哈希表行为异常。
9.String buffer和String builder
1.StringBuffer 与 StringBuilder 中的方法和功能完全是等价的
2.只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。
3.在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全而StringBuffer则每次都需要判断锁,效率相对更低
10.final、finally、finalize :
1.final:修饰符(关键字)有三种用法:修饰类、变量和方法。修饰类时,意味着它不能再派生出新的子类,即不能被继承,因此它和abstract是反义词。修饰变量时,该变量使用中不被改变,必须在声明时给定初值,在引用中只能读取不可修改,即为常量。修饰方法时,也同样只能使用,不能在子类中被重写。
2.finally:通常放在try…catch的后面构造最终执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。
3.finalize:Object类中定义的方法,Java中允许使用finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize() 方法可以整理系统资源或者执行其他清理工作。
11.集合体系:
Collection(单列集合):List(有序、可重复):ArrayList(数组实现,查询快)、LinkedList(链表实现,增删快)、Vector(线程安全,已过时):Stack(栈结构)Set(无序、唯一):TreeSet(红黑树实现,有序)、HashSet(哈希表实现):LinkedHashSet(保持插入顺序)
Map(双列集合,键值对):Hashtable(线程安全,已过时)、TreeMap(红黑树实现,按键排序)、 ConcurrentHashMap(线程安全的高效实现)、HashMap(哈希表实现):LinkedHashMap(保持插入顺序)
13.ArrarList和LinkedList
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList效率优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。 这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。
14.HashMap底层是 数组+链表+红黑树,为什么要用这几类结构
1.数组:
作用:数组是hashmap的主干,通过哈希函数快速定位元素的位置
用数组的原因:哈希表单本质是用空间换时间,数组的索引访问时间复杂度o(1),可以快速定位到目标
2.链表(解决哈希冲突):
作用:当不同键的哈希值冲突(被映射到一个数组)时,用链表将这些键值对串联起来
用链表的原因:
简单高效:链表的插入和操作是o(1)
冲突处理:哈希冲突不可避免(不同键可能哈希值相同),链表是经典解决方案
如果链表过长(哈希函数设计差或恶意攻击),查询效率会退化为o(n),性能急剧下降
3.红黑树(优化链表过长问题)
作用:当链表长度超过阈值(默认8)时,链表会转为红黑树,当长度减少到阈值以下(默认6),红黑树会退化为链表
用红黑树的原因:
平衡性:红黑树是近似平衡的二叉搜索树,查询、插入、删除的时间复杂度均为o (logn),避免链表退化o(n)
性能稳定:即使哈希冲突严重,也能保证比较好的性能
为什么不同其他树?红黑树的平衡条件更宽松,插入/删除时旋转操作更少,综合性能更高
15.HashMap和HashTable区别
1.线程安全:
hashmap是线程不安全的,使用HashMap时必须自己增加同步处理;hashtable是线程安全的,其中的方法是Synchronized,在多线程并发的情况下,可以直接使用HashTable
2.contains方法:
hashmap:只有containsValue和containsKey方法
hashtable:有contains、containsKey和containsValue三个方法,其中contains和containsValue方法功能相同。
3.key和value:
hashtable中,key和value都不允许出现null值
hashtable中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null
16、线程的创建方式
1.继承Thread类创建线程:
(1)定义一个子类继承线程类,重写run()方法
(2)创建子类对象
(3)调用对象start()方法启动线程(启动后还是执行run()方法)
优点:代码简单
缺点:已经继承了Thread类无法继承其他类,不利于扩展,任务逻辑和线程不够灵活
2.实现Runnable接口创建线程:
(1)定义一个线程任务类实现Runnable接口,重写run()方法
(2)创建任务对象
(3)把任务对象交给Thread处理
(4)调用start()方法启动线程
优点:线程任务只是实现接口,可以继续继承和实现接口,扩展性强
缺点:不能直接返回结果
3.使用Callable和Future创建线程 有返回值:
(1)得到任务对象:定义类实现Callable接口,重写call方法,封装要做的事,用FutureTask把Callable对象封装成线程任务对象
(2)把线程任务对象交给Thread处理
(3)调用Thread的start方法启动线程,执行任务
(4)线程执行完毕后,通过FutureTask的get方法去获取结果
优点:扩展性强,实现接口的同时还可以继承其他类,同时还能得到线程执行的结果
缺点:代码复杂
4.使用线程池创建线程:
线程池接口:ExecutorService
创建:
1.使用ExecutorService的实现类ThreadPoolExectutor创建一个线程池对象
2.使用Executor(线程池的工具类)调用方法返回不同特点的线程池对象
public ThreadPoolExecutor(int corePoolSize,//核心线程数int maximumPoolSize,//最大线程数long keepAliveTime,//线程空闲时间TimeUnit unit,//时间单位BlockingQueue<Runnable> workQueue,//任务队列ThreadFactory threadFactory,//线程工厂RejectedExecutionHandler handler//拒绝策略)
{...
}
17.线程的状态:
1.新建状态(New) :线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
2.就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
3.运行状态(Running):线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
4.阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(1)等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
(2)同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
(3)其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5.死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
18.流
字节流:
InputStream(输入流):
File InputStream:从文件系统中的某个文件获得输入字节用于读取诸如图像数据之类的原始字节流
Buffered InputStream:缓冲流,是InputStream的间接子类
OutputStream(输出流):
File OutputStream:用于写入诸如图像数据之类的原始字节流
Buffered OutputStream:缓冲流,是OutputStream的间接子类
字符流:
Reader(输入流):
Buffered Reader:缓冲流,特有方法readLine()
InputStreamReader:转换流,字节流通向字符流的桥梁
(FileReader:读取字符文件的便捷类)
Writer(输出流):
Buffered Writer:缓冲流,特有方法newLine()、write(String str)
OutputStreamWriter:转换流,字符流通向字节流的桥梁
(FileWriter:写入字符文件的便捷类)
19.异常
NullPointerException:空指针异常;调用了未经初始化的对象或者是不存在的对象
ClassNotFoundException:指定的类找不到;类的名称和路径加载错误,通常都是程序试图通过字符串来加载某个类时可能引发异常
NumberFormatException:字符串转换为数字异常;字符型数据中包含非数字型字符
IndexOutOfBoundsException:数组角标越界异常,常见于操作数组对象时发生
IllegalArgumentException:方法传递参数错误。
ClassCastException:数据类型转换异常。
20.反射
反射:java语言在运行时拥有一项自观的能力。通过这种能力可以了解自身的情况为下一步动作做准备。
反射机制的实现要借助4个类:class(类对象),Constructor(构造器对象),Field(属性对象),Method(方法对象)
作用:在Java运行时环境中,对于任意一个类,可以知道这个类有哪些属性和方法。对于任意一个对象,可以调用它的任意一个方法。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。
反射提供的功能:
在运行时判断任意一个对象所属的类。
在运行时构造任意一个类的对象。
在运行时判断任意一个类所具有的成员变量和方法。
在运行时调用任意一个对象的方法
21.序列化:
序列化是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可以将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。
序列化的实现:
将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法, implements Serializable只是为了标注改对象是可以被序列化的,然后使用一个流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着使用ObjectOutputStream 对象的writeObject(Object obj)方法就可以将参数为obj 的对象写出(即保存其状态),要恢复的话则用输入流
对象序列化:
作用:以内存为基准,把内存的对象存储到磁盘文件中去,使用到的流是对象字节输出流,把对象数据存入到文件中去
要求:实现序列化的接口
反序列化:
把磁盘中的对象数据恢复到内存的java对象中,使用的是对象字节输入流
22.Http
200 OK 客户端请求成功
301 Permanently Moved (永久移除),请求的 URL 已移走。Response 中应该包含一个 Location URL, 说明资源现在所处的位置
302 Temporarily Moved 临时重定向
400 Bad Request //客户端请求有语法错误,不能被服务器所理解
401 Unauthorized //请求未经授权,这个状态代码必须和 WWW-Authenticate 报头域一起使用
403 Forbidden //服务器收到请求,但是拒绝提供服务
404 Not Found //请求资源不存在,eg:输入了错误的 URL
500 Internal Server Error //服务器发生不可预期的错误
503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常
23.GET、POST
1.用途:
GET:
用于请求数据(查询、搜索、读取资源)
参数通过URL传递,适合非敏感信息
POST:
用于提交数据(如提交表单、上传文件)
参数通过请求体(Body)传输,适合敏感或大量数据(密码、文件)
2.安全性:
GET:
参数暴露在URL中,可能被浏览器历史、服务器日志记录,存在泄露风险
不安全,但可以通过HTTPS加密URL内容
POST:
参数在请求体中,不直接可见,安全性更高,但若未使用HTTPS仍可能被截获
3.数据长度限制:
GET:
URL长度受浏览器和服务器限制(通常为2KB----8KB)
超长参数可能被截断
POST:
数据通过请求体传输,理论上无长度限制,但服务器可能配置限制
4.缓存与历史记录
GET:
可被浏览器缓存,保留在历史记录中(如后退按钮可直接重新请求)
适合幂等操作(多次请求相同结果)
POST:
默认不缓存,不保留在历史记录中
避免重复提交(如支付操作需防重复)
5.幂等性:
GET:
幂等:多次请求对服务器状态无影响(仅读取数据)
可安全重试
POST:
非幂等:多次提交可能产生不同结果(如重复提交订单)
需后端防重机制(如Token校验)
6.参数类型:
GET:URL编码
POST:支持多种编码
7.使用场景:
GET:
获取数据(搜索、分页、查看)
无副作用的操作
POST:
修改数据(如增、删、改)
提交敏感或复杂数据(登录、注册)
24.Cookie、Session
Cookie 是 web 服务器发送给浏览器的一块信息,浏览器会在本地一个文件中给每个 web 服务器存储 cookie。以后浏览器再给特定的 web 服务器发送请求时,同时会发送所有为该服务器存储的 cookie
Session 是存储在 web 服务器端的一块信息。session 对象存储特定用户会话所需的属性及配置信息。当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去
无论客户端做怎样的设置,session 都能够正常工作。当客户端禁用 cookie 时将无法使用 cookie
在存储的数据量方面:session 能够存储任意的java 对象,cookie 只能存储 String 类型的对象