Java 必备八股文(一)
发布于 2021-09-07 11:25 ,所属分类:2021面试经验技巧分享
Hello,大家好,我是程序员soulmate。最近有粉丝在后台回复我能不能出一个Java程序员面试题的文章啊。毕竟最近也到了提桶
金九银十的好时期,那今天就来搞个很全的Java八股文集,可能会分好几个文章来更新哈。
一定要看到最后,文末有惊喜,废话不多说,开淦。
Part1Java基础
1Java语言的特点
简单易学; 面向对象(封装,继承,多态); 平台无关性(Java虚拟机实现平台无关性); 可靠性; 安全性; 支持多线程(C++语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而Java语言却提供了多线程支持); 支持网络编程并且很方便(Java语言诞生本身就是为简化网络编程设计的,因此Java语言不仅支持网络编程而且很方便); 编译与解释并存;
2什么是JDK?什么是JRE?
JDK:
(Java Development Kit)顾名思义它是给开发者提供的开发工具箱,是给程序开发者用的。它除了包括完整的JRE(Java Runtime Environment),Java运行环境,还包含了其他供开发者使用的工具包。
JRE:
普通用户而只需要安装JRE(Java Runtime Environment)用来运行Java程序。而程序开发者必须安装JDK来编译、调试程序。
3public、protected、default、private访问权限
public
对所有类公开,private
只有本类可以访问。这里需要注意的是protected
,除了对本类和子类公开外,还对本包所在类的公开,即同一包类的类都可以访问;default
,如果没有明确写出修饰类型则为default,它只对本包公开。这四个修饰符的访问权限从大到小为public > protected > default > private。
4java 面向对象编程四大特性
抽象性:
抽出事物的本质特性,把某类事物的共同的特点描述出来;
封装性:
把对象的属性和操作结合在一起,构成一个独立的封装体;
继承性:
使得一个类可以继承另一个类的属性和方法;
多态性:
指不同类型的对象接收相同的消息时产生不同的行为。
5Object类的常用方法
getClass:
获取当前运行时对象的 Class 对象信息。toString
返回类名@哈希码的 16 进制。equals:
通过内存地址比较两个对象是否相等,String 类重写了这个方法使用值来比较是否相等。hashCode:
返回对象的 hash 码。clone:
拷贝当前对象,必须实现 Cloneable 接口。浅拷贝对基本类型进行值拷贝,对引用类型拷贝引用;深拷贝对基本类型进行值拷贝,对引用类型对象不但拷贝对象的引用还拷贝对象的相关属性和方法。两者不同在于深拷贝创建了一个新的对象。notify:
唤醒当前对象监视器的任一个线程。notifyAll:
唤醒当前对象监视器上的所有线程。wait:
1、暂停线程的执行;
2、三个不同参数方法(等待多少毫秒;额外等待多少毫秒;一直等待)
3、与 Thread.sleep(long time) 相比,sleep 使当前线程休眠一段时间,并没有释放该对象的锁,wait 释放了锁。finalize:
对象被垃圾回收器回收时执行的方法。
6基本数据类型
整型:byte(8)、short(16)、int(32)、long(64)
浮点型:float(32)、double(64)
布尔型:boolean(8)
字符型:char(16)
7方法的重写与重载
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。 即外壳不变,核心重写!
如果父类方法访问修饰符为 private 或者 final 则子类就不能重写该方法。
父类
publicclassTest{
publicvoidoverloadTest(){
System.out.println("我是父类方法");
}
}
子类
publicclassChildTestextendsTest{
@Override
publicvoidoverloadTest(){
System.out.println("我是子类,重写父类方法");
}
}
重载(overloading) 是在一个类里面,方法名字相同,而参数列表不同。 只能通过参数列表的数量、类型、不同类型的位置区分。
被重载的方法可以改变返回类型、可以改变修饰符、也可以声明新的或更广的检查异常。方法能够在同一个类中或者在一个子类中被重载。无法以返回值类型作为重载函数的区分标准。
以下代码是重载方法的示例
publicclasstest{
publicvoidoverloadTest(){
}
publicvoidoverloadTest(inta){
}
publicvoidoverloadTest(inta,intb){
}
publicvoidoverloadTest(doublea,intb){
}
publicvoidoverloadTest(inta,doubleb){
}
publicvoidoverloadTest(doublea,doubleb){
}
publicvoidoverloadTest(doublea,doubleb,doublec){
}
}
8final
修饰基本类型变量,一经出初始化后就不能够对其进行修改。 修饰引用类型变量,不能够指向另一个引用。 修饰类或方法,不能被继承或重写。
注意:
final修饰的集合变量,例如list,add、remove等方法正常使用,只是不能被指向另一个引用,但是引用的行为不会被限制。
9Java IO/NIO
Java IO:
即Java 输入/输出系统,面向流,同步阻塞线程。
区分 Java 的输入和输出:把自己当成程序, 当你从外边读数据到自己这里就用输入(InputStream/Reader), 向外边写数据就用输出(OutputStream/Writer)。
Stream:Java 中将数据的输入输出抽象为流,流是一组有顺序的,单向的,有起点和终点的数据集合,就像水流。按照流中的最小数据单元又分为字节流和字符流。
Java NIO:
面向缓冲区,同步非阻塞IO,主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
10Java反射
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。
反射可以在一个类运行的时候获取类的信息的机制,可以获取在编译期不可能获得的类的信息。
对于任意一个对象,都能调用它的任意一个方法和属性。
因为类的信息是保存在Class对象中的,而这个Class对象是在程序运行时被类加载器(ClassLoader)动态加载的。
当类加载器装载运行了类后,动态获取Class对象的信息以及动态操作Class对象的属性和方法的功能称为Java语言的反射机制。
11JDK 动态代理
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
Interface InvocationHandler:该接口中仅定义了一个方法; Proxy:该类即为动态代理类
动态代理步骤:
创建一个实现接口InvocationHandler的类,它必须实现invoke方法 创建被代理的类以及接口 通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler handler)创建一个代理 通过代理调用方法
Part2Java集合
12常用的集合分类
Collection 接口的接口 对象的集合(单列集合)
List 接口:元素按进入先后有序保存,可重复 Stack 是Vector类的实现类 LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全 ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全 Vector 接口实现类 数组, 同步, 线程安全 Set 接口:仅接收一次,不可重复,并做内部排序 HashSet 使用hash表(数组)存储元素 LinkedHashSet 链表维护元素的插入次序 TreeSet 底层实现为二叉树,元素排好序
Map 接口 键值对的集合 (双列集合)
Hashtable 接口实现类, 同步, 线程安全 HashMap 接口实现类 ,没有同步, 线程不安全 LinkedHashMap 双向链表和哈希表实现 WeakHashMap TreeMap 红黑树对所有的key进行排序 IdentifyHashMap
13List和Set集合详解
List和Set的区别
有序性:List保证按插入顺序排序,Set存储和取出顺序不一致。
唯一性:List可以重复,Set元素唯一。
获取元素:List可以通过索引直接操作元素,Set不能根据索引获取元素。
List
(1)ArrayList:底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素
(2)LinkedList 底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素
(3)Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素
Set
(1) HashSet底层数据结构采用哈希表实现,元素无序且唯一,线程不安全,效率高,可以存储null元素,元素的唯一性是靠所存储元素类型是否重写hashCode()和equals()方法来保证的,如果没有重写这两个方法,则无法保证元素的唯一性。
(2 LinkedHashSet底层数据结构采用链表和哈希表共同实现,链表保证了元素的顺序与存储顺序一致,哈希表保证了元素的唯一性。线程不安全,效率高。
(3) TreeSet底层数据结构采用二叉树来实现,元素唯一且已经排好序;唯一性同样需要重写hashCode和equals()方法,二叉树结构保证了元素的有序性。
14Map集合详解
Map用于保存具有映射关系的数据,Map里保存着两组数据:key和value,它们都可以使任何引用类型的数据,但key不能重复。所以通过指定的key就可以取出对应的value。
请注意
: Map
没有继承 Collection
接口, Map
提供 key 到 value 的映射,你可以通过“键”查找“值”。一个 Map
中不能包含相同的 key ,每个 key 只能映射一个 value 。Map 接口提供 3 种集合的视图, Map
的内容可以被当作一组 key 集合,一组 value 集合,或者一组 key-value 映射。
HashMap 非线程安全。
HashMap:基于哈希表实现。使用HashMap要求添加的键类明确定义了hashCode()和equals()可以重写hashCode()和equals(),为了优化HashMap空间的使用,您可以调优初始容量和负载因子。TreeMap:非线程安全。
TreeMap:基于红黑树实现,没有调优选项,因为该树总处于平衡状态。
Part3Java8新特性
Java8 Lambda表达式 双冒号方法引用 函数式接口 Java8 Stream Java8 日期时间API 接口的默认方法和静态方法
Part4多线程
15synchronized
修饰代码块 底层实现,通过 monitorenter & monitorexit 标志代码块为同步代码块。 修饰方法 底层实现,通过 ACC_SYNCHRONIZED 标志方法是同步方法。 修饰类 class 对象时,实际锁在类的实例上面。
单例模式实例:
publicclassSingleton{
privatestaticvolatileSingletoninstance=null;
privateSingleton(){}
publicstaticSingletongetInstance(){
if(null==instance){
synchronized(Singleton.class){
if(null==instance){
instance=newSingleton();
}
}
}
returninstance;
}
}
偏向锁,自旋锁,轻量级锁,重量级锁
通过 synchronized 加锁,第一个线程获取的锁为偏向锁,这时有其他线程参与锁竞争,升级为轻量级锁,其他线程通过循环的方式尝试获得锁,称自旋锁。若果自旋的次数达到一定的阈值,则升级为重量级锁。需要注意的是,在第二个线程获取锁时,会先判断第一个线程是否仍然存活,如果不存活,不会升级为轻量级锁。
16Lock
ReentrantLock
基于 AQS (AbstractQueuedSynchronizer)实现,主要有 state (资源) + FIFO (线程等待队列) 组成。
公平锁与非公平锁:区别在于在获取锁时,公平锁会判断当前队列是否有正在等待的线程,如果有则进行排队。使用 lock() 和 unLock() 方法来加锁解锁。
ReentrantReadWriteLock
同样基于 AQS 实现,内部采用内部类的形式实现了读锁(共享锁)和写锁 (排它锁)。
非公平锁吞吐量高,在获取锁的阶段来分析,当某一线程要获取锁时,非公平锁可以直接尝试获取锁,而不是判断当前队列中是否有线程在等待。一定情况下可以避免线程频繁的上下文切换,这样,活跃的线程有可能获得锁,而在队列中的锁还要进行唤醒才能继续尝试获取锁,而且线程的执行顺序一般来说不影响程序的运行。
17volatile
Java内存模型
在多线程环境下,保证变量的可见性。使用了 volatile 修饰变量后,在变量修改后会立即同步到主存中,每次用这个变量前会从主存刷新。
禁止 JVM 指令重排序。
单例模式双重校验锁变量为什么使用 volatile 修饰?禁止 JVM 指令重排序,new Object()分为三个步骤:申请内存空间,将内存空间引用赋值给变量,变量初始化。如果不禁止重排序,有可能得到一个未经初始化的变量。
18线程的五种状态
New:一个新的线程被创建,还没开始运行。
Runnable
一个线程准备就绪,随时可以运行的时候就进入了 Runnable 状态。Runnable 状态可以是实际正在运行的线程,也可以是随时可以运行的线程。
多线程环境下,每个线程都会被分配一个固定长度的 CPU 计算时间,每个线程运行一会儿就会停止让其他线程运行,这样才能让每个线程公平的运行。这些等待 CPU 和正在运行的线程就处于 Runnable 状态。
Blocked
例如一个线程在等待 I/O 资源,或者它要访问的被保护代码已经被其他线程锁住了,那么它就在阻塞 Blocked 状态,这个线程所需的资源到位后就转入 Runnable 状态。Waiting(无限期等待)
如果一个线程在等待其他线程的唤醒,那么它就处于 Waiting 状态。以下方法会让线程进入等待状态:
Object.wait()
Thread.join()
LockSupport.park()Timed Waiting(有期限等待)
无需等待被其他线程显示唤醒,在一定时间后有系统自动唤醒。以下方法会让线程进入有限等待状态:Thread.sleep(sleeptime)
Object.wait(timeout)
Thread.join(timeout)
LockSupport.parkNanos(timeout)
LockSupport.parkUntil(timeout)dead 死亡状态):一个线程正常执行完毕,或者意外失败,那么就结束了。
19线程使用方式
继承 Tread 类
实现 Runnable 接口
实现 Callable 接口:带有返回值
20ThreadLocal
场景:主要用途是为了保持线程自身对象和避免参数传递,主要适用场景是按线程多实例(每个线程对应一个实例)的对象的访问,并且这个对象很多地方都要用到。
原理:为每个线程创建变量副本,不同线程之间不可见,保证线程安全。使用 ThreadLocalMap 存储变量副本,以 ThreadLocal 为 K,这样一个线程可以拥有多个 ThreadLocal 对象。
实际:使用多数据源时,需要根据数据源的名字切换数据源,假设一个线程设置了一个数据源,这个时候就有可能有另一个线程去修改数据源,可以使用 ThreadLocal 维护这个数据源名字,使每个线程持有数据源名字的副本,避免线程安全问题。
21线程池
1、 线程池分类FixThreadPool
固定数量的线程池,适用于对线程管理,高负载的系统
SingleThreadPool
只有一个线程的线程池,适用于保证任务顺序执行
CacheThreadPool
创建一个不限制线程数量的线程池,适用于执行短期异步任务的小程序,低负载系统
ScheduledThreadPool
定时任务使用的线程池,适用于定时任务
2、 线程池的几个重要参数
int corePoolSize, 核心线程数 int maximumPoolSize, 最大线程数 long keepAliveTime, TimeUnit unit, 超过 corePoolSize 的线程的存活时长,超过这个时间,多余的线程会被回收。 BlockingQueue workQueue, 任务的排队队列 ThreadFactory threadFactory, 新线程的产生方式 RejectedExecutionHandler handler, 拒绝策略
3、 线程池线程工作过程
corePoolSize -> 任务队列 -> maximumPoolSize -> 拒绝策略
核心线程在线程池中一直存活,当有任务需要执行时,直接使用核心线程执行任务。当任务数量大于核心线程数时,加入等待队列。当任务队列数量达到队列最大长度时,继续创建线程,最多达到最大线程数。当设置回收时间时,核心线程以外的空闲线程会被回收。如果达到了最大线程数还不能够满足任务执行需求,则根据拒绝策略做拒绝处理。
4、 线程池拒绝策略(默认抛出异常)
5、 如何根据 CPU 核心数设计线程池线程数量
IO 密集型 2nCPU 计算密集型 nCPU+1 其中 n 为 CPU 核心数量,可通过 Runtime.getRuntime().availableProcessors() 获得核心数:。 为什么加 1:即使当计算密集型的线程偶尔由于缺失故障或者其他原因而暂停时,这个额外的线程也能确保 CPU 的时钟周期不会被浪费。
回复 (Java八股文、八股文) :获取Java程序员八股文
回复 (简历模板) :获取300套精美简历模板
回复 (电子书):获取500本程序员必备电子书
回复 (大数据):获取大数据学习资料
回复 (101) :获取kw免费听音乐软件
回复 (彩色昵称) :获取彩色昵称,做别人列表最靓的仔
相关资源