Java面试常用,初级到高级(持续更新中....)
文章目录
- 一、java基础
- 1、基本类型
- 2、String相关
- 3、数组和list相互转换
- 4、java中的锁
- 5、常用的算法
- 二、项目层面
- 1、并发与并行的区别
- 三、常见错误解决
- 1、Java ConcurrentModificationException异常原因和解决方法
- 2、 main中不能使用this
- 四、底层的相关技术
- 1、spring如何处理循环依赖
- 2、jvm如何判断对象该回收
- 2.1)jvm判断对象是否可以被回收的方式
- 2.1.1 引用计数法
- 2.1.2 可达性算法
- 2.1.3 哪些对象可以作为GC Root呢?
- 3、jdk1.8的接口优化,stream包集合优化
- 4、线程池初始化方式有哪些?种类有哪些?参数有哪些?
- 4.1)线程池初始化两种方式
- 4.2)线程池五类
- 4.2)线程池七个参数详解
面试保持一个探讨交流的心态,因为没有人什么都懂!
也就是说,互相平等,保持开发中,编写代码的状态
欢迎加入扣扣组织,783092701
一、java基础
1、基本类型
java语言中有8种基本数据类型,分类四大类型:
逻辑类型:boolean
整数类型:byte、short、int、long
浮点类型 :float、double
字符类型:char
2、String相关
1)String是基本类型吗?
String不是基本类型,引用类型
2)String常用的方法?
split substring valueOf
3、数组和list相互转换
List list = new ArrayList();
list.add(1);
//toArray
Object[] array = list.toArray();
System.out.println("list转数组"+Arrays.toString(array));
//asList
List list2 = Arrays.asList(array);
System.out.println("数组转list2"+list2);
4、java中的锁
悲观锁与乐观锁
1)悲观锁:认为别人会修改
synchronized原始采用的是CPU悲观锁机制
2)乐观锁:认为别人不会修改
Lock 用的是乐观锁方式
实现方式:
在数据表中添加一个数据版本号version字段,表示数据被修改的次数,当数据被修改的时候,version值会加一;当线程A要重新更新数据值的时候,在读取数据的时候也会读取version值,在提交更新的时候,若刚才读取到的version值与当前数据库中的version值相等才更新,否则重新更新操作,直到更新成功
3)应用场景
乐观锁使用于写比较少(即读多)的场景,即冲突很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但是如果是写比较多的情况,一般会经常发生冲突,这就会导致上层应用会不断的进行充实,这样反倒是降低了性能,所以一般多写的场景下用悲观锁比较合适
5、常用的算法
二、项目层面
1、并发与并行的区别
并发是指一个处理器同时处理多个任务。
并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。
并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。
三、常见错误解决
1、Java ConcurrentModificationException异常原因和解决方法
1)在使用iterator迭代的时候使用synchronized或者Lock进行同步;
2)使用并发容器CopyOnWriteArrayList代替ArrayList和Vector。
2、 main中不能使用this
因为main方法是静态方法属于类,无法直接使用对象级别的变量和方法,也就是实例属性和实例方法。而关键字“ this”用作对实例的引用,因此不能在静态方法中使用“ this”引用。
原因(个人理解,勿喷):静态方法在类加载时就会被加载到方法区中,而此时,main函数还没有执行,没有new出对象th,所以无法使用对象级别的属性和方法。
四、底层的相关技术
1、spring如何处理循环依赖
【这个有点深度,暂时需要研究一下】转载 spring循环依赖探究
2、jvm如何判断对象该回收
2.1)jvm判断对象是否可以被回收的方式
要分辨一个对象是否可以被回收,有两种方式:引用计数法和可达性算法。
2.1.1 引用计数法
就是在对象被引用时,计数加1,引用断开时,计数减1。那么一个对象的引用计数为0时,说明这个对象可以被清除。
这个算法的问题在于,如果A对象引用B的同时,B对象也引用A,即循环引用,那么虽然双方的引用计数都不为0,但如果仅仅被对方引用实际上没有存在的价值,应该被GC掉。
2.1.2 可达性算法
通过引用计数法的缺陷可以看出,从被引用一方去判定其是否应该被清理过于片面,所以我们可以通过相反的方向去定位对象的存活价值:一个存活对象引用的所有对象都是不应该被清除的(Java中软引用或弱引用在GC时有不同判定表现,不在此深究)。这些查找起点被称为GC Root
2.1.3 哪些对象可以作为GC Root呢?
JAVA虚拟机栈中的本地变量引用对象
方法区中静态变量引用的对象
方法区中常量引用的对象
本地方法栈中JNI引用的对象
3、jdk1.8的接口优化,stream包集合优化
问题1:接口方法必须实现,jdk1.8如何做到可以不实现?
问题2:假设一个父接口被超级多子类继承,并且子类还会被子类的子类继承……,当父接口需要添加一个方法时,因为该方法一定会在超多个子类中被覆写的,所以工作量难以想象。
回答:可以使用default来定义普通方法
interface INew{
public default void fun() {//普通方法,有方法体
System.out.println("world!");
}
public void print();//抽象方法
}
class Person implements INew{
@Override
public void print() {
System.out.println("hello!");
}
}
public class TestDemo {
public static void main(String[] args) {
INew p=new Person();
p.print();
p.fun();//实例化对象是可以调用fun()方法的
}
}
另外一个特性:可以使用static来定义静态方法,通过接口名就可以调用
interface INew{
public default void fun() {//普通方法,有方法体
System.out.println("world!");
}
public static INew getInstance() {//定义静态方法
return new Person();
}
public void print();//抽象方法
}
class Person implements INew{
@Override
public void print() {
System.out.println("hello!");
}
}
public class TestDemo {
public static void main(String[] args) {
INew p=INew.getInstance();//静态方法可以由类名称直接调用了
p.print();
p.fun();//实例化对象是可以调用fun()方法的
}
}
4、线程池初始化方式有哪些?种类有哪些?参数有哪些?
误区:cpu过高,是单个线程请求过高问题。而不是线程过多造成的。
4.1)线程池初始化两种方式
4.2)线程池五类
- 固定数量的线程池——newFixedThreadPool
作用:这是一个线程数固定的,线程可以重用的线程池,用共享的无界队列方式运行这些线程。 - 缓存线程池——newCachedThreadPool
作用:它是根据需要,创建线程,没有核心线程,当60s内这个线程没接收到任务时,它就会被从池中收走。60s内这个线程又接收到新任务时,它就又被重用了。 - 只有一个线程的线程池——newSingleThreadPool
作用:这个线程池中只有1个线程,用无界队列的方式运行。如果这个线程突然挂掉了,线程池会新建一个新的线程顶替它,继续完成任务。 - 延时线程池——newScheduleThreadPool
作用:创建一个线程池,可以让它里面的线程延迟执行,或在规定的时间后执行。 - 并行线程池——newWorkStealingPool
作用:jdk1.8提供的线程池,底层使用ForkJoinPool实现,池中有多个任务队列。适用于任务多,可以并发执行的场景。
4.2)线程池七个参数详解
- corePoolSize 线程池核心线程大小
- maximumPoolSize 线程池最大线程数量
- keepAliveTime 空闲线程存活时间
- unit 空闲线程存活时间单位
- workQueue 工作队列
- threadFactory 线程工厂
- handler 拒绝策略
如何真正理解记忆:找一台服务器,跑一下代码就行!