Java中抽象类和接口类
在抽象类中的方法不一定是抽象方法,含有抽象方法的类必须定义成抽象类。
什么时候使用抽象类和接口
如果你拥有一些方法并且想让它们中的一些有默认实现,那么使用抽象类吧。
如果你想实现多重继承,那么你必须使用接口。由于Java不支持多继承,子类不能够继承多个类,但可以实现多个接口。因此你就可以使用接口来解决它。
如果基本功能在不断改变,那么就需要使用抽象类。如果不断改变基本功能并且使用接口,那么就需要改变所有实现了该接口的类。
抽象类的定义
public abstract class Base{
abstract void hello();
}
Method invoke()的使用
invoke方法用来反射其他类的方法.比如将方法当作参数传入函数中
如何使用
1. 需要传入的类.class.getMethod(方法名,和方法参数);
如果方法参数是数组类型 如int[],则写int[].class
PS:
getDeclaredMethod*获取的是类自身声明的所有方法,包含public、protected和private方法
getMethod*获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。
2.
method.invoke()有两个参数,如果你需要调用的函数非静态的,则为他创建一个实例作为第一个参数,如过是静态函数则为null. 第二参数为被调用函数的参数,注意需要Object封装 如new Object[]{arr}.
Java实现线程的两种方式
1.继承Thread,重写run()方法
2.实现Runable接口,再通过Thread构造函数实例化
线程安全
线程安全概念:当多个线程访问某一个类(对象或方法)时,这个类始终能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。这里的加锁机制常见的如:synchronized
synchronized:可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”。
什么是java序列化,如何实现java序列化
将对象以流的方式保存在磁盘中,实现序列化接口 序列化:把Java对象转换为字节序列的方法。 反序列化:把字节序列恢复到Java对象的过程。
工厂模式生产者、消费者模式 单例模式
请论述vector和ArrayList的区别Vector:
arrayList是非线程安全,vector是线程安全
当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。
#{}和${}的区别是什么?
#{}是预编译处理,${}是字符串替换。
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理${}时,就是把${}替换成变量的值。
使用#{}可以有效的防止SQL注入,提高系统安全性。
Mybatis是如何进行分页的?分页插件的原理是什么?
Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
启动nginx ./sbin/nginx
停止nginx ./sbin/nginx -s stop ./sbin/nginx -s quit
正向代理反向代理
https://www.cnblogs.com/Anker/p/6056540.html
nginx功能
作为http server(代替apache,对PHP需要FastCGI处理器支持)
反向代理服务器
实现负载均衡
虚拟主机
FastCGI:Nginx本身不支持PHP等语言,但是它可以通过FastCGI来将请求扔给某些语言或框架处理
nginx和apache的区别
轻量级,同样起web 服务,比apache 占用更少的内存及资源
抗并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能
高度模块化的设计,编写模块相对简单
最核心的区别在于apache是同步多进程模型,一个连接对应一个进程;nginx是异步的,多个连接(万级别)可以对应一个进程
HTTP和HTTPS区别
https://www.cnblogs.com/wqhwe/p/5407468.html
sleep() 和 wait() 有什么差别?
1、每个对象都有一个锁来控制同步访问,Synchronized关键字可以和对象的锁交互,来实现同步方法或同步块。sleep()方法正在执行的线程主动让出CPU(然后CPU就可以去执行其他任务),在sleep指定时间后CPU再回到该线程继续往下执行(注意:sleep方法只让出了CPU,而并不会释放同步资源锁!!!);wait()方法则是指当前线程让自己暂时退让出同步资源锁,以便其他正在等待该资源的线程得到该资源进而运行,只有调用了notify()方法,之前调用wait()的线程才会解除wait状态,可以去参与竞争同步资源锁,进而得到执行。(注意:notify的作用相当于叫醒睡着的人,而并不会给他分配任务,就是说notify只是让之前调用wait的线程有权利重新参与线程的调度);
2、sleep()方法可以在任何地方使用;wait()方法则只能在同步方法或同步块中使用;
3、sleep()是线程线程类(Thread)的方法,调用会暂停此线程指定的时间,但监控依然保持,不会释放对象锁,到时间自动恢复;wait()是Object的方法,调用会放弃对象锁,进入等待队列,待调用notify()/notifyAll()唤醒指定的线程或者所有线程,才会进入锁池,不再次获得对象锁才会进入运行状态;
正则表达式
SQL
distinct 唯一字段
between a and b
avg()平均数
round(a,b)保留几位小数
group by having condition(过滤条件)
COUNT() 函数返回匹配指定条件的行数
sum()总数
跨域
跨域的原因:浏览器的同源策略是浏览器上为安全性考虑实施的非常重要的安全策略(同源:协议,域名,端口三者均一致)防止恶意的网站窃取数据
调用方解决: - 1.隐藏跨域 - 反向代理 - apache服务器或nginx服务器配置
2.CORS全称是"跨域资源共享"(Cross-origin resource sharing),CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing).它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
http://www.ruanyifeng.com/blog/2016/04/cors.html
跨域又分为简单请求,非简单请求,携带cookie的情况。
简单请求只用设置addAllowedOrigin为*可解决,
自定义请求头需要设置addAllowedHeader为自定义请求头,
携带cookie后端需要setAllowCredentials同时addAllowedOrigin不能为*。前端需要withCredentials为true
保持用户在线
1:客户端输入用户名和密码,提交到服务端验证
2:服务端验证成功后,给客户端返回以下值:
uid : 用户的唯一标示
time : 当前unix时间戳
key : MD5(uid+time+"一个只有你自己知道的字符串密钥")
3:客户端保存以上3个值在本地,每次HTTP请求时,将以上3个值发送到服务端
4:服务端验证key,判断如果与客户端发送的key一致,则说明用户身份无误
5:服务端每次收到请求时,通过当前时间-客户端time字段得到的差值,可以控制这个key的有效期
二。类似第三方登录的方式
面向对象
https://www.oschina.net/translate/how-i-explained-ood-to-my-wife?lang=chs&p=1
同步异步阻塞非阻塞
https://blog.csdn.net/qq_32725491/article/details/78679414
Set和Map遍历
Set set =new HashSet();
Iterator it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
Set entryseSet=map.entrySet();
for(Map.Entry entry:entryseSet) {
System.out.println("第二种:"+entry.getKey()+":"+entry.getValue());
}
IO流
https://blog.csdn.net/chengyuqiang/article/details/79183748
单点登录
http://www.cnblogs.com/ywlaker/p/6113927.html
线程
虚拟主机,就是把一台物理服务器划分成多个“虚拟”的服务器,每一个虚拟主机都可以有独立的域名和独立的目录
结束线程的三种方式
https://blog.csdn.net/xu__cg/article/details/52831127
使用interrupt()方法后相当于打一个结束的标记,在线程run方法需要些 if(isInterrupted())的逻辑
控制反转指的是依赖反转,比如从前都是关注自己开发让后者依赖自己,现在例如spring要按照spring的标准开发,依赖于Spring才能被Spring管理,这就是控制反转
如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同
Docker镜像和容器的关系
容器可以看作为镜像的一个实例,像类和对象一样
new String()和new String(“”)都是申明一个新的空字符串,是空串不是null
Lambda表达式 stream方法是线程安全的速度慢 可以考虑使用parallelStream()方法,多线程速度快,线程不安全。原理像大数据MapReduce,将任务拆成多个小任务并行运行再结合在一起
JDK1.8HashMap思路,通过key计算哈希值当作数组下标,产生冲突则在该下标创建单链表,由于单链表查询速度慢,当冲突数量过多时会转化为红黑树提高查询速率
HashMap容量为2的倍数原因:1.提高运算速率 2.提高散列度 降低冲突 3.减少内存碎片
HashMap扩容条件 数据大于原容量3/4
常见状态码
200 请求成功
301 重定向新页面
401 权限禁止访问
404 找不到页面
500 服务器错误
502 Bad GateWay网关错误
504 GateWay TimeOut 网关无法找到服务器
如何防止SQL注入
1.正则表达式判断
2.使用PreparedStatement预编译语句
Redis常见数据类型和使用场景
String:存储一些字符串数据,json化的对象或者base64编码后的照片
hash:是一种string类型的field和value的映射表(【hmset key field1 value1 field2 value2 ...】),适用场景在于如维护用户信息中仅仅修改某个用户的一个属性上而不影响其他属性
List:有序可重复的列表,可以适用于一些如消息队列的场景中
Set:无序不可重复的集合,可以适用于去重的场景
SortSet:有序不可重复的集合,可以用于排行类等类似场景
InnoDB的行级锁的实现方式
InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!
B+Tree子节点最少两个 适合范围查询 Hash索引在数据量少的情况可能比B+树快,但只适合= in这类的查询 不适合范围查询 因为查询方式是通过哈希值计算查找对应的key
SQL调优思路
1.开启慢日志查找慢SQL
2.explain select 查找sql使用的索引
3.添加索引
最左匹配原则
mysql会一直向右匹配知道遇到范围查询(> < between like)就停止匹配 比如a=3 and b=3 and c>5 and d=6 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
=和in可以乱序,比如a=1 and b=2 and c=3建立(a,b,c)索引可以任意排序,mysql的查询优化器会帮你优化成索引可以识别的形式
MyIsam引擎在查询的时候如果另一个session在修改数据,则查询的过程中会自动添加一个表锁,待查询完毕后才会执行修改
MyIsam引擎支持表级锁不支持行级 InnoDB都支持
加入读锁(共享锁)后不允许修改数据,叫共享锁的原因是两个或多个session都加入读锁的情况下都能正常查询数据不会阻塞
session1加入写锁(排他锁) session2读的情况下会阻塞 session1加入写锁 session2写锁也会阻塞所以叫排他锁
数据库隔离级别
最低:读未提交(Read Uncommitted)
第一个事务修改了数据还没提交,第二个事务就能读到第一个事务修改后的数据,然后第一个事务回滚了 就会产生脏读
读提交(Read Committed)
第一个事务修改了数据还没提交,第二个事务读的是第一个事务数据修改前的数据,避免了脏读
事务一开启事务后查询数据未提交 事务二修改了数据并提交了 事务一再查询数据发现和第一次不一样,就这就不可重复度
可重复读(Repeated Read)
事务一和事务二开启事务 事务一查询数据 事务二修改数据并提交 事务一再查询数据与第一次一致 避免了不可重复读 事务一提交后查询才是最新数据 如果事务一修改了数据再提交,他也是按照事务二提交后的数据进行修改的 例: 原数据1500 事务1查1500 事务2 1500+100 提交 事务1查1500 事务1 -200提交 事务一查1400
事务一查询出来三条数据 事务二添加一条数据 事务一修改所有行的数据发现修改了四条 这就是幻读
串行化(Serializable)
解决幻读
Golang并发三种模式
1.通过channel 无缓冲的通道指的是通道的大小为0,也就是说,这种类型的通道在接收前没有能力保存任何值,它要求发送 goroutine 和接收 goroutine 同时准备好,才可以完成发送和接收操作
2.通过WaitGroup 主要有add done wait方法 wait只有待done到0的时候才会解除阻塞
3.在Go 1.7 以后引进的强大的Context上下文,实现并发控制
如果Where条件全部命中 则不会用Gap锁 只会加行锁
如果where条件部分命中或者不会命中 则会加Gap锁 Gap锁就是会锁没被命中范围内的行
比如查询id in (5,7,9)结果为5 9,则5-9范围内都会被锁住 gap就是索引树中插入新记录的空隙
Gap锁会用在非唯一索引或者不走索引的当前读中
通过next-key(行锁+gap锁)锁可以在可重复读的模式下防止幻读发生
where过滤行 having过滤组
SETNX key value 如果key不存在则创建 由于是原子性操作 所以可以用在分布式锁 不支持expire参数 所以要额外使用expire命令设置过期时间 后面用set代替了 set key value expire nx
redis中使用rpush指令和lpop指令可以简单实现一个异步消息队列 blpop 队列名 时间 代表在这个时间内监听队列有无消息
redis subscribe命令订阅一个主题(不需要创建) publish命令向主题推送数据 所有监听的客户端都能接收到数据 缺点是发送消息是无状态的 无法保证可达性
RDB备份的两种命令 save使用主线程创建快照 会造成阻塞 BGSAVE命令会Fork出一个子进程来创建RDB文件 不阻塞服务器进程
RDB+AOF混合模式 先以RDB载入数据 再用AOP追加数据 就不会导致数据丢失
主从模式可以让主节点进行写操作 子节点进行读操作 通过主节点创建RDB发送给子节点,在这过程中将写操作通过aof存起来,代子节点完成rdb文件载入后再接受aof文件实现数据同步
Linux对文件内容做统计 awk [options] 'cmd' file
sed 批量替换文本内容
HTTP1.1多了个场链接 HTTP2是多路复用
HTTPS过程
1.客户端先发送随机值1和客户端支持的加密算法
2.服务端收到请求后回应随机值2和确定的加密算法
3.服务端再回应一个数字证书,数字证书包括证书的颁发机构,过期时间,服务端公钥和CA的签名等
4.客户端收到证书后进行解析,解析的工作是交给TLS(SSL的升级版)去做的,解析没有问题则生成一个随机值当做预主秘钥
5.客户端通过随机值1 2 和预主秘钥组装成会话秘钥,再通过服务端的公钥加密会话秘钥。再将会话秘钥发送给服务端
6.服务端通过秘钥解密后得到随机值1 2和预主秘钥再组成会话秘钥,和客户端的会话秘钥相同
7.客户端通过会话秘钥加密一条消息发送给服务端,主要验证服务端是否正常接受客户端加密的消息。
8.同样服务端也会通过会话秘钥加密一条消息回传给客户端,如果客户端能够正常接受的话表明SSL层连接建立完成了
sync.Once能确保实例化对象Do方法在多线程环境只运行一次,内部通过互斥锁实现
slice进行切片后使用的底层数组是同一个,但如果切片后的数组扩容了则会将切片的数组拷贝一个新的进行扩容
Golang切片
切片的底层还是原数组,所以修改切片的值会影响原数组。如果切片进行扩容,则会创建一个新的切片(通常是两倍 如果原长度超过1024则是1.25倍) append指向新的切片不影响原数组和切片
切片添加元素如果没有超过原来切片的容量,则会替换覆盖原有的元素
Golang List(双向链表)
var l list.List声明的链表l可以直接使用(开箱即用)
原因是延迟初始化,就是试用的时候才初始化
1.对于Front方法和Back方法,一旦发现链表的长度为0, 直接返回nil就好了
2.对于删除元素、移动元素,以及一些用于插入元素的方法中,只要判断一下传入的元素中指向所属链表的指针,是否与当前链表的指针相等就可以了
通过上面两种机制不用每次都判断该链表是否被初始化,弥补了缺陷
Channel
channel关闭过后还发送数据会异常,不过读数据不会 所以关闭channel通常由发送方关闭
如果channel没被close 然后通过for range读取channel数据则会一直等待新数据。反之close就不会,会直接退出for
倘若通道channel的值为nil,那么这条for语句就会被永远地阻塞在有for关键字的那一行
如果select有默认分支 其他select有阻塞则会执行默认分支,如果没有默认分支则select会阻塞直到匹配到一个case
channel关闭后 i,v<-channel 如果channel没数据了则返回false
Golang函数
函数参数或者返回带有函数则称为高级函数
Golang结构体
func (cat *Cat)SetName(namestring) {
cat.name = name //会改变实际值
}
func (cat Cat)SetNameOfCopy(namestring) {
cat.name = name //不会改变实际值
}
Golang接口
接口被赋值后 会有一种特定的结构进行存储,包含两个指针分别为动态类型(被赋值的类型)和动态值(被赋值的值)
Golang不可寻址
常量的值。
基本类型值的字面量。
算术操作的结果值。
对各种字面量的索引表达式和切片表达式的结果值。不过有一个例外,对切片字面量的索引结果值却是可寻址的。对字符串变量的索引表达式和切片表达式的结果值。
对字典变量的索引表达式的结果值。
函数字面量和方法字面量,以及对它们的调用表达式的结果值。
结构体字面量的字段值,也就是对结构体字面量的选择表达式的结果值。
类型转换表达式的结果值。
类型断言表达式的结果值。
接收表达式的结果值。
总结下来是 不可变的 临时的 不安全的都不可寻址
Golang panic recover defer
panic()函数主动引发一个 panic 括号内参数为interfeact{}
recover函数可以恢复panic 返回值则是panic的参数
犹豫panic一旦执行就会停止代码继续往下运行 所以要通过defer来给recover运行的机会
例:
defer func() {
if i :=recover();i!=nil{
fmt.Println(i)
}
fmt.Println("Exit function main.")
}()
panic(errors.New("aaa"))
Golang测试
对于功能测试函数来说,其名称必须以Test为前缀,并且参数列表中只应有一个*testing.T类型的参数声明。
对于性能测试函数来说,其名称必须以Benchmark为前缀,并且唯一参数的类型必须是*testing.B类型的。
对于示例测试函数来说,其名称必须以Example为前缀,但对函数的参数列表没有强制规定。
测试的实例如果成功了则会存起来作为缓存,所以第二次运行不会显示耗时,如果代码修改了则会重新运行
测试的实例失败了则不会作缓存
不用担心缓存会影响测试的准确性
t.Fatal方法作用是在打印失败错误日志之后立即终止当前测试函数的执行并宣告测试失败。更具体地说,这相当于它们在最后都调用了t.FailNow方法。
Golang锁
互斥锁(Mutex) 读锁 (RWMutex) 读锁 Lock是写锁 RLock是读锁
读锁适用于读多写少的情况
sync.NewCond条件变量
参数是 Lock的实现
wait方法必须配合锁使用 调用wait方法会对当前goroutine的锁进行解锁然后挂起 直到调用Signal方法或Broadcast方法唤醒才重新上锁
Signal不需要也最好不要配合锁使用 在锁外调用
条件变量的Signal方法和Broadcast方法有哪些异同?
条件变量的Signal方法和Broadcast方法都是被用来发送通知的,不同的是,前者的通知只会唤醒一个因此而等待的 goroutine,而后者的通知却会唤醒所有为此等待的 goroutine。
条件变量的Wait方法总会把当前的 goroutine 添加到通知队列的队尾,而它的Signal方法总会从通知队列的队首开始,查找可被唤醒的 goroutine。所以,因Signal方法的通知,而被唤醒的 goroutine 一般都是最早等待的那一个。这两个方法的行为决定了它们的适用场景。如果你确定只有一个 goroutine 在等待通知,或者只需唤醒任意一个 goroutine 就可以满足要求,那么使用条件变量的Signal方法就好了。否则,使用Broadcast方法总没错,只要你设置好各个 goroutine 所期望的共享资源状态就可以了。
Golang Context
context.Background() 返回根节点
创建子节点的方法:
funcWithCancel(parent Context)(ctxContext, cancelCancelFunc)
funcWithDeadline(parent Context, deadline time.Time)(Context,CancelFunc)
funcWithTimeout(parent Context, timeout time.Duration)(Context,CancelFunc)
funcWithValue(parent Context, key interface{}, val interface{})Context
<-ctx.Done()类似与wait 只有执行了cancel方法才会解除阻塞
cancel方法类似于通知所有go已经完成了
ctx :=context.WithValue(context.Background(),"1","1")
newCtx:=context.WithValue(ctx, "2", "2")
value := newCtx.Value("1")
value2 := newCtx.Value("2")
fmt.Println(value)
fmt.Println(value2)
结果是 1 2 如果newCtx改为ctx结果则为 1 nil 自下向上找 如果key相同则覆盖
Golang sync.Pool
自我理解 使用通常先用Get方法再用Put方法 Get就是从临时对象池中查找有没有值 没有则New一个出来 然后再调用Put方法存进去 下次再Get的时候就不是重新创建新的对象了
Go 语言运行时系统中的垃圾回收器,所以在每次开始执行之前,都会对所有已创建的临时对象池中的值进行全面地清除。
Golang sync.Map
由于这个map不支持泛型 所以通过自己的方法去规定传入类型 比如创建一个结构体为
type ConcurrentMap struct {
m sync.Map
keyType reflect.Type
valueType reflect.Type
}
然后通过包装原本map方法对类型做判断
比如:
func (cMap *ConcurrentMap) Load(key interface{}) (value interface{}, ok bool) {
if reflect.TypeOf(key) != cMap.keyType {
return
}
return cMap.m.Load(key)
}
sync.map适合读多写少的场景,在几个写操作当中,新增键值对的操作对并发安全字典的性能影响是最大的,其次是删除操作,最后才是修改操作。
sync.map的实现由两个map完成,第一个是read(原子操作) 第二个是dirty(带锁操作) read类型是sync/atomic.Value类型所以不会用到锁而是原子操作 read只能对键值改变不能增减数量,dirty即可变成键值又可修改数量,dirty的数据会比read全,在查询read不到结果一定次数的情况下dirty会转为read 然后将dirty置nil
sync.Map在查找指定的键所对应的值的时候,总会先去只读字典中寻找,并不需要锁定互斥锁。只有当确定“只读字典中没有,但脏字典中可能会有这个键”的时候,它才会在锁的保护下去访问脏字典。
相对应的,sync.Map在存储键值对的时候,只要只读字典中已存有这个键,并且该键值对未被标记为“已删除”,就会把新值存到里面并直接返回,这种情况下也不需要用到锁。否则,它才会在锁的保护下把键值对存储到脏字典中。这个时候,该键值对的“已删除”标记会被抹去。