重写是应用在有继承的关系中的,重写父类的方法,方法体不一样。
重载是可以有不同的参数,参数类型,返回值,修饰符可以发生在子父类中,或者在本类中。
String,StringBuffer和StringBuilder
String 不能更改长度,StringBuffer和StringBuilder可以改变长度,StringBuilder单线程,StringBuffer多线程场景
1.使用String类的场景:在字符串不经常变化的场景中可以使用String类,例如常量的声明、少量的变量运算。
2.使用StringBuffer类的场景:在频繁进行字符串运算(如拼接、替换、删除等),并且运行在多线程环境中,则可以考虑使用StringBuffer,例如XML解析、HTTP参数解析和封装。
3.使用StringBuilder类的场景:在频繁进行字符串运算(如拼接、替换、和删除等),并且运行在单线程的环境中,则可以考虑使用StringBuilder,如SQL语句的拼装、JSON封装等。
1、接口类似于类,但接口的成员都没有执行方式,它只是方法、属性、事件和索引的组合而已,并且也只能包含这四种成员;类除了这四种成员之外还可以有别的成员(如字段)。
2、不能实例化一个接口,接口只包括成员的签名;而类可以实例化(abstract类除外)。
3、接口没有构造函数,类有构造函数。
4、接口不能进行运算符的重载,类可以进行运算符重载。
5、接口的成员没有任何修饰符,其成员总是公共的,而类的成员则可以有修饰符(如:虚拟或者静态)。
6、派生于接口的类必须实现接口中所有成员的执行方式,而从类派生则不然。
Class.froName
.class
类对象的getClass()
public class RandomStringGenerator<T> implements Iterable<T> {
private List<T> list;
public RandomStringGenerator(List<T> list){
this.list = list;
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
@Override
public boolean hasNext() {
return true;
}
@Override
public T next() {
return list.get((int) (list.size() * Math.random()));
}
};
}
public static void main(String[] args) {
var list = Arrays.asList("list","tree","table");
var gen = new RandomStringGenerator<String>(list);
for (var s :gen)
System.out.println(s);
// var it = gen.iterator();
// for (int i = 0; i < 100; i++) {
// System.out.println(it.next());
// }
}
}
Treeset不支持null
Map的本质时映射,kv
hashmap 哈希表实现的map
TreeSet 树实现的集合,set不支持null的容器
什么是流?
:: 运算符的作用?
Java8 Stream价值是什么?
创建Stream有几种方法?
coding: 利用parallel并发执行任务?
== 和 equals
== 在引用数据类型中是地址比较,
在基本数据类型中是值比较,
equals只能比较引用类型,是值比较,在基本数据类型的包装类中比较的是地址(重写equals方法)
流:随着时间到来的数据。
缓冲区:缓冲数据,请求太多…
缓冲的本质是排队,流的本质是数据。
没有缓冲:只能拒绝服务,性能低
有缓冲:排队处理,批量处理(批量处理请求避免拒绝服务,批量写入磁盘快过多次写入,批量执行sql快过多次执行)
Device->Kernel Space->User Space(buffer) ->Thread
IO的成本很高3次拷贝
缓冲区是不是流?
缓冲区操作的几种含义:filp/rewind/clear?(Position,Limit,Capacity)
当想缓冲区放入数据时,每放一个P指针会指向下一个空间,当要读取数据时,要进行翻转(flip)操作,重读或重写用rewind操作,清空缓冲区用clear操作
缓冲区设置多大?
NIO的Channel比缓冲区快么?
缓冲过程中,中文乱码如何处理?
并发分析数据更快么?
计算一个大文件的词频?
就是帮助mysql高效获取数据的排好序的数据结构
红黑树:平衡二叉树
哈希索引
算出key的hash值,存地址。
不适合范围查询
数据库表存储引擎:
修饰数据库表的
联合索引的底层存储结构什么样?
索引最左前缀原理
为什么使用主键自增而不是uuid?
uuid是字符串,主键自增
插入可以更快减少比较和移动.
当查的字段类型不匹配时会转型,int的字符串会转为0
索引失效:对字段进行操作
空间 128M 有三个链表
update
更新buffer pool里面的页数据
生成redoLog,commit之后持久化redoLog,顺序IO
即使数据库挂了,也可以通过redoLog还原数据redolog文件一开始就开辟48M的空间
两个redolog文件都有checkpoint到达这个点就要持久化 ,redolog文件先存在logbuffer, redolog是innodb层面的
随机IO费时
binlog 是mysql层面的用于给其他从数据库数据
undolog还原用记录之前
doublewritebuffer innodb持久化时16->4需要判断能不能写成功
把无序的数据建立有序的查询
聚簇索引:数据和索引在一起,而且有排序
非聚簇索引:叶子节点存储的不是数据,是地址
innoDB中一定有主键,主键一定是聚簇索引,不手动设置会使用唯一索引,没有唯一索引会自己生成一个rowid
MyISM使用的是非聚簇索引,没有聚簇索引,存储的内容不一样
B+树:一般时候
Hash:单条记录查询多
查询更快,占用空间更小
属性分类:共享锁,排他锁
粒度分类:行级锁,表级锁,页级所,记录锁,间隙锁,临键锁
状态分类:意向共享锁,意向排他锁
共享锁
共享锁就是读锁,S锁,当一个事务为数据加上读锁之后,其他事务只能对该数据加读锁,而不能对数据加写锁,直到所有的读锁释放之后其他事务才能对其进行加写锁。共享锁的特性主要是为了支持并发的读取数据,读取数据的时候不支持修改,避免出现重读的问题。
排他锁
排他锁又称为写锁,X锁,当一个事务为数据加上写锁时,其他请求将不能再为数据加任何锁,直到该锁释放之后,其他事务才能对数据进行加锁。排他锁的目的是在数据修改时,不允许其他人同时修改,也不能读。避免脏读,脏数据
表锁
整个表,粒度大,加锁简单,但是容易冲突 myISM
行锁
可以多行,粒度小,不容易冲突,并发比表锁高 innodb
记录锁
记录锁是行锁的一种,只查一条记录,命中条件字段是唯一索引。
页锁
介于表锁和行锁之间,
间隙锁
左开右闭
临建锁
记录锁和间隙锁的组合
意向共享锁
当一个事务试图对整个表进行加共享锁之前,首先需要获得这个表的意向共享锁
意向排他锁
当一个事务试图对整个表进行加排他锁之前,首先需要获得这个表的意向排他锁
explain
执行效率:
ALL 原子性 一起成功,一起失败 一致性 一致性关注数据的可见性,中间状态的数据对外部不可见,只有最初状态和最终状态的数据对外可见 隔离性 一个事务在最终提交前,对其他事务是不可见的 持久性 事务提交,所做的修改就会永久保存到数据库中。 A原子性由undolog日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行的sql。 C一致性由其他三大特性保证、程序代码也要保证业务上的一致性 I隔离性由MVCC来保证 D持久性 由内存+redolog来保证,mysql修改数据同时在内存和redolog记录这次操作,宕机也可以从redolog恢复 SQL 标准定义了四种隔离级别,MySQL 全都支持。这四种隔离级别分别是: 读未提交(READ UNCOMMITTED) 读提交 (READ COMMITTED Oracle mvcc) 可重复读 (REPEATABLE READ Mysql mvcc) 串行化 (SERIALIZABLE) MySQL 的 InnoDB 引擎才支持事务,其中可重复读是默认的隔离级别。 读未提交和串行化基本上是不需要考虑的隔离级别,前者不加锁限制,后者相当于单线程执行,效率太差。 读提交解决了脏读问题,行锁解决了并发更新的问题。并且 MySQL 在可重复读级别解决了幻读问题,是通过行锁和间隙锁的组合 Next-Key 锁实现的。 多版本并发控制:它的实现原理主要是版本链,undo日志 ,Read View 来实现的 mvcc只在 rc rr 从以上的描述中我们可以看出来,所谓的MVCC指的就是在使用READ COMMITTD、REPEATABLE READ这两种隔离级别的事务在执行普通的SEELCT操作时访问记录的版本链的过程,这样子可以使不同事务的读-写、写-读操作并发执行,从而提升系统性能。 除了数据 以外分别是db_trx_id、db_roll_pointer、db_row_id。 对该记录每次更新后,都会将旧值放到一条undo日志中,就算是该记录的一个旧版本,随着更新次数的增多,所有的版本都会被roll_pointer属性连接成一个链表,我们把这个链表称之为版本链,版本链的头节点就是当前记录最新的值。另外,每个版本中还包含生成该版本时对应的事务id,这个信息很重要,在根据ReadView判断版本可见性的时候会用到。 看看查询条件有没有命中索引,是不是加载了不需要的数据列,分析语句执行计划,如果只是因为数据量太大,就可以考虑横向或者纵向分表 分库分表 shardingSphere和shardingProxy MySQL 主从复制模式: 异步模式(mysql async-mode) 全同步模式 半同步 MyISAM: 不支持事务,但是每次查询都是原子的 表级锁,维护了一个总行数; MyISAM表有三个文件:索引文件、表结构文件、数据文件; 采用非聚簇索引,索引文件的数据域存储指向数据文件指针。辅索引与主索引基本一致,但是副索引不用保证唯一性。 InnoDB: 支持ACID的事务,支持事务的四种隔离级别; 支持行级锁外键约束:因此可以支持写并发; 不存储总行数; InnoDB引擎存储在一个文件空间(共享表空间,表大小不受操作系统控制,一个表可能分布在多个文件里),也有可能为多个(设置独立表空间,表的大小受操作系统文件大小限制,一般为2G)受操作系统文件大小的限制; 主键索引采用聚集索引(索引的数据域存储数据文件本身),辅索引的数据域存储主键的值;因此从辅索引查找数据,需要通过辅索引找到主键值,再访问辅索引;最好用主键自增,防止插入数据时,为维持B+树结构而调整 MyISAM 存储引擎 非聚集索引 Innodb 存储引擎 聚集索引 innodb引擎的4大特性 普通索引:允许被索引的数据列包含重复的值。 唯一索引:可以保证数据记录的唯一性 主键:是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯一标识一条记录,使用关键字primarykey来矿建 联合索引:索引可以覆盖多个数据列,就是多个列 全文索引:通过建立倒排索引,可以极大的提升检索效率,解决判断字段是否包含的问题,是目前搜索引擎使用的一种关键技术。可以通过ALTER TABLE tablename add fulltext,创建全文索引,但是比较鸡肋,现在都用ElastictSearch 索引可以极大的提高数据的查询速度。 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。 但是会降低插入,删除,更新表的速度,因为在执行这些写操作时,还要操作索引文件 索引需要占用物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大,如果非聚簇索引很多,一旦聚集索引改变,那么所有非聚簇索引都会跟着变。 排序本质就是进步的方式,进步就是无序到有序,有三种:加一减一法,分治策略,哈希函数(将无需映射成有序) 递归不终止StackOverflow(在栈上不停的push元素) 内存读取cpu缓存很快 ,写不能只写缓存 合并排序设置哨兵 copyOfRange超出会补零 流式编程 实现 IMutableSorter 桶排序要求对数据有了解 Coding:链表的表示? Coding:增删改查? 相关复杂度? Coding:合并两个链表的操作? 用哨兵 翻转链表:三指针,递归 实现队列,栈? Coding:判断链表中是否有环? 快慢指针是否相等 Coding:和CAS Loop结合? HashTable的原理? 存取映射的数据结构 说一个Hash函数? 取余 Java的HashMap怎么实现的? 除了哈希表还有那些适合存Key/Value的数据结构? ConcurrentHashMap是怎么回事?什么时候用? 两次hash JavaObject的HashCode是如何计算的? ArrayList必须开辟连续的空间,查询快,增删慢 LinkedList 不需要开辟连续的空间,查询慢,增删快 存储一对数据(Key-Value)、无序、无下标、键不可重复、值可以重复 遍历方式: 遍历用keyset先获取key,再通过map.get()获取value 还可以使用entrySet() Set 通过for遍历出所有单个的entry,再entry.getkey()entry.getvalue() copy 复制 Collection的需要的空间要提前创建好 reserse翻转 shuffle 打乱 list.toArray() Array.asList() 基本状态: Java特有状态: 保存当前线程的状态(CPU寄存器的值) 排队很危险, 内核级线程由内核调度,用户级线程有应用自己调度。 jvm不调度,只维护 在之前是用户级线程,后来jdk1为内核级线程,共享内核级线程分配的主线程。 总结:Java采用用户-内核级线程映射(总体来说是内核级线程) 用AtomicInterger不会发生竞争条件 get set 都是原子的 但是自增调用Unsafe类的cas 解决了ABA问题 版本号int stemp 基于Monitor JVM的实现 基于AQS设计 轻量级锁用java抢占资源,重量级锁用操作系统抢占资源(开销更高)自旋锁很快,spinLock。 没有资源竞争偏向锁,发生竞争就升级为轻量级锁,轻量级锁拿不到持有者权限就要升级为重量级锁。 当设置偏向锁后,当资源没有持有者,可以跳过EntrySet直接参与竞争。 Monitor 提供wait,提供notify 所有的Object都有 AbstractQueuedSynchronizer提供await,signalAll。 区别于synchronized(build-in or intrinsic lock), Java提供的另一个实现同步的体系 半壁江山synchronized 另一半AQS 继承AQS重写tryAcquire和tryRelease 用cas() 线程调用时计算前上锁,退出后解锁 栈的作用:配合程序执行,函数调用用的,提供执行程序的必须内存空间,栈的数据结构像一个数组,因为是一个连续的数据结构,栈的内存分配是指针从高为到低位,从小地址向大地址增长 栈配合程序执行用的,配合程序方法调用,存储程序的临时数据 ,也可以存对象 每一个线程都有一个栈,因为线程是程序执行的最小单位。线程可以没有堆 堆的作用:用来存储数据,通过堆来存它不是一种数据结构,是一堆数据结构。堆通过引用来引用这些结构。从小地址到大地址增长,什么都有,像一个垃圾堆。 应用通过堆存储数据(申请,回收,托管) JIT编译器一边执行程序,一边编译。GC执行引擎不只是垃圾回收器还是内存分配工具,来管理堆 ClassLoader将.class文件导入JVM,形成运行时的类 类的信息,常量,编译后的代码 Heap MethodArea Stack NativeStack ProgramCounter NativeMemory 应用在不断的弄乱空间,GC就负责整理 Stop The World ,当GC忙不过来就要STW 吞吐量: 程序执行时间的占比 -XX: GCTimeRatio=99 99:1 1% 低延时 Latency : 指GC造成的停顿(STW)时间 Pause Time: 一次STW的时间 FootPrint :指最终应用对内存的需求 当内存满了需要:STW回收。 循环引用 静态的 并发就不行了 双色,如果在标记和清除的中间有改动,会导致回收多了,所以这种状况就需要重新标记之后才能清楚。这时C为黑色,D,E会被清除。 当有一个不确定是否能清除时,要先进行标记,标记到最后,而且没有变化时,就可以清除了。然后GC需要遍历JavaHeap中所有的对象清除没有被标记的对象。 新生代发生minorGC 有eden和 survivor from (s1) to (s2) 老年代发生fullGC(标记-清除),永久代(元空间)在方法区中。 在新生代中 eden到survivor发生复制和整理 G1:目标:大内存,兼顾:Latency、Throughput,替代:CMS(尚未、部分) ZGC:低延迟的GC 最大延迟时间几个ms 暂停时间不会随着堆大小、存活对象数目增加 内存范围8MB~16TB 太大容易挂 CMS(Concurrent Mark Sweep) :减少Pause Time,覆盖Serial/Parallel Collector的场景 ,需要减少 pause Time的场景 Parallel(并行):多线程, 提供最大的Throughput Serial(连续的):Single Thread ,也能提供最大的Throughput ParNew :多线程 ZGC G1 根加载器 BootStrapClassLoader 拓展加载器 ExtensionsClassLoader 程序加载器 ApplicationClassLoader 用户加载器 CustomClassLoader 有严格定义 mark word: hashcode age 标志位 LockRecordAddress MonitorAddress ForwardingAddress klassOop: 指向Object的元数据 padding:对齐, Hotspot:8字节对齐 类的生命周期大致分为以下阶段: load : create: live: destroy/gc: 肯定是8的倍数 64个字节(8bit)才能存的下,最少16字节 markword klassoop arraylength 重写ClassLoader.loadClass() usr unix system resouce var variable data file pwd 打印当前路径 more 打印一部分 less 打印一部分 有个界面 tail 尾部一行 head 头部一行 man 查看命令 cat 查看所有 tac 倒着查看所有 grep 加限定条件 find 查找 wc -l 查看文件数量 ip addr 可以查看IP地址 netstat 显示网络状态 host 查看dns curl 开发联调工具 ‘>’ 重定向 互联网协议群: 发送消息如何确认收到? 握手: 挥手: 传输形式,二进制的json redis是一个开源的(BSD许可),内存中的数据结构存储系统,它可以作用数据库、缓存和消息中间件MQ。它支持多种类型的数据结构,如字符串,散列,列表,集合,有序集合与查询范围,bitmaps,hyperloglogs 和 地理空间索引半径查询。Redis内置了复制,LUA脚本,LRU驱动事件,事务和不同级别的磁盘持久化,并通过Redis哨兵和自动区分提供高可用性。 move key db redis-server [–port 6379] ./redis-cli [-h 127.0.0.1 -p 6379] redis-cli shutdown || kill redis-pid 给Redis发送命令有两种方式: 1、redis-cli带参数运行,如: redis-cli shutdown 这样默认是发送到本地的6379端口。 2、redis-cli不带参数运行,如: 127.0.0.1:6379> shutdown 127.0.0.1:6379> ping PONG 语法:keys pattern(*) 语法:dbsize 语法:exists key [key …] 语法:del key [key …] 语法: type key 语法:move key db 秒语法:ttl key 秒语法:expire key seconds 语法:persist key 语法:rename key newkey 语法:set key value [EX seconds] [PX milliseconds] [NX|XX] 语法:get key 语法:append key “a” 如果字符串中的值是数字类型的,可以使用incr命令每次递增,不是数字类型则报错。 语法:incr key incrby key num 自动涨num个 语法:getrange key start end 语法:setrange key start “” setex key secound “a” setnx key " " (原子操作) mset k v k v k v getset key “value” 不是原子的 列表类型是一个有序的字段串列表,内部是使用双向链表实现,所有可以向两端操作元素,获取两端的数据速度快,通过索引到具体的行数比较慢。 左端存值语法:lpush key value [value …] 右端存值语法:rpush key value [value …] 索引存值语法:lset key index value 左端弹出语法:lpop key 右端弹出语法:rpop key 语法:llen key 两边获取语法:lrange key start stop 索引获取语法:lindex key index 根据值删除语法:lrem key count value 范围删除语法:ltrim key start stop rpoplpush list list linsert list before/after “value” “new value” 没有重复的值 语法:sadd key member [member …] 语法:spop key [count] 获取所有元素语法:smembers key 随机获取语法:srandmember key count 语法:sismember key member 语法:scard key 语法:srem key member [member …] 语法:smove set1 set2 value 语法:sdiff key1 key2 (差集) 语法:sinter key1 key2 (交集) 语法:sunion key1 key2 (并集) Map集合,key—map 单个语法:hset key field value 多个语法:hmset key field value [field value …] 不存在时语法:hsetnx key field value 单个语法:hget key field 多个语法:hmget key field [field …] 获取所有键与值语法:hgetall key 获取所有字段语法:hkeys key 获取所有值语法:hvals key 语法:hexists key field 语法:hlen key 语法:hincrby key field increment 语法:hdel key field [field …] 在set的基础上,增加了一个值, 语法:zadd key [NX|XX] [CH] [INCR] score member [score member …] 语法:zscore key member 语法:zrange key start stop [WITHSCORES] 语法:zrangebyscore key min max [WITHSCORES] [LIMIT offset count] 语法:zincrby key increment member 语法:zcard key 语法:zcount key min max 语法:zrem key member [member …] 语法:zrank key member Redis事务没有隔离级别的概念! 开启事务(multi) 命令入队( … ) 执行事务( exec ) 正常执行事务! 放弃事务用 discard 编译异常 所有的命令都不会被执行 有错误的命令依旧可以正常运行 悲观锁: 乐观锁: Mysql用version redis用Watch 主要作用: “工厂模式”(DI)和“代理模式”(AOP) Spring是一个免费的开源的框架; Spring是一个轻量级的,非入侵的框架; 控制反转(IOC), 面向切面(AOP); maven 导入jar Spring Boot (build anything) 构建一切 快速开发单个微服务 约定大于配置; Spring Cloud (coordinate anything) 协调一切 Spring Cloud 基于 Spring Boot实现; Spring Cloud Data connect Flow 连接; 控制反转IOC(Inversion of Control), 是一种设计思想,DI依赖注入 是通过实现IOC的一种方法, 也有人认为DI是IOC的另一种说法. 没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象转移给第三方,反转在于对象获取的方式。 采用xml方式配置Bean的时候,Bean的定义信息和实现是分离的,当采用注解方式是就可以把两者合到一起,Bean的定义信息直接以注释的形式定义到实现类中,从而达到了0配置的实现。 控制反转是一种通过描述(xml或注解)并通过第三方生产或获取特定对象的方式。 在Spring中控制反转实现的是IOC容器, 实现方法是依赖注入(Dependency Injection, DI) 类型赋值 参数名直接赋值 在配置文件加载时,容器中管理的对象就已经初始化了 将多个bean.xml 文件导入到一个总的ApplicattionContext.cml 内容不同根据所需类型自行创建 依赖注入:set 构造器注入 其他注入 c 命名 p命名 bean作用域 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gSwrC61y-1625028583220)(…/…/…/…/25659/AppData/Roaming/Typora/typora-user-images/image-20201107092043519.png)] 要使用约束 在spring中有三种装配方式 小结: @Resourse和@Autowired的区别: @Autowired通过bytype方式实现,而且对象必须要存在,否则空指针可以手动添加(@Nullable); @Resourse默认通过byname方式实现,如果找不到,通过bytype方式实现 ,当两种方式都不行时,会报错; @Resourse默认通过byname方式实现,@Autowired通过bytype方式实现! 在spring4 之后使用注解开发,必须保证aop的包被导入 scrop(“sigleton”)||scrop(“prototype”) xml与注解: xml与注解的最佳实践: xml用来管理bean; 注解只负责完成属性注入; 我们在使用的过程中,只需要注意让注解生效 单一职责原则 一个类只应该做一件事,职责要单一,一个类的职责过多,就会产生职责耦合,当一个职责发生变化的时候,其他的职责也会收到影响。 开闭原则 软件实体应该开发扩展,关闭修改,对拓展是开放的,对更改是关闭的。 我们应该面向接口/抽象编程,在增加这个系统需求功能的时候,应该先想着扩展一个抽象的子类,而不是直接修改以前的代码。 里氏替换原则 凡是父类能够使用到父类的地方,将其替换成子类,程序也能正常运行,结果不会发生改变。 依赖倒置原则 高层模块不应该依赖于底层模块,两个模块都应该依赖于抽象类,细节也依赖于抽象,针对接口/抽象编程,不需要对具体的细节实现。 接口隔离原则 使用多个专一的接口,而不使用统一的接口,应该将一个接口分成多个专一的接口,从而将职责划分出来,降低耦合。 合成/聚合复用原则 尽量使用合成/聚合的方式来复用代码,而不推荐使用继承来复用代码。这样可以避免由于类的继承带来臃肿的功能。 迪米特法则 尽量与朋友通信,不与陌生人通信。朋友与朋友之间要保持距离。 在类加载就创建一个静态的对象供应外部对象使用,重启系统才会使对象改变,所以本身就是线程安全。但是可以通过反射来破解构造方法,产生多个实例。 懒汉式属于延迟加载,当第一次使用时才会进行实例化,但是在多线程下会产生多个对象。 通过加synchronized来保证线程安全,但是每次访问都上锁,降低效率。 当为空时,进行synchronized上锁实例化,当对象不为空时,直接返回对象。 枚举实例本身就是static final类型的,所以就只能被实例化一次。枚举本身就是线程安全的。枚举也提供了序列化的机制,其本身能够阻止默认的反序列化。 不符合开闭原则,当这个类无法工作时,其他功能也会有影响,系统扩展困难,要添加新的功能就要修改工厂逻辑,在功能较多的时候,可能会导致逻辑太复杂,简单工厂使用的静态工厂方法,使得工厂角色无法实现继承的等级结构。 工厂类负责创建的功能较少,客户端只知道传入工厂的参数,对创建对象细节不关心。 对简单工厂抽象出一个抽象工厂,保留了简单工厂的优点,让扩展变得简单,让继承变得可行,增加了多态性 更符合开闭原则,新增一种产品,只需要增加相应的具体产品和相应的工厂,更符合单一职责原则,不适用静态工厂方法,可以形成基于继承的等级结构 对系统开销大一些,添加产品时,开发新产品对工厂类还是要修改,一个具体的工厂只能创建一种具体的产品 可以在类的内部对产品簇进行管理,满足开闭原则 增加新产品时,所有的工厂都要修改 无论是什么工厂模式,最终的目的就是为了解耦。只要解耦的目的达到了,就可以了 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合的关系,目标与观察者建立了触发机制,需要有一个通知方法 抽象策略,实现抽象策略,用一个上下文进行引用 类适配器:一个类继承一个方法调用一个接口,则可以使用父类方法,调用接口方法 对象适配器:通过构造注入方式 ,不通过继承,通过注入的方式,实现解耦 复用已经有的类,将目标类和适配者类解耦,解决目标和适配者接口不一致问题 InputStream=>Reader用InputStreamReader 动态的将功能加到对象上,比继承弹性更好。 在Java中装饰者应用于Java IO标准库 抽象类使用一个模板方法将若干个(抽象,具体,钩子)方法构成。 提供代码可用性,将相同的代码抽象到父类中,将不同的代码放入不同的子类。实现了反向控制用钩子方法,比较灵活,但是灵活的同时,由于子类影响了父类,违背了里氏替换原则,会给程序带来风险。这样对抽象类就要有更完善的设计。 引入抽象类,每一个不同的类要具体实现,都要导致类的个数增加,从而增加复杂度。 不破坏源代码,不影响原来的功能 当功能类复杂,代理类多时,整个类的结构就显得比较臃肿,难以维护 cglib动态代理和jdk动态代理(需要接口)事务的基本特性 ACID
ACID靠什么保证的
InnoDB redolog写盘,InnoDB事务进入prepare状态。如果prepare成功,binlog写盘,再继续将事务日志持久化到binlog,如果持久化成功,那么InnoDB事务则进入commit状态(就是在redolog里面写一个commit记录)
隔离性有四个隔离级别
MVCC
版本链
慢查询
mysql主从同步
MyISAM 和 InnoDB的区别
三个文件: 表结构.frm 存储引擎数据.MYD 存储引擎索引.MYI 需要回表
两个文件: 表结构.frm 存储引擎数据,索引 .ibd
插入缓冲(insert buffer),二次写(double write),
自适应哈希索引(ahi),预读(read ahead)索引类型对性能影响
数据结构与算法
插入排序:
for(int i = 1; i < A.length; i++) {
// 将A[i] 插入在卡片0,到卡片i之间
// j代表抓到的牌,先放到最右侧,不断交换到对应的位置
int c = A[i];
int j = i;
for(; j > 0 && A[j-1] > c;j--) {
A[j] = A[j-1];
}
A[j] = c;
选择排序:从后往前遍历可以保证稳定性
for(int i = A.length - 1; i >= 0; i--) {
// 0 - A[i]
int j = maxIndex(A, 0, i+1);
Helper.swap(A, i, j);
}
//不稳定 选择排序比插入排序写少
static private int maxIndex(int[] A, int i, int j) {
int max = Integer.MIN_VALUE;
int maxIndex = j-1;
for(int k = i; k < j; k++) {
if(max < A[k]) {
max = A[k];
maxIndex = k;
}
}
return maxIndex;
}
//稳定
static private int maxIndex(int[] A, int i, int j) {
int max = Integer.MIN_VALUE;
int maxIndex = j-1;
for(int k = j-1; k >= i; k--) {
if(max < A[k]) {
max = A[k];
maxIndex = k;
}
}
return maxIndex;
}
分治策略(Divide And Conquer):
public void sort(int[] A) {
mergeSort(A, 0, A.length);
}
private void mergeSort(int[] A, int l, int r) {
if(r - l <= 1) {
return;
}
int mid = (l+r+1)/2;
mergeSort(A, l, mid);
mergeSort(A, mid, r);
merge(A, l, mid, r);
}
private void merge(int[] A, int l, int mid, int r) {
int[] B = Arrays.copyOfRange(A, l, mid+1);
int[] C = Arrays.copyOfRange(A, mid, r+1);
B[B.length-1] = C[C.length - 1] = Integer.MAX_VALUE;
int i = 0, j = 0;
for(int k = l; k < r; k++) {
if(B[i] < C[j]) {
A[k] = B[i++];
} else {
A[k] = C[j++];
}
}
快速排序归纳:
public List<Integer> sort(List<Integer> A) {
return this.quickSort(A);
}
private List<Integer> quickSort(List<Integer> A) {
if(A.size() <= 1) {
return A;
}
// |---left---| x | ---right ---||
var x = A.get(0);
var left = A.stream().filter(a -> a < x)
.collect(toList());
var mid = A.stream().filter(a -> a == x)
.collect(toList());
var right = A.stream().filter(a -> a > x)
.collect(toList());
left = quickSort(left);
right = quickSort(right);
left.addAll(mid);
left.addAll(right);
return left;
}
public void sort(int[] A) {
this.quickSort(A, 0, A.length);
}
private void quickSort(int[] A, int l, int r) {
if(r - l <= 1) {
return;
}
// 选择最左边的元素构造子问题集合
// 小于x的放到左边,大于x的放到右边
// int x = A[l];
// i代表x的位置
int i = partition(A, l, r);
// 省略计算x所在位置i
// 以及将所有小于x的元素放到左边,大于x元素放到右边的
quickSort(A, l, i);
quickSort(A, i+1, r);
}
private int partition(int[] A, int l, int r) {
int x = A[l];
int i = l + 1;
int j = r;
while(i != j) {
if(A[i] < x) {
i++;
} else {
Helper.swap(A, i, --j);
}
}
Helper.swap(A, i-1, l);
return i-1;
}
static class Node<T> {
Node<T> next = null;
T data;
public Node(T data){
this.data = data;
}
}
Node<T> head = null;
// O(1) 头插法
public void insert(T data) {
var node = new Node<>(data);
node.next = head;
head = node;
}
// O(n) 查询用断言
public Node<T> find(Predicate<T> predicate) {
var p = head;
while(p != null) {
if(predicate.test(p.data)) {
return p;
}
p = p.next;
}
return null;
}
public Integer size(){
var p = head;
Integer c = 0;
while(p != null) { p = p.next; c++; }
return c;
}
// O(n)
public void remove(Node<T> node){
if(head == null) {
return;
}
if(head == node) {
head = head.next;
return;
}
var slow = head;
var fast = head.next;
while(fast != node && fast != null) {
slow = fast;
fast = fast.next;
}
if(fast != null) {
slow.next = fast.next;
// fast.data = null; //删除时可以考虑回收问题,释放资源,data比较大
}
}
集合框架
集合的概念
集合的概念
集合和数组的区别
集合的位置
Collection接口
List和Set接口都实现了Collection接口,Collection接口的体系如下:
Collection特点
Collection方法
返回值类型
方法作用及描述
boolean
add(E e)
确保此集合包含指定的元素(可选操作)。
boolean
addAll(Collection extends E> c)
将指定集合中的所有元素添加到此集合(可选操作)。
void
clear()
从此集合中删除所有元素(可选操作)。
boolean
contains(Object o)
如果此集合包含指定的元素,则返回 true
。
boolean
containsAll(Collection> c)
如果此集合包含指定 集合
中的所有元素,则返回true。
boolean
equals(Object o)
将指定的对象与此集合进行比较以获得相等性。
int
hashCode()
返回此集合的哈希码值。
boolean
isEmpty()
如果此集合不包含元素,则返回 true
。
Iterator
iterator()
返回此集合中的元素的迭代器。
default Stream
parallelStream()
返回可能并行的 Stream
与此集合作为其来源。
boolean
remove(Object o)
从该集合中删除指定元素的单个实例(如果存在)(可选操作)。
boolean
removeAll(Collection> c)
删除指定集合中包含的所有此集合的元素(可选操作)。
default boolean
removeIf(Predicate super E> filter)
删除满足给定谓词的此集合的所有元素。
boolean
retainAll(Collection> c)
仅保留此集合中包含在指定集合中的元素(可选操作)。
int
size()
返回此集合中的元素数。
default Spliterator
spliterator()
创建一个Spliterator
在这个集合中的元素。
default Stream
stream()
返回以此集合作为源的顺序 Stream
。
Object[]
toArray()
返回一个包含此集合中所有元素的数组。List接口与实现类
List特点
List方法
void
add(int index, Object o)
在第index位置插入
boolean
addAll(int index, Collection c)
将指定集合中的所有元素插入到此列表中的指定位置
Object
get(int index)
返回此列表中指定位置的元素。
List
subList(int fromIndex,int toIndex)
返回fromIndex
(含)和 toIndex
之间的集合元素
ListIterator
listIterator(int index)
从列表中的指定位置开始,返回列表中的元素(按正确顺序)的列表迭代器,既可以从前往后也可以从后往前。
boolean
remove(Object o)
删除指定元素的第一个出现,传index可以删除第几个。
E
set(int index, E element)
用指定的元素修改。ArrayList
+
容量右移一位)1.5Vector
LinkedList
LinkedList 和 ArrayList区别
Set接口与实现类
Set接口特点
Set接口方法
HashSet
TreeSet
Map接口与实现类
Map
Set
视图。
V
put(K key, V value)
将指定的值与该映射中的指定键相关联
V
get(Object key)
返回到指定键所映射的值,或 null
如果此映射包含该键的映射。
Set
entrySet()
返回此地图中包含的映射的Set
视图。
Collection
values()
返回此地图中包含的值的Collection
视图。
Set
keySet()
返回此地图中包含的键的Set
视图。HashMap
TreeMap
HashTable
properties
collection常用工具方法
多线程
进程线程
进程和线程的区别?
为什么要有线程?
为什么要减少线程切换?
进程开销为什么比线程大?
JAVA线程有哪些状态?如何转换?
重新执行调度程序(操作系统),重新选择其他线程执行ThreadLocal
Java线程是内核级线程还是用户级线程?
多线程和原子操作
竞争条件是怎么产生的?解决竞争条件有哪些方法?
原子操作是如何实现的?
Java的CAS是如何实现的?
CAS的ABA问题是怎么回事?
AtomicInterger类是如何实现的?**
AtomicStampedReference解决什么问题?内部如何实现?
同步器
什么是同步?
简述下Java有哪些同步器?
synchronized是如何实现的?
ReentrantLock和synchronized的区别?
为什么需要AbstractQueuedSynchronizer?
偏向锁、轻量级锁、重量级锁是怎么回事?
Java实现生产者消费者问题
// Producer 生产者
public void readDb() throws InterruptedException {
lock.lock();
if (queue.size() == MAX) {
full.await();
return;
}
var data = readData(); // 1s
if(queue.size() == 1) {
emtpy.signalAll();
}
queue.add(data);
lock.unlock();
}
// Comsumer 消费者
public void calculate() throws InterruptedException {
lock.lock();
if (queue.size() == 0) {
emtpy.await();
return;
}
var data = queue.remove();
System.out.println("queue-size:" + queue.size());
if(queue.size() == MAX - 1) {
full.signalAll();
}
data *= 100;
lock.unlock();
}
AQS(AbstractQueuedSynchronizer)
AbstractQueuedSynchronizer可以用来做什么?
简述AbstractQueuedSynchronizer如何工作?
手写程序:如何用AbstractQueuedSynchronizer实现Mutex(互斥量)?
public class Mutex extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
return compareAndSetState(0,1);
}
@Override
protected boolean tryRelease(int arg) {
return compareAndSetState(1,0);
}
public static int i = 0;
public static void main(String[] argv) throws InterruptedException {
var mutex = new Mutex();
Thread t1 = new Thread(()->{
for (int j = 0; j < 1000; j++) {
mutex.acquire(0);
i ++;
mutex.release(0);
}
});t1.start();
Thread t2 = new Thread(()->{
for (int j = 0; j < 1000; j++) {
mutex.acquire(0);
i ++;
mutex.release(0);
}
});t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
AQS如何实现公平性?
CAS在AQS中的作用是什么?
AQS内部的CHL算法工作原理是什么?
JVM
栈和堆的区别:
JVM的内存布局:
执行引擎执行bytecode和处理GC
执行引擎调用本地库接口,执行本地方法
本地方法执行时使用Native Stack(可能形成Native Memory)JVM中Object都有哪些数据:
JVM的运行时数据都有哪些:
为什么要GC?
什么是STW?
如何提高吞吐量(Throughput)?
-XX: GCTimeRatio=19 19:1 5%
高吞吐量、低延迟和低FootPrint,可以兼得吗? 谈谈你的理解?
引用计数法有什么问题?
为什么需要3色标记而不是双色?
3色标记算法如何处理变化(mutation)?
Java堆分成哪些部分
新生代、存活代、老生代、永久代(元空间)是什么?如何工作的?
什么时候发生复制和整理?
G1/CMS/Serial/Parallel/ParNew/ZGC的区别?
如果对延迟要求高可以考虑哪种GC?
内存空间很大的情况(TB)级别推荐哪种GC?
类加载器有哪些
说一下Object在内存中的格式?
不同虚拟机可能会不同
分成头部和数据,可能还有Padding
说一下初始化一个类的过程?
类加载 disk -> memory ClassLoader
static initializers 初始化 static fields {}
触发原因:new/访问静态成员/loader加载等
加载完毕 loaded
allocate memory 分配内存
constructor 构造器
created 创建完毕就
in use Root Set可以找到(且被使用)
invisible 泄露,Root Set可以找到,但是没有使用
unreachable Root Set不可达(会被回收)
collected 要被收集
finalize 终节资源
deallocated 回收完空的Object多大?
Class Header里都有什么?
什么是双亲委派模型?
说说双亲委派的过程?
只有一个ClassLoader为什么不够用?
如何打破双亲委派模型?
Linux
TCP/IP协议
Socket、IO模型、NIO
Socket的工作原理
I/O多路复用
BIO/NIO/AIO
N(new)IO
NoSQL四大分类
键值对存储:
文档型数据库 (Bson)
列存储数据库
图形数据库
redis简介:
redis常用操作
移动键
启动Redis
连接Redis
停止Redis
发送命令
测试连通性
获取所有键
获取键总数
查询键是否存在
删除键
查询键类型
移动键
查询key的生命周期(秒)
毫秒语法:pttl key
-1:永远不过期。设置过期时间
毫秒语法:pexpire key milliseconds设置永不过期
更改键名称
redis基本数据类型:
String (字符串)
存放键值
获取键值
增长字符串
值递增/递减
语法:decr key decrby key num 自动减num个获取部分字符
替换部分字符
设置过期时间
不存在再设置(分布式锁会用到)
批量操作
组合操作
List(列表)
列表类型的元素是有序且可以重复的。存储值
弹出元素
获取元素个数
获取列表元素
删除元素
移除最后一个元素,添加到头部
插入值
集合
存储值
弹出元素
获取元素
判断集合是否存在元素
获取集合元素个数
删除集合元素
移动集合元素
不同集合元素
相同集合元素
公共集合元素
Hash (哈希)
redis字符串类型键和值是字典结构形式,这里的散列类型其值也可以是字典结构。存放键值
获取字段值
判断字段是否存在
获取字段数量
递增/减
删除字段
有序集合(zset)
存储值
获取元素分数
获取排序范围排名
获取指定分数范围排序
增加指定元素分数
获取集合元素个数
获取指定范围分数个数
删除指定元素
获取元素排名
geospatial 地理位置
Hyperloglog 计数
Bitmaps 位存储
reids的事务:
命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!Exec
Redis单条命令式保存原子性的,但是事务不保证原子性!
multi
set k1 v1
set k1 v1
get k2
set k3 v3
exec
>ok
>ok
>v2
>ok
redis加锁
redis主从复制
Spring
面试题
IOC AOP
1. Spring
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.0version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.0version>
dependency>
延申
以前的方法:
现在的方法:
private UserDao userDao;
//利用set方法进行动态实现值的注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
优点:
IOC本质
3. IOC的创建方式
3.1. 使用无参构造创建对象,默认!
3.2.假设我们要使用有参构造创建对象
<bean id="user" class="com.kuang.pojo.User">
<constructor-arg index="0" value="zhaodi000">constructor-arg>
bean>
<bean id="user" class="com.kuang.pojo.User">
<constructor-arg type="java.lang.String" value="zhaodi2222">constructor-arg>
bean>
<bean id="user" class="com.kuang.pojo.User">
<constructor-arg name="name" value="zhaodi33333">constructor-arg>
bean>
4. 配置文件
4.1.别名
<alias name="以前的名字" alias="别名">alias>
4.2.Bean的配置
<bean id="类名" class="路径" name="别名 可以是多种符号分隔">
<property name="要注入的属性" value="传入的数据">property>
bean>
4.3. import
<import resource="bean2.xml">import>
<import resource="bean3.xml">import>
<import resource="beans.xml">import>
5. DI依赖注入
5.1 构造器注入
5.2 set注入
<bean id="accountService" class="com.something.DefaultAccountService"/>
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
6. Bean的自动装配
<context:annotation-config/>
6.1 byName自动装配
<bean id="people" class="com.kuang.pojo.People" autowire="byName">
<property name="name" value="赵迪">property>
bean>
6.2 byType
<bean id="cat1" class="com.kuang.pojo.Cat">bean>
<bean class="com.kuang.pojo.Dog">bean>
<bean id="people" class="com.kuang.pojo.People" autowire="byType">
<property name="name" value="赵迪">property>
bean>
6.3使用注解自动装配
7. 使用注解开发
bean
属性注入
//等价于
衍生注解
自动装配域
@Autowired: 通过自动装配类型-名字
Autowired不能唯一自动装配需要,@Qualifiler(value=”xxx“)去配置;
@Nullable: 使用这个注解可以让字段可以为null
@Resourse:通过自动装配名字-类型
作用域
小结
<context:annotation-config/>
<context:component-scan base-package="com.kuang"/>
8. aop
设计模式原则总结
单例模式:
饿汉式:
懒汉式:
双检锁:
枚举法:
单例模式三大特点:
工厂模式:
简单工厂:
模式缺点:
适用场景:
工厂方法:
优点:
缺点:
抽象工厂:
优点:
缺点:
总结:
观察者模式:
优点:
测略模式:
适配器模式:
优点:
装饰模式:
模板方法模式:
优点:
缺点:
静态代理:
优点:
缺点:
动态代理: