前言:最近在面高级,总结一些需要知道的知识,一边在面一边在写,欢迎补充
一、java 基础
1.基本数据类型
数据类型 | 字节长度 | 类型 |
---|---|---|
byte | 1 | 整型 |
shot | 2 | 整型 |
int | 4 | 整型 |
long | 8 | 整型 |
float | 4 | 浮点型 |
double | 8 | 浮点型 |
boolean | - | 布尔型 |
char | 2 | 字符型 |
String 是java中特殊的类,用来接收字符串,String的常量池和Integer的IntegerCache异曲同工,关于String 有兴趣的朋友可以看看这篇https://www.cnblogs.com/zhangyinhua/p/7689974.html
String a = "a";
String b = new String("a");
String c = "a";
//入常量池
String d = new String("a").intern();
System.out.println(a == b);//false
System.out.println(a == c);//true
System.out.println(a == d);//true
Integer integer = 17;
Integer integer1 = 17;
System.out.println(integer == integer1);//true
Integer integer2 = 128;
Integer integer3 = 128;
System.out.println(integer2 == integer3);//false
2.特性
①基本特性
封装:属性、方法的封装
多态:就是一个类中同名的方法不同参数和返回值
继承:选择题比较多,主要涉及父类中的方法重写,和父类静态属性和方法不能重写
②高级特性
反射:运行状态中获取类的名和方法,对象也是;可以延伸出静态、动态代理的比较,也会有手写动态代理的简答题,关于反射和代理看下这两篇
https://www.cnblogs.com/yueshutong/p/9495001.html
https://www.cnblogs.com/maohuidong/p/7992894.html
//静态代理 示例,可以参考下SpringContextHolder
public interface Job {
void testJob();
}
public class JobImpl implements Job{
@Override
public void testJob() {
System.out.println("静态job");
}
}
public class Proxy implements Job{
private Job job;
@Override
public void testJob() {
init();
job.testJob();
}
private void init(){
if(job == null){
job = new JobImpl();
}
}
public static void main(String[] args) {
new Proxy().testJob();
}
}
//动态代理示例
public interface Job {
void doSomething();
}
public class JobImpl implements Job{
@Override
public void doSomething() {
System.out.println("动态job");
}
}
public class MyProxy implements InvocationHandler {
private Job job;
public MyProxy(Job j){
this.job = j;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = method.invoke(job,args);
return object;
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(job.getClass().getClassLoader(),job.getClass().getInterfaces(),this);
}
}
public class Test {
public static void main(String[] args) {
Job job = new JobImpl();
MyProxy proxy = new MyProxy(job);
Job job1 = (Job) proxy.getProxyInstance();
job1.doSomething();
}
}
泛型:只会作用于编译时期,指定数据类型,可能会问下?通配符
类型转换:回考察一些自动转换、强制转换的计算和编译问题
序列化:可能以前做xml解析的时候还能被问到,现在都是json了没怎么被问
注解:类、方法、变量、参数和包等都可以被标注,获取到的时候配合拦截
器做逻辑,和python、js的装饰器不太一样
//注解示例
@Target(value = {ElementType.FIELD,ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Hello {
String value();
}
@Hello("hello")
public static void main(String[] args) throws NoSuchMethodException {
Class cls = Test.class;
Method method = cls.getMethod("main", String[].class);
Hello hello = method.getAnnotation(Hello.class);
}
3.集合(可问点很多)

看下这篇https://www.cnblogs.com/jing99/p/7057245.html
①Collection型,存储单个key
- List接口 特点存储有序
- 实现类ArrayList和LinkedList经常拿来比,前者是数组查询快,后者是链表存取快
- Vector 和ArrayList很像,方法里全是synchronized,线程安全但是同步性差
- Set 存储无序不可重复,问的少
②map型,以key-value形式进行存储;
- Map :
接口,同步性最高 - HashMap :
key,value 可空,key不可重复,相同的key后者会把前者覆盖掉;
用链地址法(数组+链表)解决hash冲突,线程不安全;
默认容量16,启动因子0.75;
jdk1.8后增加了红黑树底层实现,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力(没用过) - HashTable :
key,value都不可空,有大量synchronized,线程安全同步性差
4.并发编程
①java内存模型
- JMM:
主内存中存储着共享变量,然后各自的本地内存中存储着共享变量的副本,并发编程就是解决线程间通信,个线程间私有内存和主内存中共享数据的读写问题 - volatile 和 synchronized:
volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
看下这篇https://www.cnblogs.com/kaleidoscope/p/9506018.html - Lock 和 synchronized:
1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
5)Lock可以提高多个线程进行读操作的效率。
看下这篇https://blog.csdn.net/qq_38200548/article/details/82943222
//synchronized
public class TestSync {
public static void main(String[] args) {
final TestSync test = new TestSync();
// 线程1
new Thread(() -> {
test.get(Thread.currentThread());
}).start();
// 线程2
new Thread(() -> {
test.get(Thread.currentThread());
}).start();
}
public synchronized void get(Thread thread) {
long start = System.currentTimeMillis();
int i = 0;
while(System.currentTimeMillis() - start <= 1) {
i++;
if(i % 4 == 0) {
System.out.println(thread.getName() + "正在进行写操作");
} else {
System.out.println(thread.getName() + "正在进行读操作");
}
}
System.out.println(thread.getName() + "读写操作完毕");
}
}
//Lock
public class TestLock {
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public static void main(String[] args) {
final TestLock lock = new TestLock();
new Thread(() -> {
lock.get(Thread.currentThread());
lock.write(Thread.currentThread());
}).start();
new Thread(() -> {
lock.get(Thread.currentThread());
lock.write(Thread.currentThread());
}).start();
}
/**
* 读操作,用读锁锁定
* @param thread
*/
public void get(Thread thread) {
boolean bool = rwl.readLock().tryLock();
if(bool){
try {
long start = System.currentTimeMillis();
while(System.currentTimeMillis() - start <= 1) {
System.out.println(thread.getName() + "正在进行读操作");
}
System.out.println(thread.getName() + "读操作完毕");
} catch (Exception e) {
} finally {
rwl.readLock().unlock();
}
}
}
/**
* 写操作,用写锁来锁定
* @param thread
*/
public void write(Thread thread) {
boolean bool = rwl.writeLock().tryLock();
if(bool){
try {
long start = System.currentTimeMillis();
while(System.currentTimeMillis() - start <= 1) {
System.out.println(thread.getName() + "正在进行写操作");
}
} catch (Exception e) {
} finally {
rwl.writeLock().unlock();
}
}
}
}
- final 在内存模型中被做了增强,使得被final修饰的参数被多线程引用时cpu不会进行重排序
public class FinalExample {
int i; //普通变量
final int j ; //final变量
static FinalExample obj;
public FinalExample(int j) {
this.j = j;
}
public void FinalExample () { //构造函数
i = 1; //写final域
}
public static void writer(int j,Thread thread) { //写线程A执行
obj = new FinalExample (j);
System.out.println(thread.getName()+" 写入: "+j);
}
public static void reader(Thread thread) { //读线程B执行
FinalExample object = obj; //读对象引用
int a = object.i; //读普通域
int b = object.j; //读final域
System.out.println(thread.getName()+" 读出 :"+b);
}
}
public class Test {
public static void main(String[] args) {
int num = 2;
new Thread(() -> {
FinalExample.writer(num, Thread.currentThread());
}).start();
new Thread(() -> {
FinalExample.reader(Thread.currentThread());
}).start();
}
}
③线程
看看这篇https://www.cnblogs.com/wxd0108/p/5479442.html
- 线程的状态:
NEW:线程刚被创建
RUNNABLE:线程处于可执行状态
BLOCKED、WAITING:线程被阻塞,具体区别后面说
TERMINATED:线程执行结束,被终止 - 如何创建线程:
自定义 Runnable 实现
继承 Thread 类 - 常用方法:
sleep 阻塞当前线程指定时长
start 真正的多线程启动,有时会问run和start的区别
interrupt 终端当前线程
join 阻塞主线程直到调用线程完成后
④生产者-消费者的三种实现方式,看下这篇https://blog.csdn.net/a491857321/article/details/79262996
⑤线程池:
- 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性:线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性。使用线程池可以进行统一分配、调优和监控。
//简单线程池示例
public interface ThreadPool <Job extends Runnable>{
//执行一个Job,job需要实现Runnable
void execute(Job job);
//关闭线程池
void shutdown();
//增加工作者线程
void addWorkers(int nums);
//减少工作者线程
void removeWorkers(int nums);
//得到正在工作的线程数
int getJobSize();
}
public class DefaultThreadPool <Job extends Runnable> implements ThreadPool<Job> {
//最大工作者线程数
private static final int MAX_WORKERS_NUMS = 10;
//默认工作者线程数
private static final int DEFAULT_WORKERS_NUMS = 5;
//最小工作者线程数
private static final int MIN_WORKERS_NUMS = 1;
//工作列表
private final LinkedList<Job> jobs = new LinkedList<Job>();
//工作者线程
private final List<Worker> workers = Collections.synchronizedList(new ArrayList<>());
private int workerNums = DEFAULT_WORKERS_NUMS;
private AtomicLong threadNum = new AtomicLong();
public DefaultThreadPool(int workerNums) {
int workNums = workerNums > MAX_WORKERS_NUMS ? MAX_WORKERS_NUMS :
workerNums < MIN_WORKERS_NUMS ? MIN_WORKERS_NUMS : workerNums;
initializaWorkers(workNums);
}
public DefaultThreadPool() {
initializaWorkers(DEFAULT_WORKERS_NUMS);
}
private void initializaWorkers(int workerNums) {
for(int i = 0; i < workerNums; i++) {
Worker worker = new Worker();
Thread thread = new Thread(worker, "WorkNumble" + threadNum.incrementAndGet());
thread.start();
}
}
@Override
public void execute(Job job) {
if(job == null) {
synchronized (jobs) {
jobs.addLast(job);
jobs.notify();
}
}
}
@Override
public void shutdown() {
for(Worker worker : workers) {
worker.showdown();
}
}
@Override
public void addWorkers(int nums) {
synchronized (jobs) {
if(nums + this.workerNums > MAX_WORKERS_NUMS)
nums = MAX_WORKERS_NUMS - nums;
initializaWorkers(nums);
this.workerNums += nums;
}
}
@Override
public void removeWorkers(int nums) {
synchronized (jobs) {
if(nums >= this.workerNums) {
}
int count = 0;
while(count < nums) {
Worker worker = workers.get(count);
if(workers.remove(worker)) {
worker.showdown();
count++;
}
}
this.workerNums -= count;
}
}
@Override
public int getJobSize() {
return jobs.size();
}
class Worker implements Runnable{
volatile private boolean running = true;
@Override
public void run() {
while(running) {
Job job = null;
synchronized (jobs) {
while(jobs.isEmpty()) {
try {
jobs.wait();
}catch (InterruptedException e) {
Thread.currentThread().interrupt();
return ;
}
}
job = jobs.removeFirst();
}
if(job != null) {
try {
job.run();
}catch (Exception e) {
}
}
}
}
public void showdown() {
running = false;
}
}
}
⑥连接池:可能回会一下等待超时模型
//连接池简单示例
public class ConnectionPool {
private LinkedList<Connection> pool = new LinkedList<Connection>();
public ConnectionPool(int initialSize) {
if (initialSize > 0) {
for (int i = 0; i < initialSize; i++) {
pool.addLast(ConnectionDriver.createConnection());
}
}
}
public void releaseConnection(Connection connection) {
if (connection != null) {
synchronized (pool) {
// 连接释放后需要进行通知,这样其他消费者能够感知到连接池中已经归还了一个连接
pool.addLast(connection);
pool.notifyAll();
}
}
}// 在mills内无法获取到连接,将会返回null
public Connection fetchConnection(long mills) throws InterruptedException {
synchronized (pool) {
// 完全超时
if (mills <= 0) {
while (pool.isEmpty()) {
pool.wait();
}
return pool.removeFirst();
} else {
long future = System.currentTimeMillis() + mills;
long remaining = mills;
while (pool.isEmpty() && remaining > 0) {
pool.wait(remaining);
remaining = future - System.currentTimeMillis();
}
Connection result = null;
if (!pool.isEmpty()) {
result = pool.removeFirst();
}
return result;
}
}
}
}
public class ConnectionDriver {
static class ConnectionHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("commit")) {
TimeUnit.MILLISECONDS.sleep(100);
}
return null;
}
}// 创建一个Connection的代理,在commit时休眠100毫秒
public static final Connection createConnection() {
return (Connection) Proxy.newProxyInstance(ConnectionDriver.class.getClassLoader(),
new Class<?>[] { Connection.class }, new ConnectionHandler());
}
}
public class ConnectionPoolTest {
static ConnectionPool pool = new ConnectionPool(10);
// 保证所有ConnectionRunner能够同时开始
static CountDownLatch start = new CountDownLatch(1);
// main线程将会等待所有ConnectionRunner结束后才能继续执行
static CountDownLatch end;
public static void main(String[] args) throws Exception {
// 线程数量,可以修改线程数量进行观察
int threadCount = 10;
end = new CountDownLatch(threadCount);
int count = 20;
AtomicInteger got = new AtomicInteger();
AtomicInteger notGot = new AtomicInteger();
for (int i = 0; i < threadCount; i++) {
Thread thread = new Thread(new ConnetionRunner(count, got, notGot), "ConnectionRunnerThread");
thread.start();
}
start.countDown();
end.await();
System.out.println("total invoke: " + (threadCount * count));
System.out.println("got connection: " + got);
System.out.println("not got connection " + notGot);
}
static class ConnetionRunner implements Runnable {
int count;
AtomicInteger got;
AtomicInteger notGot;
public ConnetionRunner(int count, AtomicInteger got, AtomicInteger notGot) {
this.count = count;
this.got = got;
this.notGot = notGot;
}
public void run() {
try {
start.await();
} catch (Exception ex) {
}
while (count > 0) {
try {
// 从线程池中获取连接,如果1000ms内无法获取到,将会返回null
// 分别统计连接获取的数量got和未获取到的数量notGot
Connection connection = pool.fetchConnection(1000);
if (connection != null) {
try {
connection.createStatement();
connection.commit();
} finally {
pool.releaseConnection(connection);
got.incrementAndGet();
}
} else {
notGot.incrementAndGet();
}
} catch (Exception ex) {
} finally {
count--;
}
}
end.countDown();
}
}
}
⑦Fork/Join:将一个线程拆分到几个几个线程中并行计算结果
//斐波那契 forkjoin
public class RecursiveTaskDemo {
private static class Fibonacci extends RecursiveTask<Integer> {
final int n;
public Fibonacci(int n) {
this.n = n;
}
@Override
protected Integer compute() {
if (n <= 1) {
return n;
}else {
Fibonacci f1 = new Fibonacci(n - 1);
f1.fork();
Fibonacci f2 = new Fibonacci(n - 1);
return f2.compute() + f1.join();
}
}
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ForkJoinPool pool = new ForkJoinPool();
Future<Integer> future = pool.submit(new Fibonacci(10));
System.out.println(future.get());
pool.shutdown();
}
}
⑧并发容器和Executor(书没看完,暂时不写)
5.IO
①IO
io操作数据流和数组,两者可以相互转换,字节流继承inputstream,outputstream;字符流继承Writer,Reader,目前还没被问到,下面是关系图

