这是个人总结的java开发面试常见的问题,大部分都有对应的解释
当jvm虚拟机第一次加载一个类的时候,会通过classpath去找到它的。class文件加载到jvm内存中
堆空间,虚拟机栈、本地方法栈、方法区、程序计数器
bootstrap classloader
extension classloader
Appclassloader
举个例子?java.lang.string
比如你自己写了一个java.lang.string当他收到类加载的请求是,他首先不会自己去加载这个类,而是把请求委托给它的父类去执行,它的父类是Extension classloader 但是没有在它的父类中找到java。lang。string、就会去找extension的父类bootstrap classloader,找到了java。lang。string、就会去加载这个类,如果没有找到,父类就会反馈给appclassloader无法加载类、这个时候才会自己去尝试加载
GC 是垃圾收集的意思,对内存堆中已经死亡的或很长时间没有用过的对象进行清除和回收。
有四种,标记清除算法,复制算法,标记整理算法,分代收集算法
分代收集是根据对象的存活时间把内存分为新生代和老年代,根据个代对象的存活特点,都有不同的垃圾回收算法。新生代采用标记—复制算法,老年代采用标记—整理算法。
它是通过引用计数法,和可访问性分析法来确定是否为垃圾对象
引用计数器算法是给每个对象设置一个计数器,当有地方引用这个对象的时候,计数器+1,当引用失效的时候,计数器-1,当计数器为0的时候,JVM就认为对象不再被使用,是“垃圾”了。
- 一级缓存:sqlSession级别的缓存;用同一个sqlSession对象,执行多次相同的查询操作,第一次从数据库中获取数据,后面的多次从缓存中读取数据一级缓存是默认开启的
- 二级缓存:sqlSessionFactory级别的缓存;用同一个sqlSessionFactory对象,获取到多个sqlSession,这多个sqlSession执行相同的查询操作,第一次从数据库中获取数据,后面的多次从缓存中获取数据①要使用二级缓存,需要手动开启
开启方法就是在mybatis的映射文件中添加一个cache标签
Spring就是一个工厂
,需要用到对象的时候到工厂中去获取,通过工厂类创建需要的对象,这样做的好处是解耦合,他有两个核心ioc和aopioc:
就是本来在类中完成对属性的赋值,现在交由spring工厂来完成,它通过di依赖注入完成属性赋值,他有三种赋值方式,设值注入,自动装配,构造注入aop:
就是面向切面编程,以横切的思想,在运行时把需要的功能添加到指定位置原理:
就是把业务代码中的冗余代码抽取出来,单独形成一个模块,这就是切面,切面与业务逻辑代码无关,但却被逻辑代码共用,把它抽取出来,提高代码的可维护性。
便于程序员的开发,解耦合,声明式事务处理,通过配置来完成事务控制,不需要手动的去控制,方便集成各种框架,降低了javaEE的开发难度
- 编程式事务处理,自己写代码控制事务,会造成代码冗余
- 还有声明式事务处理,通过aop思想完成事务控制
spring它本身不具备事务,spring事务的本质是用的数据库对事物的支持,如果没有数据库,spring的事务就不会生效,它的底层就是aop和数据库事务,通过spring把数据库的事务提取为切面,以横切的方式增加事务方法
- 可串行化的
最严格的级别,事务串行执行,资源消耗最大- 可重复读
保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失- 读已提交
大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统- 读未提交
保证了读取过程中不会读取到非法数据。
原子性
要么全部成功要么全部更新失败一致性
事务的执行前执行后都是一致的隔离性
对事物进行操作,其他事务不可以对数据进行操作持久性
对事物的更改永久保存
答单例的
SpringMVC是一个基于java实现了mvc设计模式的轻量级web框架,它的优点是可以与Spring框架集成,如ioc,aop等,支持各种请求资源的映射策略,有较清晰的角色分配,前端控制器(dispatcherServlet) , 请求到处理器映射(handlerMapping), 处理器适配器(HandlerAdapter), 视图解析器(ViewResolver)
- 在web.xml中配置spring核心控制器也叫分发器,然后在mvc配置文件中配置扫描controller包,和开启视图解析器就会把方法映射成地址
- 当有请求的时候,会先进到web。xml,如果符合分发器规则,就会到springmvc映射好的地址去匹配,如果匹配到相同的地址,就会进入对应的方法,执行方法,方法执行完后,会返回一个字符串,根据mvc的视图解析器规则解析视图,请求视图
用@ControllerAdvice,和@ExceptionHandler 注解实现
开箱即用,与springmvc和spring最大的区别就是不需要配置繁琐的配置文件,注解式的开发,便于程序员开发,他在pom.xml中去配置springboot的父接口就不需要管版本问题,避免版本冲突
可以用spring提供的一个RestTemplate方法,他里面有对应的方法去发送请求并接收响应的数据。他只要有spring环境就可以使用
bio是传统的io,它是同步阻塞式的io,并发处理能力低
nio是bio的升级版,它是同步非阻塞式io,他通过channel实现多路复用
aio是nio的升级版,它是异步非阻塞式io
他们都是无序无下标,key不可重复,value可重复,区别是,hashmap线程不安全,支持key和value,是null,hashtable线程安全,不支持kv是null
对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择,对一个有序的key集合进行遍历用treemap
HashMap是基于数组加链表实现的,当我们往HashMap中去put元素时,首先根据key的hashcode值重新计算hash值,根据hashcode值得到这个元素在数组中所对应的位置下标,如果该数组在该位置已经存在了元素,那么这个位置的元素则会以链表的形式存在,最新添加的放在链头,最早添加的放在链尾,如果在put元素的时候1该数组的该位置没有元素,就放在该位置上
- 她的底层是根据HashMap实现的
- Hashset的值放在HashMap的key上
- value统一为Present
- array可以存放基本数据类型和对象
- ArrayList只能放对象
- 都是有序有下标,元素内容可以重复
- 区别是array线程不安全,vector线程安全
- arraylist更加通用
- collection是集合的根接口,collections是集合类的一个工具类
- 迭代器是一种设计模式,它是一个轻量级的对象,使用时用()next获取下一个元素
- String不可被继承,因为它被fn修饰了,
- 前者零个或一个,后者一个或两个,他们首先不会去创建,而是去字符串常量池去找,找到的话就会引用常量池中的字符串,没找到就会在字符串常量池中创建一个,后者他还需要在堆空间也创建一个字符串对象
- String是不可变长字符串,线程安全,每次对String做改变的时候,都会生成一个新的String对象
- StringBuffer是可变长字符串,线程安全,Stringbuffer是对对象自身做改变,而不是生成新的对象
- StringBuilder可变长字符串,线程不安全
- 相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险
- 相同点都是有序有下表,元素内容可以重复
- 区别,Arraylist是数组实现,linkedlist是链表实现
- arraylist它是增删满,查询快,而linkedlist是增删快,查询慢点
- hashmap,hashtable,LinkedHashMap,parperty
- hashmap它是无序,无下标,key不可重复,value可以重复,允许null作为kv,线程不安全
- hashtable是无序,无下标,key不可重复,value可以重复,不许null作为kv,线程安全
- linkedHashmap它的特点是可以记录插入的顺序
- parperty,特点kv都是String一般用作配置信息
- hashset无序无下标元素内容不可重复,linkedhashset有序无下标,元素内容不可重复都是数组加链表实现
- 接口只能被类实现,抽象类中可以做方法的声明,也可以做方法的实现
- 接口中只能是公开静态常量,而抽象类可以有普通变量
- jdk1.8也支持接口中可以有默认方法,和静态方法
- 类可以实现很多个接口;但是只能继承一个抽象类。
- bio是面向流的,bio是同步阻塞式id,它的特点是简单易用,并发处理效率低
- nio是面向缓冲区的,同步非阻塞式io,它的特点是普通bio的升级版,通过chanainl实现多路复用,
- Lambda表达式
- 函数式接口
- 方法引用和构造器调用
- Stream API
- 接口中的默认方法和静态方法
- 反射是在运行过程中对任意一个类都能知道它的属性和方法,对于任意一个对象都能调用它的属性和方法,这就是java的反射机制,比如spring就用到了反射机制
- 它的作用有
- 在运行时判断任意一个对象所属的类
- 在运行时判断任意一个类所具有的成员变量和方法等
- 就是在运行时动态生成的类,它可以给类加一些额外的功能,比如日志和事务,给这个类创建一个代理类,这个代理类中有你本身逻辑代码和代理类中提供的代码,这个类不是定义的,而是动态生成的,好处是解耦合,便于维护
- 懒汉式,在类加载的时候就创建对象,并发效率高,空间利用率很低
- 饿汉式,什么时候使用什么时候创建对象,并发效率低,空间利用率高
- final最终的不可改变的,被他修饰的类不可改变
- (没整理全)
- 判断Obj和当前对象是不是同一个对象,是不是同一类型
- 判断obj是否为null
- equery判断内容是否相同,split对字符串拆分,length返回字符串长度
- list转数组 用ArrayList的toArray
- 数组转list 用Arrays的asList
- 序列化,把对象转化字节数组通过流发送到本地磁盘
- 反序列化就是把本地磁盘的字节数组通过流还原成java对象,需要持久化数据的时候序列化
- jdk是java运行环境和开发环境,jre是java的运行环境,包括jvm虚拟机和java类库
- get是获取服务器数据,post是给服务器传递数据
- get数据不安全,可能存在数据丢失,post数据安全,不会出现数据丢失
- get传递数据可能出现乱码,post不会出现乱码
- 轮询,权重,ip黏着
- 异步请求,局部刷新
- 一般session的sessionid是通过cookie保存到浏览器,如果浏览器禁用掉,就不能使用,但是可以在用户请求的url中吧sessionid追加在url路径后面,后续的交互场景,可以通过传递参数来获取session的数据
- 定义不同,运行时异常一般是RUntimeExeception,以及他的子类异常,而其他异常基本上都是exception的类以及子类
- jsp编译后就是servlet
- page,request,session,application
- request,response,page,pagecontext,session,application,out,config
- session是一个存在服务器上类似于散列表格的文件,里面存有我们所需要的信息,可以把它看成map,里面的key就是存放的sessionid,用户向服务器发送请求时会携带sessionid,就可以取出相应的值
- memory,innoDB这是mysql数据库默认的引擎,它使用的是B+树索引
- 尽量避免在使用where关键字对字段进行 null 值判断,会导致引擎放弃使用索引而进行全表扫描
- 尽量避免在 where 中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描
- 应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描
- in 和 not in 也要慎用,否则会导致全表扫描
- 普通索引,主键,唯一,单列/多列索引建索引
- 索引是一种数据结构,通过索引可以快速的定位到你所需要的数据
- 一般mysql索引要遵循主键外检必须加索引,然后是不频繁进行增删改的字段,经常出现在where条件后的字段,频繁的查询的字段,任何的标准表中,只允许创建最多16个索引
- 有两种,hash索引和B+树索引,mysql使用的是innoDB引擎,b+树索引
- 依赖三个特性,原子性,可见性,有序性
- 可以这样理解,并发就是在一台处理器上执行多个任务,而并行是在多个处理器上同时执行多个任务
- 每一个应用程序的运行就是一个进程,单核CPU在同一时间只能运行一个进程
- 线程是一个进程有多个线程构成,线程之间交替执行,执行由cpu决定
- 死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)。
- 继承Threand方法,覆盖run方法,实现RUNnb接口,实现run方法
- start() 方法用于启动线程,run() 方法用于执行线程的运行时代码。run() 可以重复调用,而 start() 只能调用一次。
- 让当前线程陷入睡眠,进入有限期的等待,她还持有锁
- wait让当前线程陷入等待,他会释放锁
- 线程容器,可以设定线程个数上限,根据设定的线程个数将预先创建好的线程对象存储在线程池中,线程池中的线程对象会被反复使用。
- 存储在线程池中,线程池中的线程对象会被反复使用
- 优点
- 减少创建和销毁线程的次数,从而提高效率
- synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性。
- 线程不安全就是多线程访问同一数据,破坏原子性,造成数据不统一
- 解决方法就是给临界资源上锁
- 每一个对象都有一个互斥锁标记,把锁标记交给线程管理,只有线程持有锁标记,才允许访问
- 乐观锁每次拿数据都做好最好准备,认为不会有人正在修改,所以在拿数据的时候不会上锁但是在做更新的时候会去判断有没有人在更新数据,乐观锁适合读场景,它会提高吞吐量
- 悲观锁是每次拿数据的时候都认为有人在修改,所以在拿数据的时候会上锁,阻塞等它拿到锁,传统型关系数据库中的行锁,表锁和读锁写锁都是利用悲观锁完成
- 使用场景乐观锁适合读的场景,悲观锁适合写的场景
- 首先这两个方法只能在(僧狗奈斯)同步代码块中调用,wait会释放掉对象锁,等待notify唤醒
- 守护进程就是服务其他进程,比如GC进程就是服务jvm于jvm一同结束工作
- 创建,就绪,运行,阻塞,死亡
- redis是nosql型数据库,而mysql是关系型数据库
- mysql它是将数据持久化到磁盘中,而redis它是把数据存储在缓存中,他读取速度快,但保存时间有限,最后按需要持久化到磁盘中
- Redis是一个非关系型数据库;是以key-value的方式存储数据,在内存中存储数据,支持网络传输,他一般用于做数据库的缓存系统
- 可以用redis作为数据库的缓存系统,首先,你要写一个缓存类实现mybatis的cache接口,把它的方法里面的一些参数指向redis,然后在mybatis的映射文件开启二级缓存就是添加一个cache标签,让她指向那个缓存类
- 虽然可以给redis的key设置过期时间,但是这个方法不可取,因为浪费资源,它内置有两个过期策略,一个是惰性策略,它的特点就是,放任不管,当你去获取数据库的key时,他会判断是否过期,如果过期就删除,返回null,但是时间长,有可能会oom,内存溢出,还有一种是定期策略,它是每隔一段时间都会去删除过期的key
- Redis有两种持久化机制,一种是RDB,这是系统默认的,它采用的是全本快照的方式,他保存的是真是的redis数据,还有一种AOF机制,它是以日志增量备份的方式(把对redis进行增删改查操作的命令,以日志的方式追加到日志文件中)它不存储数据,它存储的是你增删改查的命令,如果你开启了aof机制,aof和RDB同时存在,在做数据恢复的时候,会通过aof机制恢复数据
- String类型,list类型,set类型,hash类型,和sorted set
- ps显示所有运行的进程
- top显示各进程资源
- 比如hadoop,分布式文件系统,nginx负载均衡,和mycat读写都是分布式的,所谓分布式可以这样理解,就是多个服务器节点共同完成一件事情就可以认为他是分布式的架构
- 我懂的负载均衡是Nginx,它是通过反向代理模式实现负载均衡
- 举个例子,比如本来是用户直接跟服务器进行访问,获取她所需要的资源,但是现在通过Nginx作为中间件,用户直接访问nginx,而不关心具体是连接到哪一台服务器,这就叫反向代理模式,nginx他会给用户一个服务器压力不是很大的节点,
- 权重,轮询,ip黏着
- 轮询是默认的,就是依次访问
- 权重,在配置文件中配置,你配置数越大访问机滤越大
- ip黏着是对你的地址进行hash处理,对存活服务器进行取模
- 缓存雪崩可能是因为数据未加载到缓存中,或者缓存同一时间大面积的失效,从而导致所有请求都去查数据库,导致数据库CPU和内存负载过高,甚至宕机。
- 可以做redis的主从机,但是可能会在更新事务的时候出现脏数据的情况
- 它不像hdfs分布式文件系统一样会把文件拆分成block块存储,所以没有分块和块的开销,它是直接以文件存储,它最适合做图片服务器,客户端访问FastDFS完成图片的上传下载
根据实际回答,话语可以稍微包装优美一点
- CSDN,博客,B站