提示:面试题知识点不分先后排序
Map接口和collection接口是所有集合框架的父接口:
List | Set |
---|---|
有序 | 无序 |
插入多个null | 允许一个null |
元素索引 | 元素唯一性 |
支持for循环,通过下标遍历,可迭代 | 只能用迭代 |
查找元素效率高 | 删除和插入效率高 |
Map是一个键值,对集合、存储键,值之间的映射。
key | value |
---|---|
无序,唯一值 | 不要求有序,允许重复 |
Map没有继承Conllection接口, 从Map集合检查元素,只要给出键对象,就返回相应的值。
ltrator | Listterator |
---|---|
可以遍历Set和List集合 | 只能遍历List |
单向遍历 | 双向遍历 |
相同点:都不保证线程安全
ArrayList | LinkedList |
---|---|
动态数组 | 双向链表 |
效率高 | 效率低 |
不占内存 | 更占内存 |
ArrayList | Vector |
---|---|
非线程安全 | 线程安全 |
扩容+50% | +1% |
性能好 | 性能不行 |
并发三要素:原子性、可见性、有序性。
出现线程安全问题的原因:
当前任务在执行完cpu时间片切换到另一个任务之前会保存自己的状态、以方便下次再切换回这个任务时候,可以再加载这个任务的状态 “任务从保存到再加载的的过程就是一次上下文切换”
线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。
形成死锁的四个条件:互斥条件、请求与保持条件、不剥夺条件、循环等待条件
如何避免死锁:破坏产生死锁的四个条件之一就可以了
定义Thread类的子类,重写run方法。
创建自定义的线程子类对象。
调用子类实例start方法启动线程
public class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run()方法正在执行...");
}
}
public class TheadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
System.out.println(Thread.currentThread().getName() + " main()方法执行结束");
}
}
//执行结果
main main()方法执行结束
Thread-0 run()方法正在执行...
定义Runnable接口实现类,并重写run方法
创建MyRunnable实例myRunnable,以myRunnable作为target创建Thead对象,该Thread对象才是真正的线程对象
调用线程对象的start()方法
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run()方法执行中...");
}
}
public class RunnableTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
System.out.println(Thread.currentThread().getName() + " main()方法执行完成");
}
}
//执行结果
main main()方法执行结束
Thread-0 run()方法正在执行...
创建实现Callable接口的类myCallable
以myCallable为参数创建FutureTask对象
将FutureTask作为参数创建Thread对象
调用线程对象的start()方法
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() {
System.out.println(Thread.currentThread().getName() + " call()方法执行中...");
//call()方法的返回值必须与接口泛型类型一致
return 1;
}
}
public class CallableTest {
public static void main(String[] args) {
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start();
try {
Thread.sleep(1000);
System.out.println("返回结果 " + futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " main()方法执行完成");
}
}
//执行结果
Thread-0 call()方法执行中...
返回结果 1
main main()方法执行完成
主要有newFixedThreadPool,newCachedThreadPool,newSingleThreadExecutor,newScheduledThreadPool,后续详细介绍这四种线程池
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run()方法执行中...");
}
}
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run()方法执行中...");
}
}
//执行结果
线程任务开始执行
pool-1-thread-1 is running...
pool-1-thread-1 is running...
pool-1-thread-1 is running...
pool-1-thread-1 is running...
pool-1-thread-1 is running...
run()方法会当成一个main线程的普通方法执行。
start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。
五种基本状态:新建、可运行、运行、阻塞、死亡
两者相同点:可以暂停线程的执行
sieep() | wait() |
---|---|
是Thread静态方法 | 是Object类的方法 |
释放CPU不释放锁 | 释放CPU和锁 |
用于暂停执行 | 用于线程间交互/通信 |
自动苏醒。或者使用wait()超时后线程自动苏醒 | 不会自动苏醒,要调用对象上notify()或notifyAll() |
两者相同点:可以暂停线程的执行
sieep() | yield() |
---|---|
低优先级运行机会 | 更高优先级运行机会 |
转入阻塞状态 | 转入就绪状态 |
抛出异常 | 没有声明任何异常 |
Ioc容器和AOP模块
实现原理:工厂模式+反射机制
interface Fruit {
public abstract void eat();
}
class Apple implements Fruit {
public void eat(){
System.out.println("Apple");
}
}
class Orange implements Fruit {
public void eat(){
System.out.println("Orange");
}
}
class Factory {
public static Fruit getInstance(String ClassName) {
Fruit f=null;
try {
f=(Fruit)Class.forName(ClassName).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return f;
}
}
class Client {
public static void main(String[] a) {
Fruit f=Factory.getInstance("io.github.dunwu.spring.Apple");
if(f!=null){
f.eat();
}
}
}
对IOC来说,重要的是容器。容器管理Bean生命周期。控制Bean依赖注入
AOP原理:不去动原来的代码,而是基于原来代码产生代理对象,通过代理的方法去包装,就完成对以前方法的增强
AOP底层原理就是动态代理的实现
@Component:这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。
@Controller:这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 IoC 容器中。
@Service:此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component,因为它以更好的方式指定了意图。
@Repository:这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。
@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在
使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。
类级别:映射请求的 URL
方法级别:映射 URL 以及 HTTP 请求方法
编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。
声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。
(1)用户发送请求至前端控制器DispatcherServlet;
(2) DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
(3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
(4)DispatcherServlet 调用 HandlerAdapter处理器适配器;
(5)HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器);
(6)Handler执行完成返回ModelAndView;
(7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
(8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
(9)ViewResolver解析后返回具体View;
(10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
(11)DispatcherServlet响应用户。
@RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。
@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。
@ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。
@Controller 用于标记在一个类上,使用它标记的类就是一个Spring MVC Controller 对象
请求路径上有个id的变量值,可以通过@PathVariable来获取
@RequestParam用来获得静态的URL请求入参 spring注解时action里用到。
在web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf-8;
启动类注解@SpringBootApplication 。也是SpringBoot的核心注解,包含了以下三个注解:
@SpringBootConfiguration : 组合 @Configguration注解 实现配置文件的功能
@ComponentScan: Spring组件扫描
spring boot 核心的两个配置文件:
#{} | ${} |
---|---|
是占位符,预编译处理 | 是拼接符,没有预编译处理 |
是以字符串传入,将sql中的#{}替换?号 | 是原值传入,替换成变量的值 |
有效防止sql注入 | 不能防止sql注入 |
自动加上单引号 | 不会加上单引号 |
替换是在DBMS中 | 替换DBMS外 |
优点:
缺点:
某一时刻发生大规模缓存失效,所有请求发送到数据库,数据库承受大量请求崩掉
解决方案
查询缓存和数据库不存在的数据,所有请求发送到数据库上,数据库承受大量请求崩掉
解决方案
大量请求查询一个key,key失效,导致大量请求打到数据库
解决方案
系统上线后,将相关的缓存数据直接加载到缓存系统。
解决方案
Redis中,单条命令是原子性执行的。事物不保证原子性、且没有回滚
Redis的事务总是具有ACID中的一致性和隔离性
区别: | Mysql | Oracle |
---|---|---|
事物: | 在存储引擎的行级锁支持事物 | 完全支持事物 |
逻辑备份: | 需要锁定数据,才能一致 | 不锁定数据,备份数据一致 |
提交方式: | 默认自动提交 | 需要手动提交 |
数据持久性: | 更新数据会丢失 | 在线日志恢复客户提交数据 |
性能诊断: | 慢查询日志 | 各种成熟诊断工具 |
一致性: | 已读提交隔离 | 可序列化隔离 |
并发性: | 使用表级锁,事物引擎的表行级锁依赖表索引 | 行级锁,对并发支持较高 |