②NIO 是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,包括buffer、channel、selector,写过通讯其实很好理解。
③AIO 异步非阻塞的I/O模型(没用过暂时不写)
6.JVM
①JVM组成
- 程序计数器:当前线程所执行的字节码的行号指示器,用于记录正在执行的虚拟机字节指令地址;
- JVM栈:++存放编译期间可知的8种基本数据类型,及对象引用和指令地址++、方法出口等;
- 本地方法栈:本地方法栈和虚拟机栈类似,只不过本地方法栈为Native方法服务;
- 堆:堆被所有线程共享区域,java内存最大的一块,在虚拟机启动时创建,唯一目的存放对象实例。
Java堆是垃圾收集器管理的主要区域,有时候也被称为“GC堆”。因为现在收集器基本都采用分代收集算法,所有Java堆还可以细分为:新生代和老年代,再细致一点有Eden空间、From Survivor空间、To Survivor空间等,默认情况下各自占比 8:1:1。 - 方法区: 各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,习惯是也叫它永久代(permanment generation)
②四种引用
- 强引用(StrongReference):
A a = new A()的形式就是强引用,对象是强引用就不会被回收; - 软引用(SoftReference):
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存; - 弱引用(WeakReference):
在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。 - 虚引用(PhantomReference):
如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
看下这篇https://blog.csdn.net/sadeer126/article/details/89188572
③GC(还没看明白待完善)
④优化(没做过暂时不写)
二、Spring
1.spring bean
就是先编写一个XML文件,然后使用Spring框架了来读取这个配置文件就可以使用这个bean,有时候会问springbean 和 java bean 区别
2.AOP/IOC
- 面向切面编程在spring中主要表现为两个方面 :面向切面编程提供声明式事务管理 和 用户自定义的切面
依赖注入。 - 由 IOC 容器动态的将某个对象所需要的外部资源(包括对象、资源、常量数据)注入到组件( spring 中的组件如:controller, service..等)之中。
3.spring 事务
- 事务的传播行为:
① PROPAGATION_REQUIRED 支持当前事务,如果不存在,就创建一个
② PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务
③ PROPAGATION_MANDATORY 支持当前事务,如果不存在,就抛出异常
④ PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
⑤ PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
⑥ PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常
⑦ PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行 - spring隔离级别
①ISOLATION_DEFAULT:使用数据库默认的隔离级别
②ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取已改变而没有提交的数据,可能会导致脏读、幻读或不可重复读
③ISOLATION_READ_COMMITTED:允许读取事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
④ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据事务本身改变,可以阻止脏读和不可重复读,但幻读仍有可能发生
⑤ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别,确保不发生脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的
4.springmvc 执行流程:
1.用户发送出请求到前端控制器DispatcherServlet。
2.DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。
3.HandlerMapping找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
4.DispatcherServlet调用HandlerAdapter(处理器适配器)。
5.HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。
6.Controller执行完成返回ModelAndView对象。
7.HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
8.DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
9.ViewReslover解析后返回具体View(视图)。
10.DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11.DispatcherServlet响应用户。
5.spring 中的设计模式
1.简单工厂又叫做静态工厂方法(StaticFactory Method)
2.工厂方法(Factory Method)
3.单例模式(Singleton)
4.适配器(Adapter)
5.包装器(Decorator)
6.代理(Proxy)
7.观察者(Observer)
8.策略(Strategy)
9.模板方法(Template Method)
三、计算机组成原理/网络
1.OSI七层协议模型、TCP/IP四层模型和五层协议体系结构之间的关系
看下这篇https://www.cnblogs.com/wxd0108/p/7597216.html

2.TCP/IP和UDP
- TCP面向连接;UDP是无连接的,即发送数据之前不需要建立连接
- TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。 - UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
- 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
- TCP对系统资源要求较多,UDP对系统资源要求较少。
https://blog.csdn.net/zhang6223284/article/details/81414149
3.HTTP
超文本传输协议
三、DB/SQL
1.关系型DB(mysql)
①索引
②优化
2.NoSQL
①redis
②memcache
③mongodb
四、服务器/容器
1.Linux
2.Docker(入门级别,欢迎指导)
五、分布式/MQ
1.分布式理论-CAP/BASE
2.分布式锁原理及实现方式
3.分布式事务
4.JMS/MQTT
六、数据结构/算法
1.数组/链表/队列
2.树
3.堆栈
3.算法
①选择/冒泡排序
②递归
③快速排序
网友评论