JAVA八股文
这里写目录标题
- **JAVA八股文**
- 面向对象三大特征
- 接口与抽象类的区别
- 重载与重写
- ==与equals
- 异常处理机制
- HashMap原理
- 红黑树
- 乐观锁和悲观锁
- HashTable与HashMap的区别
- ArrayList和LinkedList的区别
- 如何保证ArrayList的线程安全
- 什么是线程上下文切换
- sleep()和wait()的区别
- yield()和join()区别
- 线程池7大参数
- 接口和抽象类异同点
- 深拷贝和浅拷贝
- hashCode()
- 为什么重写equal()时必须重写hashCode()
- String,StringBuffer,StringBuilder的区别
- 字符串拼接是用+还是StringBuilder
- 字符串常量池是什么
- 什么是异常(Exception)?异常(Exception)和错误(Error)有什么区别?异常(Exception)的分类?
- finally一定会被执行嘛?
- try-with-resources怎么用
- 反射
- SPI和API有什么区别
- 序列化与反序列化
- IO流:
- Java IO中的设计模式有哪些?
- BIO,NIO和AIO的区别
- **JAVA集合**
- 说说 List, Set, Queue, Map 四者的区别?
- 多线程
- 上下文切换
- 预防如何死锁
- sleep()和wait()
- 为什么wait()不定义在Thread中/sleep()定义在Thread中
- JMM(java memorry model)
- volatile关键字
- 如何禁止指令重排序
- 双重校验锁实现对象单例(线程安全)
- synchronized 关键字
- 构造方法可以用synchronized修饰嘛?
- 说一下synchronized关键字底层原理
- JDK1.6 之后的 synchronized 关键字底层做了哪些优化?
- synchronized 和 volatile 的区别?
- 计算机基础
- OSI七层模型
- TCP/IP四层模型
- 应用层协议
- 传输层协议
- 网络层协议
- 网络层接口
- TCP的三次握手与四次挥手(非常重要)
- 如何保证TCP传输的可靠性(重要)
- 从输入URL到页面展示发生了什么(非常重要)/打开一个网页会使用到哪些协议
- HTTP与HTTPS的区别(重要)
- HTTP 是不保存状态的协议, 如何保存用户状态
- Cookie被禁用怎么办
- URI与URL的区别是什么
- 操作系统
- 什么是操作系统
- 什么是系统调用
- 线程和进程的区别
- 进程有哪些状态
- 进程间的通信方式
- 进程的调度算法
- 什么是死锁
- 死锁的四个条件
- 解决死锁的方法
- 内存管理机制
- 快表和多级页表
- 页面置换算法
- 数据结构
- 数据库
- 为什么不推荐使用外键与级联
- 外键的好处
- 数据库范式
- 常见的关系型数据库MySQL、PostgreSQL、Oracle、SQL Server、SQLite(微信本地的聊天记录的存储就是⽤的 SQLite)
- Redis
- spring
- image-20230925224338260
- **JAVA八股文**
- JAVA基础
- 面向对象三大特征
- 接口与抽象类的区别
- 重载与重写
- ==与equals
- 异常处理机制
- HashMap原理
- 红黑树
- 乐观锁和悲观锁
- HashTable与HashMap的区别
- ArrayList和LinkedList的区别
- 如何保证ArrayList的线程安全
- 什么是线程上下文切换
- sleep()和wait()的区别
- yield()和join()区别
- 线程池7大参数
- 接口和抽象类异同点
- 深拷贝和浅拷贝
- hashCode()
- 为什么重写equal()时必须重写hashCode()
- String,StringBuffer,StringBuilder的区别
- 字符串拼接是用+还是StringBuilder
- 字符串常量池是什么
- 什么是异常(Exception)?异常(Exception)和错误(Error)有什么区别?异常(Exception)的分类?
- finally一定会被执行嘛?
- try-with-resources怎么用
- 反射
- SPI和API有什么区别
- 序列化与反序列化
- IO流:
- Java IO中的设计模式有哪些?
- BIO,NIO和AIO的区别
- **JAVA集合**
- 说说 List, Set, Queue, Map 四者的区别?
- 多线程
- 上下文切换
- 预防如何死锁
- sleep()和wait()
- 为什么wait()不定义在Thread中/sleep()定义在Thread中
- JMM(java memorry model)
- volatile关键字
- 如何禁止指令重排序
- 双重校验锁实现对象单例(线程安全)
- synchronized 关键字
- 构造方法可以用synchronized修饰嘛?
- 说一下synchronized关键字底层原理
- JDK1.6 之后的 synchronized 关键字底层做了哪些优化?
- synchronized 和 volatile 的区别?
- 计算机基础
- OSI七层模型
- TCP/IP四层模型
- 应用层协议
- 传输层协议
- 网络层协议
- 网络层接口
- TCP的三次握手与四次挥手(非常重要)
- 如何保证TCP传输的可靠性(重要)
- 从输入URL到页面展示发生了什么(非常重要)/打开一个网页会使用到哪些协议
- HTTP与HTTPS的区别(重要)
- HTTP 是不保存状态的协议, 如何保存用户状态
- Cookie被禁用怎么办
- URI与URL的区别是什么
- 操作系统
- 什么是操作系统
- 什么是系统调用
- 线程和进程的区别
- 进程有哪些状态
- 进程间的通信方式
- 进程的调度算法
- 什么是死锁
- 死锁的四个条件
- 解决死锁的方法
- 内存管理机制
- 快表和多级页表
- 页面置换算法
- 数据结构
- 数据库
- 为什么不推荐使用外键与级联
- 外键的好处
- 数据库范式
- 常见的关系型数据库MySQL、PostgreSQL、Oracle、SQL Server、SQLite(微信本地的聊天记录的存储就是⽤的 SQLite)
- Redis
- spring
- image-20230925224338260
面向对象三大特征
封装
继承
多态:一个对象有多种状态(对象和引用类型有继承/实现的关系)
接口与抽象类的区别
相同:
接口和抽象类都不能被实例化
实现接口或继承抽象类的普通子类都必须实现这些抽象方法
不同点:
抽象类可以包含普通方法和代码块,接口里只能包含抽象方法,静态方法和默认方法,
抽象类可以有构造方法,而接口没有
抽象类中的成员变量可以是各种类型的,接口的成员变量只能是 public static final 类型的,并且必须赋值
重载与重写
重载是发生在一个类中的
重写是继承父类重写方法(但是final和private修饰的方法不可重写)
==与equals
==:在基本类型里比较的值,在引用类型中比较的是内存地址
equals:本质与==一样,String重写了这个方法,使得可以比较string的值
重写了equals后还必须重写hashcode方法
异常处理机制
try
catch
finaly
throws声明方法可能会抛出异常,然后程序终止
2请问 QRT65 FXC
HashMap原理
1.HashMap在Jdk1.8以后是基于数组+链表+红黑树来实现的,特点是,key不能重复,可以为null,
hashmap线程不安全,安全的需要hashtable, ConcurrentHashMap , Collections.synchronizedHashMap()
2.HashMap的扩容机制:
HashMap的默认容量为16,默认的负载因子为0.75,当HashMap中元素个数超过容量乘以负载因子的个数时,就创建一个大小为前一次两倍的新数组,再将原来数组中的数据复制到新数组中。当数组长度到达64且链表长度大于8时,链表转为红黑树
3.HashMap存取原理:
(1)计算key的hash值,然后进行二次hash,根据二次hash结果找到对应的索引位置
(2)如果这个位置有值,先equals比较,若结果为true则取代该元素,若结果为false,就使用高低位平移法将节点插入链表(JDK8以前使用头插法,但是头插法在并发扩容时可能会造成环形链表或数据丢失,而高低位平移发会发生数据覆盖的情况)
ConcurrentHashMap如何保证线程安全
jdk1.7使用分段锁,将Map分为16个段,每个段都是小的hashmap,
如果想要线程安全的HashMap
(1)使用ConcurrentHashMap
在jdk7使用分段锁,将一个Map分成16(Segment)段,每个段都是小的hashmap,每次操作只对其中一个部分加锁.(二次hash)
在jdk8中用CAS(乐观锁的一种实现方式)+Synchronized(悲观锁的实现)保证线程安全
(2)使用HashTable
(3)Collections.synchronizedHashMap()方法
红黑树
是一种自平衡的二叉查找树,是一种高效的查找树。它是由 Rudolf Bayer 于1972年发明,在当时被称为对称二叉B树
效率高,可以在O(logN)的时间内完成增删改查
通过以下性质定义实现自平衡
节点是红或黑根是黑所有叶子是黑, 且为空红色节点必须有两个黑色子节点从任意节点到每个叶子的简单路径都包含相同数目的黑色节点
红黑树是由234树推过来的
乐观锁和悲观锁
- 悲观锁(Pessimistic Locking):
- 思想:悲观锁的思想是默认认为并发访问会导致冲突,因此在访问共享资源之前,会先获取锁,确保自己独占资源,其他线程需要等待锁的释放。
- 实现:常见的悲观锁实现包括 Java 中的
synchronized
关键字和数据库中的行级锁(如SELECT ... FOR UPDATE
)。悲观锁通常会导致线程阻塞,因为当一个线程获得锁时,其他线程必须等待。 - 应用场景:悲观锁适用于并发写入操作较多、读操作较少的场景,因为它会阻塞其他线程的读和写操作,确保了数据的一致性和完整性。
- 乐观锁(Optimistic Locking):
- 思想:乐观锁的思想是默认认为并发访问不会导致冲突,因此在访问共享资源时,不会立即获取锁,而是先进行操作,然后在更新时检查是否发生了冲突。如果发现冲突,会进行回滚或重试操作。
- 实现:在编程中,乐观锁通常使用版本号(Version Number)或时间戳(Timestamp)来标识数据的版本,每次读取数据时,都会记录当前版本,然后在更新数据时,比较版本号或时间戳,如果版本号发生变化,说明其他线程已经修改了数据,需要处理冲突。
- 应用场景:乐观锁适用于并发读操作较多、写操作较少的场景,因为它允许多个线程同时读取数据,只有在写入时才会进行冲突检查,提高了并发性。
HashTable与HashMap的区别
hashTable每个方法都是用synchronized修饰,线程安全,但是读写效率低
hashtable的key不允许为null
HashTable只对key进行一次hash,HashMap进行了两次Hash
HashTable底层使用的数组加链表
ArrayList和LinkedList的区别
ArrayList的底层使用动态数组,默认容量为10,容量用完就再造一个1.5倍大的新数组,然后再复制过来
LinkedList的底层使用链表
ArrayList的查找效率高,LinkedList的添加效率高
如何保证ArrayList的线程安全
1.使用collentions.synchronizedList()方法为ArrayList加锁
2.使用,Vector底层与Arraylist相同,但是每个方法都由synchronized修饰,速度很慢
3.使用juc下的CopyOnWriterArrayList,该类实现了读操作不加锁,写操作时为list创建一个副本,期间其它线程读取的都是原本list,写操作都在副本中进行,写入完成后,再将指针指向副本。
什么是线程上下文切换
当一个线程被剥夺CPU使用权,切换到另一个线程运行
sleep()和wait()的区别
wait()是Object的方法,sleep是Thread类的方法
wait()会释放锁,sleep不会释放锁
wait()在同步方法或同步代码块中执行,sleep()没有限制
(4)wait()要调用notify()或notifyall()唤醒,sleep()自动唤醒
yield()和join()区别
yield()调用后线程进入就绪状态
A线程中调用B线程的join() ,则B执行完前A进入阻塞状态
线程池7大参数
核心线程数:线程池中的基本线程数量
最大线程数:当阻塞队列满了之后,逐一启动
最大线程的存活时间:当阻塞队列的任务执行完后,最大线长的回收时间
最大线程的存活时间单位
阻塞队列:当核心线程满后,后面来的任务都进入阻塞队列
线程工厂:用于生产线程
任务拒绝策略:阻塞队列满后,拒绝任务,有四种策略(1)抛异常(2)丢弃任务不抛异常(3)打回任务(4)尝试与最老的线程竞争
接口和抽象类异同点
同:
都不能被实例化,都可以含抽象方法,都可以有默认实现方法
异:
一个类只能继承一个类但是可以实现多个接口,
接口主要用于对类的行为进行约束,实现了某个接口就具有了对应的行为,抽象类用主要用于代码复用,强调实属关系
接口的成员变量只能是public static final 类型的,不能被修改且必须有初始值,⽽抽象类的 成员变量默认 default,可在⼦类中被重新定义,也可被重新赋值。
深拷贝和浅拷贝
浅拷贝:会在堆上创建一个新对象(区别于引用拷贝),如果原对象内部是引用类型浅拷贝会直接复制内部对象的引用地址,
拷贝对象和原对象共用一个内部对象
深拷贝:完全复制对象
hashCode()
获取哈希码(int)哈希码确定对象在哈希表的位置
为什么重写equal()时必须重写hashCode()
因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals ⽅法判断两个对象是相等 的,那这两个对象的 hashCode 值也要相等。
如果重写 equals() 时没有重写 hashCode() ⽅法的话就可能会导致 equals ⽅法判断是相等的两个 对象, hashCode 值却不相等。
String,StringBuffer,StringBuilder的区别
String是不可变的字符串类,创建了之后内容不可修改,由于不可变性,在多线程下是线程安全的,适合操作少量的数据
不可变是因为数组被final修饰且为私有,不能被继承,没有提供修改的方法
StringBuffer是可变的字符串类型,允许在不创建新对象的情况下修改内容,线程安全,使用了同步(synchronized)来确保多线程环境下线程安全性
StringBuilder是可变的字符串类,允许在不创建新对象的情况下修改内容**,不是线程安全的,但是在单线程环境下比StringBuffer性能更好**
在java9之后 String StringBuilder与StringBuffer的实现改用byte数组存储
字符串拼接是用+还是StringBuilder
实际上用+会通过StringBuilder调用append()方法,拼接完后调用toString()得到一个String对象
在循环内使用+,会创建多个StringBuilder对象
字符串常量池是什么
是JVM为了提升性能减少内存消耗,对String类专门开的一块内存空间,存放使用比较频繁的字符串,避免字符串重复创建
什么是异常(Exception)?异常(Exception)和错误(Error)有什么区别?异常(Exception)的分类?
异常是指的程序执行期间发生的意外情况,他可能导致程序中断,或者产生不可预测的结果
相同点都有共同祖先:java.lang 包中的 Throwable 类
exception可以用catch来捕获,error是程序无法处理的错误,比如JVM运行错误
异常的分类:Checked Exception(受检查异常),Unchecked Exception(不受检查异常)
或者称为运行时异常/编译异常(因为时间上是可以按照运行编译划分)
finally一定会被执行嘛?
不一定,比如虚拟机死亡,线程死亡就不会
try-with-resources怎么用
面对必须要关闭的资源,优先使用try-with-resources,代码更简短,更清晰
try (Scanner scanner = new Scanner(new File("test.txt"))) {while (scanner.hasNext()) {System.out.println(scanner.nextLine());}
} catch (FileNotFoundException fnfe) {fnfe.printStackTrace();
}
反射
什么是反射?作用?反射优缺点?反射有什么安全性考虑?反射影响性能嘛?
是框架的灵魂,赋予了我们在运行时分析类以及执行类中方法的能力,
通过反射能够动态加载类在运行时加载并实例化类,创建对象,调用方法,访问字段,获取类的信息
代码更灵活,为框架提供了便利,但是有安全问题,
安全问题比如无视泛型参数,反射可以破坏封装性和访问控制在安全敏感的应用程序中,可以考虑使用 SecurityManager
和权限检查来限制反射的使用,以确保安全性。
比直接调用性能低,因为涉及查找和调用,为了提高性能可以用反射
注解也用到了反射,比如说@Component就声明了一个类为SpringBean
SPI和API有什么区别
SPI 是一种设计模式,它允许应用程序或框架定义一个服务接口,然后允许外部提供不同的实现或插件,这些实现可以在运行时被动态加载和替换,以扩展应用程序的功能。
API是一组规定的接口、方法和协议,它们定义了不同软件组件之间的通信和互操作方式。API 规定了如何与某个系统、库、框架或服务进行交互,包括提供了哪些方法、参数、返回值等信息。
API 主要用于定义标准化的接口和方法,SPI 主要用于实现可插拔的扩展功能。
一个是java层面的接口,一个是物理层面的接口
序列化与反序列化
序列化是将数据结构或对象转化为二进制字节流
反序列化就是将序列化生成的二进制字节流转换成数据结构或对象的过程
序列化是为了方便存储数据到内存或数据库之类的地方或者网络传输
IO流:
输入和输出,形似流水,称为IO流
InputStream/Reader:所有的输⼊流的基类,前者是字节输⼊流,后者是字符输⼊流。
OutputStream/Writer :所有输出流的基类,前者是字节输出流,后者是字符输出流。
Java IO中的设计模式有哪些?
装饰器模式: 可以在不改变原有对象的情况下拓展其功能。通过组合替代继承来扩展原始类的功能
BIO,NIO和AIO的区别
BIO
BIO同步阻塞IO模型,会一直阻塞,直到read返回
NIO
Non-blocking
IO多路复用模型
同步非阻塞IO模型
引入选择器的概念,可以监视多个管道,当管道有消息就通知相关线程(多路复用减少了CPU消耗)
用缓冲区读写,提高IO性能
AIO就是NIO2
异步模型
java7引入,使用异步的方式完成IO操作,
回调机制,适用于处理大量连接,连接时间不确定的情况
Netty试用过AIO但是因为在Linux系统上没有多少性能提升
- BIO适用于连接数较少的场景,但由于阻塞特性,通常不适用于高并发环境。
- NIO适用于连接数较多、连接时间较长的场景,提供了更高的并发性能。
- AIO适用于需要异步处理大量连接的场景,可以提高系统的资源利用率。
JAVA集合
java集合可能的面试点:
- 集合框架的层次结构: Java集合框架由多个接口和类组成,主要分为Collection和Map两大类。Collection包括List、Set和Queue等,而Map则包括了HashMap、TreeMap等。
- List和Set的区别: List是有序可重复的集合,允许存储相同元素,而Set是无序不可重复的集合,不允许存储相同元素。
- Map的特点: Map是一种键值对的集合,每个键对应一个唯一的值。常见的Map实现包括HashMap、TreeMap和LinkedHashMap。
- ArrayList和LinkedList的区别: ArrayList基于动态数组实现,适用于随机访问,而LinkedList基于双向链表实现,适用于插入和删除操作。
- HashMap和Hashtable的区别: HashMap是非线程安全的,而Hashtable是线程安全的。另外,HashMap允许键和值为null,而Hashtable不允许。
- HashSet和TreeSet的区别: HashSet基于哈希表实现,元素无序,查找速度快;TreeSet基于红黑树实现,元素有序,查找速度较慢。
- 迭代器(Iterator): 迭代器用于遍历集合元素,提供了安全的、一致的遍历方式,可用于Collection接口的子类。
- 泛型(Generics): 泛型使得集合能够存储指定类型的元素,提高了类型安全性和代码可读性。
- 集合的线程安全性: 大多数集合类都不是线程安全的,但可以通过Collections类的工具方法获得线程安全的集合,如
Collections.synchronizedList
。 - 集合的性能特点: 不同类型的集合在插入、删除、查找等操作上有不同的性能特点,应根据需求选择合适的集合。
- Concurrent集合: Java提供了一组专门用于多线程环境的集合类,如ConcurrentHashMap、CopyOnWriteArrayList等,用于提高多线程环境下的性能和安全性。
- 集合的适用场景: 面试官可能会询问不同集合类的适用场景,要了解何时选择List、Set、Map或其他特定的集合类型。
- 内部实现原理: 深入了解某个集合的内部实现原理,例如HashMap的哈希表结构、HashSet的基于HashMap的实现等,可以在面试中展现更深的知识。
- Java 8新增特性: Java 8引入了Stream API,可以对集合进行函数式操作,例如map、filter、reduce等,面试官可能会问及这方面的知识。
java集合也叫容器,主要由Map接口,Collection接口派生而来,
Map->键值对
Collection–>List,Set,Queue
继承关系:
说说 List, Set, Queue, Map 四者的区别?
List列表 Set集合 queue 队列 Map jian
List (对付顺序的好帮⼿): 存储的元素是有序的、可重复的。
ArrayList: Object[] 数组
Vector:Object[]数组
LinkedList :双向链表
Set (注重独⼀⽆⼆的性质): 存储的元素是⽆序的、不可重复的。
HashSet(无序):基于HashMap实现,底层用HashMap保存元素
LinkedHashSet: 是HashSet的子类,内部通过LinkedHashMap实现
TreeSet(有序,唯一):红黑树(自平衡的排序二叉树)
Queue (实现排队功能的叫号机): 按特定的排队规则来确定先后顺序,存储的元素是有序的、可 重复的。
PriorityQueue : Object[] 数组来实现⼆叉堆 (优先队列)
ArrayQueue : Object[] 数组 + 双指针
Map (⽤ key 来搜索的专家): 使⽤键值对(key-value)存储,类似于数学上的函数 y=f(x),“x” 代表 key,“y” 代表 value,key 是⽆序的、不可重复的,value 是⽆序的、可重复的,每个键最 多映射到⼀个值
HashMap:在jdk1.8之前是数组+链表,链表是为了解决哈希冲突,在jdk1.8后当链表长度大于8,将转化为红黑树(转红黑树前如果数组小于64,那么会先进行数组扩容,而不是红黑树)
LinkeedHashMap:继承自HashMap在HashMap的基础上增加了一条双向链表
HashTable:数组+链表,线程安全
TreeMap:红黑树
Deque:一般和Queue对比,Deque是双端队列
多线程
在jvm里线程与进程的关系
线程有:虚拟机栈,本地方法栈,程序计数器
堆和方法区是线程共享的
上下文切换
程序在执行的过程中如果从CPU退出(调用了sleep,wait等,时间片用完,阻塞,结束或终止)
就会线程切换,CPU保存现场和恢复现场的过程叫上下文切换
预防如何死锁
1.破坏请求与保持条件:一次申请完所有资源
2.破坏不剥夺条件:占⽤部分资源的线程进⼀步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
3.循环等待条件:若干线程之间形成头尾相接的循环等待资源关系
sleep()和wait()
sleep()没释放锁,wait()释放了锁
wait()一般用于通信交互
wait()不会自动苏醒,需要别的线程调用同一个对象的notify()才行,或者wait(longtimeout)超时后才自动苏醒
sleep()是Tread类的静态本地方法,wait()是Object类的本地方法
为什么wait()不定义在Thread中/sleep()定义在Thread中
因为sleep()涉及的是线程的睡眠,而wait()是对象锁的等待(对象
JMM(java memorry model)
java内存模型
volatile关键字
volatile可以保证变量的可见性,如果将变量声明为volatile,就 让JVM标记为该变量为共享且不稳定的,每次使用都在主存中读取
最初的目的就是禁用CPU缓存,保证每次数据都从主存中重新获取,
volatile能保证数据的可见性,但是不能保证数据的原子性,
但是synchronized关键字都可以保证
如何禁止指令重排序
volatile可以防止JVM的指令重排序,
双重校验锁实现对象单例(线程安全)
实现单例模式
public class Singleton {private volatile static Singleton uniqueInstance;private Singleton() {}public static Singleton getUniqueInstance() {//先判断对象是否已经实例过,没有实例化过才进⼊加锁代码if (uniqueInstance == null) {//类对象加锁synchronized (Singleton.class) {if (uniqueInstance == null) {uniqueInstance = new Singleton();}}}return uniqueInstance;}
}
synchronized 关键字
三个主要的使用方式
1.修饰静态方法
获取class的锁
2.修饰实例方法
获得当前对象实例的锁
3.修饰代码块
获得对象的锁和class的锁
构造方法可以用synchronized修饰嘛?
不能
因为构造方法本身就是线程安全的
说一下synchronized关键字底层原理
底层原理属于jvm层面
如果是修饰的同步语块:
synchronized 同步语句块的实现使⽤的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置, monitorexit 指令则指明同步代码块的结 束位置。
当执⾏ monitorenter 指令时,线程试图获取锁也就是获取 对象监视器 monitor 的持有权。
在执⾏ monitorenter 时,会尝试获取对象的锁,如果锁的计数器为 0 则表示锁可以被获取,获取后将 锁计数器设为 1 也就是加 1
如果修饰的方法:
没有使用上面的东西,用的ACC_SYNCHRONIZED 标识,该标识指明了该⽅法是⼀个同步⽅法。
如果是实例⽅法,JVM 会尝试获取实例对象的锁。如果是静态⽅法,JVM 会尝试获取当前 class 的 锁
两者本质都是对对象监视器monitor的获取
JDK1.6 之后的 synchronized 关键字底层做了哪些优化?
JDK1.6 对锁的实现引⼊了⼤量的优化,如偏向锁、轻量级锁、⾃旋锁、适应性⾃旋锁、锁消除、锁 粗化等技术来减少锁操作的开销。
锁主要存在四种状态,依次是:⽆锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着 竞争的激烈⽽逐渐升级。注意锁可以升级不可降级,这种策略是为了提⾼获得锁和释放锁的效率
synchronized 和 volatile 的区别?
synchronized 关键字和 volatile 关键字是两个互补的存在,⽽不是对⽴的存在! volatile 关键字是线程同步的轻量级实现,所以 volatile 性能肯定⽐ synchronized 关键字要好 。但是 volatile 关键字只能⽤于变量⽽ synchronized 关键字可以修饰⽅法以及代码块 。 volatile 关键字能保证数据的可⻅性,但不能保证数据的原⼦性。 synchronized 关键字两者都 能保证。 volatile 关键字主要⽤于解决变量在多个线程之间的可⻅性,⽽ synchronized 关键字解决的是 多个线程之间访问资源的同步性。
计算机基础
OSI七层模型
TCP/IP四层模型
应用层
传输层
网络层
网络层接口
应用层协议
HTTP,FTP,DHCP,DNS,IMAP,SMTP
传输层协议
TCP:面向连接的
UDP:无连接的
网络层协议
负责为分组交换网上的不同主机提供通信服务
IP,NAT,ICMP,ARP,
不要把运输层的“⽤户数据报 UDP”和⽹络层的“IP 数据报”弄混
网络层接口
数据链路层+物理层
MAC,以太网,差错检测,CSMA/CD,多路访问
TCP的三次握手与四次挥手(非常重要)
为什么要三次握⼿? 第 2 次握⼿传回了ACK,为什么还要传回SYN? 为什么要四次挥⼿? 为什么不能把服务器发送的 ACK 和 FIN 合并起来,变成三次挥⼿? 如果第⼆次挥⼿时服务器的 ACK 没有送达客户端,会怎样? 为什么第四次挥⼿客户端需要等待 2*MSL(报⽂段最⻓寿命)时间后才进⼊ CLOSED 状态?
https://javaguide.cn/cs-basics/network/tcp-connection-and-disconnection.html
如何保证TCP传输的可靠性(重要)
https://javaguide.cn/cs-basics/network/tcp-reliability-guarantee.html
从输入URL到页面展示发生了什么(非常重要)/打开一个网页会使用到哪些协议
1.浏览器查找域名的IP地址(DNS查找过程:浏览器缓存,路由器缓存,DNS缓存)
2.浏览器向web服务器发送一个HTTP请求(cookies随请求发送给服务器)
3.服务器处理请求(请求处理,参数,cookies 生成一个HTML响应)
4.服务器发回一个HTML响应
5.浏览器处理显示HTML内容
总的来说
HTTP的状态码
HTTP与HTTPS的区别(重要)
HTTP端口号默认为80,HTTPS的端口号默认为443
URL的前缀不同:http://与https://
安全性和资源消耗不同:因为HTTPS是运行在SSL/TLS上的HTTP协议,SSL/TLS运行在TCP上,传输的内容是经过加密了的,加密使用对称加密,加密的密钥用服务器方的证书进行了非对称加密。过程中也耗费了更多的资源
HTTP1.0和HTTP1.1的区别
HTTP1.0是短链接
HTTP1.1是长连接
1.1中增加了大量状态码,且有缓存处理,优化了网络带宽使用
HTTP 是不保存状态的协议, 如何保存用户状态
使用Session,主要作用就是通过服务端记录用户的状态,一般服务器在一段时间内保存Session,过了时间限制就会销毁Session
一般redis保存
如何实现Session跟踪,一般都是在Cookie里加一个Session ID来跟踪
Cookie被禁用怎么办
直接写在URL路径后面,
URI与URL的区别是什么
URI(Uniform Resource Identifier) 是统⼀资源标志符,可以唯⼀标识⼀个资源。
URL(Uniform Resource Locator) 是统⼀资源定位符,可以提供该资源的路径。它是⼀种具体的 URI,即 URL 可以⽤来标识⼀个资源,⽽且还指明了如何 locate 这个资源。
URI 的作⽤像身份证号⼀样,URL 的作⽤更像家庭住址⼀样。URL 是⼀种具体的 URI,它不仅唯⼀ 标识资源,⽽且还提供了定位该资源的信息。
操作系统
什么是操作系统
1.管理计算机硬件与软件资源的程序,是计算机的基石
2.操作系统屏蔽了硬件的复杂性,实现人与机器交互的程序
3.操作系统的内核负责管理系统的内存,硬件,存储,程序 ,是各部分的桥梁,决定了性能与稳定性
什么是系统调用
我们运行的程序基本是运行在用户态,如果调用系统态就需要系统调用了
系统调用分类:
对于linux来说基本都是对文件的操作,由文件拓展而来的系统分类
线程和进程的区别
由jdk8的jvm可以看出进程由线程组成,
堆和方法区是共享的,
程序计数器,虚拟机栈,本地方法栈是各用各的
进程有哪些状态
5种
创建状态
就绪状态
运行状态
阻塞状态
结束状态
进程间的通信方式
管道/匿名管道:父子进程/兄弟进程的通信
有名管道:遵循先进先出
信号:比较复杂的通信方式,用于通知接收进程某个事件已经发生
消息队列:消息的链表,消息队列克服了信号承载信息量少,管道只能承载⽆格 式字 节流以及缓冲区⼤⼩受限等缺点。
信号量:是一个计数器,计算多进程对共享数据的访问,避免竞争关系
共享内存:多个进程访问同一块内存,需要互斥锁,信号量之类的同步操作
套接字:主要用于客户端与服务器之间网络通信
进程的调度算法
先到先服务(FCFS)
短作业优先
时间片轮转
多级反馈调度算法
优先级调度
什么是死锁
多个进程/线程被阻塞,其中一个或多个在等待某个资源被释放,但是资源一直被某个被阻塞线程持有不放.
死锁的四个条件
互斥
占有并等待
非抢占
循环等待
四个都有就能引起死锁
解决死锁的方法
预防
避免
检测
解除
内存管理机制
块式
页式
段式
段页式
段是逻辑单位,页是物理单位
快表和多级页表
快表提高虚拟地址到物理地址的转换速度(Cache的概念)
多级页表(时间换空间,把不用的东西放回去)
页面置换算法
最佳页面置换算法
先进先出页面置换算法
最近最久未使用页面置换算法
最少使用页面置换算法
数据结构
线性数据结构 :数组、链表、栈、队列 图 堆 树 红⿊树 布隆过滤器
布隆过滤器:
缓存穿透的解决方案
有一个二进制数组
判断一个数组存不存在这个数组里,不存在就是0,存在就是1
来一个数据,经过不同的hash算出对应的位置填成1
在检查的时候算出hash的几个位置,如果都为1,就说明数据存在
但是布隆过滤器不方便做删除操作
保密性好,因为都是0/1而且整体看无关联
有一定概率出现误判,但是还好,误判量少(可调),只要能做到大部分的数据常存在性判断就行
误判率和 二进制位,hash次数有关
布隆过滤器防止redis缓存穿透
redis带的有布隆过滤器的实现,直接创建着用就行,设置点大小和误判率
数据库
略
为什么不推荐使用外键与级联
在阿里巴巴开发手册里说过
外键的概念必须在应用层解决
外键与级联适合单机低并发,不适合分布式,高并发集群
其他的原因就是对分库分表不友好
外键的好处
保证了数据库数据的一致性和完整性
级联操作方便,减轻代码量
之类的
不涉及分库分表,低并发还是很好用的
数据库范式
1NF,2NF,3NF,BCNF,4NF,5NF
最多考虑到BCNF
/>布谷鸟过滤器
常见的关系型数据库MySQL、PostgreSQL、Oracle、SQL Server、SQLite(微信本地的聊天记录的存储就是⽤的 SQLite)
Redis
做缓存,分布式锁,消息队列
spring
ioc aop
Spring 时代我们⼀般通过 XML ⽂件来配置 Bean,后来开发⼈员觉得 XML ⽂件来配置不太好,于是 SpringBoot 注解配置就慢慢开始流⾏起来
JAVA面试笔试
事务的基本特征
原子性,一致性,隔离性,持久性(ACID)
,m tfrd
spring的理解
开源,社区活跃,IOC,AOP,
说的系统调用最少,c有缓冲区机制,可以减少读取次数
A是随机读取,无缓冲机制
B无缓存
D字节流与字符流的桥梁
这两个数都是非new出来的Integer数,当范围在-128到127之间时,会进行缓存,当用到时会直接赋值,当他们超过128,都是新new出来的数,所以不想等。 而Integer的equals方***转为int进行比较
finally为必执行的,虽然是最后执行,但是他是在方法内的,返回的1是主方法输出
java有默认的构造方法
compareTo()是String的方法
Java中凡是可以由程序员自己起名字的都叫标识符。其涉及到的结构有:包名、类名、接口名、变量名、方法名、常量名。
① 由26个英文字母大小写,0-9,_ 或 $ 组成。
② 数字不可以开头。
③ 不可以使用关键字(class、int等)和保留字(goto和const),但能包含关键字和保留字。
④ Java中严格区分大小写,长度无限制。(例:class×,Class√)
⑤ 标识符不能包含空格。
包名:多单词组成时所有字母都小写。(例:aaabbbccc)
类名、接口名:多单词组成时,所有单词的首字母大写。(例:AaaBbbCcc)
变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写。(例:aaaBbbCcc)
常量名:所有字母都大写,多单词时每个单词之间用下划线_连接。(例:AAA_BBB_CCC)
优先级高到低排序
D是说的是,被对象用,或被类用
被类用的应该是静态变量
然后就是AB的生存周期不同
https://zhuanlan.zhihu.com/p/113007640大白话聊聊synchronized、CAS底层原理、Lock锁和锁升级原理
https://www.bilibili.com/video/BV1BB4y1X7u3/?spm_id_from=333.337.search-card.all.click【neko算法课】红黑树 插入【11期】
JAVA八股文
JAVA基础
面向对象三大特征
封装
继承
多态:一个对象有多种状态(对象和引用类型有继承/实现的关系)
接口与抽象类的区别
相同:
接口和抽象类都不能被实例化
实现接口或继承抽象类的普通子类都必须实现这些抽象方法
不同点:
抽象类可以包含普通方法和代码块,接口里只能包含抽象方法,静态方法和默认方法,
抽象类可以有构造方法,而接口没有
抽象类中的成员变量可以是各种类型的,接口的成员变量只能是 public static final 类型的,并且必须赋值
重载与重写
重载是发生在一个类中的
重写是继承父类重写方法(但是final和private修饰的方法不可重写)
==与equals
==:在基本类型里比较的值,在引用类型中比较的是内存地址
equals:本质与==一样,String重写了这个方法,使得可以比较string的值
重写了equals后还必须重写hashcode方法
异常处理机制
try
catch
finaly
throws声明方法可能会抛出异常,然后程序终止
2请问 QRT65 FXC
HashMap原理
1.HashMap在Jdk1.8以后是基于数组+链表+红黑树来实现的,特点是,key不能重复,可以为null,
hashmap线程不安全,安全的需要hashtable, ConcurrentHashMap , Collections.synchronizedHashMap()
2.HashMap的扩容机制:
HashMap的默认容量为16,默认的负载因子为0.75,当HashMap中元素个数超过容量乘以负载因子的个数时,就创建一个大小为前一次两倍的新数组,再将原来数组中的数据复制到新数组中。当数组长度到达64且链表长度大于8时,链表转为红黑树
3.HashMap存取原理:
(1)计算key的hash值,然后进行二次hash,根据二次hash结果找到对应的索引位置
(2)如果这个位置有值,先equals比较,若结果为true则取代该元素,若结果为false,就使用高低位平移法将节点插入链表(JDK8以前使用头插法,但是头插法在并发扩容时可能会造成环形链表或数据丢失,而高低位平移发会发生数据覆盖的情况)
ConcurrentHashMap如何保证线程安全
jdk1.7使用分段锁,将Map分为16个段,每个段都是小的hashmap,
如果想要线程安全的HashMap
(1)使用ConcurrentHashMap
在jdk7使用分段锁,将一个Map分成16(Segment)段,每个段都是小的hashmap,每次操作只对其中一个部分加锁.(二次hash)
在jdk8中用CAS(乐观锁的一种实现方式)+Synchronized(悲观锁的实现)保证线程安全
(2)使用HashTable
(3)Collections.synchronizedHashMap()方法
红黑树
是一种自平衡的二叉查找树,是一种高效的查找树。它是由 Rudolf Bayer 于1972年发明,在当时被称为对称二叉B树
效率高,可以在O(logN)的时间内完成增删改查
通过以下性质定义实现自平衡
节点是红或黑根是黑所有叶子是黑, 且为空红色节点必须有两个黑色子节点从任意节点到每个叶子的简单路径都包含相同数目的黑色节点
红黑树是由234树推过来的
乐观锁和悲观锁
- 悲观锁(Pessimistic Locking):
- 思想:悲观锁的思想是默认认为并发访问会导致冲突,因此在访问共享资源之前,会先获取锁,确保自己独占资源,其他线程需要等待锁的释放。
- 实现:常见的悲观锁实现包括 Java 中的
synchronized
关键字和数据库中的行级锁(如SELECT ... FOR UPDATE
)。悲观锁通常会导致线程阻塞,因为当一个线程获得锁时,其他线程必须等待。 - 应用场景:悲观锁适用于并发写入操作较多、读操作较少的场景,因为它会阻塞其他线程的读和写操作,确保了数据的一致性和完整性。
- 乐观锁(Optimistic Locking):
- 思想:乐观锁的思想是默认认为并发访问不会导致冲突,因此在访问共享资源时,不会立即获取锁,而是先进行操作,然后在更新时检查是否发生了冲突。如果发现冲突,会进行回滚或重试操作。
- 实现:在编程中,乐观锁通常使用版本号(Version Number)或时间戳(Timestamp)来标识数据的版本,每次读取数据时,都会记录当前版本,然后在更新数据时,比较版本号或时间戳,如果版本号发生变化,说明其他线程已经修改了数据,需要处理冲突。
- 应用场景:乐观锁适用于并发读操作较多、写操作较少的场景,因为它允许多个线程同时读取数据,只有在写入时才会进行冲突检查,提高了并发性。
HashTable与HashMap的区别
hashTable每个方法都是用synchronized修饰,线程安全,但是读写效率低
hashtable的key不允许为null
HashTable只对key进行一次hash,HashMap进行了两次Hash
HashTable底层使用的数组加链表
ArrayList和LinkedList的区别
ArrayList的底层使用动态数组,默认容量为10,容量用完就再造一个1.5倍大的新数组,然后再复制过来
LinkedList的底层使用链表
ArrayList的查找效率高,LinkedList的添加效率高
如何保证ArrayList的线程安全
1.使用collentions.synchronizedList()方法为ArrayList加锁
2.使用,Vector底层与Arraylist相同,但是每个方法都由synchronized修饰,速度很慢
3.使用juc下的CopyOnWriterArrayList,该类实现了读操作不加锁,写操作时为list创建一个副本,期间其它线程读取的都是原本list,写操作都在副本中进行,写入完成后,再将指针指向副本。
什么是线程上下文切换
当一个线程被剥夺CPU使用权,切换到另一个线程运行
sleep()和wait()的区别
wait()是Object的方法,sleep是Thread类的方法
wait()会释放锁,sleep不会释放锁
wait()在同步方法或同步代码块中执行,sleep()没有限制
(4)wait()要调用notify()或notifyall()唤醒,sleep()自动唤醒
yield()和join()区别
yield()调用后线程进入就绪状态
A线程中调用B线程的join() ,则B执行完前A进入阻塞状态
线程池7大参数
核心线程数:线程池中的基本线程数量
最大线程数:当阻塞队列满了之后,逐一启动
最大线程的存活时间:当阻塞队列的任务执行完后,最大线长的回收时间
最大线程的存活时间单位
阻塞队列:当核心线程满后,后面来的任务都进入阻塞队列
线程工厂:用于生产线程
任务拒绝策略:阻塞队列满后,拒绝任务,有四种策略(1)抛异常(2)丢弃任务不抛异常(3)打回任务(4)尝试与最老的线程竞争
接口和抽象类异同点
同:
都不能被实例化,都可以含抽象方法,都可以有默认实现方法
异:
一个类只能继承一个类但是可以实现多个接口,
接口主要用于对类的行为进行约束,实现了某个接口就具有了对应的行为,抽象类用主要用于代码复用,强调实属关系
接口的成员变量只能是public static final 类型的,不能被修改且必须有初始值,⽽抽象类的 成员变量默认 default,可在⼦类中被重新定义,也可被重新赋值。
深拷贝和浅拷贝
浅拷贝:会在堆上创建一个新对象(区别于引用拷贝),如果原对象内部是引用类型浅拷贝会直接复制内部对象的引用地址,
拷贝对象和原对象共用一个内部对象
深拷贝:完全复制对象
hashCode()
获取哈希码(int)哈希码确定对象在哈希表的位置
为什么重写equal()时必须重写hashCode()
因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals ⽅法判断两个对象是相等 的,那这两个对象的 hashCode 值也要相等。
如果重写 equals() 时没有重写 hashCode() ⽅法的话就可能会导致 equals ⽅法判断是相等的两个 对象, hashCode 值却不相等。
String,StringBuffer,StringBuilder的区别
String是不可变的字符串类,创建了之后内容不可修改,由于不可变性,在多线程下是线程安全的,适合操作少量的数据
不可变是因为数组被final修饰且为私有,不能被继承,没有提供修改的方法
StringBuffer是可变的字符串类型,允许在不创建新对象的情况下修改内容,线程安全,使用了同步(synchronized)来确保多线程环境下线程安全性
StringBuilder是可变的字符串类,允许在不创建新对象的情况下修改内容**,不是线程安全的,但是在单线程环境下比StringBuffer性能更好**
在java9之后 String StringBuilder与StringBuffer的实现改用byte数组存储
字符串拼接是用+还是StringBuilder
实际上用+会通过StringBuilder调用append()方法,拼接完后调用toString()得到一个String对象
在循环内使用+,会创建多个StringBuilder对象
字符串常量池是什么
是JVM为了提升性能减少内存消耗,对String类专门开的一块内存空间,存放使用比较频繁的字符串,避免字符串重复创建
什么是异常(Exception)?异常(Exception)和错误(Error)有什么区别?异常(Exception)的分类?
异常是指的程序执行期间发生的意外情况,他可能导致程序中断,或者产生不可预测的结果
相同点都有共同祖先:java.lang 包中的 Throwable 类
exception可以用catch来捕获,error是程序无法处理的错误,比如JVM运行错误
异常的分类:Checked Exception(受检查异常),Unchecked Exception(不受检查异常)
或者称为运行时异常/编译异常(因为时间上是可以按照运行编译划分)
finally一定会被执行嘛?
不一定,比如虚拟机死亡,线程死亡就不会
try-with-resources怎么用
面对必须要关闭的资源,优先使用try-with-resources,代码更简短,更清晰
try (Scanner scanner = new Scanner(new File("test.txt"))) {while (scanner.hasNext()) {System.out.println(scanner.nextLine());}
} catch (FileNotFoundException fnfe) {fnfe.printStackTrace();
}
反射
什么是反射?作用?反射优缺点?反射有什么安全性考虑?反射影响性能嘛?
是框架的灵魂,赋予了我们在运行时分析类以及执行类中方法的能力,
通过反射能够动态加载类在运行时加载并实例化类,创建对象,调用方法,访问字段,获取类的信息
代码更灵活,为框架提供了便利,但是有安全问题,
安全问题比如无视泛型参数,反射可以破坏封装性和访问控制在安全敏感的应用程序中,可以考虑使用 SecurityManager
和权限检查来限制反射的使用,以确保安全性。
比直接调用性能低,因为涉及查找和调用,为了提高性能可以用反射
注解也用到了反射,比如说@Component就声明了一个类为SpringBean
SPI和API有什么区别
SPI 是一种设计模式,它允许应用程序或框架定义一个服务接口,然后允许外部提供不同的实现或插件,这些实现可以在运行时被动态加载和替换,以扩展应用程序的功能。
API是一组规定的接口、方法和协议,它们定义了不同软件组件之间的通信和互操作方式。API 规定了如何与某个系统、库、框架或服务进行交互,包括提供了哪些方法、参数、返回值等信息。
API 主要用于定义标准化的接口和方法,SPI 主要用于实现可插拔的扩展功能。
一个是java层面的接口,一个是物理层面的接口
序列化与反序列化
序列化是将数据结构或对象转化为二进制字节流
反序列化就是将序列化生成的二进制字节流转换成数据结构或对象的过程
序列化是为了方便存储数据到内存或数据库之类的地方或者网络传输
IO流:
输入和输出,形似流水,称为IO流
InputStream/Reader:所有的输⼊流的基类,前者是字节输⼊流,后者是字符输⼊流。
OutputStream/Writer :所有输出流的基类,前者是字节输出流,后者是字符输出流。
Java IO中的设计模式有哪些?
装饰器模式: 可以在不改变原有对象的情况下拓展其功能。通过组合替代继承来扩展原始类的功能
BIO,NIO和AIO的区别
BIO
BIO同步阻塞IO模型,会一直阻塞,直到read返回
NIO
Non-blocking
IO多路复用模型
同步非阻塞IO模型
引入选择器的概念,可以监视多个管道,当管道有消息就通知相关线程(多路复用减少了CPU消耗)
用缓冲区读写,提高IO性能
AIO就是NIO2
异步模型
java7引入,使用异步的方式完成IO操作,
回调机制,适用于处理大量连接,连接时间不确定的情况
Netty试用过AIO但是因为在Linux系统上没有多少性能提升
- BIO适用于连接数较少的场景,但由于阻塞特性,通常不适用于高并发环境。
- NIO适用于连接数较多、连接时间较长的场景,提供了更高的并发性能。
- AIO适用于需要异步处理大量连接的场景,可以提高系统的资源利用率。
JAVA集合
java集合可能的面试点:
- 集合框架的层次结构: Java集合框架由多个接口和类组成,主要分为Collection和Map两大类。Collection包括List、Set和Queue等,而Map则包括了HashMap、TreeMap等。
- List和Set的区别: List是有序可重复的集合,允许存储相同元素,而Set是无序不可重复的集合,不允许存储相同元素。
- Map的特点: Map是一种键值对的集合,每个键对应一个唯一的值。常见的Map实现包括HashMap、TreeMap和LinkedHashMap。
- ArrayList和LinkedList的区别: ArrayList基于动态数组实现,适用于随机访问,而LinkedList基于双向链表实现,适用于插入和删除操作。
- HashMap和Hashtable的区别: HashMap是非线程安全的,而Hashtable是线程安全的。另外,HashMap允许键和值为null,而Hashtable不允许。
- HashSet和TreeSet的区别: HashSet基于哈希表实现,元素无序,查找速度快;TreeSet基于红黑树实现,元素有序,查找速度较慢。
- 迭代器(Iterator): 迭代器用于遍历集合元素,提供了安全的、一致的遍历方式,可用于Collection接口的子类。
- 泛型(Generics): 泛型使得集合能够存储指定类型的元素,提高了类型安全性和代码可读性。
- 集合的线程安全性: 大多数集合类都不是线程安全的,但可以通过Collections类的工具方法获得线程安全的集合,如
Collections.synchronizedList
。 - 集合的性能特点: 不同类型的集合在插入、删除、查找等操作上有不同的性能特点,应根据需求选择合适的集合。
- Concurrent集合: Java提供了一组专门用于多线程环境的集合类,如ConcurrentHashMap、CopyOnWriteArrayList等,用于提高多线程环境下的性能和安全性。
- 集合的适用场景: 面试官可能会询问不同集合类的适用场景,要了解何时选择List、Set、Map或其他特定的集合类型。
- 内部实现原理: 深入了解某个集合的内部实现原理,例如HashMap的哈希表结构、HashSet的基于HashMap的实现等,可以在面试中展现更深的知识。
- Java 8新增特性: Java 8引入了Stream API,可以对集合进行函数式操作,例如map、filter、reduce等,面试官可能会问及这方面的知识。
java集合也叫容器,主要由Map接口,Collection接口派生而来,
Map->键值对
Collection–>List,Set,Queue
继承关系:
说说 List, Set, Queue, Map 四者的区别?
List列表 Set集合 queue 队列 Map jian
List (对付顺序的好帮⼿): 存储的元素是有序的、可重复的。
ArrayList: Object[] 数组
Vector:Object[]数组
LinkedList :双向链表
Set (注重独⼀⽆⼆的性质): 存储的元素是⽆序的、不可重复的。
HashSet(无序):基于HashMap实现,底层用HashMap保存元素
LinkedHashSet: 是HashSet的子类,内部通过LinkedHashMap实现
TreeSet(有序,唯一):红黑树(自平衡的排序二叉树)
Queue (实现排队功能的叫号机): 按特定的排队规则来确定先后顺序,存储的元素是有序的、可 重复的。
PriorityQueue : Object[] 数组来实现⼆叉堆 (优先队列)
ArrayQueue : Object[] 数组 + 双指针
Map (⽤ key 来搜索的专家): 使⽤键值对(key-value)存储,类似于数学上的函数 y=f(x),“x” 代表 key,“y” 代表 value,key 是⽆序的、不可重复的,value 是⽆序的、可重复的,每个键最 多映射到⼀个值
HashMap:在jdk1.8之前是数组+链表,链表是为了解决哈希冲突,在jdk1.8后当链表长度大于8,将转化为红黑树(转红黑树前如果数组小于64,那么会先进行数组扩容,而不是红黑树)
LinkeedHashMap:继承自HashMap在HashMap的基础上增加了一条双向链表
HashTable:数组+链表,线程安全
TreeMap:红黑树
Deque:一般和Queue对比,Deque是双端队列
多线程
在jvm里线程与进程的关系
线程有:虚拟机栈,本地方法栈,程序计数器
堆和方法区是线程共享的
上下文切换
程序在执行的过程中如果从CPU退出(调用了sleep,wait等,时间片用完,阻塞,结束或终止)
就会线程切换,CPU保存现场和恢复现场的过程叫上下文切换
预防如何死锁
1.破坏请求与保持条件:一次申请完所有资源
2.破坏不剥夺条件:占⽤部分资源的线程进⼀步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
3.循环等待条件:若干线程之间形成头尾相接的循环等待资源关系
sleep()和wait()
sleep()没释放锁,wait()释放了锁
wait()一般用于通信交互
wait()不会自动苏醒,需要别的线程调用同一个对象的notify()才行,或者wait(longtimeout)超时后才自动苏醒
sleep()是Tread类的静态本地方法,wait()是Object类的本地方法
为什么wait()不定义在Thread中/sleep()定义在Thread中
因为sleep()涉及的是线程的睡眠,而wait()是对象锁的等待(对象
JMM(java memorry model)
java内存模型
volatile关键字
volatile可以保证变量的可见性,如果将变量声明为volatile,就 让JVM标记为该变量为共享且不稳定的,每次使用都在主存中读取
最初的目的就是禁用CPU缓存,保证每次数据都从主存中重新获取,
volatile能保证数据的可见性,但是不能保证数据的原子性,
但是synchronized关键字都可以保证
如何禁止指令重排序
volatile可以防止JVM的指令重排序,
双重校验锁实现对象单例(线程安全)
实现单例模式
public class Singleton {private volatile static Singleton uniqueInstance;private Singleton() {}public static Singleton getUniqueInstance() {//先判断对象是否已经实例过,没有实例化过才进⼊加锁代码if (uniqueInstance == null) {//类对象加锁synchronized (Singleton.class) {if (uniqueInstance == null) {uniqueInstance = new Singleton();}}}return uniqueInstance;}
}
synchronized 关键字
三个主要的使用方式
1.修饰静态方法
获取class的锁
2.修饰实例方法
获得当前对象实例的锁
3.修饰代码块
获得对象的锁和class的锁
构造方法可以用synchronized修饰嘛?
不能
因为构造方法本身就是线程安全的
说一下synchronized关键字底层原理
底层原理属于jvm层面
如果是修饰的同步语块:
synchronized 同步语句块的实现使⽤的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置, monitorexit 指令则指明同步代码块的结 束位置。
当执⾏ monitorenter 指令时,线程试图获取锁也就是获取 对象监视器 monitor 的持有权。
在执⾏ monitorenter 时,会尝试获取对象的锁,如果锁的计数器为 0 则表示锁可以被获取,获取后将 锁计数器设为 1 也就是加 1
如果修饰的方法:
没有使用上面的东西,用的ACC_SYNCHRONIZED 标识,该标识指明了该⽅法是⼀个同步⽅法。
如果是实例⽅法,JVM 会尝试获取实例对象的锁。如果是静态⽅法,JVM 会尝试获取当前 class 的 锁
两者本质都是对对象监视器monitor的获取
JDK1.6 之后的 synchronized 关键字底层做了哪些优化?
JDK1.6 对锁的实现引⼊了⼤量的优化,如偏向锁、轻量级锁、⾃旋锁、适应性⾃旋锁、锁消除、锁 粗化等技术来减少锁操作的开销。
锁主要存在四种状态,依次是:⽆锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着 竞争的激烈⽽逐渐升级。注意锁可以升级不可降级,这种策略是为了提⾼获得锁和释放锁的效率
synchronized 和 volatile 的区别?
synchronized 关键字和 volatile 关键字是两个互补的存在,⽽不是对⽴的存在! volatile 关键字是线程同步的轻量级实现,所以 volatile 性能肯定⽐ synchronized 关键字要好 。但是 volatile 关键字只能⽤于变量⽽ synchronized 关键字可以修饰⽅法以及代码块 。 volatile 关键字能保证数据的可⻅性,但不能保证数据的原⼦性。 synchronized 关键字两者都 能保证。 volatile 关键字主要⽤于解决变量在多个线程之间的可⻅性,⽽ synchronized 关键字解决的是 多个线程之间访问资源的同步性。
计算机基础
OSI七层模型
TCP/IP四层模型
应用层
传输层
网络层
网络层接口
应用层协议
HTTP,FTP,DHCP,DNS,IMAP,SMTP
传输层协议
TCP:面向连接的
UDP:无连接的
网络层协议
负责为分组交换网上的不同主机提供通信服务
IP,NAT,ICMP,ARP,
不要把运输层的“⽤户数据报 UDP”和⽹络层的“IP 数据报”弄混
网络层接口
数据链路层+物理层
MAC,以太网,差错检测,CSMA/CD,多路访问
TCP的三次握手与四次挥手(非常重要)
为什么要三次握⼿? 第 2 次握⼿传回了ACK,为什么还要传回SYN? 为什么要四次挥⼿? 为什么不能把服务器发送的 ACK 和 FIN 合并起来,变成三次挥⼿? 如果第⼆次挥⼿时服务器的 ACK 没有送达客户端,会怎样? 为什么第四次挥⼿客户端需要等待 2*MSL(报⽂段最⻓寿命)时间后才进⼊ CLOSED 状态?
https://javaguide.cn/cs-basics/network/tcp-connection-and-disconnection.html
如何保证TCP传输的可靠性(重要)
https://javaguide.cn/cs-basics/network/tcp-reliability-guarantee.html
从输入URL到页面展示发生了什么(非常重要)/打开一个网页会使用到哪些协议
1.浏览器查找域名的IP地址(DNS查找过程:浏览器缓存,路由器缓存,DNS缓存)
2.浏览器向web服务器发送一个HTTP请求(cookies随请求发送给服务器)
3.服务器处理请求(请求处理,参数,cookies 生成一个HTML响应)
4.服务器发回一个HTML响应
5.浏览器处理显示HTML内容
总的来说
HTTP的状态码
HTTP与HTTPS的区别(重要)
HTTP端口号默认为80,HTTPS的端口号默认为443
URL的前缀不同:http://与https://
安全性和资源消耗不同:因为HTTPS是运行在SSL/TLS上的HTTP协议,SSL/TLS运行在TCP上,传输的内容是经过加密了的,加密使用对称加密,加密的密钥用服务器方的证书进行了非对称加密。过程中也耗费了更多的资源
HTTP1.0和HTTP1.1的区别
HTTP1.0是短链接
HTTP1.1是长连接
1.1中增加了大量状态码,且有缓存处理,优化了网络带宽使用
HTTP 是不保存状态的协议, 如何保存用户状态
使用Session,主要作用就是通过服务端记录用户的状态,一般服务器在一段时间内保存Session,过了时间限制就会销毁Session
一般redis保存
如何实现Session跟踪,一般都是在Cookie里加一个Session ID来跟踪
Cookie被禁用怎么办
直接写在URL路径后面,
URI与URL的区别是什么
URI(Uniform Resource Identifier) 是统⼀资源标志符,可以唯⼀标识⼀个资源。
URL(Uniform Resource Locator) 是统⼀资源定位符,可以提供该资源的路径。它是⼀种具体的 URI,即 URL 可以⽤来标识⼀个资源,⽽且还指明了如何 locate 这个资源。
URI 的作⽤像身份证号⼀样,URL 的作⽤更像家庭住址⼀样。URL 是⼀种具体的 URI,它不仅唯⼀ 标识资源,⽽且还提供了定位该资源的信息。
操作系统
什么是操作系统
1.管理计算机硬件与软件资源的程序,是计算机的基石
2.操作系统屏蔽了硬件的复杂性,实现人与机器交互的程序
3.操作系统的内核负责管理系统的内存,硬件,存储,程序 ,是各部分的桥梁,决定了性能与稳定性
什么是系统调用
我们运行的程序基本是运行在用户态,如果调用系统态就需要系统调用了
系统调用分类:
对于linux来说基本都是对文件的操作,由文件拓展而来的系统分类
线程和进程的区别
由jdk8的jvm可以看出进程由线程组成,
堆和方法区是共享的,
程序计数器,虚拟机栈,本地方法栈是各用各的
进程有哪些状态
5种
创建状态
就绪状态
运行状态
阻塞状态
结束状态
进程间的通信方式
管道/匿名管道:父子进程/兄弟进程的通信
有名管道:遵循先进先出
信号:比较复杂的通信方式,用于通知接收进程某个事件已经发生
消息队列:消息的链表,消息队列克服了信号承载信息量少,管道只能承载⽆格 式字 节流以及缓冲区⼤⼩受限等缺点。
信号量:是一个计数器,计算多进程对共享数据的访问,避免竞争关系
共享内存:多个进程访问同一块内存,需要互斥锁,信号量之类的同步操作
套接字:主要用于客户端与服务器之间网络通信
进程的调度算法
先到先服务(FCFS)
短作业优先
时间片轮转
多级反馈调度算法
优先级调度
什么是死锁
多个进程/线程被阻塞,其中一个或多个在等待某个资源被释放,但是资源一直被某个被阻塞线程持有不放.
死锁的四个条件
互斥
占有并等待
非抢占
循环等待
四个都有就能引起死锁
解决死锁的方法
预防
避免
检测
解除
内存管理机制
块式
页式
段式
段页式
段是逻辑单位,页是物理单位
快表和多级页表
快表提高虚拟地址到物理地址的转换速度(Cache的概念)
多级页表(时间换空间,把不用的东西放回去)
页面置换算法
最佳页面置换算法
先进先出页面置换算法
最近最久未使用页面置换算法
最少使用页面置换算法
数据结构
线性数据结构 :数组、链表、栈、队列 图 堆 树 红⿊树 布隆过滤器
布隆过滤器:
缓存穿透的解决方案
有一个二进制数组
判断一个数组存不存在这个数组里,不存在就是0,存在就是1
来一个数据,经过不同的hash算出对应的位置填成1
在检查的时候算出hash的几个位置,如果都为1,就说明数据存在
但是布隆过滤器不方便做删除操作
保密性好,因为都是0/1而且整体看无关联
有一定概率出现误判,但是还好,误判量少(可调),只要能做到大部分的数据常存在性判断就行
误判率和 二进制位,hash次数有关
布隆过滤器防止redis缓存穿透
redis带的有布隆过滤器的实现,直接创建着用就行,设置点大小和误判率
数据库
略
为什么不推荐使用外键与级联
在阿里巴巴开发手册里说过
外键的概念必须在应用层解决
外键与级联适合单机低并发,不适合分布式,高并发集群
其他的原因就是对分库分表不友好
外键的好处
保证了数据库数据的一致性和完整性
级联操作方便,减轻代码量
之类的
不涉及分库分表,低并发还是很好用的
数据库范式
1NF,2NF,3NF,BCNF,4NF,5NF
最多考虑到BCNF
/>布谷鸟过滤器
常见的关系型数据库MySQL、PostgreSQL、Oracle、SQL Server、SQLite(微信本地的聊天记录的存储就是⽤的 SQLite)
Redis
做缓存,分布式锁,消息队列
spring
ioc aop
Spring 时代我们⼀般通过 XML ⽂件来配置 Bean,后来开发⼈员觉得 XML ⽂件来配置不太好,于是 SpringBoot 注解配置就慢慢开始流⾏起来
JAVA面试笔试
事务的基本特征
原子性,一致性,隔离性,持久性(ACID)
,m tfrd
spring的理解
开源,社区活跃,IOC,AOP,
说的系统调用最少,c有缓冲区机制,可以减少读取次数
A是随机读取,无缓冲机制
B无缓存
D字节流与字符流的桥梁
这两个数都是非new出来的Integer数,当范围在-128到127之间时,会进行缓存,当用到时会直接赋值,当他们超过128,都是新new出来的数,所以不想等。 而Integer的equals方***转为int进行比较
finally为必执行的,虽然是最后执行,但是他是在方法内的,返回的1是主方法输出
java有默认的构造方法
compareTo()是String的方法
Java中凡是可以由程序员自己起名字的都叫标识符。其涉及到的结构有:包名、类名、接口名、变量名、方法名、常量名。
① 由26个英文字母大小写,0-9,_ 或 $ 组成。
② 数字不可以开头。
③ 不可以使用关键字(class、int等)和保留字(goto和const),但能包含关键字和保留字。
④ Java中严格区分大小写,长度无限制。(例:class×,Class√)
⑤ 标识符不能包含空格。
包名:多单词组成时所有字母都小写。(例:aaabbbccc)
类名、接口名:多单词组成时,所有单词的首字母大写。(例:AaaBbbCcc)
变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写。(例:aaaBbbCcc)
常量名:所有字母都大写,多单词时每个单词之间用下划线_连接。(例:AAA_BBB_CCC)
优先级高到低排序
D是说的是,被对象用,或被类用
被类用的应该是静态变量
然后就是AB的生存周期不同
https://zhuanlan.zhihu.com/p/113007640大白话聊聊synchronized、CAS底层原理、Lock锁和锁升级原理
https://www.bilibili.com/video/BV1BB4y1X7u3/?spm_id_from=333.337.search-card.all.click【neko算法课】红黑树 插入【11期】