Java笔记思维导图
2023-07-04 16:50:57 0 举报
AI智能生成
java相关笔记思维导图
作者其他创作
大纲/内容
Java笔记思维导图
占用字节: 1 2 4 8整型: byte short int long字符型: char浮点型: float double布尔型: boolean1字节(byte) = 8位(bit)
基本数据类型
一个类只负责一项职责
单一职责原则
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上
接口隔离原则
高层模块不应该依赖低层模块,二者都应该依赖其抽象抽象不应该依赖细节,细节应该依赖抽象依赖倒转的中心思想是面向接口编程依赖倒转原则的设计理念是:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多,在Java中,抽象指的是接口或者抽象类,细节就是具体的实现类在使用接口或抽象类的目的是制定好规范,而不涉及具体的操作,把展现细节的任务交给他们的实现类取完成总结:一句话就是,定义好接口规范才是重中之重!有了规范接口,剩下的实现就交给具体的实现类去做!
接口传递
构造器传递
setter传递
依赖传递的三种方式
低模块尽量都要有抽象类或接口,或两者都有,程序的稳定性更好
变量的声明类型尽量是接口或抽象类,这样我们的变量引用和实际对象之间就存在一个缓冲层,利于程序的扩展和优化
继承时遵循里氏替换原则
注意事项和细节
依赖倒转原则
里氏替换原则
对扩展:开放对修改:关闭
开闭原则
又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。个人理解为:通过传参的方式进行聚合(形参里是“直接朋友关系”)
迪米特法原则
尽量使用合成或聚合的方式,而不是使用继承
合成复用原则
七大原则
JMM:Java内存模型,不存在的东西,概念!约定!关于JMM的一些同步的约定:线程中分为 工作内存、主内存1、线程解锁前,必须把共享变量立刻刷回主存;2、线程加锁前,必须读取主存中的最新值到工作内存中;3、加锁和解锁是同一把锁
1、保证可见性
2、不保证原子性
3、禁止指令重排
volatile
外框
JVM
要基于接口,抽象的去设计代码,以及使用的时候,要时刻记住可用参数进行传递!
抽象思想,自己思考
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks
JUC:指的是java.util三个并发编程工具包
1.继承Thread类
2.实现Runnable接口
3.实现Callable接口
4.线程池
是否有返回值:Runnable无返回值,Callable有返回值
是否抛出异常:call方法计算一个结果,如果不能这样做,就会抛出异常
实现方法名称不同,Runnable接口是run方法,Callable接口是call方法
Runnable接口和Callable接口区别:
实现多线程的四种方式:
进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程——资源分配的最小单元。线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程——程序执行的最小单元。进程:是一个程序,一个进程包含多个线程,且至少包含一个线程。Java默认有两个线程:main 和 GC。
Java是不能开启线程的,底层是调用start0()是一个native方法,由底层的C++方法编写。java无法直接操作硬件。
1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
3. 阻塞(BLOCKED):表示线程阻塞于锁。
4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
6. 终止(TERMINATED):表示该线程已经执行完毕。
java多线程中的阻塞状态跟等待状态到底怎么才能理解他们的不同?
https://pic4.zhimg.com/80/v2-1319f27379e4745d02b40ea12b9307cb_720w.webp
图解线程状态
线程的六种状态
1.来自不同的类wait => Object,任何对象实例都能调用sleep => Thread,Thread的静态方法
2.关于锁的释放wait会释放锁;sleep不会释放锁,它也不需要占用锁
3.使用范围、捕获异常不同wait:必须在同步代码块中使用,不需要捕获异常sleep:可以在任何地方使用,必须要捕获异常
wait/sleep区别
并发: 同一时刻多个线程访问同一个资源(多线程共享资源)例如:春运抢票、电商秒杀
并行: 多项工作一起执行,之后再汇总(可理解为异步)例如:泡方便面,电水壶烧水的同时,拆开泡面调料倒入桶中
并发、并行
用户线程:自定义线程(new Thread())守护线程:后台中一种特殊的线程,比如垃圾回收
用户线程和守护线程
进程和线程
公平锁:非常公平,不能插队,必须先来后到!(效率可能较低)
非公平锁:非常不公平,可以插队(默认都是非公平,效率较高)
可重入锁(递归锁):拿到外边的锁后,会自动拿到里面的锁(synchronized【隐式】和Lock【显式】都是可重入锁)隐式:不需要手动去操作加锁,释放锁显示:需要手动去操作加锁、释放锁
自旋锁:等待获取锁,如未获取到锁进行等待阻塞状态,直至尝试获取到锁!
如何定位死锁,解决问题?1、使用jps定位进程号,jdk的bin目录下: 有一个jps命令:jps -l2、使用jstack 进程进程号 找到死锁信息(jstack是jvm中自带的堆栈跟踪工具)命令:jstack 进程号
产生死锁的四个必要条件:互斥条件(Mutual Exclusion):每个资源最多只能由一个线程持有。请求与保持条件(Hold and Wait):线程持有至少一个资源,并且在等待其他线程拥有的资源。不可抢占条件(No Preemption):资源只能在持有者释放之后由其他线程获取,不能被强制抢占。循环等待条件(Circular Wait):存在一个线程等待列表的循环链,每个线程等待链中的资源被下一个线程持有。
死锁:两个或者两个以上进程在执行过程中,因为争夺资源而造成一种互相等待的现象,如果没有外力干涉,他们无法再继续执行下去。从而导致死锁的发生
公平锁、非公平锁、自旋锁、可重入锁、死锁
默认非公平锁,可通过构造方法传入true参数,启用公平锁
ReentranLock(可重入锁)
/** * 独占锁(写锁) 一次只能被一个线程占有 * 共享锁(读锁) 多个线程可以同时占有 * * ReadWriteLock * 读-读 可以共存! * 读-写 不可共存! * 写-写 不可共存! */public class ReadWriteLockDemo { public static void main(String[] args) { MyCache myCache = new MyCache(); for (int i = 1; i <= 5; i++) { final int temp = i; //写入 new Thread(() -> { myCache.put(temp + \"\
ReentrantReadWriteLock.ReadLoack(读锁)ReentrantReadWriteLock.WriteLoack(写锁)
底层是通过javaAPI实现
Lock锁
默认非公平锁
对于普通同步方法,锁是当前实例对象。对于静态同步方法,锁是当前类的Class对象。对于同步方法块,锁是synchronized括号里配置的对象。
synchronized实现同步的基础:Java中的每一个对象都可以作为锁。具体表现为一下3种形式。
底层是,通过进入和退出 Monitor 对象实现锁机制,代码块通过一对 monitorenter/monitorexit 指令实现(这两个指令必须成对存在)。依赖于JVM虚拟机
synchronized
1.synchronized 是内置的Java关键字,Lock是Java的一个接口2.synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁3.synchronized 会自动释放锁,Lock必须手动释放锁!如果不释放锁,会造成死锁4.synchronized 线程一,在获得锁的情况下,线程二获取不到锁,阻塞了,线程二就只能傻傻的等着;Lock就不一定会等待下去5.synchronized 可重入锁,不可以中断,非公平;Lock,可重入锁,可以判断锁,非公平/公平(可以自己设置,默认非公平锁)6.synchronized 适合锁少量的同步代码;Lock适合锁大量同步代码!7.Lock可以提高多个线程进行读操作的效率。注:在性能上来说,如果竞争资源不激烈,两者性能是差不多的,而当竞争资源非常激烈时(即大量线程同时竞争),此时Lock的性能要远远优于synchronized如果不加lock和synchronized ,怎么样保证原子性?使用原子类:CAS原理
Lock跟synchronized区别:
意思就是每个线程执行countDown() 数量-1,都会进行await(),当计数器变为0,就会被唤醒,继续往下执行!
CountDownLatch(减法计数器)
原理与减法计数器一样,但是这个可以重复使用,比如从0~30为一周前,30次后,会重置为0,继续使用!
CyclicBarrier(加法计数器)
原理:semaphore.acquire(); //获得,如果已经满了,等待,等待被释放为止!semaphore.release(); // 释放,会将当前的信号量释放+1,然后唤醒等待的线程!作用:多个共享资源互斥的使用!并发限流,控制最大的线程数! 比如停车位:有6辆车,但是只有3个停车位!那么acquire()就是获取停车位,3个停车位还没满,就能直接停,如果3个满了就等待,直到其他停车位被释放(就是别的车离开了这个停车位)后才能获得停车位!
//线程数量:停车位! 限流!注:此时停车位就3个,而下面的for循环6次代表有6辆车 Semaphore semaphore = new Semaphore(3); for (int i = 1; i <= 6; i++) { new Thread(() -> { try { //acquire() 获得 semaphore.acquire(); System.out.println(Thread.currentThread().getName() + \"抢到车位\"); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName() + \"离开车位\
Semaphore(信号量)
常用辅助类:
Java无法操作内存,那么就通过这个类去访问底层资源、去操作内存
Unsafe类
CAS也是乐观锁的一种实现方式!
CAS原理
public void increment() { lock.lock(); try { /*if (num != 0) { wait(); }*/ //防止虚假唤醒:因为用if的话,那么当wait后线程被虚假的唤醒了,就立即执行了wait();下面的代码而不经过判断。那么会造成以下问题: //1、程序逻辑混乱:如果线程在未满足其等待条件时被错误唤醒,则可能会导致程序逻辑出现问题。 //2、性能下降:如果线程频繁虚假唤醒,则可能会导致性能下降,因为线程需要不断地从等待状态转换到运行状态。 //要避免虚假唤醒,就需要讲if改为while判断 while (num != 0) { //注意:不可使用wait()或者condition.wait();因为wait()方法是Object对象提供的,而不是对象condition去等待!!! condition.await(); } num++; System.out.println(Thread.currentThread().getName() + \"=>\" + num); //通知其他线程我+1 完毕了 //lock.notifyAll();在使用Lock锁时,不能使用wait()和notifyAll()方法,因为它们只能在synchronized块中使用。需要使用Condition接口的await()和signalAll()方法来代替这两个方法。 //注意:不可使用condition.notifyAll();因为notifyAll方法是Object对象提供的,而不是对象condition去唤醒!!! condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
虚假唤醒问题,将if改为while
锁
什么情况下我们会使用 阻塞队列:多线程并发处理,线程池!
四组API
阻塞队列
SynchronousQueue进去一个元素,必须等待取出来之后,才能再往里面放一个元素!put()、take()
同步队列
三大方法:(不是重点)Executors.newSingleThreadExecutor();//创建单个线程的线程池Executors.newFixedThreadPool(5);//创建固定线程的线程池Executors.newCachedThreadPool();//创建可伸缩线程池
4大拒绝策略: * new ThreadPoolExecutor.AbortPolicy() //默认拒绝策略 银行满了,还有人进来,不处理这个人,抛出异常 * new ThreadPoolExecutor.CallerRunsPolicy() //哪来的去哪里! * new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常 * new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试和最早的竞争,也不会抛出异常!
三大方式、七大参数、四种拒绝策略
线程池的好处1、降低资源的消耗;2、提高响应的速度;3、方便线程管理。
不允许使用Executors创建,而是使用ThreadPoolExecutor去创建,原因是,Executors去默认创建的参数是Integer.MAX_VALUE≈21亿,会导致内存溢出问题!
1、CPU 密集型 电脑处理器数是几,就是几,可以保证CPU的效率最高!
2、IO 密集型 大于 程序中十分耗IO的线程数 ---> 程序中 15个大型任务 io十分占用资源! =》 30
线程池最大线程数如何设置?IO密集型,CPU密集型(调优)
线程池(重点)
JUC
线程不安全,但效率高,想要线程安全用Vecotr
ArrayList(数组)
线程安全,但效率较低
Vector(数组)
线程不安全,解决方法:Collections.synchronizedList(new LinkedList<>());
LinkedList(双向链表)
List
底层亦是HashMap线程不安全,解决如下: //解决方案一:Set<String> set = Collections.synchronizedSet(new HashSet<>()); //解决方案二:Set<String> set = new CopyOnWriteArraySet<>();
HashSet
Set
Collection
HashMap
Map
存储的元素是有序的、可重复的。
Queue(队列)
默认初始容量大小:10默认扩容因子:无,发现不够就去扩容扩容:原始大小×1.5
ArrayList
默认初始容量大小:16默认扩容因子:0.75扩容:原始大小×2
JDK1.8 之前:数组+链表
二叉查找树是二分查找的思想,查找所需的最大次数等同于二叉树的高度。在插入节点的时候也是利用类似的方法,一层一层比较大小,找到合适的插入位置。解决二叉查找树多次插入新节点而导致的不平衡的方法,就是使用红黑树。红黑树是一种自平衡的二叉查找树。除了符合二叉查找树的基本特性外,还具有下列的附加特性:根节点是黑色。节点是红色或黑色。每个叶子节点都是黑色的空节点(NIL节点)。每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。所有插入的节点,默认为红色
当前节点的父结点是红色,叔叔(爷爷节点的下面另一个子节点)是黑色的时候,且当前的结点是右子树。左旋,以父节点作为左旋
左旋
当前节点的父结点是红色,叔叔(爷爷节点的下面另一个子节点)是黑色的时候,且当前的结点是左子树。右旋,以父节点作为右旋
右旋
红黑树
JDK1.8之后:数组+链表+红黑树 解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。
HashMap 的底层实现
默认初始容量大小:11默认扩容因子:0.75扩容:原始大小×2 + 1
Hashtable
扩容相关知识点ArrayListHashMapHashtable
集合
Bean是由Spring的Bean工厂创建的,那是怎么创建的呢,在程序运行的时候,Spring会通过XML的解析类、注解解析类去进行扫描Bean,将扫描得到的Bean信息,映射为BeanDefinition类,再将每个BeanDefinition加入到一个Map集合里,然后再有工厂去这个map里面拿取到bean的信息,之后创建出来,交由SpringIOC容器进行保管!且默认都是单例
Bean
IOC控制反转
AOP面向切面编程
DI依赖注入
(1)当Bean有实现接口时,Spring就会用JDK动态代理。(2)当Bean没有实现接口时,Spring会选择CGLib代理。
Spring的动态代理选择原则
Spring
双检锁单例
class LazySingleton { String name = \"单例\"; private static boolean initialized = false; private LazySingleton() { //防止反射对其单例模式进行破坏 if (initialized) { throw new RuntimeException(\"不允许创建多个实例!\"); } initialized = true; } static LazySingleton getInstance() { return LazyHolder.LAZY_SINGLETON; } private final static class LazyHolder { private static final LazySingleton LAZY_SINGLETON = new LazySingleton(); } /** * 防止序列化破坏单例模式 */ @Serial private Object readResolve() { return LazyHolder.LAZY_SINGLETON; }}
静态内部类单例(推荐)(亦解决了反射会破坏单例)
/** * 枚举单例模式 */@Getterenum EnumSingleton implements Serializable { SINGLETON(); private Integer code; private String name; public static EnumSingleton getSingletonInstance() { return SINGLETON; }}使用枚举实现的单例模式是最安全的,无法通过反射进行实例化。当我们使用枚举来实现单例时,枚举本身就提供了保证单例唯一性和安全性的机制。
枚举单例模式(强烈推荐)
单例模式
/** * 浅拷贝 * 拷贝(克隆)PrototypePattern对象,引用地址是不同,即是两个不同的实例,但是里面的属性age、name是引用相同的地址,这就是浅拷贝! */@Dataclass PrototypePattern implements Cloneable{ private int age = 20; private String name = \"浅拷贝\"; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } /** * 或者下面的浅克隆方法 */// protected PrototypePattern clone() {// PrototypePattern prototypePattern = new PrototypePattern();// prototypePattern.setAge(this.getAge());// prototypePattern.setName(this.getName());// return prototypePattern;// }}
浅拷贝
深拷贝
原型模式
工厂方法模式关注的是一个具体产品的创建,它定义了一个创建对象的接口(工厂接口),但将具体对象的创建延迟到子类中实现。工厂方法模式适用于需要在运行时根据某些条件决定创建哪个具体对象的情况。它的特点是具体的工厂类负责创建具体的产品类,每个具体产品类对应一个具体的工厂类。
工厂方法模式
抽象工厂模式关注的是一组相关产品的创建,它提供了一个创建相关产品的接口(抽象工厂接口),具体的产品创建交由具体的工厂类实现。抽象工厂模式适用于需要一次性创建一系列相关的产品,而不是单独创建一个对象的情况。它的特点是每个具体工厂类负责创建一组相关的产品类,每个产品类对应一个具体工厂类。
抽象工厂模式
建造者模式
创建型模式
工厂方法模式,是一个具体工厂创建一个具体的类。而抽象工厂模式是一个具体工厂,创建一个具体的产品簇
适配器模式
桥接模式
组合模式
装饰器模式
外观模式
池化技术,如数据库连接池,线程池!
享元模式
是指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,代理模式属于结构型设计模式。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。Tip:说白了就是对一个对象进行代理,将获取到这个对象的所有功能,然后我们可以进行对这个对象功能的增强以及保护。增强意为原本对象的功能方法call(),那么我们可以在调用call()方法的前后进行功能增加,比如:System.out.println(\"增强功能前\");call();System.out.println(\"增强功能后\");
静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,代理类需要同步增加,违背开闭原则。所有基本是不会去使用静态代理。
静态代理
JDK使用反射机制生成代理接口,调用InvokeHandler处理;JDK动态代理机制是委托机制,只能对实现接口的类生成代理,通过反射动态实现接口类;Java提供静态方法:Proxy.newInstance();方法进行动态代理JDK动态代理生成对象的步骤如下:(1)获取被代理对象的引用,并且获取它的所有接口,反射获取。(2)JDK动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口。(3)动态生成Java代码,新加的业务逻辑方法由一定的逻辑代码调用(在代码中体现)。(4)编译新生成的Java代码.class文件。(5)重新加载到JVM中运行。
JDK动态代理
CGLib则使用的继承机制,针对类实现代理,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的因为是继承机制,不能代理final修饰的类。
CGLib动态代理
(1)JDK动态代理实现了被代理对象的接口,CGLib代理继承了被代理对象。(2)JDK动态代理和CGLib代理都在运行期生成字节码,JDK动态代理直接写Class字节码,CGLib代理使用ASM框架写Class字节码,CGlib代理实现更复杂,生成代理类比JDK动态代理效率低。(3)JDK动态代理调用代理方法是通过反射机制调用的。 CGLib代理是通过FastClass机制直接调用方法的,CGLib代理的执行效率更高。
CGLib和JDK动态代理对比
动态代理
代理模式
结构型模式
责任链模式
命令模式
解释器模式
迭代器模式
备忘录模式
策略模式
模板方法模式
观察者模式
状态模式
访问者模式
中介者模式
行为型模式
二十三种设计模式
1、Dubbo远程服务调用问题,采用的序列化方式为fastjson2,A模块服务,调用B模块服务时,B抛出自定义异常且带的参数是自定义枚举类提示信息。然而dubbo一直报错该枚举类不支持输入,错误提示:“autoType not support input”,就是反序列化失败的原因。但是即便给该类加了实现序列化接口,也不行,因为是fastjson2,不是fastjson,所以查了好多信息,也添加了dubbo配置等等,都失败。那就换个异常抛,之前没用过dubbo的异常处理,后面查了知道dubbo包下有RpcException异常类,就决定使用这个来进行抛异常。然后A服务调用方在设置全局异常,拦截RpcException异常的抛出处理后,再将信息返回给前端。其实这个问题也不是什么大问题,就fastjson的反序列化问题,。。。。。。。等大哥弄好包后再写
1、日志 90%2、堆栈信息 10%
工作中!如何排查问题?
工作中遇到问题
收藏
0 条评论
回复 删除
下一页