Java整理开发面试 作者:不愿透露姓名的王 qq:2314143004 E-mail:231414300@qq.com 1. 熟练掌握 Java 面向对象的编程思想, 使用集合类, IO 流(NIO、AIO、BIO), 多线程 JVM 操作原理和类加载机制。性能优化GC 垃圾回收原理、设计模式、反射等。 涉及:Java面向对象,集合类,IO流(NIO,AIO,BIO),多线程、线程池、反射、设计模式、JVM运行原理、类加载机制 Java面向对象思想 首先,编程语言主要分为面向过程的语言和面向对象的语言,如C语言是一种面向过程的语言,我一直在使用Java语言是面向对象的语言,对于Java一切都是对象,把现实世界中的对象放在现实世界中Java抽象表达,每个对象都有自己的特定属性和行为,这是一个类别的例子,通过相互作用传递信息来实现程序的开发Java独特的继承、包装和多态特性使它更强大。这就是我对java理解面向对象。 JDK1.8新特性 lambda 表达式:允许将函数作为参数传输到方法,简化匿名内部代码。 函数接口:使用 @FunctionalInterface 只有一种抽象方法可以通过隐式转换为标识 lambda 表达式。 方法引用:可引用现有类别或对象的方法和结构方法,进一步简化 lambda 表达式。 界面:界面可定义 default 默认修改方法可以降低界面升级的复杂性,定义静态方法。 注:引入重复注释机制,同一注释可在同一地方多次声明。注释范围也扩大了,可作用于局部变量、泛型、异常方法等。 类型推测:加强了类型推测机制,使代码更加简洁。 Optional 类别:处理空指针异常,提高代码可读性。 Stream 类别:引入函数编程风格,提供多种功能,使代码更加简洁。方法包括 forEach 遍历、count 统计个数、filter 按条件过滤,limit 取前 n 个元素、skip 跳过前 n 个元素、map 映射加工、concat 合并 stream 流等。 日期:增加日期和时间 API,新的 java.time 包主要包括处理日期、时间、日期/操作时间、时间、时间和时钟。 JavaScript:提供新的 JavaScript 发动机,允许在 JVM上运行特定 JavaScript 应用。 重写与重载的区别 重写 继承重写方法,参数数量和类型一致 重载 方法同名,但参数表不同。 集合类 Java集合存放java.util这个包是一个用来存储物体的容器。 Collection(接口) List(接口) ArrayList(类) 特点: ArrayList存取有序,可重复集合,它是异步的,线程不安全,它的底部存储结构是Object因此,数组查询速增删慢,初始容量为10,加载因子为1,扩容倍数为1.5倍。 原理: add() 添加:添加元素时,首先检查他目前的内部Object数组的当前下标 1是否超过数组的总长度,如果没有,添加,如果超过,触发扩展机制,将原数组中的数据复制到原数组的大小1.5在新数组中,新数组被赋值给原数组。 remove() 删除:ArrayList删除时,首先检查删除元素的下标是否是数组的最后一个位置。如果是,直接将此位置设置为空。如果没有,则从此下标添加1位置开始向后移动所有元素的位置,然后将数组的最后一个位置设置为空。. get() 获取:直接获取当前数组下标的元素。 set() 变化:直接将元素赋值给数组当前下标位置。 LinkedList(类) 特点: LinkedList这是一个有序的、可重复的集合,它是异步的,线程不安全,它的底层存储结构是双向链表,所以增删快,查询慢容量方面没有限制。
原理:
add() 添加:LinkedList有两个节点变量first和last,在添加元素时,会创建新节点, 将last赋值给新节点的上节点,下节点赋值为空,中间的值为要添加的元素,然后把新节点赋值给last,再判断一下上节点是不是为空,为空就说明LinkedList里原来是没有元素的,然后把新节点赋值给first,否则就将last的next指向新节点,使他形+成双向链表。
remove()删除:将这个节点删除,再将它相邻的两个节点的指向修改为相邻。
get()获取:是一个查找的过程,先比较下标和集合长度的右位移一位的值的大小,如果下标小,就从头部开始查找,如果下标大,就从尾部开始查找。
set()改变:先查找这个节点,比较下标和集合长度的右位移一位的值的大小,如果下标小,就从头部开始查找,如果下标大,就从尾部开始查找,找到节点后,将节点的元素指向新元素。
Set(接口)
HashSet(类)
特点:
HashSet是一个存取无序的,不可重复的集合,他是异步,线程不安全的,他的底层使用HashMap来存储,初始容量为16,加载因子为0.75,扩容倍数为2倍。
原理:
add()添加:就是调用了map的put方法,元素用于键,值是一个固定值,HashMap中一个键只会保存一份,所以重复添加HashMap不会变化,就实现了去重添加。
remove()删除:就是调用map的remove方法,移除HashMap中key为该元素的节点
LinkedHashSet(类)
特点:
LinkedHashSet是一个存取有序的,不可重复的集合,他是异步,线程不安全的,他的底层是LinkedHashMap,可以依靠双向链表维护元素插入顺序,初始容量为16,加载因子为0.75,扩容倍数为2倍。
原理:(使用HashSet的)
add()添加:就是调用了map的put方法,元素用于键,值是一个固定值,HashMap中一个键只会保存一份,所以重复添加HashMap不会变化,就实现了去重添加。
remove()删除:就是调用map的remove方法,移除HashMap中key为该元素的节点
TreeSet(类)
特点:
TreeSet是一个存取无序的,可排序的,不可重复的集合,他是异步,线程不安全的,他的底层是基于TreeMap来实现,所以数据结构是红黑树,容量方面没有限制。
原理:
add()添加:就是调用了map的put方法,元素用于键,值是一个固定值,TreeMap中一个键只会保存一份,所以重复添加HashMap不会变化,就实现了去重添加。
remove()删除:就是调用map的remove方法,移除TreeMap中key为该元素的节点
Map(接口)
HashMap(类)
特点:
HashMap是一个存取无序的,不可重复的Key,Value型集合,他是异步,线程不安全的,他的底层存储结构是数组+链表+红黑树,初始容量为16,加载因子为0.75,扩容倍数为2倍。
原理:
put()添加:判断新插入这个值是否导致长度已经超过了阈值,是则进行扩容,没超过走正常添加流程,先判断Key是否为null,如果为null,则把元素存储到Entry<k,v>数组的一个元素中,返回null,因为null 的哈希值是0.然后计算元素的hash 值 ,根据hash 值获取table准确的位置,遍历准确找到数组中的链表。比较hashCode 和 equals 方法检查有无碰撞,如果添加的元素与已经存在元素的 hashCode 相同并且equals 相同的话,则 把之前的value 覆盖成新加入的value,如果添加的元素和之前元素的hashCode 相同,equels不同,则数组的下标相同,会new 一个Entry对象 把 原有的对象放入 链表中,如果链表长度超过8,就转为红黑树,如果添加的元素和值之前的元素 hashCode 、equles 不相同的话,那么则会添加到table的下一个位置当中去。
remove()删除:删除指定的key,不会影响数组长度如果是数组就设为null,如果是链表就断开并删除节点再重新连接,如果是红黑树就调整树结构保持平衡,如果有必要则将红黑树缩减为链表。
get()获取:先判断这个key是否为null,如果为null,返回table下标0中的元素 null,然后调用hash 方法计算hash 值,再根据hash值计算table准确的位置,最后遍历数组的链表 :找到和key相符的并返回。
HashTable(类)
特点:HashTable是一个存取无序的,不可重复的Key,Value型集合,他是同步,线程安全的,他的底层存储结构是数组+单向链表,初始容量为11,加载因子为0.75,扩容倍数为2倍+1。
原理:
put()添加:全程用Synchronized锁的方式保证线程安全,先判断value不能为null,再遍历找到这个key,如果存在,就覆盖value的值,如果不存在,就新增,判断是否需要扩容。
remove()删除:全程用Synchronized锁的方式保证线程安全,先计算出key的hash值,再通过hash值判断位置,遍历找到链表里的节点并删除。
get()获取:全程用Synchronized锁的方式保证线程安全,先计算出key的hash值,再通过hash值判断位置,然后迭代链表,找到相对应key的value。
HashMap和HashTable的区别(主要区别)
1.HashMap允许key ,value 为空
HashTable不允许 key ,value 为空
2.HashMap是异步的,线程不安全
HashTable因为所有操作方法都加了Synchronized锁,是同步的,所以是线程安全的
3.HashMap初始容量16,扩容为原长度*2
HashTable初始容量11,扩容为原长度*2+1
4.HashMap没有contains方法
HashTable提供contains方法
LinkedHashMap(类)
特点:
LindedHashMap是一个存取有序的,不可重复的Key,Value型集合,他是异步,线程不安全的,他的底层是额外加了一层双向链表维护有序性,存储还是用HashMap的数组+链表+红黑树,初始容量为16,加载因子为0。75,扩容倍数为2倍。
原理:(put和remove使用了HashMap的)
put()添加:判断新插入这个值是否导致长度已经超过了阈值,是则进行扩容,没超过走正常添加流程,先判断Key是否为null,如果为null,则把元素存储到Entry<k,v>数组的一个元素中,返回null,因为null 的哈希值是0.然后计算元素的hash 值 ,根据hash 值获取table准确的位置,遍历准确找到数组中的链表。比较hashCode 和 equals 方法检查有无碰撞,如果添加的元素与已经存在元素的 hashCode 相同并且equals 相同的话,则 把之前的value 覆盖成新加入的value,如果添加的元素和之前元素的hashCode 相同,equels不同,则数组的下标相同,会new 一个Entry对象 把 原有的对象放入 链表中,如果链表长度超过8,就转为红黑树,如果添加的元素和值之前的元素 hashCode 、equles 不相同的话,那么则会添加到table的下一个位置当中去。
remove()删除:删除指定的key,不会影响数组长度如果是数组就设为null,如果是链表就断开并删除节点再重新连接,如果是红黑树就调整树结构保持平衡,如果有必要则将红黑树缩减为链表。
get()获取:重写了HashMap的get方法,不止会取出所索要的节点的值,而且会调整LinkedHashMap中内置的链表中该键所对应的节点的位置,将该节点置为链表的尾部。
TreeMap(类)
特点:
TreeMap是一个存取无序,自然排序的,不可重复的Key,Value型集合,他是异步,线程不安全的,他的底层存储结构是红黑树,也称平衡性二叉树,容量方面没有限制。
原理:
put()添加:先把的根节点的引用赋值给当前节点,再根据二叉树的特性,找到新的节点插入的合适位置,判断有没有外部比较器,有就用外部的,没有就用自然排序Comparable比较,最后将新节点插入并平衡下来。
remove()删除:找到这个节点,设置为Null,再运行自平衡方法。
get()获取:通过二分查找法来获。
线程安全的集合
Hashtable
Vector
Collections包装方法(synchronizedList,synchronizedSet,synchronizedMap)
java.util.concurrent包中的集合(ConcurrentHashMap,CopyOnWriteArrayList,CopyOnWriteArraySet)
ConcurrentHashMap
线程安全的的HashMap,初始容量为16,加载因子为0.75,扩容倍数为2倍。
原理:
jdk 1.5
采用了分段锁策略,内部维护了一个Segment 数组,Segment继承了ReentrantLock,所以它就是一种可重入锁。在ConcurrentHashMap中,一个Segment就是一个子哈希表,Segment里维护了一个HashEntry数组,并发环境下,对于不同Segment的数据进行操作是不用考虑锁竞争的。这样既能保证性能又能保证数据的安全性
jdk 1.8
ConcurrentHashMap取消了segment分段锁,而采用CAS算法和synchronized来保证并发安全。数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树,synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升了很多。
CopyOnWriteArrayList
线程安全的List
原理:
add()添加:
先copy出一个容器,再往新的容器里添加这个新的数据,最后把新的容器的引用地址赋值给了之前那个旧的的容器地址,但是在添加这个数据的期间,其他线程如果要去读取数据,仍然是读取到旧的容器里的数据。finally中释放锁
remove()删除:
先判断要删除的元素是否最后一个,是的话直接复制长度为旧数组的length-1即可;
如果不是,就先复制旧的数组的index前面元素到新数组中,然后再复制
*;旧数组中index后面的元素到数组中,最后再把新数组赋值给旧数组。finally中释放锁
set()改变:
先获取就数组要修改的值,判断值是否改变,改变了则copa老数组得到一个新数组,然后更新改变的值的下标位置的值,最后再把新数组赋值给旧数组,finally中释放锁
CopyOnWriteArraySet
线程安全的Set,底层是一个CopyOnWriteArrayList
原理:
add()添加:主要是调用CopyOnWriteArrayList.addIfAbsent方法
remove()删除:主要是调用CopyOnWriteArrayList.remove方法
Collections(集合工具类)
常用方法
Shuffle()随机排序
Reverse()反转排序
Sort()自然比较
copy()复制集合
swap()交换两个位置的元素位置
Collection和Collections的区别
Collection
是集合类的上层接口。本身是一个Interface,里面包含了一些集合的基本操作。Collection接口时Set接口和List接口的父接口
Collections
Collections是一个集合框架的帮助类,里面包含一些对集合的排序,搜索以及序列化的操作。最根本的是Collections是一个类,Collections 是一个包装类,Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许,一些 collection 是有序的,而另一些则是无序的。
CAS算法
采用单cpu指令执行,CAS算法中有三个参数 V:要更新的值 E 表示预期值 N 表示新值,只有当v 等于E 的时候, 才会将N付给 V ,如果不相等,那么 要么什么都不做,要么就进行无限重试,直到v==e 结束。
Java八大基本数据类型
整型
byte(1字节) 包装类Byte
short(2字节) 包装类Short
int(4字节) 包装类Integer
long(8字节) 包装类Long
字符型
char(2字节) 包装类Character
浮点型
float(4字节) 包装类Float
double(8字节) 包装类Double
布尔型
boolean(4字节) 包装类Boolean
非常注意:String不是基本数据类型,他是引用数据类型
抽象类和接口的区别
抽象类要被子类继承,接口要被类实现。
接口在jdk 1.7只能做方法声明,在1.8 之后可以有方法的实现,抽象类中可以作方法声明,也可以做方法实现。
抽象类只能单继承,接口可以多继承
抽象类有构造器,但是不能实例化,接口没有构造器
接口和抽象类的选择
1.优先使用接口
2.在既要定义子类的行为,又要为子类提供公共的功能时应选择抽象类。
String
常用方法
length()字符串的长度
charAt()截取一个字符
replace()替换
getBytes()将字符串变成一个byte数组
toCharArray()将字符串变成一个字符数组
equals()比较两个字符串是否相等,区分大小写
equalsIgnoreCase()比较两个字符串是否相等,不区分大小写
startsWith()判断字符串是不是以特定的字符开头
endsWith()判断字符串是不是以特定的字符结束
toUpperCase()将字符串转换为大写
toLowerCase()将字符串转换为小写
concat() 连接两个字符串
trim()去掉起始和结束的空格
substring()截取字符串
split()分割字符串返回一个数组
indexOf()查找字符或字符串第一次出现的地方
lastIndexOf()查找字符或字符串最后一次出现的地方
compareTo()按字典顺序比较两个字符串的大小,区分大小写
compareToIgnoreCase()按字典顺序比较两个字符串的大小,不区分大小写
String和StringBuffer的区别
1.String是常量,StringBuffer是变量
2.String处理字符串时每次都重新赋值,StringBuffer处理字符串时可以直接修改,不必生成新的对象
3.String是线程不安全的,,StringBuffer是线程安全的
stringbuffer和stringbuilder的区别
1、线程安全:StringBuffer:线程安全,StringBuilder:线程不安全。因为StringBuffer的所有公开方法都是synchronized修饰的,StringBuilder并没有StringBuilder修饰。
2、缓冲区:StringBuffer每次获取toString都会直接使用缓存区的toStringCache值来构造一个字符串。而StringBuilder则每次都需要复制一次字符数组,再构造一个字符串。所以,缓存冲这也是对 StringBuffer 的一个优化吧,不过 StringBuffer 的这个toString 方法仍然是同步的。
3、性能:StringBuffer是线程安全的,它所有公开方法都是同步的,StringBuilder是没有对方法加锁同步的,所以毫无疑问,StringBuilder的性能要远大于 StringBuffer。
IO流(NIO,AIO,BIO)
流的概念
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。它的特性是进行数据传输;
IO流分类
主要分为字符流和字节流,字符流一般用于文本文件,字节流一般用于图像或其他文件。
字符流包括了字符输入流 Reader 和字符输出流 Writer,字节流包括了字节输入流 InputStream 和字节输出流 OutputStream。字符流和字节流都有对应的缓冲流,字节流也可以包装为字符流,缓冲流带有一个 8KB 的缓冲数组,可以提高流的读写效率。除了缓冲流外还有过滤流 FilterReader、字符数组流 CharArrayReader、字节数组流 ByteArrayInputStream、文件流 FileInputStream 等。
BIO
BIO 是同步阻塞式 IO,JDK1.4 前的 IO 模型。服务器实现模式为一个连接请求对应一个线程,服务器需要为每一个客户端请求创建一个线程,如果这个连接不做任何事会造成不必要开销。可以通过线程池改善,称为伪异步 IO。适用连接数目少且服务器资源多的场景。
NIO
NIO 是 JDK1.4 引入的同步非阻塞 IO。服务器实现模式为多个连接请求对应一个线程,客户端连接请求会注册到一个多路复用器 Selector ,Selector 轮询到连接有 IO 请求时才启动一个线程处理。适用连接数目多且连接时间短的场景。
同步是指线程还是要不断接收客户端连接并处理数据,非阻塞是指如果一个管道没有数据,不需要等待,可以轮询下一个管道。
核心组件:
Selector: 多路复用器,轮询检查多个 Channel 的状态,判断注册事件是否发生,即判断 Channel 是否处于可读或可写状态。使用前需要将 Channel 注册到 Selector,注册后会得到一个 SelectionKey,通过 SelectionKey 获取 Channel 和 Selector 相关信息。
Channel: 双向通道,替换了 BIO 中的 Stream 流,不能直接访问数据,要通过 Buffer 来读写数据,也可以和其他 Channel 交互。
Buffer: 缓冲区,是一块可读写数据的内存。Buffer 三个重要属性:position 下次读写数据的位置,limit 本次读写的极限位置,capacity 最大容量。
flip 将写转为读,底层实现原理把 position 置 0,并把 limit 设为当前的 position 值。
clear 将读转为写模式(用于读完全部数据的情况,把 position 置 0,limit 设为 capacity)。
compact 将读转为写模式(用于存在未读数据的情况,让 position 指向未读数据的下一个)。
通道方向和 Buffer 方向相反,读数据相当于向 Buffer 写,写数据相当于从 Buffer 读。
使用步骤:向 Buffer 写数据,调用 flip 方法转为读模式,从 Buffer 中读数据,调用 clear 或 compact 方法清空缓冲区。
AIO
AIO 是 JDK7 引入的异步非阻塞 IO。服务器实现模式为一个有效请求对应一个线程,客户端的 IO 请求都是由操作系统先完成 IO 操作后再通知服务器应用来直接使用准备好的数据。适用连接数目多且连接时间长的场景。
异步是指服务端线程接收到客户端管道后就交给底层处理IO通信,自己可以做其他事情,非阻塞是指客户端有数据才会处理,处理好再通知服务器。
实现方式包括通过 Future 的 get 方法进行阻塞式调用以及实现 CompletionHandler 接口,重写请求成功的回调方法 completed 和请求失败回调方法 failed。
应用场景
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
创建对象的四种方式
1.new一个对象创建
2.调用对象的clone方法创建
3.利用反射,调用Class类的或者是Constructor类的newInstance方法创建
4.用反序列化,调用ObjectInputStream类的readObject方法创建
浅克隆和深克隆
浅克隆:仅仅复制所克隆的对象,而不复制它所引用的对象。
被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。
深克隆:把要复制的对象所引用的对象都复制了一遍。
那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。
static的作用
static主要有5中使用情况:成员变量、成员方法、代码块,内部类和静态导包。
static修饰成员变量:该成员变量属于类变量,可以通过 ClassName.attributeName 直接引用,而不需要new出类的实例。
static修饰成员方法:该方法属于类的方法,可以通过 ClassName.methodName 直接引用,而不需要new出类的实例。
static修饰代码块:仅在类初始化的时候执行一次,且加载顺序是严格按照类中静态资源的定义顺序来加载的;静态代码块对于定义在它之后的静态变量,可以赋值,但是不能访问。;父类代码块->子类代码块。
static修饰内部类:static不能修饰普通类,只能修饰内部类,被static修饰的内部类的创建方式: new OuterClass.InnerClass()。
static导入包:语法“import static java.lang.Math.*”,这样在类中就可以直接使用Math类中的静态方法而不需要写类名,在频繁使用某个类的时候比较方便,但是降低了可读性。并且不建议导入*
final的作用
当final修饰类的时候,说明该类不能被继承
当final修饰方法的时候,说明该方法不能被重写用来修饰类
当final修饰成员变量时,有两种情况:
如果修饰的是基本类型,说明这个变量的所代表数值永不能变(不能重新赋值)!
如果修饰的是引用类型,该变量所的引用不能变,但引用所代表的对象内容是可变的!
Object的常用方法
1,构造函数
2,hashCode和equale函数用来判断对象是否相同,
3,wait(),wait(long),wait(long,int),notify(),notifyAll()
4,toString()和getClass,
5,clone()
6,finalize()用于在垃圾回收
java并发包juc包下常用的类
瑞安踹特路可
ReentrantLock,重入锁
凯可瑞特哈希卖普
ConcurrentHashMap 线程安全的Map
考配昂外特额瑞里四特
CopyOnWriteArrayList 线程安全的List
考配昂外特额瑞塞特
CopyOnWriteArraySet 线程安全的Set
开奥当辣吃
CountDownLatch 同步计数器
哎桃卖可引忒哥
AtomicInteger 原子数
赛可北儿
CyclicBarrier 循环屏障
多线程
创建线程的方式
1.继承Thread类创建线程(单继承局限性)
2.实现Runnable接口创建线程
3.使用Callable和Future创建线程
4.使用线程池创建
线程的生命周期
线程的生命周期包含5个阶段:新建、就绪、运行、阻塞、销毁。
1.新建:使用new方法
2.就绪:调用的线程的start()方法
3.运行:run方法
4.阻塞:sleep()、wait()之后线程就处于了阻塞状态
5.销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束
线程安全和线程不安全
线程安全:在多线程环境下对共享资源做增删改的操作,保证共享资源的原子性,正确性
线程不安全:在多线程环境下对共享资源做增删改的操作,导致共享资源的结果不正确
synchronized的原理
当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。。
当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内存中读取共享变量。
synchronized的作用
synchronized是一种同步锁,作用是防止多个线程同一时间调用此代码块或者方法.
synchronized 作用于普通方法,锁的对象是当前实例
synchronized 作用于静态方法,锁的对象是类的Class对象
synchronized 作用于方法块,锁的对象是括号里匹配的对象
ReentrantLock 工作原理
必须要用肯嘚深的额卫特和赛个闹方法
必须要用 condition 的await() signal()
用cas算法比较状态值 由0 改为1 。如果成功则获取到锁。
如果不成功则再次尝试获取到锁,如果不成功则加入到队列中。并且挂起采用
synchronized和ReentrantLock的区别
synchronized 是一个关键字
ReentrantLock 是一个类
synchronized 加锁和释放锁自动加锁和释放
ReentrantLock 必须用lock() 和unlock() 加锁和释放锁
synchronized 是一个非公平锁
ReentrantLock 提供了公平锁又提供了一个非公平锁 通过构造参数 boolean 决定 是公平还是非公平
synchronized 不可限时
ReentrantLock 可限时 try 避免死锁
synchronized 不可中断
ReentrantLock 可中断 lockInterruptibly 获取锁但是响应中断
springMVC和springBean是线程安全的么,为什么,怎么避免线程不安全
Spring管理的Bean对象默认是单例模式,当多线程操作Bean对象时就会出现线程安全问题;因为在多线程中线程改变了bean对象的可变成员变量时,其他线程就无法访问该bean对象的初始状态,这样就造成数据错乱。所以需要用线程同步来处理这个问题。
volatile
修饰符关键字:用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最新的值
线程池
线程池的作用
为了避免线程的频繁创建和销毁对性能的损耗而实现对线程的复用
线程池的种类
单线程池
固定大小线程池
缓存线程池
定时线程池
自定义线程池
线程池的参数
int corePoolSize, 核心线程数
int maximumPoolSize,最大线程数
long keepAliveTime,空闲线程存活时间
TimeUnit unit,空闲线程存活时间单位
BlockingQueue workQueue,阻塞队列
ThreadFactory threadFactory,线程工厂
RejectedExecutionHandler handler 拒绝策略
线程池的工作原理(大白话)
首先,我们的线程先从核心线程数里面去执行,如果不够的话,会放到队列里面,如果还不够的话,临时线程数会开启,如果还不够的话,就执行拒绝策略,那么这个时候,如果够的情况下呢,比如我现在核心线程数在做,队列里面也没满,这个时候就有一个等待时间,过了这个等待时间后, 我的临时线程会被释放掉,这是他这个的工作原理
拒绝策略
额宝特抛了C
默认的拒绝策略为 AbortPolicy执行的结果是RejectedExecutionException
第四卡的抛了C
DiscardPolicy 执行结果什么都不做
第四卡的偶得抛了C
DiscardOldestPolicy 执行结果为从队列中舍去一个线程,执行当前线程
卡乐润死抛了C
CallerRunsPolicy 执行结果为如果当前线程没有终止,则运行当前线程
反射
反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
哪里用到反射机制
1.JDBC中,利用反射动态加载了数据库驱动程序。
2.Web服务器中利用反射调用了Sevlet的服务方法。
3.Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。
4.很多框架都用到反射机制,注入属性,调用方法,如Spring。
反射机制的优缺点
优点:可以动态执行,在运行期间根据业务功能动态执行方法、访问属性,最大限度发挥了java的灵活性。
缺点:对性能有影响,这类操作总是慢于直接执行java代码。
动态代理是什么
动态代理是运行时动态生成代理类。
动态代理的应用有 Spring AOP数据查询、测试框架的后端 mock、rpc,Java注解对象获取等。
怎么实现动态代理?
JDK 原生动态代理和 cglib 动态代理。
JDK 原生动态代理是基于接口实现的,而 cglib 是基于继承当前类的子类实现的。
使用Java的反射的方式
1.通过一个全限类名创建一个对象
2.获取构造器对象,通过构造器new出一个对象
3.通过class对象获得一个属性对象
4.通过class对象获得一个方法对象o
设计模式
创建型模式
单例模式:某个类只能有一个实例,提供一个全局的访问点。
简单工厂:一个工厂类根据传入的参量决定创建出那一种产品类的实例。
工厂方法:定义一个创建对象的接口,让子类决定实例化那个类。
抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体类。
建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造。
原型模式:通过复制现有的实例来创建新的实例。
结构型模式
适配器模式:将一个类的方法接口转换成客户希望的另外一个接口。
组合模式:将对象组合成树形结构以表示“”部分-整体“”的层次结构。
装饰模式:动态的给对象添加新的功能。
代理模式:为其他对象提供一个代理以便控制这个对象的访问。
亨元模式:通过共享技术来有效的支持大量细粒度的对象。
外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。
行为型模式
模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
观察者模式:对象间的一对多的依赖关系。
备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
中介者模式:用一个中介对象来封装一系列的对象交互。
命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。
责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。
设计的原则
单一职责原则:一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。
开闭原则:一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
里氏代换原则:所有引用基类(父类)的地方必须能透明地使用其子类的对象。
依赖倒转原则:抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。
接口隔离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
迪米特法则:一个软件实体应当尽可能少地与其他实体发生相互作用。
设计模式在项目中的使用
1、模板方法模式
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,如JdbcTemplate
2、代理
spring的Proxy模式在aop中有体现
3、观察者
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
spring中Observer模式常用的地方是listener的实现。如ApplicationListener。
4、适配器
MethodBeforeAdviceAdapter类
5、策略模式
使用了java的继承和多态
案例1:加减法计算器,定义一个计算类接口,加法和减法类都实现它,加的时候传入加法对象。
案例2:导出excel,pdf,word时,分别创建不同的对象
简单理解:执行多个事情时,创建多个对象
6、单例模式
解决一个全局使用的类频繁的创建与销毁
7、工厂模式
分为三种:简单工厂,工厂方法,抽象工厂
根据“需求”生产“产品”,解耦“需求”“工厂”和“产品”。
简单工厂:通过构造时传入的标识来生产产品,不同产品都在同一个工厂中生产,每新增加一种产品,需要改工厂类,来判断,这种判断会
标签: 复合式通用序列总线连接器cp114差压变送器aux11变送器