java基础面试准备
ArrayList和LinkedList有什么区别,如何解决线程安全问题?
ArrayList 底层结构:可变数组 增删的效率低,数组扩容,改查的效率较高
LinkedList底层结构:双向链表 增删的效率较高,通过链表追加,改查的效率较低
Java中的Collection 类中的list和set
list集合中元素有序
list每个元素都有其对应的顺序索引,即支持索引
list容器中的元素都对应一个整数型的序号记载其在容器的位置,可以根据序号存取容器中的元素
set无序,没有索引
不允许重复元素,所以最多包含一个null
标题
说一下快排和冒泡排序有什么区别?时间复杂度是多少?
快排在选取变量不好的情况下,时间复杂度是多少,并行计算中,哪种排序更适合
说一下JVM的GC
如果你申请了一块内存,但是你不想这块内存被回收,该怎么解决
你说一下StringBuffer和StringBuilder有什么区别
StringBuilder 和 StringBuffer 非常类似,均代表可变的字符序列,而且方法也一样
String: 不可变字符序列,效率低,但是复用率高
StringBuffer: 可变字符序列、效率较高(增删)、线程安全,看源码
StringBuilder: 可变字符序列、效率最高、线程不安全
String中的对象是不可变的,也就可以理解为常量,线程安全。
AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
每次对String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的
String对象。StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象
并改变对象引用。相同情况下使用StringBuilder相比使用StringBuffer仅能获得10%~15%左右
性能提升,但却要冒多线程不安全的风险
Comparable和Comparator区别是什么
在java.long包下,comparable作为一个类的内部排序实现,java中一些普通类型如string、integer等都实现了该接口,我们直接使用即可
在java.util包下,comparator是外部排序接口,使用策略模式,
HashCode
hashcode()的作用是获取哈希码,也称为散列码;它实际上是返回一个int
整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。散列表存储的是键值对,它的特点是:能根据键快速的检索出对应的值。
这其中就利用到了散列码
为什么要有hashcode
当你把对象加入hashset时,hashset会先计算对象的hashcode值来判断对象
加入的位置,同时也会与该位置其他已经加入的对象的hashcode值作比较
如果没有相符的hashcode,hashset会假设对象没有重复出现。但是如果发现
有相同的hashcode值的对象,这时会调用equals()方法来检查hashcode相等
的对象是否真的相同。如果两者相同,hashset就不会让其加入操作成功。
如果不同的话,就会重新散列到其他位置
这里是引用
什么是线程和进程
进程是程序一次执行过程,是系统运行程序的基本单位,因此进程是动态的。
系统运行一个程序即是一个进程从创建,运行到消亡的过程在java中,当我们启动main函数就是启动了一个jvm的进程,而main
函数所在的线程就是这个进程中的一个线程,也称主线程
通信:进程间通信ipc,线程间可以直接读写进程数据段(全局变量)来进行通信—需要
进行进程同步和互斥手段的辅助,以保证数据的一致性
在线程池中,线程很多的情况下,怎么保证线程数是合理的
volatile有什么用
spring了解多少,清楚spring aop?
说一下图的广度优先搜索?
红黑树了解吗
java的垃圾回收机制熟悉吗?能简单介绍一下吗
有用过redis吗?怎么用的?怎么解决缓存击穿问题
遇到内存泄漏的问题,通常是用什么方式来排查问题的
你对java的设计模式熟吗,能介绍一下工厂模式吗
你对volatile关键字了解多少
你对死锁的了解?一个线程获取锁,在代码层面是怎么实现的
springboot的核心注解有哪几个,分别是做什么的
使用java的时候,你通常是用什么方式做多线程并发的?你通常是用什么方式来实现你的功能?
你这边比如说Kafka或者说其他的消息队列用过吗?
HashMap的底层实现是什么?
你对java的反射了解吗?什么地方用的,它的实现原理是什么?
你知道get和post请求的区别吗
String类型在JVM里面是怎么表现的?
==和equals的区别
对于基本数据类型来说,==比较的是值。对于引用数据类型来说,比较的是对象的内存地址。
又因为java只有值传递,所以,对于来说,不管是比较基本数据类型,还是引用数据类型的变量
其本质比较的都是值,只是引用类型变量存的值是对象的地址equals() 不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等
Object中的equals方法:public boolean equals(Object obj) { return (this == obj); }
Object的equals方法比较的是对象的内存地址
String中的equals方法:public boolean equals(Object anObject) { if (this == anObject) { return true; }
String中的equals方法比较的是对象的值
定义了一个 String 类型的私有成员 a1,我给他赋了一个初始值,比如说 “abc”,然后我在另一个类里面,也有一个值为 “abc” 的成员变量 a2,如果比较 a1 == a2,它会相等吗?
如果我在一个方法里面定义了一个局部变量 a3,如果比较 a3 == a1,你觉得会相等吗?
如果 a4 = new String(“abc”),你觉得这个 a4 会和 a3 相等吗?
那如果变量没有 final 修饰,就是 public static String a5,我在构造函数里面给 a5 赋值为 “abc”,你觉得它会和 a1 相等吗?
设计模式了解过哪些?简单工厂模式和工厂方法模式有什么区别?除了开闭原则,还有哪些设计原则?
为什么要用依赖倒置原则?
常用的GC算法有哪些
垃圾收集器有什么问题
为什么要有内核态和用户态的区别
可作为GC Roots的对象包括:
1.虚拟机栈(栈帧中的本地变量表)中引用的对象
2.本地方法栈(native方法)中引用的对象
3.方法区中类静态属性引用的对象
4.方法区中常量引用的对象
5.所有被同步锁持有的对象
回收算法
1.标记-清除算法
该算法分为“标记”和“清除”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象
2.标记-复制算法
3.标记-整理算法
根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
4.分代收集算法
当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将 java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。
比如在新生代中,每次收集都会有大量对象死去,所以可以选择”标记-复制“算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。
线程池
start:start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的
start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法
run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
run:方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待
run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。总结:调用start方法方可启动线程,而run方法只是thread的一
个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用
run()方法,这是由jvm的内存机制规定的。
线程池种类
- 线程池首先判断核心线程池corePoolSize里的线程是否已经满了。如果不是,则创建一个新的工作线程来执行任务。否则进入2.
- 判断工作队列BlockingQueue是否已经满了,倘若还没有满,将线程放入工作队列。否则进入3.
- 判断线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行。如果线程池满了maximumPoolSize,则交给饱和策略RejectedExecutionHandler来处理任务。
newFixedThreadPool
newCachedThreadPool:线程池的线程数可达到Integer.MAX_VALUE,即2147483647,内部使用SynchronousQueue作为阻塞队列;
和newFixedThreadPool创建的线程池不同,newCachedThreadPool在没有任务执行时,当线程的空闲时间超过keepAliveTime,会自动释放线程资源,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销;
newSingleThreadExecutor:初始化的线程池中只有一个线程,如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行.
由于使用了无界队列, 所以SingleThreadPool永远不会拒绝, 即饱和策略失效
AOP手写动态代理
动态代理步骤:
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法
newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
4.通过代理调用方法
JVM 内存模型(概述)
讲讲双亲委派,为什么?
你觉得双亲委派这个名字有没有什么问题?你讲讲自己的理解
打破双亲委派的例子,举个例子?怎么打破?
你现在用什么 GC 收集器?
Eden 区的对象回收策略?
CMS 的 STW 情况?
CMS 什么情况会触发 Full GC?
新对象一定会在新生代 new 出来么?
Java8 有什么新的 GC 算法 ?
了解 G1 收集器么?
JDK 里面哪里用到了工厂模式,其它模式呢?
JDK 哪里用到了责任链模式?我说 JDK 里不太清楚,但我知道 Spring Security 里用到了责任链模式,让我详细讲一下?
多线程环境下,如何保证操作集合的线程安全?我答了加锁和使用线程安全的集合,面试官说加锁的话,如果一个线程修改的同时,另一个线程进行读操作,那么读操作操作会阻塞,有没有好的解决方案?我答了写时复制 CopyOnWriteList,讲了讲具体是怎么实现的,然后面试官追问,写操作完成的时候,更新数组的时候也是线程不安全的呀,然后我说在写操作时候加锁吧,后面面试官说其实不用加锁,更新引用的操作本来就是原子的,我只是引导你一下。
用过哪些常用的集合类?答了 ArrayList、LinkedList、HashMap,面试官说这些都是线程不安全的、然后我又说了 SynchronizedMap、ConcurrentHashMap。追问这两个有什么区别,都讲了一下、顺便讲了 ConcurrentHashMap 1.7 和 1.8 的区别还有写操作的过程,又追问 链表 转换为 红黑树 损失了什么?
JVM 的内存结构了解吗?什么东西存放在方法区?如何存储线程私有的数据?引入了 ThreadLocal、ThreadLocal 里的 ThreadLocalMap 存在哪里?ThreadLocalMap 存在线程安全问题吗?
为什么引入线程池?为什么创建一个线程比较“重”?线程池的核心参数?如果此时任务数量小于核心线程数,当有新任务到来的时候,线程池会怎么处理?讲完之后顺便讲了后面的运行流程。最后问了有哪几种拒绝策略?
开始聊 synchronized,一个线程获取锁,JVM 层面会做什么操作,怎么说明它获取到了锁?对象头存了存线程 id 以外,还存了什么数据?如果一个线程获取了锁,那它在这个方法里面调用另一个方***怎么样?为什么要这样设计,不设计成不可重入的?
创建线程有几种不同的方式?你喜欢哪一种?为什么?
继承Thread类
实现Runnable接口
实现Callable接口
应用程序可以使用Executor框架来创建线程池
实现Runnable接口这种方式更受欢迎,因为这不需要继承Thread类。在应用设计中已经继承了别的对象的情况下
这需要多继承,而java不支持多继承,只能实现接口。
Netty做RPC框架
netty是什么
netty的异步处理体现在什么方面
你知道非阻塞IO吗,讲一下流程
阻塞IO呢,讲一下流程
什么是java虚拟机
java虚拟机是一个可以执行java字节码的虚拟机进程,java源文件被编译成java虚拟机执行的字节码文件
static关键字是什么意思,java中是否可以覆盖一个private或者是static的方法
static关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下呗被访问
java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。
是否可以在static环境中访问非static变量
static变量在java中是属于类的,它在所有的实例中的值是一样的。而类被虚拟机载入的时候,会对static变量进行
初始化,如果尝试用实例来访问非static的变量,编译器会报错,因为这些变量还没有被创建出来,因为这些变量
还没有被创建出来,还没有跟任何实例关联上
自动装箱、自动拆箱
自动装箱:java编译器在基本数据类型和对应的对象包装类型之间做的一个转化。
jvm中虚拟栈中存的是对象的地址,创建的对象实质在堆中,通过地址来找到堆中的对象的过程,即为引用类型
接口&抽象类
接口是绝对抽象的、不可以被实例化,抽象类也不可以被实例化
线程的几种可用状态
1.新建new:新创建一个线程对象
2.可运行runnable:线程对象创建后,其他线程(比如main线程)调用了该对象的start方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu的使用权
3.运行running:可运行状态runnable的线程获得了cpu时间片timeslice,执行程序代码
4.阻塞block:阻塞状态是指线程因为某种原因放弃了cpu使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行runnable状态,才有机会再次获得cpu timeslice转到运行running状态
一、等待阻塞:运行running的线程执行o.wait()方法,JVM会把该线程放入等待队列waitting queue中
二、同步阻塞:运行running的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池lock pool中
三、其他阻塞:运行running的线程执行Thread.sleep或t.join方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。
当sleep状态超时、join等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行runnable状态
5.死亡dead:线程run、main方法执行结束,或者因异常退出了run方法,则该线程结束生命周期,死亡的线程不可再次复生
同步方法和同步代码块
同步方法默认用this或者当前类class对象作为锁
同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法
同步方法使用关键字synchronized修饰方法,而同步代码块主要是修饰需要进行同步的代码,用 synchronized(object){代码内容}进行修饰
如何确保N个线程可以访问N个资源同时又不导致死锁
使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制按照指定的顺序获取锁。因此,如果所有的线程
都是以同样的顺序加锁和释放锁,就不会出现死锁了
java集合类框架的基本接口有哪些
Collection: 代表一组对象,每一个对象都是它的子元素
Set:不包含重复元素的collection
List:有顺序的collection,并且可以包含重复元素
Map:可以把键映射到值的对象,键不能重复
什么是迭代器
Iterator接口提供了很多对集合元素进行迭代的方法。迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列
中的对象。
1.使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素
iterator()方法是java.lang.Iterable接口,被Collection继承。
2.next()获得序列的下一个元素
3.hasnext()检查序列是否还有元素
Iterator和ListIterator的区别是什么?
Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List
Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向
ListIterator实现了Iterator接口,并包含了其他的功能,比如:增加元素、替换元素,获取前一个
和后一个元素的索引。
java中的HashMap的工作原理是什么
键值对存储元素。hashmap需要一个hash函数,它使用hashcode()和equals方法来向集合
从集合添加和检索元素。当调用put方法,hashmap会计算key的hash值,然后把键值对存储在集合中合适的
索引上。如果key已经存在,value会被更新成新值,hashmap
文章大部分摘自网络,若有侵权,联系删除