文章目录
- java面试题大汇总
- java基础
- java的跨平台原理
- JDK和JRE的区别
- == 和equals的区别
- 基本数据类型
- 面向对象的特征
- java和C++的联系和区别
- 重载与重写
- 装箱与拆箱
- “==”与equals的区别
- 两个对象的hashCode()相同,则equals()也一定为true,对吗?
- java中的Math.round(-1.5)等于多少
- String属于基本数据类型吗
- java中操作字符串有哪些类?他们有什么区别?
- String str=“i”;与String str=new String("i");一样吗
- 如何将字符串进行反转
- String的常用方法都有哪些
- 抽象类一定要有抽象方法吗
- 普通类和抽象类有哪些区别
- 抽象类能使用final修饰吗
- 接口和抽象类的区别
- java中的IO流分几种
- BIO、NIO、AIO有什么区别
- Files的常用方法有什么
- JVM
- 容器
- java容器都有哪些
- Collection与Collections的区别
- List、Set、Map的区别
- HashTable和HashMap有什么区别
- 如何决定使用HashMap还是TreeMap
- HashMap的实现原理
- HashSet的实现原理
- hashmap的初始容量为什么是2的幂次方
- hashmap扩容的加载因子为什么是0.75
- 链表长度达到多少时转红黑树,是8吗
- ArrayList和LinkedList的区别
- 如何实现数组和List的转换
- ArrayList和Vector的区别是什么
- Array和ArrayList的区别
- 在Queue中poll()和remove()的区别是
- 哪些集合类是线程安全的
- 迭代器Iterator是什么
- Iterator怎么使用,有什么特点
- Iterator和ListIterator的区别
- 怎么确保一个集合不能被修改
- 多线程
- JMM有了解吗
- 并行和并发有什么区别
- 线程和进程的区别
- 守护线程是什么
- 创建线程有哪些方式
- 说一下runable和callable的区别
- 线程有哪些状态
- sleep和wait的区别
- notify和notifyAll的区别
- 线程的run和start方法区别
- 创建线程池有哪些方法
- 线程池有哪些状态
- 线程池中的submit方法和excute方法有什么区别
- 在java中程序中怎么保证多线程运行安全
- 多线程锁的升级原理
- 什么是死锁
- 怎么防止死锁
- ThreadLocal是什么,使用在什么样的场景
- 说一下synchronized的底层实现原理
- synchronized和volatile的区别
- synchronized和Lock的区别
- synchronized与ReentrantLock的区别
- 说一下atomic的原理
- 反射
- 对象拷贝
- java web
- 异常
- 网络
- 设计模式
- spring/spring mvc
- spring boot/spring cloud
- mybatis
- 数据库
java面试题大汇总
进行一次java相关面试题的大综合。
下面是可能涉及到的模块。
java基础
java的跨平台原理
java的跨平台原理靠的是jvm(java虚拟机)。不同的操作系统提供了不同版本的jvm,jvm以此来屏蔽不同操作系统的指令差异,只要在不同的操作系统中安装不同的jvm就可以把java应用程序放在不同的操作系统上去运行。
JDK和JRE的区别
JRE是java运行环境,是供java应用程序的用户使用的。
JDK是java开发工具,是供java开发人员使用的。包含了JRE和java的编译器
== 和equals的区别
==对于基本数据类型来说直接比较数值,对于引用类型来说比较的是其内存首地址;
equals比较的是内存中存的值是否相同。对于我们自定的类使用这个方法需要重写实现,不然效果和==是一样的。
基本数据类型
java基本数据类型有8种:
基本数据类型 字节数 byte 1 short 2 int 4 long 8 float 4 double 8 char 2 boolean 1
面向对象的特征
面向对象的特征有4个:抽象、封装、继承、多态
- 抽象:将现实生活中的对象某些共有的特征抽象成属性,行动抽象成方法
- 封装:将属性和方法封装在一个模块中,也就是一个类
- 继承:一个类可以派生出子类,子类可以继承父类所有非私有的属性和方法,还可以进行扩展
- 多态:对象调用的方法在编译时是无法确认的,只有在运行的时候才会确认。体现在重写和重载
java和C++的联系和区别
联系:都是面向对象的语言,都具有面向对象的特点:抽象、封装、继承、多台
区别:
- 指针:java中不提供指针来直接访问内存,更加安全
- 继承:java是单继承的,而C++允许多继承
- 内存:java中有自动内存管理机制,不需要我们手动去调用函数进行内存回收
重载与重写
1.重载:相同的函数名,参数不同(参数类型,个数,顺序不同)
- 重写:继承关系中,子类对父类某方法的重写
装箱与拆箱
装箱:将基本数据类型转换成包装类型
拆箱:将包装类型转换成基本数据类型实际上,运行时会进行自动装箱拆箱
“==”与equals的区别
- ==,对于基本数据类型,直接进行比较,对于引用类型的对象,比较的是内存的首地址是否相同
- equals,比较的是内存中存储的值是否相同。
原因:基本数据类型直接存储在栈中,引用类型存储在栈中,其存储地址存储在栈中
两个对象的hashCode()相同,则equals()也一定为true,对吗?
不对。hashCode一样只是两者在内存中存储的首地址是一样的,但是值不一定一样。
final在java中的作用
final是java中的修饰词,可以修饰变量、方法、类。修饰变量标识变量的值时常量,不需进行初始化且不可改变,修饰方法标识方法不可被重写,修饰类标识类不可以被继承。
java中的Math.round(-1.5)等于多少
-1
String属于基本数据类型吗
不属于
java中操作字符串有哪些类?他们有什么区别?
String,StringBuilder,StringBuffer.
String的内容不可更改,StringBuilder和StringBuffer的内容是可以更改的;
StringBuilder是线程不安全的,StringBuffer是线程安全的。
String str=“i”;与String str=new String(“i”);一样吗
分配内存的方式不一样。第一种分配在常量池中,会先在字符常量池中寻找有没有一样值的字符串,若有了就直接指向该地址,没有的话会先在常量池新建一个,再进行地址指向;
而第二种会在堆内存新建一个对象,再进行指向。
如何将字符串进行反转
可以使用StringBuilder或者StringBuffer的reserve方法进行反转
String的常用方法都有哪些
equals方法比较两个字符串的值是否一样;
charAt方法输出指定索引处字符;
getBytes方法得到字符串的字节类型的数组
indexOf方法得到指定字符的索引
replace方法进行字符替换
trim方法可以去掉字符串前后的空格
split方法可以分割字符串,返回分割后的数组
length方法返回字符串长度
toLowerCase返回转变为小写之后的字符串
toUpeerCase返回转变为大写之后的字符串
substring截取字符串
抽象类一定要有抽象方法吗
不一定
普通类和抽象类有哪些区别
抽象类不能直接进行实例化,普通方法可以
普通类不可以含有抽象方法,抽象类可以含有抽象方法
抽象类能使用final修饰吗
不能。因为final修饰过的类不能重写,而抽象类不重写就没法进行抽象方法的实现
接口和抽象类的区别
抽象类可以有构造器,而接口不可以有构造器
抽象类中的修饰符可以是任意的,而接口是可以是public的
java中可以多实现接口,但是只可以单继承抽象类
java中的IO流分几种
根据功能来分:输入流输出流;
根据类型分:字节流字符串;
BIO、NIO、AIO有什么区别
BIO:Block IO同步阻塞式IO,是我们平时使用的传统的IO
NIO:New IO同步非阻塞IO,客户端服务端通过通道Channel通讯,实现了多路复用
AIO:Asynchronized IO异步非阻塞IO
Files的常用方法有什么
exists方法判断路径是否存在
createFile创建文件
createDicrectory创建文件夹
delete删除一个文件或目录
copy复制文件
move移动文件
size查看文件个数
read读取文件
write写入文件
JVM
JVM组成
jvm主要组成:类加载器、运行时数据区、执行引擎、本地库接口
运作:由类加载器将class文件加载到内存中,运行时数据区是代码中变量存储运作的地方,相对应的执行引擎将字节码文件转为相对应的底层系统指令,交给cpu去执行,在执行的时候可能调用到本地库接口。
类加载机制
类加载机制是什么
虚拟机把描述类的数据从class字节码文件加载到内存中,并进行数据的校验、转换解析和初始化,最终形成可以在jvm上运行的类型的过程就是类加载机制
类加载的加载过程(生命周期)
加载、验证、准备、解析、初始化
加载过程是:加载、连接、初始化;
其中连接的过程是:验证、准备、解析
加载:jvm将字节码文件转化为二进制字节流,并加载到内存中
验证:验证字节流中包含的信息符合虚拟机的要求并且是安全的
准备:为类变量分配内存并且初始化值
解析:对类、接口、字段、方法等进行解析;将常量池中的符号替换转化为直接引用
初始化:收集类所有字段的赋值动作和静态代码块
类加载器
类加载器负责将class的字节码文件转换成类实例。
类加载器有三种:启动(Boostrap)类加载器、拓展(ext)类加载器、系统(system)类加载器
确定类的是否相等,除了判断权限类名之外,还要判断是不是由一个类加载器加载的。
双亲委派模型
当一个类需要被加载时,不是由本身的类加载器去加载的,而是向父类加载器发起请求去加载,依次向上请求,只有当父类无法完成加载时才向下传递给子类加载器去处理。
运行时数据区
jvm的内存结构
jvm的内存结构由虚拟机栈、堆、方法区、程序计数器和本地方法栈构成的,其中堆和方法区是线程共享的,其他的三个是线程独有的。
如下图:
方法区:主要存储类信息、常量池、编译后的字节码等数据
堆:存放初始化的对象和数组
栈:栈的结构是栈帧组成的,一个方法对应一个栈帧。栈帧中存有局部变量表,操作数栈,方法出口等信息
本地方法栈:主要是为一些native方法服务
程序计数器:记录当前程序执行指令的行数
说一下堆栈的区别
栈中存储的是局部变量,堆中存的是实体;
因为局部变量的生命周期比较短,所以栈内存更新速度比堆快;
堆中局部变量使用完了之后就会被立即释放,而堆中的数据是回收机制不定期回收的
堆分区及其垃圾回收机制
堆中分为新生代和老年代,新生代中又分为一个Eden区和两个(幸存)survivor区,survivor区分为from和to两个。
Eden区是新建对象存放的区域,当触发一次monitor gc时,会将Eden区存在的数据和survivor from区存在的数据复制到survivor to区,然后清理Eden区和survivor from区的垃圾。
每一个对象的对象头中有一个标识年龄的字段,每一次这样的monitor gc,对象的年龄都会加1,当年龄达到15(默认情况下)时,会将对象转移到老年代中去,当老年代满了,会触发full gc。
堆内存的分区比例及参数配置
新生代:老年代=1:2;通过参数
-XX:NewRatio
配置Eden:from:to = 8:1:1 ;通过参数
-XX:SurvivorRatio
配置新生代的对象呗复制15次会进入老年代; 通过参数
-XX:+mAXtENURINGtHRESHOLD
配置
GC
GC怎么判断对象是可被回收的垃圾
方法一:引用计数法,当对象被引用的时候+1,引用取消-1,计数为0的对象可以被回收;但是循环引用的情况无法解决;
方法二:可达性分析法:一般设定栈中的对象引用为GC Roots引用,用GC Roots向下搜索,指向被引用的对象,称为引用链;当对象没有Root引用时可以被回收
GC的垃圾回收算法有:
有四种:标记清除、复制、标记整理、分代
标记清除:遍历内存,对可以回收的对象进行标记,然后再回收。会引起空间碎片化的问题
复制算法:将内存划分为两个部分,第一个部分进行垃圾回收时,将可用的对象复制到另外一个部分后,进行第一部分的全清除。以空间换时间,空间利用率低
标记整理:将可用的对象标记起来,然后移动到内存的一端,再对剩下的空间进行清理
分代算法:在java的堆内存中使用分代算法,分为新生代和老年代。新创建的对象放在新生代的Eden区,新生代垃圾回收时使用的是复制算法;存活久的对象会移动到老年代,老年代中使用的是标记整理算法。
垃圾回收器
常见的几种垃圾回收器:(主要记住cms和G1)
- serial垃圾回收器:单线程的垃圾回收器,回收时其他任务都会停下来,使用复制算法
- parNew垃圾回收器:serial的多线程版本,但是也需要stop the world,使用复制算法
- CMS(concurrent mark sweep):标记清除算法,是一种以获得最短回收停顿时间为目标的收集器。使用标记清除算法。会产生大量的空间碎片
- G1收集器:标记整理算法实现。
四种引用类型
四种引用类型:强引用、弱引用、软引用、虚引用
- 强引用:一直到jvm关闭才会回收的对象。在内存不足的情况下,内存宁愿抛出异常也不会回收;
- 软引用:当内存充足的时候不会回收,当内存不足了就会回收
- 软引用:只要垃圾回收器扫描到它,就会被回收
- 虚引用:维护一个队列,被回收的引用放入队列中,用来跟踪对象呗回收的活动
引用类型 被回收时间 用途 生存时间 强引用 从来不会 对象的一般状态 jvm停止运行时 弱引用 jvm垃圾回收时 对象缓存 gc运行后 软引用 内存不足时 对象缓存 内存不足时 虚引用 未知 未知 未知
重排序
jvm自行优化的一种手段。在单线程中不影响运行结果的情况下,jvm可能会将底层的指令重排序进行优化。然而在多线程的情况下,就可能会影响运行的结果。
?内存屏障
?happen-before原则
jvm的配置参数
堆栈配置相关
-Xmx3550m
:最大堆大小为3550m
-Xms3550m
:设置初始堆大小为3550m
-Xmn2g
:设置年轻代大小为2g
-Xss128k
:设置每个线程的堆栈大小为128k
-XX:MaxPermSize=16m
:设置持久代(方法区+其他)大小为16m
-XX:NewRatio=4
:设置新生代和年老代的比值
-XX:SurvivorRatio=4
:设置Eden区和一个survivor区的 比值
-XX:MaxTenuringThreshold=0
:这只对象多少岁进入老年代
查看堆栈情况的指令
jstack pid
容器
java容器都有哪些
ArrayList、LinckedList、Vector、HashSet、HashMap、HashTable、ConcurrentHashMap
集合的大分类
集合的分类有两种:
- Collection:只存储值value
- Map:存储键值对key-value
Collection
Collection只存储值value,这种存储方式的集合有两类:
- List:有序的,可重复的
- Set:无序的,不可重复的
List
List存储的数据是有序的,可重复的,派生了两种子类:
- ArraysList:底层实现是数组
- LinkedList:底层实现是链表
一、 ArraysList
底层实现是数组,查找快,插入和删除效率较低
二、 LinkedList
底层实现是链表,查找慢,插入和删除效率较高
数组和链表的区别:
- 存储位置的不同:数组相邻位置的元素在物理空间上也是相邻的,而链表不一定(可能相邻,也可能不相邻);
- 存储空间的不同:数组要求一片连续的空间来存储每一个元素,可能会造成空间的浪费,而链表不一定要连续的空间,可以利用零碎的空间;
- 在操作的效率上:数组查找特定的元素效率比较高(因为可能根据下标索引直接查找到元素),但插入和删除操作效率比较低(因为需要对元素挨个进行移动);而链表的插入和删除操作效率比较高(只需要更改next空间的指向即可),查找效率低(因为需要从第一个元素开始一个一个找到目标元素)
Set
Set存储的数据是无序的,不可重复的。常用的子类有:
- HashSet:无序的
- LinkedHashSet:有序的,根据插入的顺序排序
Map
Map存储数据是以键值对key-value形式来存储的。其中key值不可以重复。
Map常用的子类有:
- HashMap
- HashTable
- ConcurrentHashMap
HashMap
- HashMap可以把null作为key值和value值
- HashMap是线程不安全的,效率高
HashTable
- HashTable不可以把null作为key值和value值
- HashTable是线程安全的,效率低
CurrentHashMap
CurrentHashMap既保证了线程安全,又保证了效率
Collection与Collections的区别
Collection是java中的一个集合接口,提供了一些对集合进行基本操作的方法,直接实现类有List和Set;
Collections是工具类,提供了许多方法对集合进行操作,如排序、搜索、线程安全等。
List、Set、Map的区别
List和Set是Collection的子类,存的是单个的值,Map是以键值对的形式存储的
List是有序的可重复的,Set是无序的不可重复的
HashTable和HashMap有什么区别
一是HashMap允许以null作为键和值,而HashTable是不允许的
二是HashMap是线程不安全的,HashTable是线程安全的
如何决定使用HashMap还是TreeMap
若要对Map进行插入删除操作时,应当使用HashMap,而若有排序需求的应当使用TreeMap
HashMap的实现原理
HashMap在jdk1.8之前是以数组+链表的形式实现的,在1.8之后是以数组+链表+红黑树的形式实现的
HashSet的实现原理
HashSet是以HashMap实现的。HashSet的值存在hashMap的key值中
hashmap的初始容量为什么是2的幂次方
hashmap进行hash运算的时候是用&位运算的方式,使用2的指数次进行位与计算发生冲突的概率相对比较低,使得元素可以相对均匀地进行存储,从而提高访问的效率。
hashmap扩容的加载因子为什么是0.75
0.75是时间利用率和空间利用率的一个平衡,若使用较高的负载因子,虽提高了空间利用率,但是hash冲突过多,不利于访问,牺牲了时间利用率;当负载因子过低时,冲突较少,提高了访问的效率,但是需要的空间比较大,牺牲了空间利用率。因此,采用了两者平衡的一个负载因子0.75
链表长度达到多少时转红黑树,是8吗
当数组长度小于64时候,优先进行扩容;
当数组长度大于64后,在进行转红黑树之前会先把节点插入链表中,所以实际上应该是9
ArrayList和LinkedList的区别
ArrayList底层是以数组来实现的,LinkedList的底层是以双向链表来实现的。所以ArrayList的查询效率较高,插入和删除的效率较低,而LinkedList的查询效率较低,插入和删除的效率较高
如何实现数组和List的转换
List转数组:toArray方法
数组转List:asList方法
ArrayList和Vector的区别是什么
Vector是线程安全的,而ArrayList并不能保证线程的安全,所以ArrayList的性能更好
Array和ArrayList的区别
数组的长度不可改变,而ArrayList的长度是可以延长的
数组可以容纳基本数据类型,也可以容纳对象,而ArrayList只可以容纳对象
数组没有提供列表那么多的方法
在Queue中poll()和remove()的区别是
poll方法在获取元素失败的时候会返回空,而remove会抛出异常
哪些集合类是线程安全的
Vector、HashTable、stack、enumeration(枚举,相当于迭代器)
迭代器Iterator是什么
迭代器是用来对集合进行遍历使用的
Iterator怎么使用,有什么特点
调用Collection下的实现类提供iterator方法来获得Iterator类的实例,使用next方法可以获得下一个元素,而hasNext检查序列中是否还有可遍历的元素
Iterator和ListIterator的区别
ListIterator只可以使用在ArrayList上,而Iterator可以在Set和List中使用
Iterator只可以单向遍历,而ListIterator既可以向前也可以向后
怎么确保一个集合不能被修改
使用Collections下的unmodifiablexxx方法返回的集合时不可以被修改的,修改会报错,如unmodifiableMap、unmodifiableList、unmodifiableSet
注:不能使用final来实现,因为final修饰变量若是基本数据类型值不能修改,而修饰引用类型是地址不可修改,但是值可以修改,集合都是引用类型的。
多线程
JMM有了解吗
JMM是对于多线程的java内存模型。在JMM中,临界资源、共享变量在主内存中,每个线程将他们要用的变量复制一份副本到自己的本地工作内存中进行操作。在JMM中有8种基本的原子操作,分别是lock、read、load、use、assig、store、write、unlock
并行和并发有什么区别
并行是指多个事件发生在同一时刻,如多个任务在多个cpu或者一个cpu的多个核上同时进行;
并发是一个时刻只有一个事件发生,但同一时间间隔发生多个事件,如一个cpu时间片切换进行
线程和进程的区别
进程是运行中的程序,是系统进行资源分配的调度的基本单位,一个进程中可以有一个或多个线程,线程是争夺CPU时间片的基本单位。
各进程都有自己独立的内存空间,而一个进程中的线程之间共享内存空间,所以进程之间的切换没有线程那么快
守护线程是什么
守护线程是在程序运行时在后台提供一种通用服务的线程。
创建线程有哪些方式
- 继承Thread方法
- 实现Runable接口
- 实现Callable接口
- 线程池
说一下runable和callable的区别
runable需要实现的是run方法,callable需要实现的是call方法;
callable可以得到返回值,而runable没有返回值
线程有哪些状态
新建、就绪、运行、阻塞、死亡
sleep和wait的区别
sleep是Thread中的方法,wait是Object中的方法
sleep不释放锁,而wait会释放锁
notify和notifyAll的区别
notify只是随机唤醒一个线程,而notifyAll会唤醒所有等待的线程,再让他们自己去竞争cpu时间片
线程的run和start方法区别
start方法是用来唤醒线程的,唤醒之后的线程进入就绪状态,而获得时间片的线程再去执行run方法,run方法是线程的执行体
创建线程池有哪些方法
- newFixedThreadPool
创建一个固定长度的线程池,当提交一个任务就创建一个线程,直到达到线程的最大数量后线程数量将不再发生变化,当线程发生异常结束时,线程池会补充一个新的线程
- newCacheThreadPool
创建一个可缓存的线程池,线程池的数量不受限制,当线程的数量超过了处理的需求时候,就回收空闲的线程,当有新的需求时候就创建新的线程
- newSingleThreadExcutor
单线程的线程池,只创建一个线程来执行任务,当线程发生异常结束时,创建一个新的线程来替代。按照任务在队列中的顺序来串行执行。
- newScheduledThreadPool
创建一个固定长度的线程池,而且以延迟或定时的方式来执行任务。
线程池有哪些状态
- running:刚创建的线程池就处于Running状态
- shutdown:不接受新的任务,但是能处理已排队的任务
- stop:不接受新的任务,不处理已排队的任务,中断正在处理的任务
- tidying:当shutdown状态的线程池中任务数为0后进入tidying状态
- terminated:线程池彻底终止
转换图:
线程池中的submit方法和excute方法有什么区别
submit方法的参数可以是Runable接口的,也可以是Callable接口的,而excute参数只能是runable接口的;
submit方法有返回值,而excute方法没有返回值;
submit中抛出异常会内部处理,而excute需要我们try catch
在java中程序中怎么保证多线程运行安全
多线程安全问题体现在:原子性、可见性和有序性
Atomic、synchronized、Lock可以解决原子性
synchronized、Lock和volatile可以解决可见性问题
Happens-Before规则可以解决有序性问题
多线程锁的升级原理
多线程锁从低到高有:无锁、偏向锁、轻量级锁、重量级锁
在锁对象的对象头里有一个ThreadId,在锁的创始状态下,ThreadId是空的,当下是无锁状态,当有线程获得锁时,ThreadId中存该线程的Id,升级为偏向锁,当有其他的线程争夺锁时,偏向锁升级为轻量级锁,而线程通过自旋继续获取锁,执行了一定的次数还没有得到锁之后,就会升级为重量级锁。
什么是死锁
死锁是多个进程相互等待的一种僵局状况
怎么防止死锁
死锁发生有四个条件:互斥、请求与保持、不可剥夺、循环等待
要防止死锁需要破坏这些条件,互斥条件是资源使用的原则,不能破坏;
破坏请求与保持条件:线程一次性获得所有他要用的资源
破坏不可剥夺条件:进程不能获得所需的全部资源时就进行等待,并且要释放已经获得的资源给其他进程使用
破坏循环等待条件:将资源进行编号,紧缺资源编大号,进程按编号次序申请资源,要申请大号资源首先要获得小号的资源
ThreadLocal是什么,使用在什么样的场景
ThreadLocal是为线程提供独立的变量副本,使每个线程都能够独立地改变属于自己的副本,而不影响其他线程所对应的副本。
常用于数据库连接和session管理等
说一下synchronized的底层实现原理
synchronized底层是用监视器monitor来实现的,其中有两个指令,一个是monitorenter,一个是monitorexit,分别是进入锁和退出锁的指令。
synchronized和volatile的区别
synchronized是线程安全的,可以保证原子性,而volatile无法保证原子性
synchronized和Lock的区别
synchronized是基于JVM的关键字,而Lock是API层面的接口
synchronized在遇到异常时会自动释放锁,而Lock需要我们手动释放锁,否则会发生死锁的现象
synchronized是不能中断的,而Lock可以使用interrupt方法中断
synchronized无法获得锁的获取状态而Lock可以
synchronized与ReentrantLock的区别
synchronized是一个关键字,ReentrantLock是Lock的一个实现类,ReentrantLock包括了synchronized中的所有功能,还进行了扩展;
说一下atomic的原理
会调用unsafe类中的一些方法来对底层进行操作
反射
什么是反射
反射是在运行的时候可以获取到任意类名称,所有属性、方法、注解、类型、类加载器等
什么是java序列化?什么情况下需要序列化
序列化是将对象转化为字节流的过程;
当java对象需要在网络中传输或持久化存储在文件中时,就需要用到序列化。
动态代理是什么?有哪些应用
动态代理,是在运行的时候动态地创建目标类,可以调用和扩展目标类的方法。
常使用的场景有:Spring的AOP、事务、权限、日志。
怎么实现动态代理
首先需要定义一个接口,然后要有一个实现InvocationHandler的类的处理类;
第二种方式是使用CGLIB,先要引入相关的jar包
对象拷贝
为什么要使用对象克隆
当我们需要操作对象,但是却希望保留之前的数据时,可以使用克隆
如何实现对象克隆
方法一:实现Cloneable接口并重写clone方法进行克隆
方法二:实现Serializable接口,通过对对象的序列化和反序列化来实现克隆,可以实现真正的深克隆
深拷贝和浅拷贝的区别
浅拷贝只拷贝到基本数据类型,而对于对象类型拷贝的是其地址,更改会影响到原对象;
深拷贝不仅拷贝了基本数据类型,对于对象类型也会拷贝一份副本,更改不会影响到原对象。
java web
get和post请求
自己理解:他们都是http的请求方式。get、post、put、delete分别对应着对资源的查、改、增、删操作。所以,get主要用来对资源的获取/查询,post主要用来对资源进行更新的操作。
回答:
- GET请求的数据会在地址栏显示出来,而POST请求的数据不在地址栏中,而是包在http的包体中
- 由于1,GET请求的数据多少有限制(地址栏的长度有限制),而POST的请求数据的数量没有限制
- 由于1,GET请求的安全性没有POST的高
怎么编写Servlet
实现HttpServlet类,重写doGet或者doPost方法,也可以重写service方法来调用相应的doGet或doPost方法来完成对请求的响应。
Servlet的生命周期
- 服务器启动后,加载并实例化servlet
- servlet通过调用init方法进行初始化
- 请求来的时候,调用service方法处理客户端的请求
- 服务器关闭,通过调用destroy方法来销毁servlet
- java的垃圾回收机制进行空间回收
forward与redirect的区别
- forward服务器端发起的请求。浏览器的地址不会改变,只是将请求到的数据include进来
- redirect客户端发起的请求。redirect会使浏览器跳转到新的地址,加载一个新的页面
jsp和servlet有什么区别
jsp是一种特殊的servlet,jsp最终也会被翻译成servlet;
jsp侧重于视图,而servlet侧重于业务逻辑;
另外,jsp是在前端代码中插入java语言代码,而在servlet中要写入前端代码需要用write来拼接前端代码,比较麻烦。
jsp有哪些内置对象?作用分别是什么
request:封装客户端的请求
response:封装服务器端对客户端的响应
session:封装会话的对象
application:风咋混个服务器运行环境的对象
page:jsp对象本身
pageContext:可以通过该对象获得其他对象
config:web应用的配置对象
out:输出服务器响应对象的输出流
excetion:封装页面抛出的异常对象
说一下4种作用域
page的作用范围是一个页面;
application的作用范围是整个应用程序
session的作用范围是一次会话中
request的作用范围是一次请求
session和cookie的区别
session和cookie都是用于会话跟踪技术的;session的实现依赖于cookie,因为cookie中存有一个sessionId来标识session
session将数据存在服务器端,而cookie将数据存储在客户端
所以session的安全性高于cookie
说一下session的工作原理
session是一个存在服务器端的一个类似于散列表格的文件。相当于一个map,键值存sessionId
如果客户端禁止cookie能实现session吗
可以手动在url中加入sessionId,或者将sessionId存在数据库或文件中来实现
如何避免sql注入
使用prepredStatement来传参数
使用正则表达式来过滤参数
jsp页面中检查是否包含非法字符
异常
throw和throws的区别
throws是用在方法上的,后声明可能出现的异常类;
而throw用在方法中,用来抛出异常
final、finally、finalized的区别
final是修饰词,用来修饰变量表示变量的值不可改变,修饰方法表示方法不可被重写,修饰类表示类不可被继承;
finally是在异常处理中使用的,用finally包含的代码块无论是否发生异常都会运行
try-catch-finally中,如果catch中return了,finally还会执行吗
会,在return之前先执行finally的代码
常见的异常类有哪些
空指针异常;数组越界;找不到类;类型转换异常;sql异常等
网络
http响应码中301和302代表的是什么?有什么区别?
301和302都表示url发生了转移
301表示永久地转移;302表示暂时性的转移
forward和redirect有什么区别
forward是由服务器端发生的一次请求,实现局部的刷新,地址栏不会改变;
redirect相当于客户端发起的两次请求,是重新加载了一个页面,地址栏会改变
简述tcp和udp的区别
tcp是面向连接的,udp是无连接地发送数据;
tcp保证了可靠性,而udp不保证可靠性;
udp保证实时性;
tcp是点到点的,而udp支持一对一,一对多,多对多的交互通信
tcp为什么要三次握手
为了实现可靠数据传输,通信双方都要维护一个序列号,三次握手,既要保证发送方发送出了正确的数据,又要保证接收方接收到了正确的数据。
OSI的七层模型有哪些
物理层、链路链路层、网络层、传输层、会话层、表示层、应用层
get和post的区别
get对应着数据的获取,post对应着数据的更改;
get请求会将请求参数加载地址中,而post请求是将数据包含在包体中;
所以post的安全性高于get;且地址栏的长度是有限的,所以get能传输的参数是有限的,post可以传输的数据是不限制大小的;
如何实现跨域
通过jsonp实现跨域
说一下jsonp的实现原理
jsonp是json+padding,动态创建script标签,通过script标签的src属性可以获得任何域下的js脚本。
设计模式
说一说你熟悉的设计模式
单例模式:单例模式分为饿汉式和懒汉式,饿汉式。饿汉式是一开始就对实例进行创建,而懒汉式是在需要的时候才开始创建;单例模式的实现重点在于将构造方法私有化,在类的内部进行实例的创建,并提供方法给外部获取。
观察者模式:对象间一对多的依赖关系,当这一个对象状态发生改变的时候,所有依赖他的对象都得到通知并被自动更新
装饰者模式:对已有的业务逻辑进行封装,使其获得额外测功能
适配器模式:将两个不相同的对象联系在一起
工厂模式:工厂模式分为简单工厂模式、工厂方法模式和抽象方法模式;
简单工厂模式主要包含一个抽象的接口,一些接口的实现类和一个工厂类,
工厂方法模式主要包含抽象产品、具体产品、抽象工厂、具体工厂,
抽象工厂模式与工厂方法模式的区别是,工厂方法模式的工厂只生产单一的产品,而抽象工厂模式的工厂生产多种产品
- 简单工厂和抽象工厂的区别
spring/spring mvc
为什么要使用spring
spring是在企业级开发中简化开发工作而存在的,是一个轻量级的ioc和aop的容器框架
解释一下什么是aop
aop是面向切面编程,就是在java各类中都会使用到的部分抽离出来形成一个模块,如日志、事务、权限等
解释一下什么是ioc
ioc是控制反转,我们以往创建对象使用new的形式来创建,现在将创建对象的权利交给spring容器类实现,需要使用的时候再进行依赖注入
spring有哪些主要的模块
数据访问/继承、web、aop、核心容器、测试
spring中常用的注入方法有
构造器注入、setter注入、基于注解注入
spring中的bean是线程安全的吗
本身是不具有线程安全的
spring中支持几种bean作用域
- spring中自动装配bean的方法有哪些
可以使用autowired注解进行自动装配
spring的事务实现方式有哪些
使用@Transactional注解
- 首先在application配置中配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
- 在web的配置中开启对注解的解析
<tx:annocation-driven transaction-manager="transactionManager" proxy-target-class="true"></tx:annocation-driven>
- 在代码中使用transactional注解
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=RuntimeException.class)
- 说一下spring的事务隔离
说一下spring mvc的运作流程
- 客户端发起的请求会提交到前端控制器dispatchServlet;
- 前端控制器根据HandlerMapping映射查找对应的Controller
- Controller进行业务逻辑处理,返回ModelAndView
- ModelAndView提交到前端处理器进行解析
- 将解析结果发到浏览器去进行显示
spring mvc有哪些组件
dispatcherServlet中央处理器
controller控制器
handlerMapper映射处理器
@RequestMapping的作用是什么
进行地址映射,找到对应的handler
@Autowired的作用是什么
自动装配
spring boot/spring cloud
- 什么是spring boot
spring boot是一种简化框架使用的框架。以往使用spring框架总是需要一个繁琐的配置文件,而spring boot的主要就是简化配置。
- 为什么要用spring boot
spring boot使编码、配置、部署、监控都变得简单许多
- spring boot核心配置文件是什么
spring boot中的核心配置文件是application和bootstrp;
bootstrap是系统级别的,application是应用级别的
- spring boot配置文件有哪几种类型,他们有什么区别
spring boot的配置文件类型有application和yml两种;
application是以key=值的形式书写,yml是以key:值的形式编写。yml会以空格来区分层级,容易因为马虎而出错
- spring boot有哪些方式来实现热部署
方法一:在springboot插件配置中加入springloaded的依赖,之后使用maven指令来运行
方法二:添加spring-boot-devtools依赖
mybatis
mybatis中的#{}和${}是什么区别
#是预编译的,$是字符串的替换
mybatis在处理#的时候先将其用?替换,再用praparedStatement来设置参数;可以防止sql注入
而$是直接进行字符串的替换;
mybatis有几种分页方法
方法一:数组分页:将查询出来的所有数据放到list中进行截取
方法二:使用sql语句,如limit或rownum的方式,传入参数
方法三:使用拦截器
方法四: 使用RowBounds
RowBounds是一次性查询全部结果吗?为什么
不是。虽然他可以一次性查出很多数据,但是在mybatis有一个fetch size的配置,会限制一次最多出查出数据的多少
mybatis逻辑分页和物理分页的区别
逻辑分页是将数据一次查询出很多,再去进行一个结果集的分页,在数据量比较小的情况下效率较高;
物理分页是在查询的时候就只返回指定页数的数据,在数据量大的情况下效率较高
mybatis是否支持延迟加载?延迟加载的原理是什么
mybatis中可以通过配置支持延迟加载
原理是动态代理
说一下mybatis的一级缓存和二级缓存
一级缓存是默认开启的,作用在一次会话中,查询的结果缓存起来,下次再有相同的查询时就直接在缓存中取,提高效率
二级缓存是全局缓存,可用在不同会话之间。
数据库
数据库的三范式是什么
范式是规范的意思。
第一范式是说数据表的列不可再分隔
第二范式是说数据表中的每行数据都有一个唯一的标识,例如设置主键
第三范式是说数据表中不能含有其他数据表除了主键之外的其他列,例如设置外键
一张自增表里面总共有17条数据,删除了最后2条,重启mysql数据库,又插入一条数据,此时id是几
18
如何获取数据库版本
select version() 可以得到mysql的数据库版本
说一下ACID是什么
事务的四个特性:
原子性、一致性、隔离性、持久性
事务
事务是定义的一系列操作的序列,这些操作要么都执行,要么都不执行,只要其中任意一个操作失败了,其他的操作也不能成功。
事务的四个特征
- 原子性:事务的操作是不可分割的,要么都执行,要么都不执行
- 一致性:事务的每项操作的执行结果状态必须是一致的,要么都成功,要么都不成功。若中途失败了,则前面的成功操作会回滚。
- 隔离性:程序中往往存在多个事务可能处理同样的数据,为了保护数据不被破坏,每个事务是隔离的。
- 持久性:事务处理后的结果时持久的,不会因为系统故障而有所变动
事务并发的问题
- 脏读:一个事务读到了另外的事务没有提交的数据。(读到了另外这个事务回滚前的数据)
- 不可重复读:一个事务中两次读取同一数据的值不相同。(第二次读,读到其他事务提交后的数据)
- 幻读:一个事务按搜索条件读取数据,第二次读取的数据比第一次读取到的行数多(第二次读,其他事务插入并提交了符合搜索条件的数据)
事务隔离级别
- 读未提交:最低级别的的隔离,以上三种情况都不可避免;
- 读已提交: 可避免脏读
- 可重复读:可避免脏读、不可重复读
- 串行化:可避免以上三种情况
索引
索引的作用
索引是对数据库中的某一列或者多列的值进行排序的一种结构,使用索引可快速访问数据库中特定的信息
索引底层是什么数据结构
B+树。
B+树
- B+树中,最大元素一定存在根节点中
- B+树中,父节点的元素会在子节点中出现
- B+树中,叶子结点有地址指向(类指针),构成链表
B+树较之B-树的优点
- 单一节点存储更多的元素,减少查询的IO次数
- 所有查询都是查询到子节点,查询性能更稳定
- 所有的叶子结点形成有序链表,便于范围查询
怎么验证mysql的索引是满足需求的
使用语句
explain select * from table where type = 1
可以查看sql是如何执行的,以此来看是否满足
char和varchar的区别
char是定长的,varchar是不定长的
float和double的区别
float是4个字节的,而double是8个字节的
mysql的内连接、做连接、右连接的区别
内连接将匹配的关联数据都显示出来
左连接是以左表作为主表,保留左表的数据,显示右表符合条件的数据
右连接以右表作为主表,保留右表的数据,显示左表符合条件的数据
说一下mysql的常用引擎
InnoDB引擎、MyIasm
where、group by、having
- where子句用来筛选from子句中指定的操作所产生的行
- group by子句对where子句的结构进行分组
- having子句用来从分组的结果中筛选行
having和where的区别
- where操作在分组之前,having子句在分组之后
- having中可以包含聚合函数
mysql分页和oracle分页
- mysql是使用关键字limit来进行分页的
...limit offset,size
- oracle分页使用rownum关键字取数据的行数,再加where条件语句来限定行数范围
select * from (select rownum rn,* from (select * from table order by co) where rn <= 15 )t where t.rn >= 5
in和exist的区别
in是先进行子查询,再用子查询的结果去和主查询做笛卡尔积;
exists是loop循环主查询的数据去和子查询做条件比对,结果为true就保留数据,结果为false就删除数据;
所以当子表小主表大的就用in,子表大主表小的就用exists
乐观锁和悲观锁
在数据库并发情况下,为了防止出现类似于超卖的情况,一般需要进行加锁处理。
乐观锁认为一般不会出现并发问题,只有在更新数据的时候才进行加锁处理;而悲观锁认为总是会出现并发问题,一开始就进行加锁处理。
悲观锁
在mysql中悲观锁的用法:
设置取消自动提交
在查询数据的时候使用:select * from . for update
乐观锁
在mysql中乐观锁的用法:
- 添加一个字段为version
- 查询时,连带查询出版本号version
- 当更新数据的时候需要比对版本号,可成功更新时版本号+1
如果有很多用户同时访问并修改数据库中的某个字段值,怎么保证数据的正确性
可以在代码层面进行同步,也可以在数据库加锁;
代码同步可以使用synchronized或者lock;
数据库加锁可以使用悲观锁或者乐观锁,悲观锁可以通过for update加锁;乐观锁可以通过添加版本字段,通过比较后更改的方式来实现
说一下mysql的行锁和表锁
行锁锁住的只是一行数据,而表锁锁住的是整张表
说一下乐观锁和悲观锁
乐观锁认为一般情况不会发生并发问题,只有在写数据的时候才会加锁
悲观锁认为总是会发生问题,所以从一开始就加锁,无论是读还是写
mysql的乐观锁需要自己去实现,通过添加版本号字段来实现
mysql的悲观锁可以通过在查询后加上for update来实现
数据库中的事务是怎么实现的
事务日志,在日志中记录下数据和进行的操作
MVCC,在数据库表中隐藏有两个列,一是记录数据的创建时间,二是记录数据的过期时间,里面存储的是版本号
- mysql问题排查都有哪些手段
使用show processlist命令查看当前所有连接信息
使用explain命令查询sql语句的执行计划
开启慢查询日志,查看慢查询的sql
- 如何做mysql的性能优化
为搜索字段添加索引
避免使用select *,而是列出具体的字段