From 4e1d9b3f1f7974d8234ea9e10578a898d6056320 Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 11:13:59 +0800 Subject: [PATCH 01/19] Delete Dubbo.md --- Docs/Dubbo.md | 271 -------------------------------------------------- 1 file changed, 271 deletions(-) delete mode 100644 Docs/Dubbo.md diff --git a/Docs/Dubbo.md b/Docs/Dubbo.md deleted file mode 100644 index f258442..0000000 --- a/Docs/Dubbo.md +++ /dev/null @@ -1,271 +0,0 @@ -## Dubbo - -#### 1.什么是Dubbo? - -Dubbo是基于Java的高性能轻量级的RPC分布式服务框架,现已成为 Apache 基金会孵化项目。 - -官网:http://dubbo.apache.org/en-us/ - -#### 2.为什么要使用Dubbo? - -背景: - -随着互联网的快速发展,Web应用程序的规模不断扩大,最后我们发现传统的垂直体系结构(整体式)已无法解决。分布式服务体系结构和流计算体系结构势在必行,迫切需要一个治理系统来确保体系结构的有序发展。 - -- 开源免费 -- 一些核心业务被提取并作为独立的服务提供服务,逐渐形成一个稳定的服务中心,这样前端应用程序就可以更好地响应变化多端的市场需求 -- 分布式框架能承受更大规模的流量 -- 内部基于netty性能高 - -#### 3.Dubbo提供了哪3个关键功能? - -基于接口的远程调用 - -容错和负载均衡 - -自动服务注册和发现 - -#### 4.你知道哪些机构在用Dubbo吗? - -![image-20200423105332840](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423105332840.png) - -#### 5.Dubbo服务的关键节点有哪些? - -![image-20200423105925624](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423105925624.png) - -#### 6.说一下Dubbo服务注册流程? - -1. 服务容器负责启动,加载,运行服务提供者。 -2. 服务提供者在启动时,向注册中心注册自己提供的服务。 -3. 服务消费者在启动时,向注册中心订阅自己所需的服务。 -4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。 -5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。 -6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。 - -#### 7.能画一下服务注册流程图吗? - -![image-20200423110344448](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423110344448.png) - -#### 8.Dubbo架构的特点? - -连通性、健壮性、伸缩性、以及向未来架构的升级性。 - -#### 9.对jdk的最小版本需求? - -jdk1.6+ - -#### 10.注册中心的选择? - -一般来说选中Zookeeper更稳定更合适。 - -除了Zookeeper还有Redis注册中心、Multicast注册中心、Simple注册中心。 - -#### 11.Dubbo的核心配置?用途? - -![image-20200423111538534](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423111538534.png) - -#### 12.配置优先级规则? - -![image-20200423112118958](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423112118958.png) - -优先级从高到低: - -- JVM -D参数,当你部署或者启动应用时,它可以轻易地重写配置,比如,改变dubbo协议端口; -- XML, XML中的当前配置会重写dubbo.properties中的; -- Properties,默认配置,仅仅作用于以上两者没有配置时。 - -#### 13.如何用代码方式绕过注册中心点对点直连? - -```java -… - -ReferenceConfig reference = new ReferenceConfig(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏 -// 如果点对点直连,可以用reference.setUrl()指定目标地址,设置url后将绕过注册中心, -// 其中,协议对应provider.setProtocol()的值,端口对应provider.setPort()的值, -// 路径对应service.setPath()的值,如果未设置path,缺省path为接口名 -reference.setUrl("dubbo://10.20.130.230:20880/com.xxx.XxxService"); - -… -``` - -#### 14.Dubbo配置来源有几种?分别是? - -4种 - -- JVM System Properties,-D参数 -- Externalized Configuration,外部化配置 -- ServiceConfig、ReferenceConfig等编程接口采集的配置 -- 本地配置文件dubbo.properties - -#### 15.如何禁用某个服务的启动检查? - -```xml - -``` - -#### 16.Dubbo 负载均衡策略?默认是? - -- 随机负载平衡(默认) - -- RoundRobin负载平衡 - -- 最小活动负载平衡 - -- 一致的哈希负载平衡 - -#### 17.上线兼容老版本? - -多版本号(version) - -#### 18.开发测试环境,想绕过注册中心如何配置? - -- xml - -```xml - - -``` - -- -D - - ```powershell - java -Dcom.alibaba.xxx.XxxService=dubbo://localhost:20890 - ``` - -- .properties - - ```properties - java -Ddubbo.resolve.file=xxx.properties - ``` - -```properties -com.alibaba.xxx.XxxService=dubbo://localhost:20890 -``` - -#### 19.集群容错几种方法? - -![image-20200423121735540](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423121735540.png) - -#### 20.Dubbo有几种配置方式? - -1. Spring -2. Java API - -#### 21.Dubbo有哪些协议?推荐? - -- dubbo://(推荐) -- rmi:// -- hessian:// -- http:// -- webservice:// -- thrift:// -- memcached:// -- redis:// -- rest:// - -#### 22.Dubbo使用什么通信框架? - -dubbo使用netty。 - -#### 23.dubbo协议默认端口号?http协议默认端口?hessian?rmi? - -- dubbo:20880 -- http:80 -- hessian:80 -- rmi:80 - -#### 24.Dubbo默认序列化框架?其他的你还知道? - -- dubbo协议缺省为hessian2 -- rmi协议缺省为java -- http协议缺省为json - -#### 25.一个服务有多重实现时,如何处理? - -可以用group分组,服务提供方和消费放都指定同一个group。 - -#### 26.Dubbo服务调用默认是阻塞的?还有其他的? - -默认是同步等待结果阻塞的,同时也支持异步调用。 - -Dubbo 是基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小,异步调用会返回一个 Future 对象。 - -#### 27.Dubbo服务追踪解决方案? - -- Zipkin -- Pinpoint -- SkyWalking - -#### 28.Dubbo不维护了吗?Dubbo和Dubbox有什么区别? - -现在进入了Apache,由apache维护。 - -Dubbox是当当的扩展项目。 - -#### 29.Dubbox有什么新功能? - -- 支持REST风格远程调用(HTTP + JSON/XML) - -- 支持基于Kryo和FST的Java高效序列化实现 - -- 支持基于嵌入式Tomcat的HTTP remoting体系 -- 升级Spring -- 升级ZooKeeper客户端 - -#### 30.io线程池大小默认? - -cpu个数 + 1 - -#### 31.dubbo://协议适合什么样的服务调用? - -采用单一长链接和NIO异步通讯,适用于小数量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。 - -不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。 - -![image-20200423154308365](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423154308365.png) - -#### 32.自动剔除服务什么原理? - -zookeeper临时节点,会话保持原理。 - -#### 33.从 `2.0.5` 版本开始,dubbo支持通过x命令来进行服务治理? - -telnet - -#### 34.如何用命令查看服务列表? - -```powershell -telnet localhost 20880 -``` - -进入命令行。然后执行 ls相关命令: - -- `ls`: 显示服务列表 -- `ls -l`: 显示服务详细信息列表 -- `ls XxxService`: 显示服务的方法列表 -- `ls -l XxxService`: 显示服务的方法详细信息列表 - -#### 35.Dubbo框架设计是怎样的? - -![image-20200423162348971](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423162348971.png) - -各层说明: - -- **config 配置层**:对外配置接口,以 `ServiceConfig`, `ReferenceConfig` 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类 -- **proxy 服务代理层**:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 `ServiceProxy` 为中心,扩展接口为 `ProxyFactory` -- **registry 注册中心层**:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 `RegistryFactory`, `Registry`, `RegistryService` -- **cluster 路由层**:封装多个提供者的路由及负载均衡,并桥接注册中心,以 `Invoker` 为中心,扩展接口为 `Cluster`, `Directory`, `Router`, `LoadBalance` -- **monitor 监控层**:RPC 调用次数和调用时间监控,以 `Statistics` 为中心,扩展接口为 `MonitorFactory`, `Monitor`, `MonitorService` -- **protocol 远程调用层**:封装 RPC 调用,以 `Invocation`, `Result` 为中心,扩展接口为 `Protocol`, `Invoker`, `Exporter` -- **exchange 信息交换层**:封装请求响应模式,同步转异步,以 `Request`, `Response` 为中心,扩展接口为 `Exchanger`, `ExchangeChannel`, `ExchangeClient`, `ExchangeServer` -- **transport 网络传输层**:抽象 mina 和 netty 为统一接口,以 `Message` 为中心,扩展接口为 `Channel`, `Transporter`, `Client`, `Server`, `Codec` -- **serialize 数据序列化层**:可复用的一些工具,扩展接口为 `Serialization`, `ObjectInput`, `ObjectOutput`, `ThreadPool` - -#### 36.你读过Dubbo的源码吗? - -这个问题其实面试中如果问dubbo的话,基本就会带这个问题。有时间的话,大家可以下载源码,读一读,如果大家有兴趣的话,我会出后续文章。 - -参考:http://dubbo.apache.org/en-us/ - -![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) - From 0a9c40f248c8a3c844b4604a06852dea373dcca8 Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 11:14:09 +0800 Subject: [PATCH 02/19] =?UTF-8?q?Delete=20=E5=BC=82=E5=B8=B8&=E5=8F=8D?= =?UTF-8?q?=E5=B0=84.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...2\345\270\270&\345\217\215\345\260\204.md" | 109 ------------------ 1 file changed, 109 deletions(-) delete mode 100644 "Docs/ \345\274\202\345\270\270&\345\217\215\345\260\204.md" diff --git "a/Docs/ \345\274\202\345\270\270&\345\217\215\345\260\204.md" "b/Docs/ \345\274\202\345\270\270&\345\217\215\345\260\204.md" deleted file mode 100644 index fbc8555..0000000 --- "a/Docs/ \345\274\202\345\270\270&\345\217\215\345\260\204.md" +++ /dev/null @@ -1,109 +0,0 @@ -## 异常&反射 - -![image-20200414175020396](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414175020396.png) - -#### 1.**error和exception有什么区别**? - -error表示系统级的错误,是java运行环境内部错误或者硬件问题,不能指望程序来处理这样的问题,除了退出运行外别无选择,它是Java虚拟机抛出的。 - -exception 表示程序需要捕捉、需要处理的异常,是由与程序设计的不完善而出现的问题,程序必须处理的问题。 - -#### 2.说出5个常见的**RuntimeException**? - -(1)Java.lang.NullPointerException 空指针异常;出现原因:调用了未经初始化的对象或者是不存在的对象。 - -(2)Java.lang.NumberFormatException 字符串转换为数字异常;出现原因:字符型数据中包含非数字型字符。 - -(3)Java.lang.IndexOutOfBoundsException 数组角标越界异常,常见于操作数组对象时发生。 - -(4)Java.lang.IllegalArgumentException 方法传递参数错误。 - -(5)Java.lang.ClassCastException 数据类型转换异常。 - -#### 3.**throw和throws的区别**? - -throw: - -  (1)throw 语句用在方法体内,表示抛出异常,由方法体内的语句处理。 - -  (2)throw 是具体向外抛出异常的动作,所以它抛出的是一个异常实例,执行 throw 一定是抛出了某种异常。 - -throws: - -​ (1)@throws 语句是用在方法声明后面,表示如果抛出异常,由该方法的调用者来进行异常的处理。 - -​ (2)throws 主要是声明这个方法会抛出某种类型的异常,让它的使用者要知道需要捕获的异常的类型。 - -​ (3)throws 表示出现异常的一种可能性,并不一定会发生这种异常。 - -#### 4.Java中异常分类 - -按照异常处理时机: - -编译时异常(受控异常(CheckedException))和运行时异常(非受控异常(UnCheckedException)) - -#### 5.如何自定义异常 - -继承Exception是检查性异常,继承RuntimeException是非检查性异常,一般要复写两个构造方法,用throw抛出新异常 - -如果同时有很多异常抛出,那可能就是异常链,就是一个异常引发另一个异常,另一个异常引发更多异常,一般我们会找它的原始异常来解决问题,一般会在开头或结尾,异常可通过initCause串起来,可以通过自定义异常 - -#### 6.Java中异常处理 - -首先处理异常主要有两种方式:一种try catch,一种是throws。 - -1. try catch: - -- try{} 中放入可能发生异常的代码。catch{}中放入对捕获到异常之后的处理。 - -2.throw throws: - -- throw是语句抛出异常,出现于函数内部,用来抛出一个具体异常实例,throw被执行后面的语句不起作用,直接转入异常处理阶段。 -- throws是函数方法抛出异常,一般写在方法的头部,抛出异常,给方法的调用者进行解决。 - -#### 7.什么是Java反射机制? - -Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。 - -#### 8.举例什么地方用到反射机制? - -1. JDBC中,利用反射动态加载了数据库驱动程序。 -2. Web服务器中利用反射调用了Sevlet的服务方法。 -3. Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。 -4. 很多框架都用到反射机制,注入属性,调用方法,如Spring。 - -#### 9.java反射机制的作用 - -- 在运行时判定任意一个对象所属的类 -- 在运行时构造任意一个类的对象; -- 在运行时判定任意一个类所具有的成员变量和方法; -- 在运行时调用任意一个对象的方法; -- 生成动态代理; - -#### 10.Java反射机制类 - -``` -java.lang.Class; //类 -java.lang.reflect.Constructor;//构造方法 -java.lang.reflect.Field; //类的成员变量 -java.lang.reflect.Method;//类的方法 -java.lang.reflect.Modifier;//访问权限 -``` - -#### 11.反射机制优缺点? - -优点:运行期类型的判断,动态加载类,提高代码灵活度。 - -缺点:性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。 - -#### 12.利用反射创建对象? - -1.通过一个全限类名创建一个对象 Class.forName(“全限类名”); 例如:com.mysql.jdbc.Driver Driver类已经被加载到 jvm中,并且完成了类的初始化工作就行了 类名.class; 获取Class<?> clz 对象 对象.getClass(); 2.获取构造器对象,通过构造器new出一个对象 Clazz.getConstructor([String.class]); Con.newInstance([参数]); 3.通过class对象创建一个实例对象(就相当与new类名()无参构造器) Cls.newInstance(); - -参考: - -- https://blog.csdn.net/qq_37875585/article/details/89340495 - -- https://www.cnblogs.com/whoislcj/p/6038511.html - - ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file From fbe78717bbe74e167e345786318f54b5077021da Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:35:04 +0800 Subject: [PATCH 03/19] Delete IO&NIO.md --- Docs/IO&NIO.md | 175 ------------------------------------------------- 1 file changed, 175 deletions(-) delete mode 100644 Docs/IO&NIO.md diff --git a/Docs/IO&NIO.md b/Docs/IO&NIO.md deleted file mode 100644 index ef262b9..0000000 --- a/Docs/IO&NIO.md +++ /dev/null @@ -1,175 +0,0 @@ -## IO&NIO - - - -![image-20200414192748596](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414192748596.png) - -#### 1.**什么是IO流?** - -它是一种数据的流从源头流到目的地。比如文件拷贝,输入流和输出流都包括了。输入流从文件中读取数据存储到进程(process)中,输出流从进程中读取数据然后写入到目标文件。 - -#### 2.java中有几种类型的流? - -按照单位大小:字符流、字节流。按照流的方向:输出流、输入流。 - -#### 3.字节流和字符流哪个好?怎么选择? - -1. 缓大多数情况下使用字节流会更好,因为字节流是字符流的包装,而大多数时候 IO 操作都是直接操作磁盘文件,所以这些流在传输时都是以字节的方式进行的(图片等都是按字节存储的) -2. 如果对于操作需要通过 IO 在内存中频繁处理字符串的情况使用字符流会好些,因为字符流具备缓冲区,提高了性能 - -#### 4.读取数据量大的文件时,速度会很慢,如何选择流? - -字节流时,选择BufferedInputStream和BufferedOutputStream。 -字符流时,选择BufferedReader 和 BufferedWriter - -#### 5. IO模型有几种? - -阻塞IO、非阻塞IO、多路复用IO、信号驱动IO以及异步IO。 - -#### 6.阻塞IO**(blocking IO)** - - 应用程序调用一个IO函数,导致应用程序阻塞,如果数据已经准备好,从内核拷贝到用户空间,否则一直等待下去。一个典型的读操作流程大致如下图,当用户进程调用recvfrom这个系统调用时,kernel就开始了IO的第一个阶段:准备数据,就是数据被拷贝到内核缓冲区中的一个过程(很多网络IO数据不会那么快到达,如没收一个完整的UDP包),等数据到操作系统内核缓冲区了,就到了第二阶段:将数据从内核缓冲区拷贝到用户内存,然后kernel返回结果,用户进程才会解除block状态,重新运行起来。**blocking IO的特点就是在IO执行的两个阶段用户进程都会block住;** - -![image-20200416135005801](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416135005801.png) - -#### 7.**非阻塞I/O(nonblocking IO)** - - 非阻塞I/O模型,我们把一个套接口设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,**会大量的占用CPU的时间**。 - -​ 当用户进程发出read操作时,如果kernel中数据还没准备好,那么并不会block用户进程,而是立即返回error,用户进程判断结果是error,就知道数据还没准备好,用户可以再次发read,直到kernel中数据准备好,并且用户再一次发read操作,产生system call,那么kernel 马上将数据拷贝到用户内存,然后返回;所以**nonblocking IO的特点是用户进程需要不断的主动询问kernel数据好了没有。** - -  阻塞IO一个线程只能处理一个IO流事件,要想同时处理多个IO流事件要么多线程要么多进程,这样做效率显然不会高,而非阻塞IO可以一个线程处理多个流事件,只要不停地询所有流事件即可,当然这个方式也不好,当大多数流没数据时,也是会大量浪费CPU资源;为了避免CPU空转,引进代理(select和poll,两种方式相差不大),代理可以观察多个流I/O事件,空闲时会把当前线程阻塞掉,当有一个或多个I/O事件时,就从阻塞态醒过来,把所有IO流都轮询一遍,于是没有IO事件我们的程序就阻塞在select方法处,即便这样依然存在问题,我们从select出只是知道有IO事件发生,却不知道是哪几个流,还是只能轮询所有流,**epoll**这样的代理就可以把哪个流发生怎样的IO事件通知我们;  - -![image-20200416135208788](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416135208788.png) - -#### 8.**I/O多路复用模型(IO multiplexing)** - - I/O多路复用就在于单个进程可以同时处理多个网络连接IO,基本原理就是select,poll,epoll这些个函数会不断轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程,这三个functon会阻塞进程,但和IO阻塞不同,这些函数可以同时阻塞多个IO操作,而且可以同时对多个读操作,写操作IO进行检验,直到有数据到达,才真正调用IO操作函数,调用过程如下图;**所以IO多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中任意一个进入就绪状态,select函数就可以返回。** - -  IO多路复用的优势在于并发数比较高的IO操作情况,可以同时处理多个连接,和bloking IO一样socket是被阻塞的,只不过在多路复用中socket是被select阻塞,而在阻塞IO中是被socket IO给阻塞。 - -![image-20200416135308008](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416135308008.png) - -#### 9.信号驱动I/O模型 - -可以用信号,让内核在描述符就绪时发送SIGIO信号通知我们,通过sigaction系统调用安装一个信号处理函数。该系统调用将立即返回,我们的进程继续工作,也就是说它没有被阻塞。当数据报准备好读取时,内核就为该进程产生一个SIGIO信号。我们随后既可以在信号处理函数中调用recvfrom读取数据报,并通知主循环数据已经准备好待处理。**特点:等待数据报到达期间进程不被阻塞。主循环可以继续执行,只要等待来自信号处理函数的通知:既可以是数据已准备好被处理,也可以是数据报已准备好被读取** - -![image-20200416140944940](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416140944940.png) - -#### 10.异步 I/O(asynchronous IO) - -异步IO告知内核启动某个操作,并让内核在整个操作(包括将内核数据复制到我们自己的缓冲区)完成后通知我们,调用aio_read(Posix异步I/O函数以aio_或lio_开头)函数,给内核传递描述字、缓冲区指针、缓冲区大小(与read相同的3个参数)、文件偏移以及通知的方式,然后系统立即返回。我们的进程不阻塞于等待I/0操作的完成。当内核将数据拷贝到缓冲区后,再通知应用程序。 - -用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了 - -![image-20200416141152742](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416141152742.png) - -#### 11.NIO与IO的区别? - - NIO即New IO,这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多。在Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO。 - -![image-20200416143304767](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416143304767.png) - -#### 12.NIO和IO适用场景 - - NIO是为弥补传统IO的不足而诞生的,但是尺有所短寸有所长,**NIO也有缺点,因为NIO是面向缓冲区的操作,每一次的数据处理都是对缓冲区进行的,那么就会有一个问题,在数据处理之前必须要判断缓冲区的数据是否完整或者已经读取完毕,如果没有,假设数据只读取了一部分,那么对不完整的数据处理没有任何意义。**所以每次数据处理之前都要检测缓冲区数据。 -  那么NIO和IO各适用的场景是什么呢? -  如果需要管理同时打开的成千上万个连接,这些**连接每次只是发送少量的数据**,例如聊天服务器,这时候用NIO处理数据可能是个很好的选择。 -  而如果只**有少量的连接**,而这些连接每次要发送大量的数据,这时候传统的IO更合适。使用哪种处理数据,需要在数据的响应等待时间和检查缓冲区数据的时间上作比较来权衡选择。 - -#### 13.NIO核心组件 - -channel、buffer、selector - -#### 14.什么是channel - -一个Channel(通道)代表和某一实体的连接,这个实体可以是文件、网络套接字等。也就是说,通道是Java NIO提供的一座桥梁,用于我们的程序和操作系统底层I/O服务进行交互。 - -通道是一种很基本很抽象的描述,和不同的I/O服务交互,执行不同的I/O操作,实现不一样,因此具体的有FileChannel、SocketChannel等。 - -通道使用起来跟Stream比较像,可以读取数据到Buffer中,也可以把Buffer中的数据写入通道。 - -![image-20200416150623938](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416150623938.png) - -当然,也有区别,主要体现在如下两点: - -- 一个通道,既可以读又可以写,而一个Stream是单向的(所以分 InputStream 和 OutputStream) -- 通道有非阻塞I/O模式 - -#### 15.Java NIO中最常用的通道实现? - -- FileChannel:读写文件 -- DatagramChannel: UDP协议网络通信 -- SocketChannel:TCP协议网络通信 -- ServerSocketChannel:监听TCP连接 - -#### 16.Buffer是什么? - -NIO中所使用的缓冲区不是一个简单的byte数组,而是封装过的Buffer类,通过它提供的API,我们可以灵活的操纵数据。 - -与Java基本类型相对应,NIO提供了多种 Buffer 类型,如ByteBuffer、CharBuffer、IntBuffer等,区别就是读写缓冲区时的单位长度不一样(以对应类型的变量为单位进行读写)。 - -#### 17.核心Buffer实现有哪些? - -核心的buffer实现有这些:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer,涵盖了所有的基本数据类型(4类8种,除了Boolean)。也有其他的buffer如MappedByteBuffer。 - -#### 18.buffer读写数据基本操作 - -1)、将数据写入buffer -2)、调用buffer.flip() -3)、将数据从buffer中读取出来 -4)、调用buffer.clear()或者buffer.compact() - -在写buffer的时候,buffer会跟踪写入了多少数据,需要读buffer的时候,需要调用flip()来将buffer从写模式切换成读模式,读模式中只能读取写入的数据,而非整个buffer。 -  当数据都读完了,你需要清空buffer以供下次使用,可以有2种方法来操作:调用clear() 或者 调用compact()。 -  区别:clear方法清空整个buffer,compact方法只清除你已经读取的数据,未读取的数据会被移到buffer的开头,此时写入数据会从当前数据的末尾开始。 - -```java -// 创建一个容量为48的ByteBuffer -ByteBuffer buf = ByteBuffer.allocate(48); -// 从channel中读(取数据然后写)入buffer -int bytesRead = inChannel.read(buf); -// 下面是读取buffer -while (bytesRead != -1) { - buf.flip(); // 转换buffer为读模式 - System.out.print((char) buf.get()); // 一次读取一个byte - buf.clear(); //清空buffer准备下一次写入 -} - -``` - -#### 19.Selector是什么? - -Selector(选择器)是一个特殊的组件,用于采集各个通道的状态(或者说事件)。我们先将通道注册到选择器,并设置好关心的事件,然后就可以通过调用select()方法,静静地等待事件发生。 - -#### 20.通道可以监听那几个事件? - -通道有如下4个事件可供我们监听: - -- Accept:有可以接受的连接 -- Connect:连接成功 -- Read:有数据可读 -- Write:可以写入数据了 - -#### 21.为什么要用Selector? - -如果用阻塞I/O,需要多线程(浪费内存),如果用非阻塞I/O,需要不断重试(耗费CPU)。Selector的出现解决了这尴尬的问题,非阻塞模式下,通过Selector,我们的线程只为已就绪的通道工作,不用盲目的重试了。比如,当所有通道都没有数据到达时,也就没有Read事件发生,我们的线程会在select()方法处被挂起,从而让出了CPU资源。 - -#### 22.Selector处理多Channel图文说明 - -![image-20200416161430793](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416161430793.png) - - - -要使用一个Selector,你要先注册这个Selector的Channels。然后你调用Selector的select()方法。这个方法会阻塞,直到它注册的Channels当中有一个准备好了的事件发生了。当select()方法返回的时候,线程可以处理这些事件,如新的连接的到来,数据收到了等。 - -参考: - -- https://www.cnblogs.com/sharing-java/p/10791802.html - -- https://blog.csdn.net/zengxiantao1994/article/details/88094910 - -- https://www.cnblogs.com/xueSpring/p/9513266.html - - -![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) From 534ec49ad55441d94ccba6ae836ee1a0aabd19cd Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:36:25 +0800 Subject: [PATCH 04/19] Delete JVM.md --- Docs/JVM.md | 395 ---------------------------------------------------- 1 file changed, 395 deletions(-) delete mode 100644 Docs/JVM.md diff --git a/Docs/JVM.md b/Docs/JVM.md deleted file mode 100644 index 5aee119..0000000 --- a/Docs/JVM.md +++ /dev/null @@ -1,395 +0,0 @@ -## JVM - - - -#### 1.JDK、JRE、JVM关系? - -Jdk (Java Development Kit) : java语言的软件开发包。包括Java运行时环境Jre。 - -Jre (Java Runtime Environment) :Java运行时环境,包括Jvm。 - -Jvm (Java Virtual Machine) : - -- 一种用于计算机设备的规范。 -- Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。 - -Jdk包括Jre,Jre包括jvm。 - -#### 2.启动程序如何查看加载了哪些类,以及加载顺序? - -java -XX:+TraceClassLoading 具体类 - -Java -verbose 具体类 - -#### 3. class字节码文件10个主要组成部分? - -- MagicNumber -- Version -- Constant_pool -- Access_flag -- This_class -- Super_class -- Interfaces -- Fields -- Methods -- Attributes - -#### 4.画一下jvm内存结构图? - -![image-20200424132830267](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200424132830267.png) - -#### 5.程序计数器 - -属于线程私有内存。占用一块非常小的空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的指令的字节码,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器来完成。 - -#### 6.Java虚拟机栈 - -属于线程私有内存。它的生命周期与线程相同,虚拟机栈描述的是Java方法执行内存模型;每个方法被执行的时候都会同时创建一个栈桢用于存储局部变量表、操作栈、动态链接、方法出口信息等。每一个方法被调用直至执行完成的过程,就对应着一个栈帧再虚拟机中从入栈到出栈的过程。 - -#### 7.本地方法栈 - -本地方法栈与虚拟机栈所发挥的作用是非常相似的,只不过虚拟机栈对虚拟机执行Java方法服务,而本地栈是为虚拟机使用到Native方法服务。 - -#### 8.Java堆 - -是Java虚拟机所管理的内存中最大的一块。Java堆事被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。 - -Tips:但随着JIT编译器的发展与逃逸分析技术的逐渐成熟,栈上分配、标亮替换优化技术将会导师一些微妙的变化发生,所有的对象都分配在堆上就不那么绝对了。 - -#### 9.方法区 - -是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 - -#### 10.运行时常量池? - -是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息,还有一项是常量池(Constant PoolTable)用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放道方法区的运行时常量池中。 - -#### 11.什么时候抛出StackOverflowError? - -如果线程请求的栈深度大于虚拟机所允许的深度,则抛出StackOverflowError。 - -#### 12.Java7和Java8在内存模型上有什么区别? - -Java8取消了永久代,用元空间(Metaspace)代替了,元空间是存在本地内存(Native memory)中。 - -#### 13.程序员最关注的两个内存区域? - -堆(Heap)和栈(Stack),一般大家会把Java内存分为堆内存和栈内存,这是一种比较粗糙的划分方式,但实际上Java内存区域是很复杂的。 - -#### 14.直接内存是什么? - -直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但这部分内存也频繁被实用,也有OutOfMemoryError异常的出现的可能。 - -Tips:JDK1.4中加入了NIO(new input/output)类,引入了一种基于通道(Channe)与缓冲区(Buffer)的I/O方式,也可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer的对象作为这块内存的引用进行操作。 - -#### 15.除了哪个区域外,虚拟机内存其他运行时区域都会发生OutOfMemoryError? - -程序计数器。 - -#### 16.什么情况下会出现堆内存溢出? - -堆内存存储对象实例。我们只要不断地创建对象。并保证gc roots到对象之间有可达路径来避免垃圾回收机制清除这些对象。就会在对象数量到达最大。堆容量限制后,产生内存溢出异常。 - -#### 17.如何实现一个堆内存溢出? - -```java -public class Cat { - - public static void main(String[] args) { - List list = new ArrayList(); - while (true) { - list.add(new Cat()); - } - } - -} -``` - - - -#### 18.空间什么情况下会抛出OutOfMemoryError? - -如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError。 - -#### 19.如何实现StrackOverflowError? - -```java - public static void main(String[] args) { - eat(); - } - - public static void eat () { - eat(); - } -``` - -#### 20.如何设置直接内存容量? - -通过 -XX:MaxDirectMemorySize指定,如果不指定,则默认与Java堆的最大值一样。 - -#### 21.Java堆内存组成? - -![image-20200424170643045](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200424170643045.png) - -堆大小 = 新生代 + 老年代。如果是Java8则没有Permanent Generation。 - -其中新生代(Young) 被分为 Eden和S0(from)和S1(to)。 - -#### 22.Edem : from : to默认比例是? - -Edem : from : to = 8 : 1 : 1 - -此比例可以通过 –XX:SurvivorRatio 来设定 - -#### 23.垃圾标记阶段? - -在GC执行垃圾回收之前,为了区分对象存活与否,当对象被标记为死亡时,GC才回执行垃圾回收,这个过程就是垃圾标记阶段。 - -#### 24.引用计数法? - -比如对象a,只要任何一个对象引用了a,则a的引用计数器就加1,当引用失效时,引用计数器就减1,当计数器为0时,就可以对其回收。 - -但是无法解决循环引用的问题。 - -#### 25.根搜索算法? - -跟搜索算法是以跟为起始点,按照从上到下的方式搜索被根对象集合所连接的目标对象是否可达(使用根搜索算法后,内存中 的存活对象都会被根对象集合直接或间接连接着),如果目标对象不可达,就意味着该对象己经死亡,便可以在 instanceOopDesc的 Mark World 中将其标记为垃圾对象。 - -在根搜索算法中, 只有能够被根对象集合直接或者间接连接的对象才是存活对象。 - -#### 26.JVM中三种常见的垃圾收集算法? - -标记-清除算法(Mark_Sweep) - -复制算法(Copying) - -标记-压缩算法(Mark-Compact) - -#### 27.标记-清除算法? - -首先标记出所有需要回收的对象,在标记完成后统一回收掉所有的被标记对象。 - -![image-20200424181634238](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200424181634238.png) - -缺点: - -- 标记和清除的效率都不高。 -- 空间问题,清除后产生大量不连续的内存随便。如果有大对象会出现空间不够的现象从而不得不提前触发另一次垃圾收集动作。 - -#### 28.复制算法? - - 他将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块内存用完了,就将还存活的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。 - -![image-20200424183117930](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200424183117930.png) - -优点: - -解决了内存碎片问题。 - -缺点: - -将原来的内存缩小为原来的一半,存活对象越多效率越低。 - -#### 29.标记-整理算法? - -先标记出要被回收的对象,然后让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。解决了复制算法和标记清理算法的问题。 - -![image-20200424184054165](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200424184054165.png) - -#### 30.分代收集算法? - -当前商业虚拟机的垃圾收集都采用“分代手机算法”,其实就根据对象存活周期的不同将内存划分为几块,一般是新老年代。根据各个年代的特点采用最适当的收集算法。 - -#### 31.垃圾收集器? - -如果说垃圾收集算法是方法论,那么垃圾收集器就是具体实现。连线代表可以搭配使用。 - -![image-20200424191253710](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200424191253710.png) - - - -#### 32.Stop The World? - -进行垃圾收集时,必须暂停其他所有工作线程,Sun将这种事情叫做"Stop The World"。 - -#### 33.Serial收集器? - -单线程收集器,单线程的含义在于它会 stop the world。垃圾回收时需要stop the world ,直到它收集结束。所以这种收集器体验比较差。 - -#### 34.PartNew收集器? - -Serial收集器的多线程版本,除了使用采用并行收回的方式回收内存外,其他行为几乎和Serial没区别。 - -可以通过选项“-XX:+UseParNewGC”手动指定使用 ParNew收集器执行内存回收任务。 - -#### 36.Parallel Scavenge? - -是一个新生代收集器,也是复制算法的收集器,同时也是多线程并行收集器,与PartNew 不同是,它重点关注的是程序达到一个可控制的吞吐量(Thoughput,CPU 用于运行用户代码 的时间/CPU 总消耗时间,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)), 高吞吐量可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务,主要适用于在后台运算而不需要太多交互的任务。 - -他可以通过2个参数精确的控制吞吐量,更高效的利用cpu。 - -分别是: -XX:MaxCcPauseMillis 和 -XX:GCTimeRatio - -#### 37.Parallel Old收集器? - -Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法。JDK 1.6中才开始提供。 - -#### 38.CMS 收集器? - -Concurrent Mar Sweep 收集器是一种以获取最短回收停顿时间为目标的收集器。重视服务的响应速度,希望系统停顿时间最短。采用标记-清除的算法来进行垃圾回收。 - -#### 39.CMS垃圾回收的步骤? - -1. 初始标记 (stop the world) - -2. 并发标记 - -3. 重新标记 (stop the world) - -4. 并发清除 - - 初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快。 - - 并发标记就是进行Gc Roots Tracing的过程。 - - 重新标记则是为了修正并发标记期间,因用户程序继续运行而导致的标记产生变动的那一部分对象的标记记录,这个阶段停顿时间一般比初始标记时间长,但是远比并发标记时间短。 - - 整个过程中并发标记时间最长,但此时可以和用户线程一起工作。 - -#### 41.CMS收集器优点?缺点? - -优点: - -并发收集、低停顿 - -缺点: - -- 对cpu资源非常敏感。 -- 无法处理浮动垃圾。 -- 内存碎片问题。 - -#### 42.G1收集器? - -Garbage First 收集器是当前收集器技术发展的最前沿成果。jdk 1.6_update14中提供了 g1收集器。 - - G1收集器是基于标记-整理算法的收集器,它避免了内存碎片的问题。 - -可以非常精确控制停顿时间,既能让使用者明确指定一个长度为 M毫秒的时间片段内,消耗在垃圾收集上的时间不多超过N毫秒,这几乎已经是实时java(rtsj)的垃圾收集器特征了。 - -#### 42. G1收集器是如何改进收集方式的? - -极力避免全区域垃圾收集,之前的收集器进行收集的范围都是整个新生代或者老年代。而g1将整个Java堆(包括新生代、老年代)划分为多个大小固定的独立区域,并且跟踪这些区域垃圾堆积程度,维护一个优先级李彪,每次根据允许的收集时间,优先回收垃圾最多的区域。从而获得更高的效率。 - -#### 43.虚拟机进程状况工具? - -jps (Jvm process status tool ),他的功能与ps类似。 - -可以列出正在运行的虚拟机进程,并显示执行主类(Main Class,main()函数所在的类)的名称,以及浙西进程的本地虚拟机的唯一ID。 - -语法 : jps [options] [hostid] - --q 主输出lvmid,省略主类的名称 - --m 输出虚拟机进程启动时传递给主类main()函数的参数 - --l 输出主类全名,如果进程执行是Jar包,输出Jar路径 - --v 输出虚拟机进程启动时JVM参数 - -#### 44.虚拟机统计信息工具? - -jstat(JVM Statistics Montoring Tool)是用于监视虚拟机各种运行状态信息命令行工机具。他可以显示本地或远程虚拟机进程中的类装载、内存、垃圾收集、jit编译等运行数据。 - -jstat [option vmid [interval[s|ms] [count]] ] - -interval 查询间隔 - -count 查询次数 - -如果不用这两个参数,就默认查询一次。 - -option代表用户希望查询的虚拟机信息,主要分3类: - -- 类装载 -- 垃圾收集 -- 运行期编译状况 - -#### 45.jstat 工具主要选项? - --class 监视类装载、卸载数量、总空间及类装载锁消耗的时间 - --gc 监视Java堆状况,包括Eden区,2个survivor区、老年代 - --gccapacity 监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用的最大和最小空间 - --gcutil 监视内容与-gc基本相同,主要关注已经使用空间站空间百分比 - --gccause 与-gcutil 功能一样,但是会额外输出导致上一次GC产生的原因 - --gcnew 监视新生代的GC的状况 - --gcnewcapacity 监视内容与 -gcnew基本相同,输出主要关注使用到的最大最小空间 - --gcold 监视老年代的GC情况 - --gcoldcapacity 监控内容与 -gcold基本相同,主要关注使用到的最大最小空间 - --compiler 输出jit 编译器编译过的方法、耗时等信息 - -#### 45.配置信息工具? - -jinfo(Configuration Info for Java)的作用是实时地查看和调整虚拟机的各项参数。 - -使用jps 命令的 -v 参数可以查看虚拟机启动时显示指定的参数列表。 - -jinfo 语法: jinfo [option] pid - -#### 46.内存映像工具? - -jmap(Memory Map for Java) 命令用于生成堆转储快照(一般称为heapdump或dump文件)。 - - 语法 :jmap [option] vmid - -它还可以查询finalize执行队列,Java堆和永久代的详细信息,如果空间使用率、当前用的是哪种收集器等。 - -- -dump 生成Java堆转储快照,其中live自参数说明是否只dump出存活对象 -- -finalizerinfo 显示在F -Queue 中等待Finalizer线程执行finalize方法的对象。只在Linux/Solaris平台下有效 -- -heap 显示Java堆详细信息,如使用哪种回收器、参数配置、分代状况。 -- -histo 显示堆中对象统计信息、包括类、实例数量和合计容量。 -- -F 当虚拟机进程对-dump选项没有响应时,可使用这个选项强制生成dump快照。 - -#### 47.虚拟机堆转存储快照分析工具? - -jhat ( JVM Heap Analysis Tool) 用来分析jmap生成的堆转储快照。 - -#### 48.堆栈跟踪工具? - -jstack(Stack Trace for Java) 命令用于生成虚拟机当前时刻的线程快照(一般称为thread dump 或javacore文件)。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因。 - -jstack [option] vmid - --F 当正常输出的请求不被响应时,强制输出线程堆栈 - --l 除堆栈外,显示关于锁的附加信息 - --m 如果调用本地方法的花,可以显示C/C++ 的堆栈 - -#### 49.除了命令行,还有什么可视化工具? - -JConsole 和 VisualVM,这两个工具是JDK的正式成员。 - -#### 50.类加载过程? - -加载-》验证-》准备-》解析-》初始化-》使用-》卸载 - -参考: - -- 《深入理解JVM & G1 GC》 - -- 《深入理解Java虚拟机:JVM高级特性与最佳实践》 - -- 《实战Java虚拟机:JVM故障诊断与性能优化》 - - -![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) - From d9efb24d0010204cc1aa67d0e58d52f3b077d01f Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:36:47 +0800 Subject: [PATCH 05/19] =?UTF-8?q?Delete=20Java=E5=9F=BA=E7=A1=80.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "Docs/Java\345\237\272\347\241\200.md" | 692 ------------------------- 1 file changed, 692 deletions(-) delete mode 100644 "Docs/Java\345\237\272\347\241\200.md" diff --git "a/Docs/Java\345\237\272\347\241\200.md" "b/Docs/Java\345\237\272\347\241\200.md" deleted file mode 100644 index e1447c9..0000000 --- "a/Docs/Java\345\237\272\347\241\200.md" +++ /dev/null @@ -1,692 +0,0 @@ -## Java基础题 - -#### 1.Java语言的三大特性 - -1. 封装: - - 首先,属性可用来描述同一类事物的特征,方法可描述一类事物可做的操作。封装就是把属于同一类事物的共性(包括属性与方法)归到一个类中,以方便使用。 - - 1)概念:封装也称为信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。系统的其他部分只有通过包裹在数据外面的被授权的操作来与这个抽象数据类型交流与交互。也就是说,用户无需知道对象内部方法的实现细节,但可以根据对象提供的外部接口(对象名和参数)访问该对象。 - 2)好处:(1)实现了专业的分工。将能实现某一特定功能的代码封装成一个独立的实体后,各程序员可以在需要的时候调用,从而实现了专业的分工。(2)隐藏信息,实现细节。通过控制访问权限可以将可以将不想让客户端程序员看到的信息隐藏起来,如某客户的银行的密码需要保密,只能对该客户开发权限。 - -2. 继承: - 就是个性对共性的属性与方法的接受,并加入个性特有的属性与方法 - 1.概念:一个类继承另一个类,则称继承的类为子类,被继承的类为父类。 - 2.目的:实现代码的复用。 - 3.理解:子类与父类的关系并不是日常生活中的父子关系,子类与父类而是一种特殊化与一般化的关系,是is-a的关系,子类是父类更加详细的分类。如class dog extends animal,就可以理解为dog is a animal.注意设计继承的时候,若要让某个类能继承,父类需适当开放访问权限,遵循里氏代换原则,即向修改关闭对扩展开放,也就是开-闭原则。 - 4.结果:继承后子类自动拥有了父类的属性和方法,但特别注意的是,父类的私有属性和构造方法并不能被继承。 - 另外子类可以写自己特有的属性和方法,目的是实现功能的扩展,子类也可以复写父类的方法即方法的重写。 - -3. 多态: - 多态的概念发展出来,是以封装和继承为基础的。 - 多态就是在抽象的层面上实施一个统一的行为,到个体(具体)的层面上时,这个统一的行为会因为个体(具体)的形态特征而实施自己的特征行为。(针对一个抽象的事,对于内部个体又能找到其自身的行为去执行。) - 1.概念:相同的事物,调用其相同的方法,参数也相同时,但表现的行为却不同。 - 2.理解:子类以父类的身份出现,但做事情时还是以自己的方法实现。子类以父类的身份出现需要向上转型(upcast),其中向上转型是由JVM自动实现的,是安全的,但向下转型(downcast)是不安全的,需要强制转换。子类以父类的身份出现时自己特有的属性和方法将不能使用。 - -#### 2.Java语言主要特性 - -1. **Java语言是易学的。**Java语言的语法与C语言和C++语言很接近,使得大多数程序员很容易学习和使用Java。 - -2. **Java语言是强制面向对象的。**Java语言提供类、接口和继承等原语,为了简单起见,只支持类之间的单继承,但支持接口之间的多继承,并支持类与接口之间的实现机制(关键字为implements)。 - -3. **Java语言是分布式的**。Java语言支持Internet应用的开发,在基本的Java应用编程接口中有一个网络应用编程接口(java net),它提供了用于网络应用编程的类库,包括URL、URLConnection、Socket、ServerSocket等。Java的RMI(远程方法激活)机制也是开发分布式应用的重要手段。 - -4. **Java语言是健壮的。**Java的强类型机制、异常处理、垃圾的自动收集等是Java程序健壮性的重要保证。对指针的丢弃是Java的明智选择。 - -5. **Java语言是安全的。**Java通常被用在网络环境中,为此,Java提供了一个安全机制以防恶意代码的攻击。如:安全防范机制(类ClassLoader),如分配不同的名字空间以防替代本地的同名类、字节代码检查。 - -6. **Java语言是体系结构中立的。**Java程序(后缀为java的文件)在Java平台上被编译为体系结构中立的字节码格式(后缀为class的文件),然后可以在实现这个Java平台的任何系统中运行。 - -7. **Java语言是解释型的。**如前所述,Java程序在Java平台上被编译为字节码格式,然后可以在实现这个Java平台的任何系统的解释器中运行。(一次编译,到处运行) - -8. **Java是性能略高的**。与那些解释型的高级脚本语言相比,Java的性能还是较优的。 - -9. **Java语言是原生支持多线程的。**在Java语言中,线程是一种特殊的对象,它必须由Thread类或其子(孙)类来创建。 - -#### 3. JDK 和 JRE 有什么区别 - -- JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境。 -- JRE:Java Runtime Environment 的简称,Java 运行环境,为 Java 的运行提供了所需环境。 - -具体来说 JDK 其实包含了 JRE,同时还包含了编译 Java 源码的编译器 Javac,还包含了很多 Java 程序调试和分析的工具。简单来说:如果你需要运行 Java 程序,只需安装 JRE 就可以了,如果你需要编写 Java 程序,需要安装 JDK。 - -#### 4.Java基本数据类型及其封装类 - -![image-20200413145454097](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200413145454097.png) - -Tips:boolean类型占了单独使用是4个字节,在数组中又是1个字节 -基本类型所占的存储空间是不变的。这种不变性也是Java具有可移植性的原因之一。 -基本类型放在栈中,直接存储值。 -所有数值类型都有正负号,没有无符号的数值类型。 - -为什么需要封装类? -因为泛型类包括预定义的集合,使用的参数都是对象类型,无法直接使用基本数据类型,所以Java又提供了这些基本类型的封装类。 - -基本类型和对应的封装类由于本质的不同。具有一些区别: -1.基本类型只能按值传递,而封装类按引用传递。 -2.基本类型会在栈中创建,而对于对象类型,对象在堆中创建,对象的引用在栈中创建,基本类型由于在栈中,效率会比较高,但是可能存在内存泄漏的问题。 - -#### 5.如果main方法被声明为private会怎样? - -能正常编译,但运行的时候会提示”main方法不是public的”。在idea中如果不用public修饰,则会自动去掉可运行的按钮。 - -#### 6.说明一下public static void main(String args[])这段声明里每个关键字的作用 - -public: main方法是Java程序运行时调用的第一个方法,因此它必须对Java环境可见。所以可见性设置为pulic. - -static: Java平台调用这个方法时不会创建这个类的一个实例,因此这个方法必须声明为static。 - -void: main方法没有返回值。 - -String是命令行传进参数的类型,args是指命令行传进的字符串数组。 - -#### 7.==与equals的区别 - -==比较两个对象在内存里是不是同一个对象,就是说在内存里的存储位置一致。两个String对象存储的值是一样的,但有可能在内存里存储在不同的地方 . - -==比较的是引用而equals方法比较的是内容。public boolean equals(Object obj) 这个方法是由Object对象提供的,可以由子类进行重写。默认的实现只有当对象和自身进行比较时才会返回true,这个时候和==是等价的。String, BitSet, Date, 和File都对equals方法进行了重写,对两个String对象 而言,值相等意味着它们包含同样的字符序列。对于基本类型的包装类来说,值相等意味着对应的基本类型的值一样。 - -```java -public class EqualsTest { - public static void main(String[] args) { - String s1 = “abc”; - String s2 = s1; - String s5 = “abc”; - String s3 = new String(”abc”); - String s4 = new String(”abc”); - System.out.println(”== comparison : ” + (s1 == s5)); - System.out.println(”== comparison : ” + (s1 == s2)); - System.out.println(”Using equals method : ” + s1.equals(s2)); - System.out.println(”== comparison : ” + s3 == s4); - System.out.println(”Using equals method : ” + s3.equals(s4)); - } -} -``` - -结果: - -```tex -== comparison : true -== comparison : true -Using equals method : true -false -Using equals method :true -``` - -#### 8.Object有哪些公用方法 - -**Object是所有类的父类,任何类都默认继承Object** - -**clone** 保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。 - -**equals** 在Object中与==是一样的,子类一般需要重写该方法。 - -**hashCode** 该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。 - -**getClass** final方法,获得运行时类型 - -**wait** 使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。 **wait()** 方法一直等待,直到获得锁或者被中断。 **wait(long timeout)** 设定一个超时间隔,如果在规定时间内没有获得锁就返回。 - -**调用该方法后当前线程进入睡眠状态,直到以下事件发生** - -1、其他线程调用了该对象的notify方法。 2、其他线程调用了该对象的notifyAll方法。 3、其他线程调用了interrupt中断该线程。 4、时间间隔到了。 5、此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。 - -**notify** 唤醒在该对象上等待的某个线程。 - -**notifyAll** 唤醒在该对象上等待的所有线程。 - -**toString** 转换成字符串,一般子类都有重写,否则打印句柄。 - -#### 9.为什么Java里没有全局变量? - -全局变量是全局可见的,Java不支持全局可见的变量,因为:全局变量破坏了引用透明性原则。全局变量导致了命名空间的冲突。 - -#### 10.while循环和do循环有什么不同? - -while结构在循环的开始判断下一个迭代是否应该继续。do/while结构在循环的结尾来判断是否将继续下一轮迭代。do结构至少会执行一次循环体。 - -#### 11.char型变量中能不能存储一个中文汉字?为什么? - -可以。Java默认Unicode编码。Unicode码占16位。 char两个字节刚好16位。 - -#### 12.public,private,protected的区别,继承方法与访问权限 - -![image-20200413174229242](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200413174229242.png) - - Tips:不写默认default - -#### 13.**float f=3.4;是否正确?** - -不正确。3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换float f =(float)3.4; 或者写成float f =3.4F。 - -#### 14.**short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?** - -对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型。而short s1 = 1; s1 += 1;+=操作符会进行**隐式自动类型转换**,是 **Java 语言规定的运算符**;Java编译器会对它进行特殊处理,因此可以正确编译。因为s1+= 1;相当于s1 = (short)(s1 + 1)。 - -#### 15.**&和&&的区别?** - -1. &:(1)按位与;(2)逻辑与。 - - 按位与: 0 & 1 = 0 ; 0 & 0 = 0; 1 & 1 = 1 - - 逻辑与: a == b & b ==c (即使a==b已经是 false了,程序还会继续判断b是否等于c) - -2.&&: 短路与 - -​ a== b && b== c (当a==b 为false则不会继续判断b是否等与c) - -比如判断某对象中的属性是否等于某值,则必须用&&,否则会出现空指针问题。 - -#### 16.IntegerCache - -```Java -public class IntegerTest { - - public static void main(String[] args) { - - Integer a = 100, b = 100 ,c = 129,d = 129; - - System.out.println(a==b); - - System.out.println(c==d); - } -} - -``` - - 结果: - -```tex -true -false -``` - -小朋友,你是否有很多问号? - -来解释一下: - -```java - /** - * Cache to support the object identity semantics of autoboxing for values between - * -128 and 127 (inclusive) as required by JLS. - * - * The cache is initialized on first usage. The size of the cache - * may be controlled by the {@code -XX:AutoBoxCacheMax=} option. - * During VM initialization, java.lang.Integer.IntegerCache.high property - * may be set and saved in the private system properties in the - * sun.misc.VM class. - */ - - private static class IntegerCache { - static final int low = -128; - static final int high; - static final Integer cache[]; - - static { - // high value may be configured by property - int h = 127; - String integerCacheHighPropValue = - sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); - if (integerCacheHighPropValue != null) { - try { - int i = parseInt(integerCacheHighPropValue); - i = Math.max(i, 127); - // Maximum array size is Integer.MAX_VALUE - h = Math.min(i, Integer.MAX_VALUE - (-low) -1); - } catch( NumberFormatException nfe) { - // If the property cannot be parsed into an int, ignore it. - } - } - high = h; - - cache = new Integer[(high - low) + 1]; - int j = low; - for(int k = 0; k < cache.length; k++) - cache[k] = new Integer(j++); - - // range [-128, 127] must be interned (JLS7 5.1.7) - assert IntegerCache.high >= 127; - } - - private IntegerCache() {} - } -``` - - - -```Java - public static Integer valueOf(int i) { - assert IntegerCache.high >= 127; - if (i >= IntegerCache.low && i <= IntegerCache.high) - return IntegerCache.cache[i + (-IntegerCache.low)]; - return new Integer(i); - } -``` - -通过源码,我们可以看出, -128~127之间做了缓存。考虑到高频数值的复用场景,这样做还是很合理的,合理优化。最大边界可以通过-XX:AutoBoxCacheMax进行配置。 - -#### 17.Locale类是什么? - -Locale类用来根据语言环境来动态调整程序的输出。 - -#### 18.Java中final、finally、finalize的区别与用法 - -1. final - final是一个修饰符也是一个关键字。 - -- 被final修饰的类无法被继承 -- 对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改; - 如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。但是它*指向的对象的内容是可变的*。 -- 被final修饰的方法将无法被重写,但*允许重载* - 注意:类的private方法会隐式地被指定为final方法。 - -2. finally - finally是一个关键字。 - -- finally在异常处理时提供finally块来执行任何清除操作。不管有没有异常被抛出或者捕获,finally块都会执行,通常用于释放资源。 -- finally块正常情况下一定会被执行。但是有至少两个极端情况: - 如果对应的try块没有执行,则这个try块的finally块并不会被执行 - 如果在try块中jvm关机,例如system.exit(n),则finally块也不会执行(都拔电源了,怎么执行) -- finally块中如果有return语句,则会覆盖try或者catch中的return语句,导致二者无法return,所以强烈建议finally块中不要存在return关键字 - -3. finalize - finalize()是Object类的protected方法,子类可以覆盖该方法以实现资源清理工作。 - *GC在回收对象之前都会调用该方法* - finalize()方法是存在很多问题的: - -- java语言规范并不保证finalize方法会被及时地执行,更根本不会保证它们一定会被执行 -- finalize()方法可能带来性能问题,因为JVM通常在单独的低优先级线程中完成finalize的执行 -- finalize()方法中,可将待回收对象赋值给GC Roots可达的对象引用,从而达到对象再生的目的 -- finalize方法最多由GC执行一次(但可以手动调用对象的finalize方法) - -#### 19.hashCode()和equals()的区别 - -下边从两个角度介绍了他们的区别:一个是性能,一个是可靠性。他们之间的主要区别也基本体现在这里。 - -1.equals()既然已经能实现对比的功能了,为什么还要hashCode()呢? -因为重写的equals()里一般比较的比较全面比较复杂,这样效率就比较低,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率很高。 - -2.hashCode()既然效率这么高为什么还要equals()呢? -因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题),所以hashCode()只能说是大部分时候可靠,并不是绝对可靠,所以我们可以得出(PS:以下两条结论是重点,很多人面试的时候都说不出来): - -equals()相等的两个对象他们的hashCode()肯定相等,也就是用equals()对比是绝对可靠的。 - -hashCode()相等的两个对象他们的equals()不一定相等,也就是hashCode()不是绝对可靠的。 -**扩展** - -1.阿里巴巴开发规范明确规定: - -只要重写 equals,就必须重写 hashCode; - -因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须重写这两个方法; - -如果自定义对象做为 Map 的键,那么必须重写 hashCode 和 equals; - -String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象作为 key 来使用; - -2、什么时候需要重写? -一般的地方不需要重载hashCode,只有当类需要放在HashTable、HashMap、HashSet等等hash结构的集合时才会重载hashCode。 - -3、那么为什么要重载hashCode呢? -如果你重写了equals,比如说是基于对象的内容实现的,而保留hashCode的实现不变,那么很可能某两个对象明明是“相等”,而hashCode却不一样。 - -这样,当你用其中的一个作为键保存到hashMap、hasoTable或hashSet中,再以“相等的”找另一个作为键值去查找他们的时候,则根本找不到。 - -4、为什么equals()相等,hashCode就一定要相等,而hashCode相等,却不要求equals相等? - -因为是按照hashCode来访问小内存块,所以hashCode必须相等。 -HashMap获取一个对象是比较key的hashCode相等和equals为true。 -之所以hashCode相等,却可以equal不等,就比如ObjectA和ObjectB他们都有属性name,那么hashCode都以name计算,所以hashCode一样,但是两个对象属于不同类型,所以equals为false。 - -5、为什么需要hashCode? - -通过hashCode可以很快的查到小内存块。 -通过hashCode比较比equals方法快,当get时先比较hashCode,如果hashCode不同,直接返回false。 - -#### 20.深拷贝和浅拷贝的区别是什么? - -浅拷贝 - -**(1)、定义** - -被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。即对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。”里面的对象“会在原来的对象和它的副本之间共享。 - -简而言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象 -**(2)、浅拷贝实例** - -```java -package com.test; - -public class ShallowCopy { - public static void main(String[] args) throws CloneNotSupportedException { - Teacher teacher = new Teacher(); - teacher.setName("riemann"); - teacher.setAge(27); - - Student2 student1 = new Student2(); - student1.setName("edgar"); - student1.setAge(18); - student1.setTeacher(teacher); - - Student2 student2 = (Student2) student1.clone(); - System.out.println("拷贝后"); - System.out.println(student2.getName()); - System.out.println(student2.getAge()); - System.out.println(student2.getTeacher().getName()); - System.out.println(student2.getTeacher().getAge()); - - System.out.println("修改老师的信息后——————"); - // 修改老师的信息 - teacher.setName("Games"); - System.out.println(student1.getTeacher().getName()); - System.out.println(student2.getTeacher().getName()); - - } -} - -class Teacher implements Cloneable { - private String name; - private int age; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } -} - -class Student2 implements Cloneable { - private String name; - private int age; - private Teacher teacher; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - public Teacher getTeacher() { - return teacher; - } - - public void setTeacher(Teacher teacher) { - this.teacher = teacher; - } - - public Object clone() throws CloneNotSupportedException { - Object object = super.clone(); - return object; - } -} - - -``` - -输出结果: - -```sqlite -拷贝后 -edgar -18 -riemann -27 -修改老师的信息后—————— -Games -Games -``` - -结果分析: 两个引用student1和student2指向不同的两个对象,但是两个引用student1和student2中的两个teacher引用指向的是同一个对象,所以说明是浅拷贝。 - -![image-20200413210910649](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200413210910649.png) - -深拷贝 - -**(1)、定义** - -深拷贝是一个整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。 - -简而言之,深拷贝把要复制的对象所引用的对象都复制了一遍。 - -**(2)、深拷贝实例** - -```java -package com.test; - -public class DeepCopy { - public static void main(String[] args) throws CloneNotSupportedException { - Teacher2 teacher = new Teacher2(); - teacher.setName("riemann"); - teacher.setAge(27); - - Student3 student1 = new Student3(); - student1.setName("edgar"); - student1.setAge(18); - student1.setTeacher(teacher); - - Student3 student2 = (Student3) student1.clone(); - System.out.println("拷贝后"); - System.out.println(student2.getName()); - System.out.println(student2.getAge()); - System.out.println(student2.getTeacher().getName()); - System.out.println(student2.getTeacher().getAge()); - - System.out.println("修改老师的信息后——————"); - // 修改老师的信息 - teacher.setName("Games"); - System.out.println(student1.getTeacher().getName()); - System.out.println(student2.getTeacher().getName()); - } -} - -class Teacher2 implements Cloneable { - private String name; - private int age; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - public Object clone() throws CloneNotSupportedException { - return super.clone(); - } -} - -class Student3 implements Cloneable { - private String name; - private int age; - private Teacher2 teacher; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - public Teacher2 getTeacher() { - return teacher; - } - - public void setTeacher(Teacher2 teacher) { - this.teacher = teacher; - } - - public Object clone() throws CloneNotSupportedException { - // 浅复制时: - // Object object = super.clone(); - // return object; - - // 改为深复制: - Student3 student = (Student3) super.clone(); - // 本来是浅复制,现在将Teacher对象复制一份并重新set进来 - student.setTeacher((Teacher2) student.getTeacher().clone()); - return student; - - } -} -``` - -输出结果: - -```sqlite -拷贝后 -edgar -18 -riemann -27 -修改老师的信息后—————— -Games -riemann -``` - -结果分析: -两个引用student1和student2指向不同的两个对象,两个引用student1和student2中的两个teacher引用指向的是两个对象,但对teacher对象的修改只能影响student1对象,所以说是深拷贝。 - -![image-20200413211228832](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200413211228832.png) - -#### 21.Java 中操作字符串都有哪些类?它们之间有什么区别? - -String、StringBuffer、StringBuilder。 - -String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。 - -StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。 - -#### 22.String str="a"与 String str=new String("a")一样吗? - -不一样,因为内存的分配方式不一样。 - -String str="a"; -> 常量池 String str=new String("a") -> 堆内存 - -#### 23.**抽象类能使用 final 修饰吗?** - -不能。定义抽象类就是让其他类继承的,而 final修饰的类不能被继承。 - -#### 24.static关键字5连问 - -(1)抽象的(abstract)方法是否可同时是静态的(static)? - -​ 抽象方法将来是要被重写的,而静态方法是不能重写的,所以这个是错误的。 - - (2)是否可以从一个静态(static)方法内部发出对非静态方法的调用? - -​ 不可以,静态方法只能访问静态成员,非静态方法的调用要先创建对象。 - - (3) static 可否用来修饰局部变量? - -​ static 不允许用来修饰局部变量 - -(4)内部类与静态内部类的区别? - -静态内部类相对与外部类是独立存在的,在静态内部类中无法直接访问外部类中变量、方法。如果 要访问的话,必须要new一个外部类的对象,使用new出来的对象来访问。但是可以直接访问静态的变量、调用静态的方法; - -普通内部类作为外部类一个成员而存在,在普通内部类中可以直接访问外部类属性,调用外部类的方法。 - -如果外部类要访问内部类的属性或者调用内部类的方法,必须要创建一个内部类的对象,使用该对象访问属性或者调用方法。 - -如果其他的类要访问普通内部类的属性或者调用普通内部类的方法,必须要在外部类中创建一个普通内部类的对象作为一个属性,外同类可以通过该属性调用普通内部类的方法或者访问普通内部类的属性 - -如果其他的类要访问静态内部类的属性或者调用静态内部类的方法,直接创建一个静态内部类对象即可。 - -(5)Java中是否可以覆盖(override) 一个private或者是static的方法? - -Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,所以概念上不适用。 - -#### 25. 重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分? - -方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求,不能根据返回类型进行区分。 - -![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) - -#### 26.Java的四种引用 - -**1、强引用** - -最普遍的一种引用方式,如String s = "abc",变量s就是字符串“abc”的强引用,只要强引用存在,则垃圾回收器就不会回收这个对象。 - -**2、软引用(SoftReference)** - -用于描述还有用但非必须的对象,如果内存足够,不回收,如果内存不足,则回收。一般用于实现内存敏感的高速缓存,软引用可以和引用队列ReferenceQueue联合使用,如果软引用的对象被垃圾回收,JVM就会把这个软引用加入到与之关联的引用队列中。 - -**3、弱引用(WeakReference)** - -弱引用和软引用大致相同,弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。 - -**4、虚引用(PhantomReference)** - -就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。 虚引用主要用来跟踪对象被垃圾回收器回收的活动。 - -**虚引用与软引用和弱引用的一个区别在于:** - -虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。 - -#### 27.**Java 中Comparator 与Comparable 有什么不同?** - -Comparable 接口用于定义对象的自然顺序,是排序接口,而 comparator 通常用于定义用户定制的顺序,是比较接口。我们如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序。Comparable 总是只有一个,但是可以有多个 comparator 来定义对象的顺序。 - -#### 28. Java 序列化,反序列化? - -Java 序列化就是指将对象转换为字节序列的过程,反序列化是指将字节序列转换成目标对象的过程。 - -#### 29.什么情况需要Java序列化? - -当 Java 对象需要在网络上传输 或者 持久化存储到文件中时。 - -#### 30.序列化的实现? - -让类实现Serializable接口,标注该类对象是可被序列。 - -#### 31.如果某些数据不想序列化,如何处理? - -在字段面前加 transient 关键字,例如: - -```java - transient private String phone;//不参与序列化 -``` - -#### 32.**Java泛型和类型擦除?** - -泛型即参数化类型,在创建集合时,指定集合元素的类型,此集合只能传入该类型的参数。类型擦除:java编译器生成的字节码不包含泛型信息,所以在编译时擦除:1.泛型用最顶级父类替换;2.移除。 - From ad06cbbf589e7e02a76ae79f667d64508ba40916 Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:37:08 +0800 Subject: [PATCH 06/19] Delete Kafka.md --- Docs/Kafka.md | 163 -------------------------------------------------- 1 file changed, 163 deletions(-) delete mode 100644 Docs/Kafka.md diff --git a/Docs/Kafka.md b/Docs/Kafka.md deleted file mode 100644 index 54627c7..0000000 --- a/Docs/Kafka.md +++ /dev/null @@ -1,163 +0,0 @@ -## Kafka - -#### 1.什么是kafka? - -Apache Kafka是由Apache开发的一种发布订阅消息系统。 - -#### 2.kafka的3个关键功能? - -- 发布和订阅记录流,类似于消息队列或企业消息传递系统。 -- 以容错的持久方式存储记录流。 -- 处理记录流。 - -#### 3.kafka通常用于两大类应用? - -- 建立实时流数据管道,以可靠地在系统或应用程序之间获取数据 -- 构建实时流应用程序,以转换或响应数据流 - -#### 4.kafka特性? - -1. 消息持久化 -2. 高吞吐量 -3. 扩展性 -4. 多客户端支持 -5. Kafka Streams -6. 安全机制 -7. 数据备份 -8. 轻量级 -9. 消息压缩 - -#### 5.kafka的5个核心Api? - -- Producer API - -- Consumer API - -- Streams API - -- Connector API - -- Admin API - -#### 6.什么是Broker(代理)? - -Kafka集群中,一个kafka实例被称为一个代理(Broker)节点。 - -#### 7.什么是Producer(生产者)? - -消息的生产者被称为Producer。 - -Producer将消息发送到集群指定的主题中存储,同时也自定义算法决定将消息记录发送到哪个分区? - -#### 8.什么是Consumer(消费者)? - -消息的消费者,从kafka集群中指定的主题读取消息。 - -#### 9.什么是Topic(主题)? - -主题,kafka通过不同的主题却分不同的业务类型的消息记录。 - -#### 10.什么是Partition(分区)? - -每一个Topic可以有一个或者多个分区(Partition)。 - -#### 11.分区和代理节点的关系? - -一个分区只对应一个Broker,一个Broker可以管理多个分区。 - -#### 12.什么是副本(Replication)? - -每个主题在创建时会要求制定它的副本数(默认1)。 - -#### 13.什么是记录(Record)? - -实际写入到kafka集群并且可以被消费者读取的数据。 - -每条记录包含一个键、值和时间戳。 - -#### 14.kafka适合哪些场景? - -日志收集、消息系统、活动追踪、运营指标、流式处理、时间源等。 - -#### 15.kafka磁盘选用上? - -SSD的性能比普通的磁盘好,这个大家都知道,实际中我们用普通磁盘即可。它使用的方式多是顺序读写操作,一定程度上规避了机械磁盘最大的劣势,即随机读写操作慢,因此SSD的没有太大优势。 - -#### 16.使用RAID的优势? - -- 提供冗余的磁盘存储空间 -- 提供负载均衡 - -#### 17.磁盘容量规划需要考虑到几个因素? - -- 新增消息数 -- 消息留存时间 -- 平均消息大小 -- 备份数 -- 是否启用压缩 - -#### 18.Broker使用单个?多个文件目录路径参数? - -log.dirs 多个 - -log.dir 单个 - -#### 19.一般来说选择哪个参数配置路径?好处? - -log.dirs - -好处: - -提升读写性能,多块物理磁盘同时读写高吞吐。 - -故障转移。一块磁盘挂了转移到另一个上。 - -#### 20.自动创建主题的相关参数是? - -auto.create.topics.enable - -#### 21.解决kafka消息丢失问题? - -- 不要使用 producer.send(msg),而要使用 producer.send(msg, callback)。 -- 设置 acks = all。 -- 设置 retries 为一个较大的值。 -- 设置 unclean.leader.election.enable = false。 -- 设置 replication.factor >= 3。 -- 设置 min.insync.replicas > 1。 -- 确保 replication.factor > min.insync.replicas。 -- 确保消息消费完成再提交。 - -#### 22.如何自定分区策略? - -显式地配置生产者端的参数partitioner.class - -参数为你实现类的 全限定类名,一般来说实现partition方法即可。 - -#### 23.kafka压缩消息可能发生的地方? - -Producer 、Broker。 - -#### 24.kafka消息重复问题? - -做好幂等。 - -数据库方面可以(唯一键和主键)避免重复。 - -在业务上做控制。 - -#### 25.你知道的kafka监控工具? - -- JMXTool 工具 -- Kafka Manager -- Burrow -- JMXTrans + InfluxDB + Grafana -- Confluent Control Center - -参考: - -- 《Kafka并不难学》 -- 《kafka入门与实践》 -- 极客时间:Kafka核心技术与实战 -- http://kafka.apache.org/ - -![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) From ce5232eb0aa9ef1af86bbccbf49a57cdb782fd74 Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:37:33 +0800 Subject: [PATCH 07/19] Delete Linux.md --- Docs/Linux.md | 788 -------------------------------------------------- 1 file changed, 788 deletions(-) delete mode 100644 Docs/Linux.md diff --git a/Docs/Linux.md b/Docs/Linux.md deleted file mode 100644 index 25d5ad2..0000000 --- a/Docs/Linux.md +++ /dev/null @@ -1,788 +0,0 @@ -## Linux - -#### 1.什么是Linux? - -是一套免费使用和自由传播的类UNIX操作系统,其内核由林纳斯·本纳第克特·托瓦兹于1991年第一次释出,它主要受到Minix和Unix思想的启发,是一个基于POSIX和Unix的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的Unix工具软件、应用程序和网络协议。它支持32位和64位硬件。 - -#### 2.Linux内核主要负责哪些功能 - -- 系统内存管理 -- 软件程序管理 -- 硬件设备管理 -- 文件系统管理 - -#### 3.交互方式 - -控制台终端、图形化终端 - -#### 4.启动shell - -GNU bash shell能提供对linux 系统的交互式访问。作为普通程序运行,通常在用户登陆终端时启动。登录时系统启动的shell依赖与用户账户的配置。 - -#### 5.bash手册 - -大多数linux发行版自带以查找shell命令及其他GNU工具信息的在线手册。man命令用来访问linux系统上的手册页面。当用man命令查看手册,使用分页的程序来现实的。 - -#### 6.登陆后你在的位置? - -一般登陆后,你的位置位于自己的主目录中。 - -#### 7.绝对文件路径?相对文件路径?快捷方式? - -绝对文件路径:描述了在虚拟目录结构中该目录的确切位置,以虚拟目录跟目录开始,相当于目录全名。 - -以正斜线(/)开始,比如 /usr/local。 - -相对文件路径:允许用户执行一个基于当前位置的目标文件路径。 - -比如:当前在/usr/local - -``` -➜ local ls -Caskroom Frameworks bin go lib sbin var -Cellar Homebrew etc include opt share -➜ local cd go -``` - -快捷方式(在相对路径中使用): - -单点符(.) : 表示当前目录; 双点符(..) : 表示当前目录的父目录。 - -#### 8.迷路,我的当前位置在哪? - -pwd 显示当前目录 - -``` -[root@iz2ze76ybn73dvwmdij06zz local]# pwd -/usr/local -``` - -#### 9.如何切换目录? - -语法: cd destination - -destination : 相对文件路径或绝对文件路径 - -可以跳到存在的任意目录。 - -#### 10.如何查看目录中的文件?区分哪些是文件哪些是目录?递归查? - -ls 命令会用最基本的形式显示当前目录下的文件和目录: - -``` -➜ local ls -Caskroom Frameworks bin go lib sbin var -Cellar Homebrew etc include opt share -``` - -可以看出默认是按照字母序展示的 - -一般来说,ls命令回显示不同的颜色区分不同的文件类型,如果没有安装颜色插件可以用ls -F来区分哪些是目录(目录带/),哪些是文件(文件不带/) - - ls -R 递归展示出目录下以及子目录的文件,目录越多输出越多 - -#### 11.创建文件?创建目录?批量创建? - -创建文件:touch 文件名 - -批量创建文件: touch 文件名 文件名 … - -``` -➜ test touch a -➜ test ls -a -➜ test touch b c -➜ test ls -a b c -``` - -创建目录:mkdir 目录名 - -批量创建目录: mkdir 目录名 目录名 … - -``` -➜ test mkdir aa -➜ test mkdir bb cc -➜ test ls -a aa b bb c cc -➜ test ls -F -a aa/ b bb/ c cc/ -``` - -#### 12.删除文件?强制删除?递归删除? - -语法: rm destination - --i 询问是否删除,-r 递归删除,-f 强制删除。 - -rm不能删除有文件的目录,需要递归删除。 - -``` -➜ xktest rm jdk -rm: jdk: is a directory -➜ xktest rm -r jdk -➜ xktest ls -``` - -rm -i 询问删除,建议大家平时删除多用 -i,确定一下再删除。 - -``` -➜ xktest touch tomcat -➜ xktest rm -i tomcat -remove tomcat? n -``` - -rm -rf 会直接删除,没有警告信息,使用必须谨慎**。 - -#### 13.制表符自动补全? - -有的时候文件的名字很长,很容易拼出错即使拼写对了也很浪费时间。 - -``` -➜ xktest ls java* -javaxiaokaxiu -``` - - 比如操作javaxiaokaxiu这个文件时,输入到java的时候,然后按制表键(tab)就会补全成javaxiaokaxiu,是不是方便多了。 - -#### 14.复制文件 - -语法: cp source target - -如果target不存在则直接创建,如果存在,默认不会提醒你是否需要覆盖,需要加-i就会询问你是否覆盖,n否y是。 - -``` -➜ xktest cp a c -➜ xktest cp -i a c -overwrite c? (y/n [n]) y -➜ xktest ls -a c -``` - -#### 15.重新命名文件?移动文件? - -语法 : mv soucre target - -重命名: - -``` -➜ xktest ls -➜ xktest touch java -➜ xktest ls -java -➜ xktest mv java java1.8 -➜ xktest ls -java1.8 -``` - -移动文件: - -新建jdk目录把java1.8文件移动到jdk目录下。 - -``` -➜ xktest ls -java1.8 -➜ xktest mkdir jdk -➜ xktest mv java1.8 jdk -➜ xktest ls -R -jdk - -./jdk: -java1.8 -``` - -#### 16.什么是链接文件? - -如过需要在系统上维护同一文件的两份或者多份副本,除了保存多分单独的物理文件副本之外。还可以采用保存一份物理文件副本和多个虚拟副本的方法,这种虚拟的副本就叫做链接。 - -#### 17.查看文件类型?字符编码? - -语法: file destination - -``` -➜ apache file tomcat -tomcat: ASCII text -``` - -可以看出,file命令可以显示文件的类型text以及字符编码ASCII - -#### 18.查看整个文件?按照有文本显示行号?无文本显示行号? - -语法 : cat destination - --n 显示行号,-b 有文本的显示行号。 (默认是不显示行号的) - -``` -➜ apache cat -n tomcat - 1 text - 2 text - 3 - 4 start - 5 stop - 6 restart - 7 end -➜ apache cat -b tomcat - 1 text - 2 text - - 3 start - 4 stop - 5 restart - 6 end -``` - -#### 19.查看部分文件 - -语法 : tail destination - -默认情况会展示文件的末尾10行。 -n 行数,显示最后n行。 - -``` -➜ apache tail -n 2 tomcat -restart -end -``` - -语法: head destination - -默认情况会展示文件的开头10行。 -n 行数,显示开头n行。 - -``` -➜ apache head -n 2 tomcat -text -text -``` - -#### 20.数据排序?对数字进行排序?对月份排序? - -默认情况下,文件的数据展示是按照原顺序展示的。sort命令可以对文本文件中的数据进行排序。sort默认会把数据当成字符处理。 - -语法: sort destination - -sort -n 所以排序数字时需要用-n,它的含义是说当前排序是的数字。 - -sort -M 比如月份Jan、Feb、Mar,如果希望它按照月份排序,加入-M就会按照月份的大小来排序。 - -#### 21.查找匹配数据?反向搜? - -语法: grep [options] pattern [file] - -该命令会查找匹配执行模式的字符串的行,并输出。 - -``` -➜ apache grep start tomcat -start -restart -``` - --v 反向搜 - -``` -➜ apache grep -v start tomcat -text -text - -stop -end -``` - --n 显示行号 - --c 显示匹配的行数 - -#### 22.压缩工具有哪些? - -![image-20200421122324314](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200421122324314.png) - -#### 23.如何压缩文件?如何解压文件? - -比如以.gz的格式举例。 - -压缩语法: gzip destination - -``` -➜ apache gzip tomcat -➜ apache ls -tomcat.gz -``` - -解压语法: gunzip destination - -``` -➜ apache gunzip tomcat.gz -➜ apache ls -tomcat -``` - -#### 24.Linux广泛使用的归档数据方法? - -虽然zip命令能压缩和解压单个文件,但是更多的时候广泛使用tar命令来做归档。 - -语法: tar function [options] obj1 obj2 - -![image-20200421122932671](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200421122932671.png) - -``` -➜ apache tar -cvf service.tar service1 service2 // 创建规定文件service.tar -a service1 -a service2 -➜ apache tar -tf service.tar //查看文件中的目录内容 -service1 -service2 -➜ apache tar zxvf service.tar //解压 -x service1 -x service2 -``` - -#### 25.如何查看命令历史记录? - - history 命令可以展示你用的命令的历史记录。 - -``` - 4463 touch service1 service2 - 4464 ls - 4465 tar -cvf service.tar service1 service2 - 4466 tar -tf service.tar - 4467 tar zxvf service - 4468 tar zxvf service.t - 4469 tar zxvf service.tar - 4470 ls - 4471 tar -zxvf service.tar - 4472 ls -``` - -#### 26.查看已有别名?建立属于自己的别名? - -alias -p 查看当前可用别名 - -``` -[root@iz2ze76ybn73dvwmdij06zz ~]# alias -p -alias cp='cp -i' -alias egrep='egrep —color=auto' -alias fgrep='fgrep —color=auto' -alias grep='grep —color=auto' -alias l.='ls -d .* —color=auto' -alias ll='ls -l —color=auto' -``` - -alias li = 'ls -li' 创建别名 - -#### 27.什么是环境变量? - -bash shell用一个叫作环境变量(environment variable)的特性来存储有关shell会话和工作环境的信息。这项特性允许你在内存中存储数据,以便程序或shell中运行的脚本能够轻松访问到它们。这也是存储持久数据的一种简便方法。 - -在bash shell中,环境变量分为两类: - - 全局变量:对于 shell会话和所有生成的子shell都是可见的。 局部变量: 只对创建他们的shell可见。 - -#### 28.储存用户的文件是?包括哪些信息? - -/etc/passwd存储来一些用户有关的信息。 - -``` -[root@iz2ze76ybn73dvwmdij06zz ~]# cat /etc/passwd -root:x:0:0:root:/root:/bin/bash -bin:x:1:1:bin:/bin:/sbin/nologin -``` - -文件信息包括如下内容。 - -- 登录用户名 -- 用户密码 -- 用户账户的UID(数字形式) -- 用户账户的组ID(GID)(数字形式) -- 用户账户的文本描述(称为备注字段) -- 用户HOME目录的位置 -- 用户的默认shell - -#### 29.账户默认信息?添加账户?删除用户? - -``` -[root@iz2ze76ybn73dvwmdij06zz ~]# useradd -D//查看系统默认创建用户信息 -GROUP=100 -HOME=/home -INACTIVE=-1 -EXPIRE= -SHELL=/bin/bash -SKEL=/etc/skel -CREATE_MAIL_SPOOL=yes -[root@iz2ze76ybn73dvwmdij06zz ~]# useradd xiaoka//添加用户 - -[root@iz2ze76ybn73dvwmdij06zz /]# userdel xiaoka//删除用户 -``` - -#### 30.查看组信息?如何创建组?删除组? - -``` -[root@iz2ze76ybn73dvwmdij06zz ~]# cat /etc/group -root:x:0: -bin:x:1: -daemon:x:2: -sys:x:3: -adm:x:4: -tty:x:5: -disk:x:6: -[root@iz2ze76ybn73dvwmdij06zz ~]# groupadd java //创建组 -[root@iz2ze76ybn73dvwmdij06zz ~]# groupdel java //创建组 -``` - -#### 31.文件描述符?每个描述符的含义? - -``` -[root@iz2ze76ybn73dvwmdij06zz xiaoka]# ls -l -总用量 0 --rw-r—r— 1 root root 0 4月 21 13:17 a --rw-r—r— 1 root root 0 4月 21 13:17 b --rw-r—r— 1 root root 0 4月 21 13:17 c --rw-r—r— 1 root root 0 4月 21 13:17 d --rw-r—r— 1 root root 0 4月 21 13:17 e -``` - -1、文件类型: - -- -代表文件 -- d代表目录 -- l代表链接 -- c代表字符型设备 -- b代表块设备 -- n代表网络设备 - -2、访问权限符号: - -- r代表对象是可读的 -- w代表对象是可写的 -- x代表对象是可执行的 - -若没有某种权限,在该权限位会出现单破折线。 - -3、这3组权限分别对应对象的3个安全级别: - -- 对象的属主 -- 对象的属组 -- 系统其他用户 - -#### 31.修改权限? - -chmod options mode file - -比如给文件附加可以执行权限: - -``` -[root@xiaoka ~]# chmod +x filename -``` - -#### 32.如何执行可以执行文件? - -``` -[root@xiaoka ~]# sh sleep.sh -hello,xiaoka -[root@xiaoka ~]# ./sleep.sh -hello,xiaoka -``` - -#### 33.列出已经安装的包?安装软件?更新软件?卸载? - -列出已经安装的包: yum list installed - -安装软件: yum install package_name - -更新软件: yum update package_name - -卸载软件:yum remove package_name //只删除软件包保留数据文件和配置文件 - -如果不希望保留数据文件和配置文件 - -可以执行:yum erase package_name - -#### 34.源码安装通常的路子? - -``` - tar -zxvf xx.gz //解包 - cd xx - ./configure - make - make install -``` - -#### 35.vim编辑器几种操作模式?基本操作? - -操作模式: - -- 普通模式 -- 插入模式 - - 基础操作: - -- h:左移一个字符。 -- j:下移一行(文本中的下一行)。 -- k:上移一行(文本中的上一行)。 -- l:右移一个字符。 - -vim提供了一些能够提高移动速度的命令: - -- PageDown(或Ctrl+F):下翻一屏 -- PageUp(或Ctrl+B):上翻一屏。 -- G:移到缓冲区的最后一行。 -- num G:移动到缓冲区中的第num行。 -- gg:移到缓冲区的第一行。 - -退出vim: - -- q:如果未修改缓冲区数据,退出。 -- q!:取消所有对缓冲区数据的修改并退出。 -- w filename:将文件保存到另一个文件中。 -- wq:将缓冲区数据保存到文件中并退出。 - -#### 36.查看设备还有多少磁盘空间? - -df 可以查看所有已挂在磁盘的使用情况。 - --m 用兆字节,G代替g字节 - -``` -[root@iz2ze76ybn73dvwmdij06zz ~]# df -文件系统 1K-块 已用 可用 已用% 挂载点 -devtmpfs 1931568 0 1931568 0% /dev -tmpfs 1940960 0 1940960 0% /dev/shm -tmpfs 1940960 720 1940240 1% /run -tmpfs 1940960 0 1940960 0% /sys/fs/cgroup -/dev/vda1 41152812 9068544 30180560 24% / -tmpfs 388192 0 388192 0% /run/user/0 -``` - -###### 快速判断某个特定目录是否有超大文件? - -默认情况,du会显示当前目录的所有文件、目录、子目录的磁盘使用情况。 - -``` -[root@iz2ze76ybn73dvwmdij06zz src]# du -4 ./debug -4 ./kernels -12 -``` - -#### 37.默认进程信息显示? - -ps它能输出运行在系统上的所有程序的许多信息。 - -默认情况下ps值显示运行在当前控制台下的当前用户的进程。 - -``` -[root@iz2ze76ybn73dvwmdij06zz ~]# ps - PID TTY TIME CMD -10102 pts/0 00:00:00 bash -10131 pts/0 00:00:00 ps -``` - -#### 38.实时监测进程 - -与ps相比,top可以实时监控进程信息。 - -![image-20200421114633852](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200421114633852.png) - -平均负载有3个值:最近1分钟的、最近5分钟的和最近15分钟的平均负载。值越大说明系统 的负载越高。由于进程短期的突发性活动,出现最近1分钟的高负载值也很常见,但如果近15分 钟内的平均负载都很高,就说明系统可能有问题。 - -#### 39.如何中断一个进程? - -在一个终端中, Ctrl + c - -通过这个命令许多(不是全部)命令行程序都可以被中断。 - -#### 40.如何把一个进程放到后台运行? - -``` -[root@iz2ze76ybn73dvwmdij06zz ~]# ./sleep.sh & -``` - -此时,进程并不能被Ctrl + c 中断。 - -#### 41.如何停止一个进程? - -kill命令被用来给程序发送信号。如果没有指定信号,默认发送TERM(终止)信号。 - -语法 : kill [-signal] PID … - -![image-20200421141556974](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200421141556974.png) - -\#### - -#### 42.验证网络可链接命令是什么?什么原理? - - ping。这个 ping 命令发送一个特殊的网络数据包(叫做 IMCP ECHO REQUEST)到一台指定的主机。大多数接收这个包的网络设备将会回复它,来允许网络连接验证。 - -![image-20200421142307602](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200421142307602.png) - -一旦启动,ping会持续在特定时间(默认1秒)发送数据包。 - -#### 43.查看某端口是否被占用? - -netstat -ntulp|grep 8080 - -``` -[root@iz2ze76ybn73dvwmdij06zz ~]# netstat -ntulp|grep 8080 -tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 4517/java -``` - -参数说明: - -- -t (tcp) 仅显示tcp相关选项 -- -u (udp)仅显示udp相关选项 -- -n 拒绝显示别名,能显示数字的全部转化为数字 -- -l 仅列出在Listen(监听)的服务状态 -- -p 显示建立相关链接的程序名 - -#### 44.如何查找匹配的文件?基于文件属性? - - find 程序能基于各种各样的属性,搜索一个给 定目录(以及它的子目录),来查找文件。 - -find 命令的最简单使用是,搜索一个或多个目录。 - -普通查找,按照name查找: - -``` -[root@iz2ze76ybn73dvwmdij06zz ~]# find -name xiaoka -./xiaoka -``` - -文件类型查找: - -比如,输出我们的家目录文件数量 - -``` -[root@iz2ze76ybn73dvwmdij06zz ~]# find ~|wc -l -17130 -``` - -根据文件类型查: - -``` -[root@iz2ze76ybn73dvwmdij06zz ~]# find ~ -type d | wc -l -7340 -``` - -find支持的类型: b 块设备文件、 c 字符设备文件、d 目录、f 普通文件、l 符号链接 - -#### 45.如何查看当前主机名?如何修改?如何重启后生效? - -``` -[root@iz2ze76ybn73dvwmdij06zz ~]# hostname//查看当前主机名 -iz2ze76ybn73dvwmdij06zz -[root@iz2ze76ybn73dvwmdij06zz ~]# hostname xiaoka//修改当前主机名 -[root@iz2ze76ybn73dvwmdij06zz ~]# hostname -xiaoka -``` - -大家知道一般来讲命令重启就会失效,目前基本上用的centos7的比较多,两种方式可以支持重启生效。 - -一、命令 - -``` -[root@iz2ze76ybn73dvwmdij06zz ~]# hostnamectl set-hostname xiaoka -[root@iz2ze76ybn73dvwmdij06zz ~]# hostname -xiaoka -[root@xiaoka ~]# -``` - - 二、修改配置文件:/etc/hostname - -``` -[root@xiaoka ~]# vim /etc/hostname -``` - -#### 46.如何写一条规则,拒绝某个ip访问本机8080端口? - -``` -iptables -I INPUT -s ip -p tcp —dport 8080 -j REJECT -``` - -#### 47.哪个文件包含了主机名和ip的映射关系? - -/etc/hosts - -#### 48.如何用sed只打印第5行?删除第一行?替换字符串? - - 只打印第5行: - -``` -➜ apache sed -n "5p" tomcat -stop -``` - -删除第一行: - -``` -[root@xiaoka ~]# cat story -Long ago a lion and a bear saw a kid. -They sprang upon it at the same time. -The lion said to the bear, “I caught this kid first, and so this is mine.” -[root@xiaoka ~]# cat story -They sprang upon it at the same time. -The lion said to the bear, “I caught this kid first, and so this is mine.” -``` - -替换字符串: - -``` -➜ apache cat story -Long ago a lion and a bear saw a kid. -They sprang upon it at the same time. -The lion said to the bear, “I caught this kid first, and so this is mine.” -➜ apache sed 's#this#that#g' story -Long ago a lion and a bear saw a kid. -They sprang upon it at the same time. -The lion said to the bear, “I caught that kid first, and so that is mine.” -``` - -#### 49.打印文件第一行到第三行? - -​ 文件tomcat中内容: - -``` -➜ apache cat tomcat -text21 -text22 -text23 -start -stop -restart -end -➜ apache head -3 tomcat -text21 -text22 -text23 -➜ apache sed -n '1,3p' tomcat -text21 -text22 -text23 -➜ apache awk 'NR>=1&&NR<=3' tomcat -text21 -text22 -text23 -``` - -#### 50.如何用awk查看第2行倒数第3个字段? - -``` -➜ apache awk 'NR==3{print $(NF-2)}' story -this -➜ apache cat story -Long ago a lion and a bear saw a kid. -They sprang upon it at the same time. -The lion said to the bear, “I caught this kid first, and so this is mine.” -``` - -参考: - -- 《鸟哥Linux私房菜》 - -- 《快乐的命令行》 - -- 《Linux命令行与shell脚本编程大全(第3版)》 - -- 《Linux从入门到精通》 - -- 百度百科 - -- ​ 公众号:《马里奥玩Python》 - - ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file From 9b2a96f1e991971d3f97e48e7aa0382fb5892344 Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:37:43 +0800 Subject: [PATCH 08/19] Delete MySql.md --- Docs/MySql.md | 584 -------------------------------------------------- 1 file changed, 584 deletions(-) delete mode 100644 Docs/MySql.md diff --git a/Docs/MySql.md b/Docs/MySql.md deleted file mode 100644 index c07b842..0000000 --- a/Docs/MySql.md +++ /dev/null @@ -1,584 +0,0 @@ -## Mysql - - - -#### 1.什么是数据库? - -数据库是“按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。 - -#### 2.如何查看某个操作的语法? - - 比如看建表的语法: - -``` -mysql> ? create table -Name: 'CREATE TABLE' -Description: -Syntax: -CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name - (create_definition,…) - [table_options] - [partition_options] - -CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name - [(create_definition,…)] - [table_options] - [partition_options] - [IGNORE | REPLACE] - [AS] query_expression - -CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name -``` - -#### 3.MySql的存储引擎有哪些? - -MyISAM、 InnoDB、BDB、MEMORY、MERGE、EXAMPLE、NDB Cluster、 ARCHIVE、CSV、BLACKHOLE、FEDERATED。 - -Tips:InnoDB和BDB提供事务安全表,其他存储引擎都是非事务安全表。 - -#### 4.常用的2种存储引擎? - -1.Myisam是Mysql的默认存储引擎,当create创建新表时,未指定新表的存储引擎时,默认使用Myisam。 - -每个MyISAM 在磁盘上存储成三个文件。文件名都和表名相同,扩展名分别是 .frm (存储表定义) 、.MYD (MYData,存储数据)、.MYI (MYIndex,存储索引)。 - -数据文件和索引文件可以放置在不同的目录,平均分布io,获得更快的速度。 - -2.InnoDB 存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是对比 Myisam 的存储引擎,InnoDB 写的处理效率差一些并且会占用更多的磁盘空间以保留数据和索引。 - -#### 6.可以针对表设置引擎吗?如何设置? - -可以, ENGINE=xxx 设置引擎。 - -代码示例: - -``` -create table person( - id int primary key auto_increment, - username varchar(32) -) ENGINE=InnoDB -``` - -#### 6.选择合适的存储引擎? - -选择标准: 根据应用特点选择合适的存储引擎,对于复杂的应用系统可以根据实际情况选择 多种存储引擎进行组合. 下面是常用存储引擎的适用环境: - -1. MyISAM: 默认的 MySQL 插件式存储引擎, 它是在 Web、 数据仓储和其他应用环境下最常使用的存储引擎之一。 -2. InnoDB:用于事务处理应用程序,具有众多特性,包括 ACID 事务支持。 -3. Memory: 将 所有数据保存在RAM 中, 在 需要快速查找引用和其他类似数据的环境下,可 提供极快的访问。 -4. Merge:允许 MySQL DBA 或开发人员将一系列等同的 MyISAM 表以逻辑方式组合在一起,并作为 1 个对象引用它们。对于诸如数据仓储等 VLDB 环境十分适合。 - -#### 7.选择合适的数据类型 - -前提: 使用适合存储引擎。 - -选择原则: 根据选定的存储引擎,确定如何选择合适的数据类型下面的选择方法按存储引擎分类 : - -1. MyISAM 数据存储引擎和数据列 - - MyISAM数据表,最好使用固定长度的数据列代替可变长度的数据列。 - -2. MEMORY存储引擎和数据列 - - MEMORY数据表目前都使用固定长度的数据行存储,因此无论使用CHAR或VARCHAR列都没有关系。两者都是作为CHAR类型处理的。 - -3. InnoDB 存储引擎和数据列 - - 建议使用 VARCHAR类型 - - 对于InnoDB数据表,内部的行存储格式没有区分固定长度和可变长度列(所有数据行 都使用指向数据列值的头指针) ,因此在本质上,使用固定长度的CHAR列不一定比使 用可变长度VARCHAR列简单。 因而, 主要的性能因素是数据行使用的存储总量。 由于 CHAR 平均占用的空间多于VARCHAR,因此使用VARCHAR来最小化需要处理的数据行的存储总 量和磁盘I/O是比较好的。 - -#### 8.char & varchar - -保存和检索的方式不同。它们的最大长度和是否尾部空格被保留等方面也不同。在存储或检索过程中不进行大小写转换。 - -#### 9.Mysql字符集 - -mysql服务器可以支持多种字符集 (可以用show character set命令查看所有mysql支持 的字符集) ,在同一台服务器、同一个数据库、甚至同一个表的不同字段都可以指定使用不 同的字符集。 - -mysql的字符集包括字符集(CHARACTER)和校对规则(COLLATION)两个概念。 - -#### 10.如何选择字符集? - -建议在能够完全满足应用的前提下,尽量使用小的字符集。因为更小的字符集意味着能够节省空间、 减少网络传输字节数,同时由于存储空间的较小间接的提高了系统的性能。 - -有很多字符集可以保存汉字,比如 utf8、gb2312、gbk、latin1 等等,但是常用的是 gb2312 和 gbk。因为 gb2312 字库比 gbk 字库小,有些偏僻字(例如:洺)不能保存,因此 在选择字符集的时候一定要权衡这些偏僻字在应用出现的几率以及造成的影响, 不能做出肯 定答复的话最好选用 gbk。 - -#### 11.什么是索引? - -在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。 - -#### 12.索引设计原则? - -1. 搜索的索引列,不 一定是所要选择的列。最适合索引的列是出现在WHERE子句中的列,或连接子句中指定的列,而不是出现在SELECT 关键字后的选择列表中的列。 - -2. 使用惟一索引。考虑某列中值的分布。 对于惟一值的列,索引的效果最好,而具有多个 重复值的列,其索引效果最差。 - -3. 使用短索引。如果对串列进行索引,应该指定一个前缀长度,只要有可能就应该这做样。 例如,如果有一个 CHAR(200) 列,如果在前 10 个或 20 个字符内,多数值是惟一的, 那么就不要对整个列进行索引。 - -4. 利用最左前缀。在创建 一个 n 列的索引时,实际是创建了 MySQL 可利用的 n 个索引。 多列索引可起几个索引的作用,因为可利用索引中最左边的列集来匹配行。 这样的列集 称为最左前缀。 (这与索引一个列的前缀不同,索引一个列的前缀是利用该的n前个字 符作为索引值 ) - -5. 不要过度索引。每个额外的索引都要占用额外的磁盘空间,并降低写操作的性能,这一点我们前面已经介绍 过。在修改表的内容时,索引必须进行更新,有时可能需要重构, 因此, 索引越多,所花的时间越长。 - - 如果有一个索引很少利用或从不使用,那么会不必要地减缓表的修改速度。 此外,MySQL 在生成一个执行计划时,要考虑各个索引,这也要费时间。 - - 创建多余的索引给查询优化带来了更多的工作。索引太多,也可能会使 MySQL选择不到所要使用的 最好索引。 只保持所需的索引有利于查询优化。 如果想给已索引的表增加索引, 应 该考虑所要增加的索引是否是现有多列索引的最左索引。 - -6. 考虑在列上进行的比较类型。 索引可用于“ <”、“ < = ”、“ = ”、“ > =”、“ > ”和 BETWEEN 运算。在模式具有一个直接量前缀时,索引也用于 LIKE 运算。如果只将某个列用于其他类型的运算时(如 STRCMP( )) ,对其进行索引没有价值。 - -#### 13.MySql有哪些索引? - -- ###### 数据结构角度 - -1. BTREE -2. HASH -3. FULLTEXT -4. R-Tree - -- 物理存储角度 - -1、聚集索引(clustered index) - -2、非聚集索引(non-clustered index) - -- 从逻辑角度 - -1. 普通索引:仅加速查询 -2. 唯一索引:加速查询 + 列值唯一(可以有null) -3. 主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个 -4. 组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并 -5. 全文索引:对文本的内容进行分词,进行搜索 - -#### 14.Hash索引和B+树索引的底层实现原理: - -hash索引底层就是hash表,进行查找时,调用一次hash函数就可以获取到相应的键值,之后进行回表查询获得实际数据.B+树底层实现是多路平衡查找树.对于每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据. - -那么可以看出他们有以下的不同: - -- hash索引进行等值查询更快(一般情况下),但是却无法进行范围查询. - -因为在hash索引中经过hash函数建立索引之后,索引的顺序与原顺序无法保持一致,不能支持范围查询.而B+树的的所有节点皆遵循(左节点小于父节点,右节点大于父节点,多叉树也类似),天然支持范围. - -- hash索引不支持使用索引进行排序,原理同上. -- hash索引不支持模糊查询以及多列索引的最左前缀匹配.原理也是因为hash函数的不可预测.**AAAA**和**AAAAB**的索引没有相关性. -- hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询. -- hash索引虽然在等值查询上较快,但是不稳定.性能不可预测,当某个键值存在大量重复的时候,发生hash碰撞,此时效率可能极差.而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节点,且树的高度较低. - -因此,在大多数情况下,直接选择B+树索引可以获得稳定且较好的查询速度.而不需要使用hash索引. - -#### **15. 非聚簇索引一定会回表查询吗?** - -不一定,这涉及到查询语句所要求的字段是否全部命中了索引,如果全部命中了索引,那么就不必再进行回表查询. - -举个简单的例子,假设我们在员工表的年龄上建立了索引,那么当进行`select age from employee where age < 20`的查询时,在索引的叶子节点上,已经包含了age信息,不会再次进行回表查询. - -#### 16.如何查询最后一行记录? - -``` -select * from table_name order by id desc limit 1; -``` - -#### 17.MySQL自增id不连续问题? - -- 唯一键冲突 -- 事务回滚 -- 批量申请自增id的策略 - -#### 18.sql注入问题? - -原因:用户传入的参数中注入符合sql的语法,从而破坏原有sql结构语意,达到攻击效果。 - -#### 19.什么是3NF(范式)? - -- 1NF 指的是数据库表中的任何属性都具有原子性的,不可再分解 -- 2NF 是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性 -- 3NF是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余 - -#### 20. NULL和空串判断? - -NULL值是没有值,,它不是空串。如果指定''(两个单引号,其间没有字符),这在NOT NULL列中是允许的。空串是一个有效的值,它不是无值。 - -判断NULL需要用 IS NULL 或者 IS NOT NULL。 - -#### 21.什么是事务? - -可以用来维护数据库的完整性,它保证成批的MySQL操作要么完全执行,要么完全不执行。 - -#### 22.事务4个特性? - -事务是必须满足4个条件(ACID): - -- **原子性 Atomicity:**一个事务中的所有操作,要么全部完成,要么全部不完成,最小的执行单位。 -- **一致性 Consistency:**事务执行前后,都处于一致性状态。 -- **隔离性 Isolation:**数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。 -- **持久性 Durability:**事务执行完成后,对数据的修改就是永久的,即便系统故障也不会丢失。 - -#### 23.事务隔离级别分别是? - -- READ_UNCOMMITTED 这是事务最低的隔离级别,它充许另外一个事务可以看到这个事务未提交的数据。解决第一类丢失更新的问题,但是会出现脏读、不可重复读、第二类丢失更新的问题,幻读 。 -- READ_COMMITTED 保证一个事务修改的数据提交后才能被另外一个事务读取,即另外一个事务不能读取该事务未提交的数据。解决第一类丢失更新和脏读的问题,但会出现不可重复读、第二类丢失更新的问题,幻读问题 -- REPEATABLE_READ 保证一个事务相同条件下前后两次获取的数据是一致的 (注意是 一个事务,可以理解为事务间的数据互不影响)解决第一类丢失更新,脏读、不可重复读、第二类丢失更新的问题,但会出幻读。 -- SERIALIZABLE 事务串行执行,解决了脏读、不可重复读、幻读。但效率很差,所以实际中一般不用。 - -#### 24.InnoDB默认事务隔离级别?如何查看当前隔离级别 - -可重复读(REPEATABLE-READ) - -查看: - -``` -mysql> select @@global.tx_isolation; -+———————————+ -| @@global.tx_isolation | -+———————————+ -| REPEATABLE-READ | -+———————————+ -1 row in set, 1 warning (0.01 sec) -``` - -#### 25.什么是锁? - -数据库的锁是为了支持对共享资源进行并发访问,提供数据的完整性和一致性,这样才能保证在高并发的情况下,访问数据库的时候,数据不会出现问题。 - -#### 26.死锁? - -是指两个或两个以上进程执行过程中,因竞争共享资源造成的相互等待现象。 - -#### 27.如何处理死锁? - -- 设置超时时间。超时后自动释放。 -- 发起死锁检测,主动回滚其中一条事务,让其他事务继续执行。 - -#### 28.如何创建用户?授权? - -创建用户: - -``` -CREATE USER 'username'@'host' IDENTIFIED BY 'password'; -``` - -授权: - -``` -GRANT privileges ON databasename.tablename TO 'username'@'host'; -``` - -- username:用户名 -- host:可以登陆的主机地址。本地用户用localhost表示,任意远程主机用通配符%。 -- password:登陆密码,密码可以为空表示不需要密码登陆服务器 -- databasename: 数据库名称。 -- tablename:表名称,*代表所有表。 - -#### 29.如何查看表结构? - -​ desc table_name; - -``` -mysql> desc zipkin_spans; -+———————+———————+———+——+————+———+ -| Field | Type | Null | Key | Default | Extra | -+———————+———————+———+——+————+———+ -| trace_id_high | bigint(20) | NO | PRI | 0 | | -| trace_id | bigint(20) | NO | PRI | NULL | | -| id | bigint(20) | NO | PRI | NULL | | -| name | varchar(255) | NO | MUL | NULL | | -| parent_id | bigint(20) | YES | | NULL | | -| debug | bit(1) | YES | | NULL | | -| start_ts | bigint(20) | YES | MUL | NULL | | -| duration | bigint(20) | YES | | NULL | | -+———————+———————+———+——+————+———+ -8 rows in set (0.01 sec) -``` - -#### 30.Mysql删除表的几种方式?区别? - -1.delete : 仅删除表数据,支持条件过滤,支持回滚。记录日志。因此比较慢。 - -``` -delete from table_name; -``` - -2.truncate: 仅删除所有数据,不支持条件过滤,不支持回滚。不记录日志,效率高于delete。 - -``` -truncate table table_name; -``` - -3.drop:删除表数据同时删除表结构。将表所占的空间都释放掉。删除效率最高。 - -``` -drop table table_name; -``` - -#### 31.like走索引吗? - -Xxx% 走索引, %xxx不走索引。 - -#### 32.什么是回表? - -在普通索引查到主键索引后,再去主键索引定位记录。等于说非主键索引需要多走一个索引树。 - -#### 33.如何避免回表? - -索引覆盖被查询的字段。 - -#### 34.索引覆盖是什么? - -如果一个索引包含(或覆盖)所有需要查询的字段的值,称为‘覆盖索引’。 - -#### 35.视图的优缺点? - -**优点** - -> 简单化,数据所见即所得 -> -> 安全性,用户只能查询或修改他们所能见到得到的数据 -> -> 逻辑独立性,可以屏蔽真实表结构变化带来的影响 - -**缺点** - -> 性能相对较差,简单的查询也会变得稍显复杂 -> -> 修改不方便,特变是复杂的聚合视图基本无法修改 - -#### 36.主键和唯一索引区别? - -本质区别,主键是一种约束,唯一索引是一种索引。 - -主键不能有空值(非空+唯一),唯一索引可以为空。 - -主键可以是其他表的外键,唯一索引不可以。 - -一个表只能有一个主键,唯一索引 可以多个。 - -都可以建立联合主键或联合唯一索引。 - -主键-》聚簇索引,唯一索引->非聚簇索引。 - -#### 37.如何随机获取一条记录? - -``` -SELECT * FROM table_name ORDER BY rand() LIMIT 1; -``` - -#### 38.Mysql中的数值类型? - -![image-20200422123555254](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200422123555254.png) - -#### 39.查看当前表有哪些索引? - -``` -show index from table_name; -``` - -#### 40.索引不生效的情况? - -- 使用不等于查询 -- NULL值 -- 列参与了数学运算或者函数 -- 在字符串like时左边是通配符.比如 %xxx -- 当mysql分析全表扫描比使用索引快的时候不使用索引. -- 当使用联合索引,前面一个条件为范围查询,后面的即使符合最左前缀原则,也无法使用索引. - -#### 41.MVVC? - -MVCC 全称是多版本并发控制系统,InnoDB 的 MVCC 是通过在每行记录后面保存两个隐藏的列来实现,这两个列一个保存了行的创建时间,一个保存行的过期时间(删除时间)。当然存储的并不是真实的时间而是系统版本号(system version number)。每开始一个新的事务,系统版本号都会自动新增,事务开始时刻的系统版本号会作为事务的版本号,用来查询到每行记录的版本号进行比较。 - -#### 42.sql语句的执行流程? - -客户端连接数据库,验证身份。 - -获取当前用户权限。 - -当你查询时,会先去缓存看看,如果有返回。 - -如果没有,分析器对sql做词法分析。 - -优化器对sql进行“它认为比较好的优化”。 - -执行器负责具体执行sql语句。 - -最后把数据返回给客户端。 - -#### 43.如何获取select 语句执行计划? - -explain sql; - -#### 44.explain列有哪些?含义? - -![image-20200501154131370](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200501154131370.png) - -一、 id - -SQL查询中的序列号。 - -id列数字越大越先执行,如果说数字一样大,那么就从上往下依次执行。 - -二、select_type - - ![image-20200501154212022](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200501154212022.png) - -三、table - -显示这一行的数据是关于哪张表的。不一定是实际存在的表名。 可以为如下的值: - -- : 引用id为M和N UNION后的结果。 -- : 引用id为N的结果派生出的表。派生表可以是一个结果集,例如派生自FROM中子查询的结果。 -- : 引用id为N的子查询结果物化得到的表。即生成一个临时表保存子查询的结果。 - -四、type - -这是**最重要的字段之一**,显示查询使用了何种类型。从最好到最差的连接类型依次为: - -> system,const,eq_ref,ref,fulltext,ref_or_null,index_merge,unique_subquery,index_subquery,range,index,ALL - -1、system - -表中只有一行数据或者是空表,这是const类型的一个特例。且只能用于myisam和memory表。如果是Innodb引擎表,type列在这个情况通常都是all或者index - -2、const - -最多只有一行记录匹配。当联合主键或唯一索引的所有字段跟常量值比较时,join类型为const。其他数据库也叫做唯一索引扫描 - -3、eq_ref - -多表join时,对于来自前面表的每一行,在当前表中只能找到一行。这可能是除了system和const之外最好的类型。当主键或唯一非NULL索引的所有字段都被用作join联接时会使用此类型。 - -eq_ref可用于使用'='操作符作比较的索引列。比较的值可以是常量,也可以是使用在此表之前读取的表的列的表达式。 - -> 相对于下面的ref区别就是它使用的唯一索引,即主键或唯一索引,而ref使用的是非唯一索引或者普通索引。 eq_ref只能找到一行,而ref能找到多行。 - -4、ref - -对于来自前面表的每一行,在此表的索引中可以匹配到多行。若联接只用到索引的最左前缀或索引不是主键或唯一索引时,使用ref类型(也就是说,此联接能够匹配多行记录)。 - -ref可用于使用'='或'<=>'操作符作比较的索引列。 - -5、 fulltext - -使用全文索引的时候是这个类型。要注意,**全文索引的优先级很高**,若全文索引和普通索引同时存在时,mysql不管代价,优先选择使用全文索引 - -6、ref_or_null - -跟ref类型类似,只是增加了null值的比较。实际用的不多。 - -7、index_merge - -表示查询使用了两个以上的索引,最后取交集或者并集,常见and ,or的条件使用了不同的索引,官方排序这个在ref_or_null之后,但是实际上由于要读取多个索引,性能可能大部分时间都不如range - -8、unique_subquery - -用于where中的in形式子查询,子查询返回不重复值唯一值,可以完全替换子查询,效率更高。 该类型替换了下面形式的IN子查询的ref: `value IN (SELECT primary_key FROM single_table WHERE some_expr)` - -9、index_subquery - -该联接类型类似于unique_subquery。适用于非唯一索引,可以返回重复值。 - -10、range - -索引范围查询,常见于使用 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN()或者like等运算符的查询中。 - -11、index - -索引全表扫描,把索引从头到尾扫一遍。这里包含两种情况: 一种是查询使用了覆盖索引,那么它只需要扫描索引就可以获得数据,这个效率要比全表扫描要快,因为索引通常比数据表小,而且还能避免二次查询。在extra中显示Using index,反之,如果在索引上进行全表扫描,没有Using index的提示。 - -12、all - -全表扫描,性能最差。 - -五、possible_keys - -查询可能使用到的索引都会在这里列出来。 - -六、Key - -key列显示MySQL实际使用的键(索引) - -要想强制MySQL使用或忽视possible_keys列中的索引,可以使用FORCE INDEX、USE INDEX或者IGNORE INDEX。 - -select_type为index_merge时,这里可能出现两个以上的索引,其他的select_type这里只会出现一个。 - -七、key_len - -表示索引中使用的字节数。 - -key_len只计算where条件用到的索引长度,而排序和分组就算用到了索引,也不会计算到key_len中。 - -不损失精确性的情况下,长度越短越好 。 - -八、ref - -表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值。 - -九、rows - -rows 也是一个重要的字段。 这是mysql估算的需要扫描的行数(不是精确值)。 - -十、Extra - -该列包含MySQL解决查询的详细信息,有以下几种情况: - -- Using where:列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤。 -- Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询。 -- Using filesort:MySQL中无法利用索引完成的排序操作称为“文件排序”。 -- Using join buffer:改值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。 -- Impossible where:这个值强调了where语句会导致没有符合条件的行。 -- Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行。 - -链接:https://www.jianshu.com/p/8fab76bbf448 - -#### 45.MySql最多创建多少列索引? - -16 - -#### 46.为什么最好建立一个主键? - -主键是数据库确保数据行在整张表唯一性的保障,即使业务上本张表没有主键,也建议添加一个自增长的ID列作为主键.设定了主键之后,在后续的删改查的时候可能更加快速以及确保操作数据范围安全. - -#### 47.**字段为什么要求建议为not null?** - -MySQL官网这样介绍: - -> NULL columns require additional space in the rowto record whether their values are NULL. For MyISAM tables, each NULL columntakes one bit extra, rounded up to the nearest byte. - -null值会占用更多的字节,且会在程序中造成很多与预期不符的情况. - -#### 48.**varchar(10)和int(10)代表什么含义** - -varchar的10代表了申请的空间长度,也是可以存储的数据的最大长度,而int的10只是代表了展示的长度,不足10位以0填充.也就是说,int(1)和int(10)所能存储的数字大小以及占用的空间都是相同的,只是在展示时按照长度展示。 - -#### 49.视图是什么?对比普通表优势? - -视图(View)是一种虚拟存在的表,对于使用视图的用户来说基本上是透明的。视图并 不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时 动态生成的。 - - 视图相对于普通的表的优势主要包括以下几项。 - -- 简单:使用视图的用户完全不需要关心后面对应的表的结构、关联条件和筛选条件, - - 对用户来说已经是过滤好的复合条件的结果集。 - -- 安全:使用视图的用户只能访问他们被允许查询的结果集,对表的权限管理并不能 - - 限制到某个行某个列,但是通过视图就可以简单的实现。 - -- 数据独立:一旦视图的结构确定了,可以屏蔽表结构变化对用户的影响,源表增加 - - 列对视图没有影响;源表修改列名,则可以通过修改视图来解决,不会造成对访问 者的影响。 - -#### 50.count(*)在不同引擎的实现方式? - -MyISAM :把一个表的总行数存在了磁盘上,执行 count(*) 的时候会直接返回这个数,效率很高。 - -InnoDB : 比较麻烦,它执行 count(*) 的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数。 - -参考: - -- 《深入浅出MySQL》 -- 《高性能MySql》 -- 《MySQL技术内幕(第5版)》 -- 《MySQL必知必会》 -- 极客时间:MySQL实战45讲 -- 百度百科 - -![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file From a9267fa59e82ff2f30bbee95feddf13260b8bb91 Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:37:54 +0800 Subject: [PATCH 09/19] Delete Mybatis.md --- Docs/Mybatis.md | 245 ------------------------------------------------ 1 file changed, 245 deletions(-) delete mode 100644 Docs/Mybatis.md diff --git a/Docs/Mybatis.md b/Docs/Mybatis.md deleted file mode 100644 index 5e1579c..0000000 --- a/Docs/Mybatis.md +++ /dev/null @@ -1,245 +0,0 @@ -## Mybatis - -#### 1.什么是Mybatis? - -MyBatis 是一款优秀的支持自定义 SQL 查询、存储过程和高级映射的持久层框架,消除了 几乎所有的 JDBC 代码和参数的手动设置以及结果集的检索 。 MyBatis 可以使用 XML 或注解进 行配置和映射, MyBatis 通过将参数映射到配置的 SQL 形成最终执行的 SQL 语句 ,最后将执行 SQL 的结果映射成 Java对象返回。 - -#### 2.Hibernate优点? - -Hibernate建立在POJO和数据库表模型的直接映射关系上。通过xml或注解即可和数据库表做映射。通过pojo直接可以操作数据库的数据。它提供的是全表的映射模型。 - -- 消除代码映射规则,被分离到xml或注解里配置。 -- 无需在管理数据库连接,配置在xml中即可。 -- 一个会话中,不要操作多个对象,只要操作Session对象即可。 -- 关闭资源只需关闭Session即可。 - -#### 3.Hibernate缺点? - -- 全表映射带来的不便,比如更新需要发送所有的字段。 -- 无法根据不同的条件组装不同的sql。 -- 对多表关联和复杂的sql查询支持较差,需要自己写sql,返回后,需要自己将数据组成pojo。 -- 不能有效支持存储过程。 -- 虽然有hql,但是性能较差,大型互联网需要优化sql,而hibernate做不到。 - -#### 4.Mybatis优点? - -1. 小巧,学习成本低,会写sql上手就很快了。 -2. 比jdbc,基本上配置好了,大部分的工作量就专注在sql的部分。 -3. 方便维护管理,sql不需要在Java代码中找,sql代码可以分离出来,重用。 -4. 接近jdbc,灵活,支持动态sql。 -5. 支持对象与数据库orm字段关系映射。 - -#### 5.Mybatis缺点? - -- 由于工作量在sql上,需要 sql的熟练度高。 -- 移植性差。sql语法依赖数据库。不同数据库的切换会因语法差异,会报错。 - -#### 6.什么时候用Mybatis? - -如果你需要一个灵活的、可以动态生成映射关系的框架。目前来说,因为适合,互联网项目用Mybatis的比例还是很高滴。 - -#### 7.Mybatis的核心组件有哪些?分别是? - -SqlSessionFactoryBuilder(构造器) :它会根据配置信息或者代码来生成SqlSessionFactory。 - -SqlSessionFactory(工厂接口):依靠工厂来生成SqlSession。 - -SqlSession(会话):是一个既可以发送 sql去执行返回结果,也可以获取Mapper接口。 - -SQL Mapper:它是新设计的组件,是由一个Java接口和XML文件(或注解)构成的。需要给出对象的SQl和映射规则。它负责发送SQL去执行,并返回结果。 - -#### 8.#{}和${}的区别是什么? - -${}是字符串替换,#{}是预编译处理。一般用#{}防止 sql注入问题。 - -#### 9.Mybatis中9个动态标签是? - -- if -- c h o o s e (when 、 oterwise) -- trim (where、 set) -- foreach -- bind - -#### 10.xml映射文件中,有哪些标签? - -select|insert|updae|delete|resultMap|parameterMap|sql|include|selectKey 加上9个动态标签,其中为sql片段标签,通过标签引入sql片段,为不支持自增的主键生成策略标签。 - -#### 11.Mybatis支持注解吗?优点?缺点? - -支持。 - -优点:对于需求简单sql逻辑简单的系统,效率较高。 - -缺点: 当sql变化需要重新编译代码,sql复杂时,写起来更不方便,不好维护。 - -#### 12.Mybatis动态sql? - -Mybatis 动态 sql 可以让我们在 Xml 映射文件内,以标签的形式编写动态 sql,完成逻辑 -判断和动态拼接 sql 的功能 - -#### 13.**Mybatis 是如何进行分页的?分页插件的原理是什么?** - -1)Mybatis 使用 RowBounds 对象进行分页,也可以直接编写 sql 实现分页,也可以使用 -Mybatis 的分页插件。 -2)分页插件的原理:实现 Mybatis 提供的接口,实现自定义插件,在插件的拦截方法内拦 -截待执行的 sql,然后重写 sql。 -举例:select from student,拦截 sql 后重写为:select t. from (select from student)t -limit 0,10 - -#### 14.如何获取自增主键? - -注解: - -```java -@Options(useGeneratedKeys =true, keyProperty =”id”) - -int insert( ); -``` - -Xml: - -```xml - - -sql - - -``` - -#### 15.为什么Mapper接口没有实现类,却能被正常调用? - -这是因为MyBatis在Mapper接口上使用了动态代理。 - -#### 16.用注解好还是xml好? - -简单的增删改查可以注解。 - -复杂的sql还是用xml,官方也比较推荐xml方式。 - -xml的方式更便于统一维护管理代码。 - -#### 17.如果不想手动指定别名,如何用驼峰的形式自动映射? - -mapUnderscoreToCamelCase=true - -#### 18.当实体属性名和表中字段不一致,怎么办? - - 一、别名: 比如 order_num - -select order_num orderNum - -二、通过做映射 - -​ - -三、如果是驼峰注解用17问的方式。 - -#### 19.嵌套查询用什么标签? - -association 标签的嵌套查询常用的属性如下 。 - -select:另一个映射查询的 id, MyBatis会额外执行这个查询获取嵌套对象的结果。 - -column:列名(或别名),将主查询中列的结果作为嵌套查询的 参数,配置 方式如 column={propl=coll , prop2=col2}, propl 和 prop2 将作为嵌套查询的参数。 - -fetch Type:数据加载方式,可选值为 lazy 和 eager,分别为延迟加载和积极加载, 这个配置会覆盖全局的 lazyLoadingEnabled 配置 。 - -#### 20.like模糊查询怎么写? - -(1)'%${value}%' 不推荐 - -(2)CONCAT('%',#{value},'%') 推荐 - - (3) like '%'|| #{value} || '%' - -(4)使用bind - - - - LIKE #{pattern} - -#### 21.Mybatis支持枚举吗? - -支持。有转换器EnumTypeHandler和EnumOrdinalTypeHandler - -```xml - - - -``` - -#### 22.SqlSessionFactoryBuilder生命周期? - -利用xml或者Java代码获得资源构建SqlSessionFactoryBuilder,它可以创建多个SessionFactory,一旦构建成功SessionFactory,就可以回收它了。 - -#### 23.一级缓存的结构?如何开启一级缓存?如何不使用一级缓存? - -Map 。默认情况下,一级缓存是开启的。< select >标签内加属性flushCache=true。 - -#### 24.二级缓存如何配置? - -```xml - -〈 !一其他自己直一 〉 - - -``` - -这个参数是二级缓存的全局开关,默认值是 true,初始状态 为启用状态 。 如果把这个参数设置为 false,即使有后面 的 二级缓 存配置,也不会生效 。 - -#### 25.**简述 Mybatis 的插件运行原理,以及如何编写一个插件?** - -1)Mybatis 仅可以编写针对 ParameterHandler、ResultSetHandler、StatementHandler、 -Executor 这 4 种接口的插件,Mybatis 通过动态代理,为需要拦截的接口生成代理对象以实 -现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是 -InvocationHandler 的 invoke()方法,当然,只会拦截那些你指定需要拦截的方法。 -2)实现 Mybatis 的 Interceptor 接口并复写 intercept()方法,然后在给插件编写注解,指定 -要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。 - -#### 26.二级缓存的回收策略有哪些? - -eviction (收回策略) - -LRU(最近最少使用的) : 移除最长时间不被使用的对象,这是默认值 。 - - IFO(先进先出〉 : 按对象进入缓存的顺序来移除它们 。 - - SOFT(软引用) : 移除基于垃圾回收器状态和软引用规则的对象 。 - -WEAK (弱引用) : 更积极地移除基于垃圾收集器状态和弱引用规则的对象 。 - -#### 27.Mybatis的Xml文件中id可以重复吗? - -同一namespace下,id不可重复。不同namespace下,可以重复。 - -#### 28. 和Mybatis搭配java框架中比较好用的缓存框架?有哪些特点? - -EhCache是一个纯牌的 Java进程内的缓存框架,具有快速、精干等特点。 - -具体来说, EhCache主要的特性如下 。 - -- 快速。 -- 简单。 -- 多种缓存策略 。 - -- 缓存数据有内存和磁盘两级,无须担心容量问题 。 - -- 缓存数据会在虚拟机重启 的过程中写入磁盘。 - -- 可 以通过 RMI、可插入 API 等方式进行分布式缓存。 - -- .具有缓存和缓存管理器的侦 昕接口。 - -- 支持多缓存管理器实例 以及一个实例的多个缓存区域。 - -参考: - -- 《 MyBatis从入门到精通》 - -- 《深入浅出 MyBatis技术原理与实战》 - -- 《MyBatis技术》 - - -![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) - From d1e5953aae1d1217a5a493d40593c746cc9cbfc9 Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:38:38 +0800 Subject: [PATCH 10/19] Delete Nginx.md --- Docs/Nginx.md | 184 -------------------------------------------------- 1 file changed, 184 deletions(-) delete mode 100644 Docs/Nginx.md diff --git a/Docs/Nginx.md b/Docs/Nginx.md deleted file mode 100644 index 47aca8b..0000000 --- a/Docs/Nginx.md +++ /dev/null @@ -1,184 +0,0 @@ -## Nginx - -#### 1.什么是nginx? - -Nginx是一个高性能的HTTP和反向代理服务器。同时也是一个 IMAP/POP3/SMTP 代理服务器。 官方网站:http://nginx.org。 - -#### 2.nginx主要特征? - -处理静态文件,索引文件以及自动索引;打开文件描述符缓冲. 无缓存的反向代理加速,简单的负载均衡和容错. FastCGI,简单的负载均衡和容错.模块化的结构。包括 gzipping, byte ranges, chunked responses,以及 SSI-filter 等filter。如果由 FastCGI 或其它代理服务器处理单页中存在的多个 SSI,则这项处理可以并行 运行,而不需要相互等待。 - -支持 SSL 和 TLSSNI. - -Nginx 它支持内核 Poll 模型,能经受高负载的考验,有报告表明能支持高达 50,000 个并发连接数。 - -Nginx 具有很高的稳定性。 例如当前 apache 一旦上到 200 个以上进程,web 响应速度就明显非常缓慢了。而 Nginx 采取了分阶段资源分配技术,使得它的 CPU 与内存占用率非常低。nginx 官方表示保持 10,000 个没有活动的连接,它只占 2.5M 内存,所以类似 DOS 这样的攻击对 nginx 来说基本上是毫无用处的。 - -Nginx 支持热部署。它的启动特别容易, 并且几乎可以做到 7*24 不间断运行,即使运 行数个月也不需要重新启动。对软件版本进行进行热升级。 - -Nginx 采用 master-slave 模型,能够充分利用 SMP 的优势,且能够减少工作进程在磁 盘 I/O 的阻塞延迟。当采用 select()/poll()调用时,还可以限制每个进程的连接数。 - -Nginx 代码质量非常高,代码很规范,手法成熟, 模块扩展也很容易。特别值得一提的是强大的 Upstream 与 Filter 链。 - -Nginx 采用了一些 os 提供的最新特性如对 sendfile (Linux2.2+),accept-filter (FreeBSD4.1+),TCP_DEFER_ACCEPT (Linux 2.4+)的支持,从而大大提高了性能。 - -免费开源,可以做高并发负载均衡。 - -#### 3.nginx 常用命令? - -启动 nginx 。 -停止 nginx -s stop 或 nginx -s quit 。 -重载配置 ./sbin/nginx -s reload(平滑重启) 或 service nginx reload 。 -重载指定配置文件 .nginx -c /usr/local/nginx/conf/nginx.conf 。 -查看 nginx 版本 nginx -v 。 -检查配置文件是否正确 nginx -t 。 -显示帮助信息 nginx -h 。 - -#### 4.工作模式及连接数上限? - -```nginx -events { - -use epoll; #epoll 是多路复用 IO(I/O Multiplexing)中的一种方 式,但是仅用于 linux2.6 以上内核,可以大大提高 nginx 的性能 - -worker_connections 1024;#单个后台 worker process 进程的最大并发链接数 - -# multi_accept on; -} -``` - -#### 5.nginx负载均衡几种算法? - -5种。 - -1.轮询模式(默认) -每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。 -2.权重模式 -指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况 -3.IP_hash模式 (IP散列) -每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。 -4.url_hash模式 -5.fair模式 -按后端服务器的响应时间来分配请求,响应时间短的优先分配。 - -#### 6.nginx有几种进程模型? - -分为master-worker模式和单进程模式。在master-worker模式下,有一个master进程和至少一个的worker进程,单进程模式顾名思义只有一个进程。 - -#### 7.如何定义错误提示页面? - -\# 定义错误提示页面 - -```nginx -error_page 500 502 503 504 /50x.html; - -location = /50x.html { root /root; - -} -``` - -#### 8.如何精准匹配路径? - -location =开头表示精准匹配 - -```nginx -location = /get { -#规则 A } -``` - -#### 9.路径匹配优先级? - -多个 location 配置的情况下匹配顺序为 -首先匹配 =,其次匹配^~, 其次是按文件中顺序的正则匹配,最后是交给 / 通用匹配。当 有匹配成功时候,停止匹配,按当前匹配规则处理请求。 - -#### 10.如何把请求转发给后端应用服务器? - -```nginx -location = / { - -proxy_pass http://tomcat:8080/index - -} -``` - -#### 11.如何根据文件类型设置过期时间? - -```nginx -location ~* \.(js|css|jpg|jpeg|gif|png|swf)$ { -if (-f $request_filename) { -expires 1h; -break; -} -} -``` - -#### 12.禁止访问某个目录? - -```nginx -location ^~/path/ { - deny all; -} - - -``` - -#### 13.nginx负载均衡实现过程? - -首先在 http 模块中配置使用 upstream 模块定义后台的 webserver 的池子,名为 proxy-web,在池子中我们可以添加多台后台 webserver,其中状态 检查、调度算法都是在池子中配置;然后在 serverr 模块中定义虚拟主机,但是这个虚拟主 机不指定自己的 web 目录站点,它将使用 location 匹配 url 然后转发到上面定义好的 web 池子中,最后根据调度策略再转发到后台 web server 上 。 - -#### 14.负载均衡配置? - -```nginx -Upstream proxy_nginx { - -server 192.168.0.254 weight=1max_fails=2 fail_timeout=10s ; -server 192.168.0.253 weight=2 max_fails=2fail_timeout=10s; -server192.168.0.252 backup; server192.168.0.251 down; - -} - -server{ - listen 80; - server_name xiaoka.com; - -location / { - - proxy_pass http:// proxy_nginx; - proxy_set_header Host - proxy_set_header X-Real-IP - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - -} -``` - -#### 15.设置超时时间? - -```nginx -http { - ………. - keepalive_timeout 60; ###设置客户端连接保持会话的超时时间,超过这个时间,服务器会关闭该连接。 tcp_nodelay on; - \####打开 tcp_nodelay,在包含了 keepalive 参数才有效 - client_header_timeout 15; ####设置客户端请求头读取超时时间,如果超过这个时间,客户端还没有发送任何数据, Nginx 将返回“Request time out(408)”错误 - client_body_timeout 15; - -\####设置客户端请求主体读取超时时间,如果超过这个时间,客户端还没有发送任何数据, Nginx 将返回“Request time out(408)”错误 - send_timeout 15; ####指定响应客户端的超时时间。这个超过仅限于两个连接活动之间的时间,如果超过这 个时间,客户端没有任何活动,Nginx 将会关闭连接。 - -…… } -``` - -#### 16.开启压缩功能好处?坏处? - -好处:压缩是可以节省带宽,提高传输效率 - -坏处:但是由于是在服务器上进行压缩,会消耗服务器起源 - -参考: - -- 《Nginx从入门到精通》 - -- 《Nginx高性能Web服务器详解》 - -- 《深入理解nginx》 - - ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) From 7401e0a3d4d7490d67905c6c810d7202cd9abfcb Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:38:46 +0800 Subject: [PATCH 11/19] Delete Redis.md --- Docs/Redis.md | 433 -------------------------------------------------- 1 file changed, 433 deletions(-) delete mode 100644 Docs/Redis.md diff --git a/Docs/Redis.md b/Docs/Redis.md deleted file mode 100644 index 148dbb9..0000000 --- a/Docs/Redis.md +++ /dev/null @@ -1,433 +0,0 @@ -## Redis - -#### 1.Redis是什么? - -Redis是一个开放源代码(BSD许可)的内存中数据结构存储,可用作数据库,缓存和消息代理,是一个基于键值对的NoSQl数据库。 - -#### 2.Redis特性? - -- 速度快 -- 基于键值对的数据结构服务器 -- 丰富的功能、丰富的数据结构 -- 简单稳定 -- 客户端语言多 -- 持久化 -- 主从复制 -- 高可以 & 分布式 - -#### 3.Redis合适的应用场景? - -- 缓存 -- 排行榜 -- 计数器 -- 分布式会话 -- 分布式锁 -- 社交网络 -- 最新列表 -- 消息系统 - -#### 4.除了Redis你还知道哪些NoSQL数据库? - -MongoDB、MemcacheDB、Cassandra、CouchDB、Hypertable、Leveldb。 - -#### 5.Redis和Memcache区别? - -支持的存储类型不同,memcached只支持简单的k/v结构。redis支持更多类型的存储结构类型(详见问题6)。 - -memcached数据不可恢复,redis则可以把数据持久化到磁盘上。 - -新版本的redis直接自己构建了VM 机制 ,一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。 - -redis当物理内存用完时,可以将很久没用到的value交换到磁盘。 - -#### 6.Redis的有几种数据类型? - -基础:字符串(String)、哈希(hash)、列表(list)、集合(set)、有序集合(zset)。 - -还有HyperLogLog、流、地理坐标等。 - -#### 7.Redis有哪些高级功能? - -消息队列、自动过期删除、事务、数据持久化、分布式锁、附近的人、慢查询分析、Sentinel 和集群等多项功能。 - -#### 8.安装过Redis吗,简单说下步骤? - - 1.下载Redis指定版本源码安装包压缩到当前目录。 - -1. 解压缩Redis源码安装包。 - -2. 建立一个redis目录软链接,指向解压包。 - -3. 进入redis目录 - -4. 编译 - -5. 安装 - - 对于使用docker的童靴来说就比较容易了。 - - docker pull redis - -#### 9.redis几个比较主要的可执行文件?分别是? - -![image-20200425221430652](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200425221430652.png) - -#### 10.启动Redis的几种方式? - -1.默认配置 : - -./redis-server - -2.运行启动: redis-server 加上要修改配置名和值(可以是多对),没有配置的将使用默认配置。 - -例如: redis-server ———port 7359 - -3.指定配置文件启动: - -./redis-server /opt/redis/redis.conf - -#### 11.Redis配置需要自己写?如何配置? - -redis目录下有一个redis.conf的模板配置。所以只需要复制模板配置然后修改即可。 - -一般来说大部分生产环境都会用指定配置文件的方式启动redis。 - -#### 12.Redis客户端命令执行的方式? - -1.交互方式: - -``` -redis-cli -h 127.0.0.1 -p 6379 -``` - -连接到redis后,后面执行的命令就可以通过交互方式实现了。 - -2.命令行方式: - -``` -redis-cli -h 127.0.0.1 -p 6379 get value -``` - -#### 13.如何停止redis服务? - -Kill -9 pid (粗暴,请不要使用,数据不仅不会持久化,还会造成缓存区等资源不能被优雅关闭) - -可以用redis 的shutdown 命令,可以选择是否在关闭前持久化数据。 - -``` -redis-cli shutdown nosave|save -``` - -#### 14.如何查看当前键是否存在? - -exists key - -#### 15.如何删除数据? - -del key - -#### 16.redis为什么快?单线程? - -- redis使用了单线程架构和I/O多路复用模型模型。 -- 纯内存访问。 -- 由于是单线程避免了线程上下文切换带来的资源消耗。 - -#### 17.字符串最大不能超过多少? - -512MB - -#### 18.redis默认分多少个数据库? - -16 - -#### 19.redis持久化的几种方式? - -RDB、AOF、混合持久化。 - -#### 20.RDB持久化? - -RDB(Redis DataBase)持久化是把当前进程数据生成快照保存到硬盘的过程。 - -Tips:是以二进制的方式写入磁盘。 - -#### 21.RDB的持久化是如何触发的? - -手动触发: - -save: 阻塞当前Redis服务器,直到RDB过程完成为止,如果数据比较大的话,会造成长时间的阻塞, - -线上不建议。 - -bgsave:redis进程执行 fork操作创作子进程,持久化由子进程负责,完成后自动结束,阻塞只发生在 - -fork阶段,一半时间很短。 - -自动触发: - -save xsecends n: - -表示在x秒内,至少有n个键发生变化,就会触发RDB持久化。也就是说满足了条件就会触发持久化。 - -flushall : - -主从同步触发 - -#### 22.RDB的优点? - -- rdb是一个紧凑的二进制文件,代表Redis在某个时间点上的数据快照。 -- 适合于备份,全量复制的场景,对于灾难恢复非常有用。 -- Redis加载RDB恢复数据的速度远快于AOF方式。 - -#### 23.RDB的缺点? - -- RDB没法做到实时的持久化。中途意外终止,会丢失一段时间内的数据。 -- RDB需要fork()创建子进程,属于重量级操作,可能导致Redis卡顿若干秒。 - -#### 24.如何禁用持久化? - -一般来说生成环境不会用到,了解一下也有好处的。 - -``` -config set save "" -``` - -#### 25.AOF持久化? - -AOF(append only file)为了解决rdb不能实时持久化的问题,aof来搞定。以独立的日志方式记录把每次命令记录到aof文件中。 - -#### 26.如何查询AOF是否开启? - -``` -config get appendonly -``` - -#### 27.如何开启AOF? - -命令行方式: 实时生效,但重启后失效。 - -``` -config set appendonly -``` - -配置文件:需要重启生效,重启后依然生效。 - -``` -appendonly yes -``` - -#### 28.AOF工作流程? - -1.所有写入命令追加到aof_buf缓冲区。 - -2.AOF缓冲区根据对应的策略向硬盘做同步操作。 - -3.随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。 - -4.当redis服务器重启时,可以加载AOF文件进行数据恢复。 - -#### 29.为什么AOF要先把命令追加到缓存区(aof_buf)中? - -Redis使用单线程响应命令,如果每次写入文件命令都直接追加到硬盘,性能就会取决于硬盘的负载。如果使用缓冲区,redis提供多种缓冲区策略,在性能和安全性方面做出平衡。 - -#### 30.AOF持久化如何触发的? - -自动触发:满足设置的策略和满足重写触发。 - -策略:(在配置文件中配置) - -![image-20200427101221526](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200427101221526.png - -手动触发:(执行命令) - -``` -bgrewriteaof -``` - -#### 31.AOF优点? - -- AOF提供了3种保存策略:每秒保存、跟系统策略、每次操作保存。实时性比较高,一般来说会选择每秒保存,因此意外发生时顶多失去一秒的数据。 -- 文件追加写形式,所以文件很少有损坏问题,如最后意外发生少写数据,可通过redis-check-aof工具修复。 -- AOF由于是文本形式,直接采用协议格式,避免二次处理开销,另外对于修改也比较灵活。 - -#### 32.AOF缺点? - -- AOF文件要比RDB文件大。 -- AOF冷备没RDB迅速。 -- 由于执行频率比较高,所以负载高时,性能没有RDB好。 - -#### 33.混合持久化?优缺点? - -一般来说我们的线上都会采取混合持久化。redis4.0以后添加了新的混合持久化方式。 - -优点: - -- 在快速加载的同时,避免了丢失过更多的数据。 - -缺点: - -- 由于混合了两种格式,所以可读性差。 -- 兼容性,需要4.0以后才支持。 - -#### 34.Redis的Java客户端官方推荐?实际选择? - -官方推荐的有3种:Jedis、Redisson和lettuce。 - -一般来说用的比较多的有:Jedis|Redisson。 - -Jedis:更轻量、简介、不支持读写分离需要我们来实现,文档比较少。API提供了比较全面的Redis命令的支持。 - -Redisson:基于Netty实现,性能高,支持异步请求。提供了很多分布式相关操作服务。高级功能能比较多,文档也比较丰富,但实用上复杂度也相对高。和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。 - -#### 35.Redis事务? - -事务提供了一种将多个命令请求打包,一次性、按顺序的执行多个命令的机制。并且在事务执行期间,服务器不会中断事务而改去执行其他客户端命令请求,它会 - -#### 36.Redis事务开始到结束的几个阶段? - -- 开启事务 -- 命令入队 -- 执行事务/放弃事务 - -#### 37.Redis中key的过期操作? - -​ 设置key的生存时间为n秒 - -``` -expire key nseconds -``` - -​ 设置key的生存时间为nmilliseconds - -``` -pxpire key milliseconds -``` - -​ 设置过期时间为timestamp所指定的秒数时间戳 - -``` -expireat key timespamp -``` - - 设置过期时间为timestamp毫秒级时间戳 - -``` -pexpireat key millisecondsTimestamp -``` - -#### 38.Redis过期键删除策略? - -定时删除:在设置的过期时间同时,创建一个定时器在键的过期时间来临时,立即执行队键的操作删除。 - -惰性删除:放任过期键不管,但每次从键空间中获取键时,都检查取得的键是否过期,如果过期就删除,如果没有就返回该键。 - -定期删除:每隔一段时间执行一次删除过期键操作,并通过先吃删除操作执行的时长和频率来减少删除操作对cpu时间的影响。 - -#### 39.Pipeline是什么?为什么要它? - -命令批处理技术,对命令进行组装,然后一次性执行多个命令。 - -可以有效的节省RTT(Round Trip Time 往返时间)。 - -经过测试验证: - -- pipeline执行速度一般比逐条执行快。 -- 客户端和服务的网络延越大,pipeline效果越明显。 - -#### 40.如何获取当前最大内存?如何动态设置? - -获取最大内存: - -``` -config get maxmemory -``` - -设置最大内存: - -命令设置: - -``` -config set maxmemory 1GB -``` - -#### 41.Redis内存溢出控制? - -当Redis所用内存达到maxmemory上限时,会出发相应的溢出策略。 - -#### 42.Redis内存溢出策略? - -1.noeviction(默认策略):拒绝所有写入操作并返回客户端错误信息(error) OOM command not allowed when used memory,只响应读操作。 - -1. volatile-lru:根据LRU算法删除设置了超时属性(expire)的键,直到腾出足够空间为止。如果没有可删除的键对象,回退到noeviction策略。 -2. allkeys-lru:根据LRU算法删除键,不管数据有没有设置超时属性, 直到腾出足够空间为止。 -3. allkeys-random:随机删除所有键,直到腾出足够空间为止。 -4. volatile-random:随机删除过期键,直到腾出足够空间为止。 -5. volatile-tth根据键值对象的ttl属性,删除最近将要过期数据。如果没有,回退到noeviction策略。 - -#### 43.Redis高可用方案? - -Redis Sentinel(哨兵)能自动完成故障发现和转移。 - -#### 44.Redis集群方案? - -Twemproxy、Redis Cluster、Codis。 - -#### 45.Redis Cluster槽范围? - -0~16383 - -#### 46.Redis锁实现思路? - -setnx (set if not exists),如果创建成功则表示获取到锁。 - -setnx lock true 创建锁 - -del lock 释放锁 - -如果中途崩溃,无法释放锁? - -此时需要考虑到超时时间的问题。比如 :expire lock 300 - -由于命令是非原子的,所以还是会死锁,如何解决? - -Redis 支持 set 并设置超时时间的功能。 - -比如: set lock true ex 30 nx - -#### 47.什么是布隆过滤器? - -是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。 - -Tips:当判断一定存在时,可能会误判,当判断不存在时,就一定不存在。 - -#### 48.什么是缓存穿透?处理问题? - -缓存穿透:缓存层不命中,存储层不命中。 - -处理方式1:缓存空对象,不过此时会占用更多内存空间,所以根据大家业务特性去设置超时时间来控制内存占用的问题。 - -处理方式2:布隆过滤器。 - -#### 49.什么是缓存预热? - -就是系统上线后,提前将相关数据加载到缓存系统,避免用户先查库,然后在缓存。 - -#### 50.什么是缓存雪崩?处理问题? - -缓存雪崩:由于缓存层承载着大量请求,有效的保护了存储层,但如果存储层由于某些原因不能提供服务,存储层调用暴增,造成存储层宕机。 - -处理: - -- 保证缓存层服务高可用性。 -- 对缓存系统做实时监控,报警等。 -- 依赖隔离组件为后端限流并降级。 -- 做好持久化,以便数据的快速恢复。 - -参考: - -- 《Redis深度历险:核心原理和应用实践》 -- 《Redis开发与运维》 -- 《Redis设计与实现》 -- https://redis.io/ -- 百度百科 - -![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file From 70d39e43991691f87a4ffeea476043bc477346a4 Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:38:54 +0800 Subject: [PATCH 12/19] Delete Spring.md --- Docs/Spring.md | 344 ------------------------------------------------- 1 file changed, 344 deletions(-) delete mode 100644 Docs/Spring.md diff --git a/Docs/Spring.md b/Docs/Spring.md deleted file mode 100644 index f89a218..0000000 --- a/Docs/Spring.md +++ /dev/null @@ -1,344 +0,0 @@ -## Spring - -#### 1.Spring框架? - -Spring框架是由于软件开发的复杂性而创建的,Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事。从简单性、可测性和松耦合性角度而言,绝大部分Java应用都可以用Spring。 - -#### 2.Spring的整体架构? - -![image-20200425151510055](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200425151510055.png) - - - -大约分为20个模块。 - -#### 3.Spring可以做什么? - -![image-20200425152654798](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200425152654798.png) - -#### 4.Spring的优点?缺点? - -优点: - -- Spring属于低侵入设计。 -- IOC将对象之间的依赖关系交给Spring,降低组件之间的耦合,实现各个层之间的解耦,让我们更专注于业务逻辑。 -- 提供面向切面编程。 -- 对各种主流插件提供很好的集成支持。 -- 对事务支持的很好,只要配置即可,无须手动控制。 - -缺点: - -- 依赖反射,影响性能。 - -#### 5.你能说几个Spring5的新特性吗? - -- spring5整个框架基于java8 -- 支持http/2 -- Spring Web MVC支持最新API -- Spring WebFlux 响应式编程 -- 支持Kotlin函数式编程 - -#### 6.IOC? - -负责创建对象、管理对象(通过依赖注入)、整合对象、配置对象以及管理这些对象的生命周期。 - -#### 7.什么是依赖注入? - -依赖注入是Spring实现IoC的一种重要手段,将对象间的依赖关系的控制权从开发人员手里转移到容器。 - -#### 8.IOC注入哪几种方式? - -1.构造器注入 - -2.setter注入 - -3.接口注入(我们几乎不用) - -#### 9.IOC优点?缺点? - -优点: - -- 组件之间的解耦,提高程序可维护性、灵活性。 - -缺点: - -- 创建对象步骤复杂,有一定学习成本。 -- 利用反射创建对象,效率上有损。(对于代码的灵活性和可维护性来看,Spring对于我们的开发带来了很大的便利,这点损耗不算什么哦) - -#### 10.bean的生命周期? - -1.Spring 对bean进行实例化。 - -2.Spring将值和bean的引用注入到 bean对应的属性中。 - -3.如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBeanName()方法。 - -4.如果bean实现了BeanFactoryAware接口, Spring将调用setBeanFactory()方法,将 bean所在的应用引用传入进来。 - -5.如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用引用传入进来。 - -6.如果bean实现了BeanPostProcessor 接口,Spring将调用他们的post-ProcessBeforeInitalization()方法。 - -7.如果bean实现了InitializingBean接口,Spring将调用他们的after-PropertiesSet()方法,类似地,如果bean使用init-method声明了初始化方法,该方法也会被调用。 - -8.如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessAfterInitialization()方法。 - -9.此时, bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用被销毁。 - -10.如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。 - -#### 11.Spring有几种配置方式? - -- 基于xml -- 基于注解 -- 基于Java - -#### 12.Spring中的bean有几种scope? - -- singleton: 单例,每一个bean只创建一个对象实例。 -- prototype,原型,每次对该bean请求调用都会生成各自的实例。 -- request,请求,针对每次HTTP请求都会生成一个新的bean。表示在一次 HTTP 请求内有效。 -- session,在一个http session中,一个bean定义对应一个bean实例。 -- global session:在一个全局http session中,一个bean定义对应一个bean实例。 - -#### 13.什么是AOP(面向切面编程)? - -在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。 - -#### 14.切面有几种类型的通知?分别是? - -前置通知(Before): 目标方法被调用之前调用通知功能。 - -后置通知(After): 目标方法完成之后调用通。 - -返回通知(After-returning): 目标方法成功执行之后调用通知。 - -异常通知(After-throwing): 目标方法抛出异常后调用通知。 - -环绕通知(Around): 在被通知的方法调用之前和调用之后执行自定义的行为。 - -#### 15.什么是连接点 (Join point)? - -连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。 - -#### 16.什么是切点(Pointcut)? - -切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。有些AOP框架允许我们创建动态的切点,可以根据运行时的决策(比如方法的参数值)来决定是否应用通知。 - -#### 17.什么是切面(Aspect)? - -切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 - -#### 18.织入(Weaving)? - -织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。 - -#### 19.引入(Introduction)? - -􏶸􏰫􏶹􏵡􏰸􏱲􏰇􏲘􏱴􏰉􏵇􏶺􏶻􏶼􏰽􏱸􏵼􏶽􏶾􏱎􏶸􏰫􏶹􏵡􏰸􏱲􏰇􏲘􏱴􏰉􏵇􏶺􏶻􏶼􏰽􏱸􏵼􏶽􏶾􏱎引入允许我们向现有的类添加新方法或属性。 - -#### 20.在目标对象的生命周期里有多个点可以进行织入? - -- 编译期:切面在目标类编译时被织入。AspectJ的织入编译器就是以这种方式织入切面的。 -- 类加载期:切面在目标类加载到JVM时被织入。它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ 5的加载时织入(load-time weaving,LTW)就支持以这种方式织入切面。 -- 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。Spring AOP就是以这种方式织入切面的。 - -#### 21.AOP动态代理策略? - -- 如果目标对象实现了接口,默认采用JDK 动态代理。可以强制转为CgLib实现AOP。 -- 如果没有实现接口,采用CgLib进行动态代理。 - -#### 22.什么是MVC框架? - -MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。 - -MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。 - -#### 23.什么是SpringMVC? - -SpringMVC是Spring框架的一个模块。是一个基于MVC的框架。 - -#### 24.SpringMVC的核心? - -DispatcherServlet - -#### 25.SpringMVC的几个组件? - -DispatcherServlet : 前端控制器,也叫中央控制器。相关组件都是它来调度。 - -HandlerMapping : 处理器映射器,根据URL路径映射到不同的Handler。 - -HandlerAdapter : 处理器适配器,按照HandlerAdapter的规则去执行Handler。 - -Handler : 处理器,由我们自己根据业务开发。 - -ViewResolver : 视图解析器,把逻辑视图解析成具体的视图。 - -View : 一个接口,它的实现支持不同的视图类型(freeMaker,JSP等) - -#### 26.SpringMVC工作流程? - -1.用户请求旅程的第一站是DispatcherServlet。 - -2.收到请求后,DispatcherServlet调用HandlerMapping,获取对应的Handler。 - -3.如果有拦截器一并返回。 - -4.拿到Handler后,找到HandlerAdapter,通过它来访问Handler,并执行处理器。 - -5.执行Handler的逻辑。 - -6.Handler会返回一个ModelAndView对象给DispatcherServlet。 - -7.将获得到的ModelAndView对象返回给DispatcherServlet。 - -8.请求ViewResolver解析视图,根据逻辑视图名解析成真正的View。 - -9.返回View给DispatcherServlet。 - -10.DispatcherServlet对View进行渲染视图。 - -11.DispatcherServlet响应用户。 - -#### 27.SpringMVC的优点? - -1.具有Spring的特性。 - -2.可以支持多种视图(jsp,freemaker)等。 - -3.配置方便。 - -4.非侵入。 - -5.分层更清晰,利于团队开发的代码维护,以及可读性好。 - -Tips:Jsp目前很少有人用了。 - -#### 28.单例bean是线程安全的吗? - -不是。具体线程问题需要开发人员来处理。 - -#### 29.Spring从哪两个角度实现自动装配? - -组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。 - -自动装配(autowiring):Spring自动满足bean之间的依赖。 - -#### 30.自动装配有几种方式?分别是? - -no - 默认设置,表示没有自动装配。 - -byName : 根据名称装配。 - -byType : 根据类型装配。 - -constructor : 把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应入参中。 - -autodetect :先尝试constructor装配,失败再尝试byType方式。 - -default:由上级标签的default-autowire属性确定。 - -#### 31.说几个声明Bean 的注解? - -- @Component -- @Service -- @Repository -- @Controller - -#### 32.注入Java集合的标签? - -- 允许有相同的值。 -- 不允许有相同的值。 -- 键和值都只能为String类型。 -- < map > 键和值可以是任意类型。 - -#### 33.**Spring支持的ORM**? - -1. Hibernate -2. iBatis -3. JPA (Java Persistence API) -4. TopLink -5. JDO (Java Data Objects) -6. OJB - -#### 34.@Repository注解? - - Dao 层实现类注解,扫描注册 bean。 - -#### 35.@Value注解? - -讲常量、配置中的变量值、等注入到变量中。 - -#### 36.@Controller注解? - -定义控制器类。 - -#### 37.声明一个切面注解是哪个? - -@Aspect - -#### 38.映射web请求的注解是? - -@RequestMapping - -#### 39.@ResponseBody注解? - -作用是将返回对象通过适当的转换器转成置顶格式,写进response的body区。通常用来返回json、xml等。 - -#### 40.@ResponseBody + @Controller =? - -@RestController - -#### 41.接收路径参数用哪个注解? - -@PathVariable - -#### 42.@Cacheable注解? - -用来标记缓存查询。 - -#### 43.清空缓存是哪个注解? - -@CacheEvict - -#### 44.@Component注解? - -泛指组件,不好归类时,可以用它。 - -#### 45.**BeanFactory 和 ApplicationContext**区别? - -![image-20200428210402245](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200428210402245.png) - -#### 46.@Qualifier注解? - -当创建多个相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配,在这种情况下,你可以使用 @Qualifier 注释和 @Autowired 注释通过指定哪一个真正的 bean 将会被装配来消除混乱。 - -#### 47.事务的注解是? - -@Transaction - -#### 48.Spring事务实现方式有? - -声明式:声明式事务也有两种实现方式。 - -- xml 配置文件的方式。 -- 注解方式(在类上添加 @Transaction 注解)。 - -编码式:提供编码的形式管理和维护事务。 - -#### 49.什么是事务传播? - -事务在嵌套方法调用中如何传递,具体如何传播,取决于事务传播行为。 - -#### 50.Spring事务传播行为有哪些? - -![image-20200428223544334](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200428223544334.png) - -参考: - -- 《Spring in action 4》 -- 《SPRING技术内幕》 -- 《Spring源码深度解析》 -- 《Spring5企业级开发实战》 -- https://spring.io -- 百度百科 \ No newline at end of file From 866662fe5aeffe5dc7e52cb02190d3a159e3e3af Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:39:02 +0800 Subject: [PATCH 13/19] Delete SpringBoot.md --- Docs/SpringBoot.md | 285 --------------------------------------------- 1 file changed, 285 deletions(-) delete mode 100644 Docs/SpringBoot.md diff --git a/Docs/SpringBoot.md b/Docs/SpringBoot.md deleted file mode 100644 index 9c927e5..0000000 --- a/Docs/SpringBoot.md +++ /dev/null @@ -1,285 +0,0 @@ -## SpringBoot - -#### 1.什么是SpringBoot? - -通过Spring Boot,可以轻松地创建独立的,基于生产级别的Spring的应用程序,您可以“运行”它们。大多数Spring Boot应用程序需要最少的Spring配置。 - -#### 2.SpringBoot的特征? - -- 创建独立的Spring应用程序 -- 直接嵌入Tomcat,Jetty或Undertow(无需部署WAR文件) -- 提供固化的“starter”依赖项,以简化构建配置 -- 尽可能自动配置Spring和3rd Party库 -- 提供可用于生产的功能,例如指标,运行状况检查和外部化配置 -- 完全没有代码生成,也不需要XML配置 - -#### 3.如何快速构建一个SpringBoot项目? - -- 通过Web界面使用。http://start.spring.io -- 通过Spring Tool Suite使用。 -- 通过IntelliJ IDEA使用。 -- 使用Spring Boot CLI使用。 - -#### 4.SpringBoot启动类注解?它是由哪些注解组成? - -@SpringBootApplication - -- @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。 -- @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项。 -- @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) -- @ComponentScan:Spring组件扫描 - -#### 5.什么是yaml? - -YAML(/ˈjæməl/,尾音类似camel骆驼)是一个可读性高,用来表达数据序列化的格式。YAML参考了其他多种语言,包括:C语言、Python、Perl。更具有结构性。 - -#### 6.SpringBoot支持配置文件的格式? - -1.properties - -```properties -java.xiaokaxiu.name = xiaoka -``` - -2.yml - -```yaml -java: - xiaokaxiu: - name: xiaoka -``` - -#### 7.SpringBoot启动方式? - -1. main方法 - -2. 命令行 java -jar 的方式 - -3. mvn/gradle - -#### 8.SpringBoot需要独立的容器运行? - -不需要,内置了 Tomcat/Jetty。 - -#### 9.SpringBoot配置途径? - -1. 命令行参数 -2. java:comp/env里的JNDI属性 -3. JVM系统属性 -4. 操作系统环境变量 -5. 随机生成的带random.*前缀的属性(在设置其他属性时,可以引用它们,比如${random. long}) -6. 应用程序以外的application.properties或者appliaction.yml文件 -7. 打包在应用程序内的application.properties或者appliaction.yml文件 -8. 通过@PropertySource标注的属性源 -9. 默认属性 - -tips:这个列表按照优先级排序,也就是说,任何在高优先级属性源里设置的属性都会覆盖低优先级的相同属性。 - -#### 10.application.properties和application.yml文件可放位置?优先级? - -1. 外置,在相对于应用程序运行目录的/config子目录里。 - -2. 外置,在应用程序运行的目录里。 - -3. 内置,在config包内。 - -4. 内置,在Classpath根目录。 - - 这个列表按照优先级排序,优先级高的会覆盖优先级低的。 - - 当然我们可以自己指定文件的位置来加载配置文件。 - -```powershell -java -jar xiaoka.jar ———spring.config.location=/home/application.yml -``` - -#### 11.SpringBoot自动配置原理? - -@EnableAutoConfiguration (开启自动配置) -该注解引入了AutoConfigurationImportSelector,该类中的方法会扫描所有存在META-INF/spring.factories的jar包。 - -#### 12.SpringBoot热部署方式? - -- spring-boot-devtools - -- Spring Loaded - -- Jrebel - -- 模版热部署 - - -#### 13.**bootstrap.yml** 和**application.yml**? - -bootstrap.yml 优先于application.yml - -#### 14.SpringBoot如何修改端口号? - -yml中: - -```yaml -server : - port : 8888 - -``` - -properties: - -```properties -server.port = 8888 -``` - -命令1: - -```powershell -java -jar xiaoka.jar ——— server.port=8888 -``` - -命令2: - -```powershell -java - Dserver.port=8888 -jar xiaoka.jar -``` - -#### 15.开启SpringBoot特性的几种方式? - -1. 继承spring-boot-starter-parent项目 -2. 导入spring-boot-dependencies项目依赖 - -#### 16.SpringBoot如何兼容Spring项目? - -在启动类加: - -@ImportResource(locations = {"classpath:spring.xml"}) - -#### 17.SpringBoot配置监控? - -```xml - - org.springframework.boot - spring-boot-starter-actuator - -``` - -#### 18.获得Bean装配报告信息访问哪个端点? - -/beans 端点 - -#### 19.关闭应用程序访问哪个端点? - -/shutdown - -该端点默认是关闭的,如果开启,需要如下设置。 - -```yaml - endpoints: - shutdown: - enabled: true -``` - -或者properties格式也是可以的。 - -#### 20.查看发布应用信息访问哪个端点? - -/info - -#### 21.针对请求访问的几个组合注解? - -@PatchMapping - -@PostMapping - -@GetMapping - -@PutMapping - -@DeleteMapping - -#### 22.SpringBoot 中的starter? - -可以理解成对依赖的一种合成,starter会把一个或一套功能相关依赖都包含进来,避免了自己去依赖费事,还有各种包的冲突问题。大大的提升了开发效率。 - -并且相关配置会有一个默认值,如果我们自己去配置,就会覆盖默认值。 - -#### 23.SpringBoot集成Mybatis? - -mybatis-spring-boot-starter - -#### 24.什么是SpringProfiles? - -一般来说我们从开发到生产,经过开发(dev)、测试(test)、上线(prod)。不同的时刻我们会用不同的配置。Spring Profiles 允许用户根据配置文件(dev,test,prod 等)来注册 bean。它们可以让我们自己选择什么时候用什么配置。 - -#### 25.不同的环境的配置文件? - -可以是 application-{profile}.properties/yml ,但默认是启动主配置文件application.properties,一般来说我们的不同环境配置如下。 - -- `application.properties`:主配置文件 -- `application-dev.properties`:开发环境配置文件 -- `application-test.properties`:测试环境配置文件 -- `application.prop-properties`:生产环境配置文件 - -#### 26.如何激活某个环境的配置? - -比如我们激活开发环境。 - -yml: - -```yaml -spring: - profiles: - active: dev -``` - -properties: - -```properties -spring.profiles.active=dev -``` - -命令行: - -```powershell -java -jar xiaoka-v1.0.jar ———spring.profiles.active=dev -``` - -#### 27.编写测试用例的注解? - -@SpringBootTest - -#### 28.SpringBoot异常处理相关注解? - - @ControllerAdvice - -@ExceptionHandler - -#### 29.SpringBoot 1.x 和 2.x区别?······· - -1. SpringBoot 2基于Spring5和JDK8,Spring 1x用的是低版本。 -2. 配置变更,参数名等。 -3. SpringBoot2相关的插件最低版本很多都比原来高 -4. 2.x配置中的中文可以直接读取,不用转码 -5. Actuator的变化 -6. CacheManager 的变化 - -#### 30.SpringBoot读取配置相关注解有? - -- @PropertySource -- @Value -- @Environment -- @ConfigurationProperties - -参考: - -- 《SpringBoot实战(第4版)》 - -- 《Spring Boot编程思想》 - -- 《深入浅出Spring Boot 2.x》 - -- https://spring.io/projects/spring-boot - -- 百度百科 - - ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) - -## \ No newline at end of file From 58665add4cf7d2cf5dbe7241d8665429b0baa146 Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:39:13 +0800 Subject: [PATCH 14/19] Delete SpringCloud.md --- Docs/SpringCloud.md | 235 -------------------------------------------- 1 file changed, 235 deletions(-) delete mode 100644 Docs/SpringCloud.md diff --git a/Docs/SpringCloud.md b/Docs/SpringCloud.md deleted file mode 100644 index 1c79996..0000000 --- a/Docs/SpringCloud.md +++ /dev/null @@ -1,235 +0,0 @@ -## SpringCloud - -#### 1.什么是SpringCloud? - -Spring Cloud为开发人员提供了工具,以快速构建分布式系统中的一些常见模式(例如,配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁,领导选举,分布式会话,群集状态)。它们可以在任何分布式环境中正常工作,包括开发人员自己的笔记本电脑,裸机数据中心以及Cloud Foundry等托管平台。 - -#### 2.什么是微服务? - -所谓的微服务是SOA架构下的最终产物,该架构的设计目标是为了肢解业务,使得服务能够独立运行。微服务设计原则: - -1、各司其职 。 - -2、服务高可用和可扩展性。 - -#### 3.SpringCloud有哪些特征? - -Spring Cloud专注于为典型的用例和可扩展性机制(包括其他用例)提供良好的开箱即用体验。 - -- 分布式/版本化配置 -- 服务注册和发现 -- 路由 -- 服务到服务的调用 -- 负载均衡 -- 断路器 -- 全局锁 -- 领导选举和集群状态 -- 分布式消息传递 - -#### 4.SpringCloud核心组件? - -Eureka : 注册中心 - -Ribbon :客服端负载均衡 - -Hystrix : 服务容错处理 - -Feign: 声明式REST客户端 - -Zuul : 服务网关 - -Config : 分布式配置 - -#### 5.SpringCloud基于什么协议? - -HTTP - -#### 6.SpringCloud和Dubbo区别? - -![image-20200429230443620](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200429230443620.png) - -#### 7.Eureka是什么? - -云端服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。 - -#### 8.服务治理的基础角色? - -服务注册中心:提供服务注册与发现的能力。 - -服务提供者:提供服务的应用,会把自己提供的服务注册到注册中心。 - -服务消费者:服务的消费者,从注册中心获取服务列表。 - -#### 9.什么是服务续约? - -在注册完服务以后,服务提供者会维护一个心跳来向注册中心证明自己还活着,以此防止被“剔除服务”。 - -#### 10.什么是服务下线? - -当服务实例进行正常关闭时,会发送一个REST请求(我要下线了)给注册中心,收到请求后,将该服务状态设置下线(DOWN),并把这事件传播出去。 - -#### 11.什么是失效剔除? - -当服务非正常下线时,可能服务注册中心没有收到下线请求,注册中心会创建一个定时任务(默认60s)将没有在固定时间(默认90s)内续约的服务剔除掉。 - -#### 12.什么是自我保护机制? - -在运行期间,注册中心会统计心跳失败比例在15分钟之内是否低于85%,如果低于的情况,注册中心会将当前注册实例信息保护起来,不再删除这些实例信息,当网络恢复后,退出自我保护机制。 - -自我保护机制让服务集群更稳定、健壮。 - -#### 13.Ribbon是什么? - -提供云端负载均衡,有多种负载均衡策略可供选择,可配合服务发现和断路器使用。 - -#### 14.Ribbon负载均衡的注解是? - -@LoadBalanced - -#### 15.Ribbon负载均衡策略有哪些? - -RandomRule : 随机。 - -RoundRobinRule : 轮询。 - -RetryRule : 重试。 - -WeightedResponseTimeRule : 权重。 - -ClientConfigEnabledRoundRobinRule : 一般不用,通过继承该策略,默认的choose就实现了线性轮询机制。可以基于它来做扩展。 - -BestAvailableRule : 通过便利负载均衡器中维护的所有服务实例,会过滤到故障的,并选择并发请求最小的一个。 - -PredicateBasedRule : 先过滤清单,再轮询。 - -AvailabilityFilteringRule :继承了父类的先过滤清单,再轮询。调整了算法。 - -ZoneAvoidanceRule : 该类也是PredicateBasedRule的子类,它可以组合过滤条件。以ZoneAvoidancePredicate为主过滤条件,以AvailabilityPredicate为次过滤条件。 - -#### 16.什么是服务熔断? - -服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。 - -#### 17.什么是服务降级? - -服务降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。 - -#### 18.什么是Hystrix? - -熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。 - -#### 19.断路器Hystrix的有哪些功能? - -- 通过第三方客户端访问依赖服务出现高延迟或者失败时,为系统提供保护和控制 。 -- 在复杂的分布式系统中防止级联失败(服务雪崩效应) 。 -- 快速失败 (Failfast) 同时能快速恢复。 -- 提供失败回滚 (Fallback) 和优雅的服务降级机制。 -- 提供近实时的监控、 报警和运维控制手段。 - -#### 20.Hystrix将远程调用封装到? - -HystrixCommand 或者 HystrixObservableCommand对象中。 - -#### 21.启动熔断降级服务的注解? - -@EnableHystrix - -#### 22.什么是Feign? - -Feign是一种声明式、模板化的HTTP客户端。 - -#### 23.Feign优点? - -1.feign采用的是基于接口的注解。 2.feign整合了ribbon,具有负载均衡的能力。 3.整合了Hystrix,具有熔断的能力。 - -#### 24.什么是Config? - -配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git以及Subversion。 - -#### 25.Config组件中的两个角色? - -Config Server : 配置中心服务端。 - -Config Client : 配置中心客户端。 - -#### 26.什么是Zuul? - -Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。 - -#### 27.使用Zuul的优点? - -- 方便监控。可以在微服务网管手机监控数据并将其推送到外部系统进行分析。 -- 方便认证。可在网关进行统一认证,然后在讲请求转发到后端服务。 -- 隐藏架构实现细节,提供统一的入口给客户端请求,减少了客户端和每个微服务的交互次数。 -- 可以统一处理切面任务,避免每个微服务自己开发,提升效率。 -- 高可用高伸缩性的服务,避免单点失效。 - -#### 28.Zuul的核心是? - -过滤器。 - -#### 29.Zuul有几种过滤器类型?分别是? - -4种。 - -pre : 可以在请求被路由之前调用。 - -适用于身份认证的场景,认证通过后再继续执行下面的流程。 - -route : 在路由请求时被调用。 - -适用于灰度发布场景,在将要路由的时候可以做一些自定义的逻辑。 - -post :在 route 和 error 过滤器之后被调用。 - -这种过滤器将请求路由到达具体的服务之后执行。适用于需要添加响应头,记录响应日志等应用场景。 - -error : 处理请求时发生错误时被调用。 - -在执行过程中发送错误时会进入 error 过滤器,可以用来统一记录错误信息。 - -#### 30.什么是Sleuth? - -日志收集工具包,封装了Dapper和log-based追踪以及Zipkin和HTrace操作,为SpringCloud应用实现了一种分布式追踪解决方案。 - -#### 31.Sleuth帮助我们做了哪些工作? - -- 可以方便的了解到每个采样的请求耗时,分析出哪些服务调用比较耗时。 -- 对于程序未捕捉的异常,可以在集成Zipkin服务页面上看到。 -- 识别调用比较频繁的服务,从而进行优化。 - -#### 32.什么是Bus? - -事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署。 - -#### 33.eureka比zookeeper的优势在? - -A:高可用 C:一致性,P:分区容错性 - -Zookeeper保证了CP,Eureka保证了AP。 - -Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像Zookeeper那样使整个微服务瘫痪。 - -#### 34.什么是Stream? - -数据流操作开发包,封装了与Redis,Rabbit、Kafka等发送接收消息。 - -#### 35.更多知识? - -SpringCloud这个体系东西还是挺大的,小编会不断的丰富内容以及优化。欢迎大家关注我的公众号获得最新动态《Java小咖秀》。 - -参考: - -- 《Spring Cloud微服务实战》 - -- 《Spring Cloud微服务全栈技术与案例解析》 - -- 《Spring Cloud微服务架构开发实战》 - -- ​ https://spring.io/projects/spring-cloud - -- ​ https://www.springcloud.cc/ - -- ​ 百度百科 - - ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file From f88b4e36c2f04f77cb987bab58cd561f38077376 Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:39:23 +0800 Subject: [PATCH 15/19] =?UTF-8?q?Delete=20java=E9=9B=86=E5=90=88.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "Docs/java\351\233\206\345\220\210.md" | 635 ------------------------- 1 file changed, 635 deletions(-) delete mode 100644 "Docs/java\351\233\206\345\220\210.md" diff --git "a/Docs/java\351\233\206\345\220\210.md" "b/Docs/java\351\233\206\345\220\210.md" deleted file mode 100644 index 0740258..0000000 --- "a/Docs/java\351\233\206\345\220\210.md" +++ /dev/null @@ -1,635 +0,0 @@ -## java集合 - -![image-20200414114452792](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414114452792.png) - - - -#### 1.**Java集合框架的基础接口有哪些?** - -Collection为集合层级的根接口。一个集合代表一组对象,这些对象即为它的元素。Java平台不提供这个接口任何直接的实现。 - -Set是一个不能包含重复元素的集合。这个接口对数学集合抽象进行建模,被用来代表集合,就如一副牌。 - -List是一个有序集合,可以包含重复元素。你可以通过它的索引来访问任何元素。List更像长度动态变换的数组。 - -Map是一个将key映射到value的对象.一个Map不能包含重复的key:每个key最多只能映射一个value。 - -一些其它的接口有Queue、Dequeue、SortedSet、SortedMap和ListIterator。 - -#### 2.Collection 和 Collections 有什么区别? - -- Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如 List、Set 等。 -- Collections 是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供的排序方法: Collections. sort(list)。 - -#### 3.List、Set、Map是否继承自Collection接口? - -List、Set 是,Map 不是。Map是键值对映射容器,与List和Set有明显的区别,而Set存储的零散的元素且不允许有重复元素(数学中的集合也是如此),List是线性结构的容器,适用于按数值索引访问元素的情形。 - -#### 4.**Collections.sort排序内部原理** - -在Java 6中Arrays.sort()和Collections.sort()使用的是MergeSort,而在Java 7中,内部实现换成了TimSort,其对对象间比较的实现要求更加严格。 - -#### 5.List、Set、Map 之间的区别是什么? - -List、Set、Map 的区别主要体现在两个方面:元素是否有序、是否允许元素重复。 - -三者之间的区别,如下表: - -![image-20200414130713655](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414130713655.png) - -#### 6.HashMap 和 Hashtable 有什么区别? - -(1)HashMap允许key和value为null,而HashTable不允许。 - -(2)HashTable是同步的,而HashMap不是。所以HashMap适合单线程环境,HashTable适合多线程环境。 - -(3)在Java1.4中引入了LinkedHashMap,HashMap的一个子类,假如你想要遍历顺序,你很容易从HashMap转向LinkedHashMap,但是HashTable不是这样的,它的顺序是不可预知的。 - -(4)HashMap提供对key的Set进行遍历,因此它是fail-fast的,但HashTable提供对key的Enumeration进行遍历,它不支持fail-fast。 - -(5)HashTable被认为是个遗留的类,如果你寻求在迭代的时候修改Map,你应该使用CocurrentHashMap。 - -#### 7.如何决定使用 HashMap 还是 TreeMap? - -对于在 Map 中插入、删除、定位一个元素这类操作,HashMap 是最好的选择,因为相对而言 HashMap 的插入会更快,但如果你要对一个 key 集合进行有序的遍历,那 TreeMap 是更好的选择。 - -#### 8.说一下 HashMap 的实现原理? - -HashMap 基于 Hash 算法实现的,我们通过 put(key,value)存储,get(key)来获取。当传入 key 时,HashMap 会根据 key. hashCode() 计算出 hash 值,根据 hash 值将 value 保存在 bucket 里。当计算出的 hash 值相同时,我们称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。当 hash 冲突的个数比较少时,使用链表否则使用红黑树。 - -#### 9.说一下 HashSet 的实现原理? - -HashSet 是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值。 - -#### 10.ArrayList 和 LinkedList 的区别是什么? - -- 数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。 - -- 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。 - -- 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。 - - 综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。 - -#### 11.**为何Map接口不继承Collection接口?** - -尽管Map接口和它的实现也是集合框架的一部分,但Map不是集合,集合也不是Map。因此,Map继承Collection毫无意义,反之亦然。 - -如果Map继承Collection接口,那么元素去哪儿?Map包含key-value对,它提供抽取key或value列表集合的方法,但是它不适合“一组对象”规范。 - -#### 12.**ArrayList和Vector有何异同点?** - -ArrayList和Vector在很多时候都很类似。 - -(1)两者都是基于索引的,内部由一个数组支持。 - -(2)两者维护插入的顺序,我们可以根据插入顺序来获取元素。 - -(3)ArrayList和Vector的迭代器实现都是fail-fast的。 - -(4)ArrayList和Vector两者允许null值,也可以使用索引值对元素进行随机访问。 - -以下是ArrayList和Vector的不同点。 - -(1)Vector是同步的,而ArrayList不是。然而,如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWriteArrayList。 - -(2)ArrayList比Vector快,它因为有同步,不会过载。 - -(3)ArrayList更加通用,因为我们可以使用Collections工具类轻易地获取同步列表和只读列表。 - -#### 13.Array 和 ArrayList 有何区别? - -- Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。 -- Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。 -- Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。 - -#### 14.在 Queue 中 poll()和 remove()有什么区别? - -- 相同点:都是返回第一个元素,并在队列中删除返回的对象。 -- 不同点:如果没有元素 poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 异常。 - -代码示例: - -``` -Queue queue = new LinkedList(); -queue. offer("string"); // add -System. out. println(queue. poll()); -System. out. println(queue. remove()); -System. out. println(queue. size()); -``` - -#### 15.LinkedHashMap有什么特点? - -LinkedHashMap是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。 - -#### 16.HashMap的底层实现原理?(高频问题) - -从结构实现来讲,HashMap是数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下如所示。 - -![image-20200414150006324](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414150006324.png) - -这里需要讲明白两个问题:数据底层具体存储的是什么?这样的存储方式有什么优点呢? - -(1) 从源码可知,HashMap类中有一个非常重要的字段,就是 Node[] table,即哈希桶数组,明显它是一个Node的数组。我们来看Node[JDK1.8]是何物。 - -``` -static class Node implements Map.Entry { - final int hash; //用来定位数组索引位置 - final K key; - V value; - Node next; //链表的下一个node - - Node(int hash, K key, V value, Node next) { … } - public final K getKey(){ … } - public final V getValue() { … } - public final String toString() { … } - public final int hashCode() { … } - public final V setValue(V newValue) { … } - public final boolean equals(Object o) { … } -} -``` - -Node是HashMap的一个内部类,实现了Map.Entry接口,本质是就是一个映射(键值对)。上图中的每个黑色圆点就是一个Node对象。 - -(2) HashMap就是使用哈希表来存储的。哈希表为解决冲突,可以采用开放地址法和链地址法等来解决问题,Java中HashMap采用了链地址法。链地址法,简单来说,就是数组加链表的结合。在每个数组元素上都一个链表结构,当数据被Hash后,得到数组下标,把数据放在对应下标元素的链表上。例如程序执行下面代码: - -``` - map.put("美团","小美"); -``` - -系统将调用"美团"这个key的hashCode()方法得到其hashCode 值(该方法适用于每个Java对象),然后再通过Hash算法的后两步运算(高位运算和取模运算,下文有介绍)来定位该键值对的存储位置,有时两个key会定位到相同的位置,表示发生了Hash碰撞。当然Hash算法计算结果越分散均匀,Hash碰撞的概率就越小,map的存取效率就会越高。 - -如果哈希桶数组很大,即使较差的Hash算法也会比较分散,如果哈希桶数组数组很小,即使好的Hash算法也会出现较多碰撞,所以就需要在空间成本和时间成本之间权衡,其实就是在根据实际情况确定哈希桶数组的大小,并在此基础上设计好的hash算法减少Hash碰撞。那么通过什么方式来控制map使得Hash碰撞的概率又小,哈希桶数组(Node[] table)占用空间又少呢?答案就是好的Hash算法和扩容机制。 - -在理解Hash和扩容流程之前,我们得先了解下HashMap的几个字段。从HashMap的默认构造函数源码可知,构造函数就是对下面几个字段进行初始化,源码如下: - -``` - int threshold; // 所能容纳的key-value对极限 - final float loadFactor; // 负载因子 - int modCount; - int size; -``` - -首先,Node[] table的初始化长度length(默认值是16),Load factor为负载因子(默认值是0.75),threshold是HashMap所能容纳的最大数据量的Node(键值对)个数。threshold = length * Load factor。也就是说,在数组定义好长度之后,负载因子越大,所能容纳的键值对个数越多。 - -结合负载因子的定义公式可知,threshold就是在此Load factor和length(数组长度)对应下允许的最大元素数目,超过这个数目就重新resize(扩容),扩容后的HashMap容量是之前容量的两倍。默认的负载因子0.75是对空间和时间效率的一个平衡选择,建议大家不要修改,除非在时间和空间比较特殊的情况下,如果内存空间很多而又对时间效率要求很高,可以降低负载因子Load factor的值;相反,如果内存空间紧张而对时间效率要求不高,可以增加负载因子loadFactor的值,这个值可以大于1。 - -size这个字段其实很好理解,就是HashMap中实际存在的键值对数量。注意和table的长度length、容纳最大键值对数量threshold的区别。而modCount字段主要用来记录HashMap内部结构发生变化的次数,主要用于迭代的快速失败。强调一点,内部结构发生变化指的是结构发生变化,例如put新键值对,但是某个key对应的value值被覆盖不属于结构变化。 - -在HashMap中,哈希桶数组table的长度length大小必须为2的n次方(一定是合数),这是一种非常规的设计,常规的设计是把桶的大小设计为素数。相对来说素数导致冲突的概率要小于合数,具体证明可以参考[http://blog.csdn.net/liuqiyao_01/article/details/14475159](https://link.zhihu.com/?target=http%3A//blog.csdn.net/liuqiyao_01/article/details/14475159),Hashtable初始化桶大小为11,就是桶大小设计为素数的应用(Hashtable扩容后不能保证还是素数)。HashMap采用这种非常规设计,主要是为了在取模和扩容时做优化,同时为了减少冲突,HashMap定位哈希桶索引位置时,也加入了高位参与运算的过程。 - -这里存在一个问题,即使负载因子和Hash算法设计的再合理,也免不了会出现拉链过长的情况,一旦出现拉链过长,则会严重影响HashMap的性能。于是,在JDK1.8版本中,对数据结构做了进一步的优化,引入了红黑树。而当链表长度太长(默认超过8)时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能,其中会用到红黑树的插入、删除、查找等算法。 - -功能实现-方法 - -HashMap的内部功能实现很多,本文主要从根据key获取哈希桶数组索引位置、put方法的详细执行、扩容过程三个具有代表性的点深入展开讲解。 - -1.确定哈希桶数组索引位置 - -不管增加、删除、查找键值对,定位到哈希桶数组的位置都是很关键的第一步。前面说过HashMap的数据结构是数组和链表的结合,所以我们当然希望这个HashMap里面的元素位置尽量分布均匀些,尽量使得每个位置上的元素数量只有一个,那么当我们用hash算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,不用遍历链表,大大优化了查询的效率。HashMap定位数组索引位置,直接决定了hash方法的离散性能。先看看源码的实现(方法一+方法二): - -``` -方法一: -static final int hash(Object key) { //jdk1.8 & jdk1.7 - int h; - // h = key.hashCode() 为第一步 取hashCode值 - // h ^ (h >>> 16) 为第二步 高位参与运算 - return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); -} -方法二: -static int indexFor(int h, int length) { //jdk1.7的源码,jdk1.8没有这个方法,但是实现原理一样的 - return h & (length-1); //第三步 取模运算 -} -``` - -这里的Hash算法本质上就是三步:**取key的hashCode值、高位运算、取模运算**。 - -对于任意给定的对象,只要它的hashCode()返回值相同,那么程序调用方法一所计算得到的Hash码值总是相同的。我们首先想到的就是把hash值对数组长度取模运算,这样一来,元素的分布相对来说是比较均匀的。但是,模运算的消耗还是比较大的,在HashMap中是这样做的:调用方法二来计算该对象应该保存在table数组的哪个索引处。 - -这个方法非常巧妙,它通过h & (table.length -1)来得到该对象的保存位,而HashMap底层数组的长度总是2的n次方,这是HashMap在速度上的优化。当length总是2的n次方时,h& (length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。 - -在JDK1.8的实现中,优化了高位运算的算法,通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16),主要是从速度、功效、质量来考虑的,这么做可以在数组table的length比较小的时候,也能保证考虑到高低Bit都参与到Hash的计算中,同时不会有太大的开销。 - -下面举例说明下,n为table的长度。 - -![image-20200414151128860](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414151128860.png) - -2.HashMap的put方法 - -HashMap的put方法执行过程可以通过下图来理解,自己有兴趣可以去对比源码更清楚地研究学习。 - -![image-20200414151233037](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414151233037.png) - -①.判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容; - -②.根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向⑥,如果table[i]不为空,转向③; - -③.判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的是hashCode以及equals; - -④.判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向⑤; - -⑤.遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可; - -⑥.插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容。 - -JDK1.8HashMap的put方法源码如下: - -``` -public V put(K key, V value) { - 2 // 对key的hashCode()做hash - 3 return putVal(hash(key), key, value, false, true); - 4 } - 5 - 6 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, - 7 boolean evict) { - 8 Node[] tab; Node p; int n, i; - 9 // 步骤①:tab为空则创建 -10 if ((tab = table) == null || (n = tab.length) == 0) -11 n = (tab = resize()).length; -12 // 步骤②:计算index,并对null做处理 -13 if ((p = tab[i = (n - 1) & hash]) == null) -14 tab[i] = newNode(hash, key, value, null); -15 else { -16 Node e; K k; -17 // 步骤③:节点key存在,直接覆盖value -18 if (p.hash == hash && -19 ((k = p.key) == key || (key != null && key.equals(k)))) -20 e = p; -21 // 步骤④:判断该链为红黑树 -22 else if (p instanceof TreeNode) -23 e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); -24 // 步骤⑤:该链为链表 -25 else { -26 for (int binCount = 0; ; ++binCount) { -27 if ((e = p.next) == null) { -28 p.next = newNode(hash, key,value,null); - //链表长度大于8转换为红黑树进行处理 -29 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st -30 treeifyBin(tab, hash); -31 break; -32 } - // key已经存在直接覆盖value -33 if (e.hash == hash && -34 ((k = e.key) == key || (key != null && key.equals(k)))) break; -36 p = e; -37 } -38 } -39 -40 if (e != null) { // existing mapping for key -41 V oldValue = e.value; -42 if (!onlyIfAbsent || oldValue == null) -43 e.value = value; -44 afterNodeAccess(e); -45 return oldValue; -46 } -47 } - -48 ++modCount; -49 // 步骤⑥:超过最大容量 就扩容 -50 if (++size > threshold) -51 resize(); -52 afterNodeInsertion(evict); -53 return null; -54 } - -``` - -3.扩容机制 - -扩容(resize)就是重新计算容量,向HashMap对象里不停的添加元素,而HashMap对象内部的数组无法装载更多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。当然Java里的数组是无法自动扩容的,方法是使用一个新的数组代替已有的容量小的数组,就像我们用一个小桶装水,如果想装更多的水,就得换大水桶。 - -我们分析下resize的源码,鉴于JDK1.8融入了红黑树,较复杂,为了便于理解我们仍然使用JDK1.7的代码,好理解一些,本质上区别不大,具体区别后文再说。 - -``` - 1 void resize(int newCapacity) { //传入新的容量 - 2 Entry[] oldTable = table; //引用扩容前的Entry数组 - 3 int oldCapacity = oldTable.length; - 4 if (oldCapacity == MAXIMUM_CAPACITY) { //扩容前的数组大小如果已经达到最大(2^30)了 - 5 threshold = Integer.MAX_VALUE; //修改阈值为int的最大值(2^31-1),这样以后就不会扩容了 - 6 return; - 7 } - 8 - 9 Entry[] newTable = new Entry[newCapacity]; //初始化一个新的Entry数组 -10 transfer(newTable); //!!将数据转移到新的Entry数组里 -11 table = newTable; //HashMap的table属性引用新的Entry数组 -12 threshold = (int)(newCapacity * loadFactor);//修改阈值 -13 } -``` - -这里就是使用一个容量更大的数组来代替已有的容量小的数组,transfer()方法将原有Entry数组的元素拷贝到新的Entry数组里。 - -``` - 1 void transfer(Entry[] newTable) { - 2 Entry[] src = table; //src引用了旧的Entry数组 - 3 int newCapacity = newTable.length; - 4 for (int j = 0; j < src.length; j++) { //遍历旧的Entry数组 - 5 Entry e = src[j]; //取得旧Entry数组的每个元素 - 6 if (e != null) { - 7 src[j] = null;//释放旧Entry数组的对象引用(for循环后,旧的Entry数组不再引用任何对象) - 8 do { - 9 Entry next = e.next; -10 int i = indexFor(e.hash, newCapacity); //!!重新计算每个元素在数组中的位置 -11 e.next = newTable[i]; //标记[1] -12 newTable[i] = e; //将元素放在数组上 -13 e = next; //访问下一个Entry链上的元素 -14 } while (e != null); -15 } -16 } -17 } -``` - -newTable[i]的引用赋给了e.next,也就是使用了单链表的头插入方式,同一位置上新元素总会被放在链表的头部位置;这样先放在一个索引上的元素终会被放到Entry链的尾部(如果发生了hash冲突的话),这一点和Jdk1.8有区别,下文详解。在旧数组中同一条Entry链上的元素,通过重新计算索引位置后,有可能被放到了新数组的不同位置上。 - -下面举个例子说明下扩容过程。假设了我们的hash算法就是简单的用key mod 一下表的大小(也就是数组的长度)。其中的哈希桶数组table的size=2, 所以key = 3、7、5,put顺序依次为 5、7、3。在mod 2以后都冲突在table[1]这里了。这里假设负载因子 loadFactor=1,即当键值对的实际大小size 大于 table的实际大小时进行扩容。接下来的三个步骤是哈希桶数组 resize成4,然后所有的Node重新rehash的过程。 - -![image-20200414151607099](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414151607099.png) - -下面我们讲解下JDK1.8做了哪些优化。经过观测可以发现,我们使用的是2次幂的扩展(指长度扩为原来2倍),所以,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置。看下图可以明白这句话的意思,n为table的长度,图(a)表示扩容前的key1和key2两种key确定索引位置的示例,图(b)表示扩容后key1和key2两种key确定索引位置的示例,其中hash1是key1对应的哈希与高位运算结果。 - -![image-20200414151625660](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414151625660.png) - -元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit(红色),因此新的index就会发生这样的变化: - -![img](https://pic2.zhimg.com/80/b2cb057773e3d67976c535d6ef547d51_1440w.png) - -因此,我们在扩充HashMap的时候,不需要像JDK1.7的实现那样重新计算hash,只需要看看原来的hash值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“原索引+oldCap”,可以看看下图为16扩充为32的resize示意图: - -![image-20200414151745341](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414151745341.png) - -这个设计确实非常的巧妙,既省去了重新计算hash值的时间,而且同时,由于新增的1bit是0还是1可以认为是随机的,因此resize的过程,均匀的把之前的冲突的节点分散到新的bucket了。这一块就是JDK1.8新增的优化点。有一点注意区别,JDK1.7中rehash的时候,旧链表迁移新链表的时候,如果在新表的数组索引位置相同,则链表元素会倒置,但是从上图可以看出,JDK1.8不会倒置。有兴趣的同学可以研究下JDK1.8的resize源码,写的很赞,如下: - -``` -1 final Node[] resize() { - 2 Node[] oldTab = table; - 3 int oldCap = (oldTab == null) ? 0 : oldTab.length; - 4 int oldThr = threshold; - 5 int newCap, newThr = 0; - 6 if (oldCap > 0) { - 7 // 超过最大值就不再扩充了,就只好随你碰撞去吧 - 8 if (oldCap >= MAXIMUM_CAPACITY) { - 9 threshold = Integer.MAX_VALUE; -10 return oldTab; -11 } -12 // 没超过最大值,就扩充为原来的2倍 -13 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && -14 oldCap >= DEFAULT_INITIAL_CAPACITY) -15 newThr = oldThr << 1; // double threshold -16 } -17 else if (oldThr > 0) // initial capacity was placed in threshold -18 newCap = oldThr; -19 else { // zero initial threshold signifies using defaults -20 newCap = DEFAULT_INITIAL_CAPACITY; -21 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); -22 } -23 // 计算新的resize上限 -24 if (newThr == 0) { -25 -26 float ft = (float)newCap * loadFactor; -27 newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? -28 (int)ft : Integer.MAX_VALUE); -29 } -30 threshold = newThr; -31 @SuppressWarnings({"rawtypes","unchecked"}) -32 Node[] newTab = (Node[])new Node[newCap]; -33 table = newTab; -34 if (oldTab != null) { -35 // 把每个bucket都移动到新的buckets中 -36 for (int j = 0; j < oldCap; ++j) { -37 Node e; -38 if ((e = oldTab[j]) != null) { -39 oldTab[j] = null; -40 if (e.next == null) -41 newTab[e.hash & (newCap - 1)] = e; -42 else if (e instanceof TreeNode) -43 ((TreeNode)e).split(this, newTab, j, oldCap); -44 else { // 链表优化重hash的代码块 -45 Node loHead = null, loTail = null; -46 Node hiHead = null, hiTail = null; -47 Node next; -48 do { -49 next = e.next; -50 // 原索引 -51 if ((e.hash & oldCap) == 0) { -52 if (loTail == null) -53 loHead = e; -54 else -55 loTail.next = e; -56 loTail = e; -57 } -58 // 原索引+oldCap -59 else { -60 if (hiTail == null) -61 hiHead = e; -62 else -63 hiTail.next = e; -64 hiTail = e; -65 } -66 } while ((e = next) != null); -67 // 原索引放到bucket里 -68 if (loTail != null) { -69 loTail.next = null; -70 newTab[j] = loHead; -71 } -72 // 原索引+oldCap放到bucket里 -73 if (hiTail != null) { -74 hiTail.next = null; -75 newTab[j + oldCap] = hiHead; -76 } -77 } -78 } -79 } -80 } -81 return newTab; -82 } - -``` - -#### 17.HashMap并发安全的问题 - -并发的多线程使用场景中使用HashMap可能造成死循环。代码例子如下(便于理解,仍然使用JDK1.7的环境): - -``` -public class HashMapInfiniteLoop { - - private static HashMap map = new HashMap(2,0.75f); - public static void main(String[] args) { - map.put(5, "C"); - - new Thread("Thread1") { - public void run() { - map.put(7, "B"); - System.out.println(map); - }; - }.start(); - new Thread("Thread2") { - public void run() { - map.put(3, "A); - System.out.println(map); - }; - }.start(); - } -} -``` - -其中,map初始化为一个长度为2的数组,loadFactor=0.75,threshold=2*0.75=1,也就是说当put第二个key的时候,map就需要进行resize。 - -通过设置断点让线程1和线程2同时debug到transfer方法(3.3小节代码块)的首行。注意此时两个线程已经成功添加数据。放开thread1的断点至transfer方法的“Entry next = e.next;” 这一行;然后放开线程2的的断点,让线程2进行resize。结果如下图。 - -![img](https://pic4.zhimg.com/80/fa10635a66de637fe3cbd894882ff0c7_1440w.png) - -注意,Thread1的 e 指向了key(3),而next指向了key(7),其在线程二rehash后,指向了线程二重组后的链表。 - -线程一被调度回来执行,先是执行 newTalbe[i] = e, 然后是e = next,导致了e指向了key(7),而下一次循环的next = e.next导致了next指向了key(3)。 - -![img](https://pic4.zhimg.com/80/d39d7eff6e8e04f98f5b53bebe2d4d7f_1440w.png) - -e.next = newTable[i] 导致 key(3).next 指向了 key(7)。注意:此时的key(7).next 已经指向了key(3), 环形链表就这样出现了。 - -![img](https://pic2.zhimg.com/80/5f3cf5300f041c771a736b40590fd7b1_1440w.png) - -于是,当我们用线程一调用map.get(11)时,悲剧就出现了——Infinite Loop。 - -#### 18.**JDK1.8与JDK1.7的性能对比** - -HashMap中,如果key经过hash算法得出的数组索引位置全部不相同,即Hash算法非常好,那样的话,getKey方法的时间复杂度就是O(1),如果Hash算法技术的结果碰撞非常多,假如Hash算极其差,所有的Hash算法结果得出的索引位置一样,那样所有的键值对都集中到一个桶中,或者在一个链表中,或者在一个红黑树中,时间复杂度分别为O(n)和O(lgn)。 鉴于JDK1.8做了多方面的优化,总体性能优于JDK1.7,下面我们从两个方面用例子证明这一点。 - -Hash较均匀的情况 - -为了便于测试,我们先写一个类Key,如下: - -``` -class Key implements Comparable { - - private final int value; - - Key(int value) { - this.value = value; - } - - @Override - public int compareTo(Key o) { - return Integer.compare(this.value, o.value); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) - return false; - Key key = (Key) o; - return value == key.value; - } - - @Override - public int hashCode() { - return value; - } -} -``` - -这个类复写了equals方法,并且提供了相当好的hashCode函数,任何一个值的hashCode都不会相同,因为直接使用value当做hashcode。为了避免频繁的GC,我将不变的Key实例缓存了起来,而不是一遍一遍的创建它们。代码如下: - -``` -public class Keys { - - public static final int MAX_KEY = 10_000_000; - private static final Key[] KEYS_CACHE = new Key[MAX_KEY]; - - static { - for (int i = 0; i < MAX_KEY; ++i) { - KEYS_CACHE[i] = new Key(i); - } - } - - public static Key of(int value) { - return KEYS_CACHE[value]; - } -} -``` - -现在开始我们的试验,测试需要做的仅仅是,创建不同size的HashMap(1、10、100、……10000000),屏蔽了扩容的情况,代码如下: - -``` -static void test(int mapSize) { - - HashMap map = new HashMap(mapSize); - for (int i = 0; i < mapSize; ++i) { - map.put(Keys.of(i), i); - } - - long beginTime = System.nanoTime(); //获取纳秒 - for (int i = 0; i < mapSize; i++) { - map.get(Keys.of(i)); - } - long endTime = System.nanoTime(); - System.out.println(endTime - beginTime); - } - - public static void main(String[] args) { - for(int i=10;i<= 1000 0000;i*= 10){ - test(i); - } - } - -``` - -在测试中会查找不同的值,然后度量花费的时间,为了计算getKey的平均时间,我们遍历所有的get方法,计算总的时间,除以key的数量,计算一个平均值,主要用来比较,绝对值可能会受很多环境因素的影响。结果如下: - -![img](https://pic3.zhimg.com/80/2fd11382221dfa34ab61fbee11adba3a_1440w.png) - -通过观测测试结果可知,JDK1.8的性能要高于JDK1.7 15%以上,在某些size的区域上,甚至高于100%。由于Hash算法较均匀,JDK1.8引入的红黑树效果不明显,下面我们看看Hash不均匀的的情况。 - -Hash极不均匀的情况 - -假设我们又一个非常差的Key,它们所有的实例都返回相同的hashCode值。这是使用HashMap最坏的情况。代码修改如下: - -``` -class Key implements Comparable { - - //… - - @Override - public int hashCode() { - return 1; - } -} -``` - -仍然执行main方法,得出的结果如下表所示: - -![img](https://pic1.zhimg.com/80/608ce9cf1ca4bc9fe08826402a3bc4e8_1440w.png) - -从表中结果中可知,随着size的变大,JDK1.7的花费时间是增长的趋势,而JDK1.8是明显的降低趋势,并且呈现对数增长稳定。当一个链表太长的时候,HashMap会动态的将它替换成一个红黑树,这话的话会将时间复杂度从O(n)降为O(logn)。hash算法均匀和不均匀所花费的时间明显也不相同,这两种情况的相对比较,可以说明一个好的hash算法的重要性。 - -测试环境:处理器为2.2 GHz Intel Core i7,内存为16 GB 1600 MHz DDR3,SSD硬盘,使用默认的JVM参数,运行在64位的OS X 10.10.1上。 - -#### 18.HashMap操作注意事项以及优化? - -(1) 扩容是一个特别耗性能的操作,所以当程序员在使用HashMap的时候,估算map的大小,初始化的时候给一个大致的数值,避免map进行频繁的扩容。 - -(2) 负载因子是可以修改的,也可以大于1,但是建议不要轻易修改,除非情况非常特殊。 - -(3) HashMap是线程不安全的,不要在并发的环境中同时操作HashMap,建议使用ConcurrentHashMap。 - -(4) JDK1.8引入红黑树大程度优化了HashMap的性能。 - -(5) 还没升级JDK1.8的,现在开始升级吧。HashMap的性能提升仅仅是JDK1.8的冰山一角。 - -参考: - -- https://zhuanlan.zhihu.com/p/21673805 - -- https://www.jianshu.com/p/939b8a672070 - -- https://www.jianshu.com/p/c45b6d782e91 - -- https://blog.csdn.net/Mrs_chens/article/details/92761868 - -- https://blog.csdn.net/riemann_/article/details/87217229 - -- https://blog.csdn.net/qq_34626097/article/details/83053004 - -- https://blog.csdn.net/u010887744/article/details/50575735 - - ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) - From f5767a4f14f17e7b752f40a24979d5dcc46e9255 Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:39:34 +0800 Subject: [PATCH 16/19] =?UTF-8?q?Delete=20=E5=A4=9A=E7=BA=BF=E7=A8=8B.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\345\244\232\347\272\277\347\250\213.md" | 1113 ----------------- 1 file changed, 1113 deletions(-) delete mode 100644 "Docs/\345\244\232\347\272\277\347\250\213.md" diff --git "a/Docs/\345\244\232\347\272\277\347\250\213.md" "b/Docs/\345\244\232\347\272\277\347\250\213.md" deleted file mode 100644 index 40ee12c..0000000 --- "a/Docs/\345\244\232\347\272\277\347\250\213.md" +++ /dev/null @@ -1,1113 +0,0 @@ -## 多线程 - -#### 1.**什么是进程**? - -进程是系统中正在运行的一个程序,程序一旦运行就是进程。 - -进程可以看成程序执行的一个实例。进程是系统资源分配的独立实体,每个进程都拥有独立的地址空间。一个进程无法访问另一个进程的变量和数据结构,如果想让一个进程访问另一个进程的资源,需要使用进程间通信,比如管道,文件,套接字等。 - -#### 2.什么是线程? - -是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 - -#### 3.线程的实现方式? - -1.继承Thread类 - -2.实现Runnable接口 - -3.使用Callable和Future - -#### 4.**Thread 类中的start() 和 run() 方法有什么区别**? - -1.start()方法来启动线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;通过调用Thread类的start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。 -2.run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。 - -#### 5.线程NEW状态 - -new创建一个Thread对象时,并没处于执行状态,因为没有调用start方法启动改线程,那么此时的状态就是新建状态。 - -#### 6.线程RUNNABLE状态 - -线程对象通过start方法进入runnable状态,启动的线程不一定会立即得到执行,线程的运行与否要看cpu的调度,我们把这个中间状态叫可执行状态(RUNNABLE)。 - -#### 7.线程的RUNNING状态 - -一旦cpu通过轮询货其他方式从任务可以执行队列中选中了线程,此时它才能真正的执行自己的逻辑代码。 - -#### 8.线程的BLOCKED状态 - -线程正在等待获取锁。 - -- 进入BLOCKED状态,比如调用了sleep,或者wait方法 -- 进行某个阻塞的io操作,比如因网络数据的读写进入BLOCKED状态 -- 获取某个锁资源,从而加入到该锁的阻塞队列中而进入BLOCKED状态 - -#### 9.线程的TERMINATED状态 - -TERMINATED是一个线程的最终状态,在该状态下线程不会再切换到其他任何状态了,代表整个生命周期都结束了。 - -下面几种情况会进入TERMINATED状态: - -- 线程运行正常结束,结束生命周期 -- 线程运行出错意外结束 -- JVM Crash 导致所有的线程都结束 - -#### 10.线程状态转化图 - -![image-20200501131131886](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200501131131886.png) - -#### 11.i——————与System.out.println()的异常 - -示例代码: - -```java -public class XkThread extends Thread { - - private int i = 5; - - @Override - public void run() { - System.out.println("i=" + (i——————) + " threadName=" + Thread.currentThread().getName()); - } - - public static void main(String[] args) { - XkThread xk = new XkThread(); - Thread t1 = new Thread(xk); - Thread t2 = new Thread(xk); - Thread t3 = new Thread(xk); - Thread t4 = new Thread(xk); - Thread t5 = new Thread(xk); - - t1.start(); - t2.start(); - t3.start(); - t4.start(); - t5.start(); - } -} - -``` - -结果: - -```sqlite -i=5 threadName=Thread-1 -i=2 threadName=Thread-5 -i=5 threadName=Thread-2 -i=4 threadName=Thread-3 -i=3 threadName=Thread-4 -``` - -虽然println()方法在内部是同步的,但i——————的操作却是在进入println()之前发生的,所以有发生非线程安全的概率。 - - println()源码: - -```java - public void println(String x) { - synchronized (this) { - print(x); - newLine(); - } - } -``` - -#### 12.如何知道代码段被哪个线程调用? - -```java - System.out.println(Thread.currentThread().getName()); -``` - -#### 13.线程活动状态? - -```java -public class XKThread extends Thread { - - @Override - public void run() { - System.out.println("run run run is " + this.isAlive() ); - } - - public static void main(String[] args) { - XKThread xk = new XKThread(); - System.out.println("begin ——— " + xk.isAlive()); - xk.start(); - System.out.println("end ————— " + xk.isAlive()); - - } -} - -``` - -#### 14.sleep()方法 - -方法sleep()的作用是在指定的毫秒数内让当前的“正在执行的线程”休眠(暂停执行)。 - -#### 15.如何优雅的设置睡眠时间? - -jdk1.5 后,引入了一个枚举TimeUnit,对sleep方法提供了很好的封装。 - -比如要表达2小时22分55秒899毫秒。 - -```java -Thread.sleep(8575899L); -TimeUnit.HOURS.sleep(3); -TimeUnit.MINUTES.sleep(22); -TimeUnit.SECONDS.sleep(55); -TimeUnit.MILLISECONDS.sleep(899); -``` - -可以看到表达的含义更清晰,更优雅。 - -#### 16.停止线程 - -run方法执行完成,自然终止。 - -stop()方法,suspend()以及resume()都是过期作废方法,使用它们结果不可预期。 - -大多数停止一个线程的操作使用Thread.interrupt()等于说给线程打一个停止的标记, 此方法不回去终止一个正在运行的线程,需要加入一个判断才能可以完成线程的停止。 - -#### 17.interrupted 和 isInterrupted - -interrupted : 判断当前线程是否已经中断,会清除状态。 - -isInterrupted :判断线程是否已经中断,不会清除状态。 - -#### 18.yield - -放弃当前cpu资源,将它让给其他的任务占用cpu执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得cpu时间片。 - -测试代码:(cpu独占时间片) - -```java -public class XKThread extends Thread { - - @Override - public void run() { - long beginTime = System.currentTimeMillis(); - int count = 0; - for (int i = 0; i < 50000000; i++) { - count = count + (i + 1); - } - long endTime = System.currentTimeMillis(); - System.out.println("用时 = " + (endTime - beginTime) + " 毫秒! "); - } - - public static void main(String[] args) { - XKThread xkThread = new XKThread(); - xkThread.start(); - } - -} -``` - -结果: - -```sqlite -用时 = 20 毫秒! -``` - -加入yield,再来测试。(cpu让给其他资源导致速度变慢) - -```java -public class XKThread extends Thread { - - @Override - public void run() { - long beginTime = System.currentTimeMillis(); - int count = 0; - for (int i = 0; i < 50000000; i++) { - Thread.yield(); - count = count + (i + 1); - } - long endTime = System.currentTimeMillis(); - System.out.println("用时 = " + (endTime - beginTime) + " 毫秒! "); - } - - public static void main(String[] args) { - XKThread xkThread = new XKThread(); - xkThread.start(); - } - -} - -``` - -结果: - -```sqlite -用时 = 38424 毫秒! -``` - -#### 19.线程的优先级 - -在操作系统中,线程可以划分优先级,优先级较高的线程得到cpu资源比较多,也就是cpu有限执行优先级较高的线程对象中的任务,但是不能保证一定优先级高,就先执行。 - -Java的优先级分为1~10个等级,数字越大优先级越高,默认优先级大小为5。超出范围则抛出:java.lang.IllegalArgumentException。 - -#### 20.优先级继承特性 - -线程的优先级具有继承性,比如a线程启动b线程,b线程与a优先级是一样的。 - -#### 21.谁跑的更快? - -设置优先级高低两个线程,累加数字,看谁跑的快,上代码。 - -```java -public class Run extends Thread{ - - public static void main(String[] args) { - try { - ThreadLow low = new ThreadLow(); - low.setPriority(2); - low.start(); - - ThreadHigh high = new ThreadHigh(); - high.setPriority(8); - high.start(); - - Thread.sleep(2000); - low.stop(); - high.stop(); - System.out.println("low = " + low.getCount()); - System.out.println("high = " + high.getCount()); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - } - -} - -class ThreadHigh extends Thread { - private int count = 0; - - public int getCount() { - return count; - } - - - @Override - public void run() { - while (true) { - count++; - } - } -} - -class ThreadLow extends Thread { - private int count = 0; - - public int getCount() { - return count; - } - - - @Override - public void run() { - while (true) { - count++; - } - } -} -``` - -结果: - -```sqlite -low = 1193854568 -high = 1204372373 - -``` - -#### 22.线程种类 - -Java线程有两种,一种是用户线程,一种是守护线程。 - -#### 23.守护线程的特点 - -守护线程是一个比较特殊的线程,主要被用做程序中后台调度以及支持性工作。当Java虚拟机中不存在非守护线程时,守护线程才会随着JVM一同结束工作。 - -#### 24.Java中典型的守护线程 - -GC(垃圾回收器) - -#### 25.如何设置守护线程 - -Thread.setDaemon(true) - -PS:Daemon属性需要再启动线程之前设置,不能再启动后设置。 - -#### 25.Java虚拟机退出时Daemon线程中的finally块一定会执行? - -Java虚拟机退出时Daemon线程中的finally块并不一定会执行。 - -代码示例: - -```java -public class XKDaemon { - public static void main(String[] args) { - Thread thread = new Thread(new DaemonRunner(),"xkDaemonRunner"); - thread.setDaemon(true); - thread.start(); - - } - - static class DaemonRunner implements Runnable { - - @Override - public void run() { - try { - SleepUtils.sleep(10); - } finally { - System.out.println("Java小咖秀 daemonThread finally run …"); - } - - } - } -} -``` - -结果: - -``` - -``` - - 没有任何的输出,说明没有执行finally。 - -#### 26.设置线程上下文类加载器 - -​ 获取线程上下文类加载器 - -```java -public ClassLoader getContextClassLoader() -``` - -​ 设置线程类加载器(可以打破Java类加载器的父类委托机制) - -```java -public void setContextClassLoader(ClassLoader cl) -``` - -#### 27.join - -join是指把指定的线程加入到当前线程,比如join某个线程a,会让当前线程b进入等待,直到a的生命周期结束,此期间b线程是处于blocked状态。 - -#### 28.什么是synchronized? - -synchronized关键字可以时间一个简单的策略来防止线程干扰和内存一致性错误,如果一个对象是对多个线程可见的,那么对该对想的所有读写都将通过同步的方式来进行。 - -#### 29.synchronized包括哪两个jvm重要的指令? - -monitor enter 和 monitor exit - -#### 30.synchronized关键字用法? - -可以用于对代码块或方法的修饰 - -#### 31.synchronized锁的是什么? - -普通同步方法 —————> 锁的是当前实力对象。 - -静态同步方法—————> 锁的是当前类的Class对象。 - -同步方法快 —————> 锁的是synchonized括号里配置的对象。 - -#### 32.Java对象头 - -synchronized用的锁是存在Java对象头里的。对象如果是数组类型,虚拟机用3个字宽(Word)存储对象头,如果对象是非数组类型,用2字宽存储对象头。 - -Tips:32位虚拟机中一个字宽等于4字节。 - -#### 33.Java对象头长度 - -![image-20200418215447539](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200418215447539.png) - -#### 34.Java对象头的存储结构 - - 32位JVM的Mark Word 默认存储结构 - -![image-20200418220122794](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200418220122794.png) - -#### 35.Mark Word的状态变化 - -Mark Word 存储的数据会随着锁标志为的变化而变化。 - -![image-20200418220322880](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200418220322880.png) - -64位虚拟机下,Mark Word是64bit大小的 - -![image-20200418224055558](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200418224055558.png) - -#### 36.锁的升降级规则 - -Java SE 1.6 为了提高锁的性能。引入了“偏向锁”和轻量级锁“。 - -Java SE 1.6 中锁有4种状态。级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。 - -锁只能升级不能降级。 - -#### 37.偏向锁 - -大多数情况,锁不仅不存在多线程竞争,而且总由同一线程多次获得。当一个线程访问同步块并获取锁时,会在对象头和栈帧中记录存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行 cas操作来加锁和解锁,只需测试一下对象头 Mark Word里是否存储着指向当前线程的偏向锁。如果测试成功,表示线程已经获得了锁,如果失败,则需要测试下Mark Word中偏向锁的标示是否已经设置成1(表示当前时偏向锁),如果没有设置,则使用cas竞争锁,如果设置了,则尝试使用cas将对象头的偏向锁只想当前线程。 - -#### 38.关闭偏向锁延迟 - -java6和7中默认启用,但是会在程序启动几秒后才激活,如果需要关闭延迟, - --XX:BiasedLockingStartupDelay=0。 - -#### 39.如何关闭偏向锁 - -JVM参数关闭偏向锁:-XX:-UseBiasedLocking=false,那么程序默认会进入轻量级锁状态。 - -Tips:如果你可以确定程序的所有锁通常情况处于竞态,则可以选择关闭。 - -#### 40.轻量级锁 - -线程在执行同步块,jvm会现在当前线程的栈帧中创建用于储存锁记录的空间。并将对象头中的Mark Word复制到锁记录中。然后线程尝试使用cas将对象头中的Mark Word替换为之乡锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。 - -#### 41.轻量锁的解锁 - -轻量锁解锁时,会使原子操作cas将 displaced Mark Word 替换回对象头,如果成功则表示没有竞争发生,如果失败,表示存在竞争,此时锁就会膨胀为重量级锁。 - -#### 42.锁的优缺点对比 - -![image-20200419110938271](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200419110938271.png) - -#### 43.什么是原子操作 - -不可被中断的一个或一系列操作 - -#### 44.Java如何实现原子操作 - -Java中通过锁和循环cas的方式来实现原子操作,JVM的CAS操作利用了处理器提供的CMPXCHG指令来实现的。自旋CAS实现的基本思路就是循环进行CAS操作直到成功为止。 - -#### 45.CAS实现原子操作的3大问题 - -ABA问题,循环时间长消耗资源大,只能保证一个共享变量的原子操作 - -#### 46.什么是ABA问题 - -问题: - -因为cas需要在操作值的时候,检查值有没有变化,如果没有变化则更新,如果一个值原来是A,变成了B,又变成了A,那么使用cas进行检测时会发现发的值没有发生变化,其实是变过的。 - -解决: - -添加版本号,每次更新的时候追加版本号,A-B-A —> 1A-2B-3A。 - -从jdk1.5开始,Atomic包提供了一个类AtomicStampedReference来解决ABA的问题。 - -#### 47.CAS循环时间长占用资源大问题 - -如果jvm能支持处理器提供的pause指令,那么效率会有一定的提升。 - -一、它可以延迟流水线执行指令(de-pipeline),使cpu不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,有些处理器延迟时间是0。 - -二、它可以避免在退出循环的时候因内存顺序冲突而引起的cpu流水线被清空,从而提高cpu执行效率。 - -#### 48.CAS只能保证一个共享变量原子操作 - -一、对多个共享变量操作时,可以用锁。 - -二、可以把多个共享变量合并成一个共享变量来操作。比如,x=1,k=a,合并xk=1a,然后用cas操作xk。 - -Tips:java 1.5开始,jdk提供了AtomicReference类来保证饮用对象之间的原子性,就可以把多个变量放在一个对象来进行cas操作。 - -#### 49.volatile关键字 - -volatile 是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性“。 - -Java语言规范第3版对volatile定义如下,Java允许线程访问共享变量,为了保证共享变量能准确和一致的更新,线程应该确保排它锁单独获得这个变量。如果一个字段被声明为volatile,Java线程内存模型所有线程看到这个变量的值是一致的。 - -#### 50.等待/通知机制 - -一个线程修改了一个对象的值,而另一个线程感知到了变化,然后进行相应的操作。 - -#### ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png)51.wait - -方法wait()的作用是使当前执行代码的线程进行等待,wait()是Object类通用的方法,该方法用来将当前线程置入“预执行队列”中,并在 wait()所在的代码处停止执行,直到接到通知或中断为止。 - -在调用wait之前线程需要获得该对象的对象级别的锁。代码体现上,即只能是同步方法或同步代码块内。调用wait()后当前线程释放锁。 - -#### 52.notify - -notify()也是Object类的通用方法,也要在同步方法或同步代码块内调用,该方法用来通知哪些可能灯光该对象的对象锁的其他线程,如果有多个线程等待,则随机挑选出其中一个呈wait状态的线程,对其发出 通知 notify,并让它等待获取该对象的对象锁。 - -#### 53.notify/notifyAll - -notify等于说将等待队列中的一个线程移动到同步队列中,而notifyAll是将等待队列中的所有线程全部移动到同步队列中。 - -#### 54.等待/通知经典范式 - -等待 - -```java -synchronized(obj) { - while(条件不满足) { - obj.wait(); - } - 执行对应逻辑 -} -``` - -通知 - -```java -synchronized(obj) { - 改变条件 - obj.notifyAll(); -} -``` - -##### 55.ThreadLocal - -主要解决每一个线程想绑定自己的值,存放线程的私有数据。 - -##### 56.ThreadLocal使用 - -获取当前的线程的值通过get(),设置set(T) 方式来设置值。 - -```java -public class XKThreadLocal { - - public static ThreadLocal threadLocal = new ThreadLocal(); - - public static void main(String[] args) { - if (threadLocal.get() == null) { - System.out.println("未设置过值"); - threadLocal.set("Java小咖秀"); - } - System.out.println(threadLocal.get()); - } - -} -``` - -输出: - -```sqlite -未设置过值 -Java小咖秀 -``` - -Tips:默认值为null - -#### 57.解决get()返回null问题 - -通过继承重写initialValue()方法即可。 - -代码实现: - -```java -public class ThreadLocalExt extends ThreadLocal{ - - static ThreadLocalExt threadLocalExt = new ThreadLocalExt(); - - @Override - protected Object initialValue() { - return "Java小咖秀"; - } - - public static void main(String[] args) { - System.out.println(threadLocalExt.get()); - } -} - -``` - -输出结果: - -```java -Java小咖秀 -``` - -#### 58.Lock接口 - -锁可以防止多个线程同时共享资源。Java5前程序是靠synchronized实现锁功能。Java5之后,并发包新增Lock接口来实现锁功能。 - -#### 59.Lock接口提供 synchronized不具备的主要特性 - -![image-20200419193439709](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200419193439709.png) - -#### 60.重入锁 ReentrantLock - -支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁。除此之外,该锁的还支持获取锁时的公平和非公平性选择。 - -#### 61.重进入是什么意思? - -重进入是指任意线程在获取到锁之后能够再次获锁而不被锁阻塞。 - -该特性主要解决以下两个问题: - -一、锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是则再次成功获取。 - -二、所得最终释放。线程重复n次是获取了锁,随后在第n次释放该锁后,其他线程能够获取到该锁。 - -#### 62.ReentrantLock默认锁? - -默认非公平锁 - -代码为证: - -```java - final boolean nonfairTryAcquire(int acquires) { - final Thread current = Thread.currentThread(); - int c = getState(); - if (c == 0) { - if (compareAndSetState(0, acquires)) { - setExclusiveOwnerThread(current); - return true; - } - } - else if (current == getExclusiveOwnerThread()) { - int nextc = c + acquires; - if (nextc < 0) // overflow - throw new Error("Maximum lock count exceeded"); - setState(nextc); - return true; - } - return false; - } -``` - - - -#### 63.公平锁和非公平锁的区别 - -公平性与否针对获取锁来说的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序,也就是FIFO。 - -#### 64.读写锁 - -#### 读写锁允许同一时刻多个读线程访问,但是写线程和其他写线程均被阻塞。读写锁维护一个读锁一个写锁,读写分离,并发性得到了提升。 - -Java中提供读写锁的实现类是ReentrantReadWriteLock。 - -#### 65.LockSupport工具 - -定义了一组公共静态方法,提供了最基本的线程阻塞和唤醒功能。 - -![image-20200419223303672](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200419223303672.png) - -#### 66.Condition接口 - -提供了类似Object监视器方法,与 Lock配合使用实现等待/通知模式。 - -#### 67.Condition使用 - -代码示例: - -```java -public class XKCondition { - Lock lock = new ReentrantLock(); - Condition cd = lock.newCondition(); - - public void await() throws InterruptedException { - lock.lock(); - try { - cd.await();//相当于Object 方法中的wait() - } finally { - lock.unlock(); - } - } - - public void signal() { - lock.lock(); - try { - cd.signal(); //相当于Object 方法中的notify() - } finally { - lock.unlock(); - } - } - -} - -``` - -#### 68.ArrayBlockingQueue? - -一个由数据支持的有界阻塞队列,此队列FIFO原则对元素进行排序。队列头部在队列中存在的时间最长,队列尾部存在时间最短。 - -#### 69.PriorityBlockingQueue? - -一个支持优先级排序的无界阻塞队列,但它不会阻塞数据生产者,而只会在没有可消费的数据时,阻塞数据的消费者。 - -#### 70.DelayQueue? - -是一个支持延时获取元素的使用优先级队列的实现的无界阻塞队列。队列中的元素必须实现Delayed接口和 Comparable接口,在创建元素时可以指定多久才能从队列中获取当前元素。 - -#### 71.Java并发容器,你知道几个? - -ConcurrentHashMap、CopyOnWriteArrayList 、CopyOnWriteArraySet 、ConcurrentLinkedQueue、 - -ConcurrentLinkedDeque、ConcurrentSkipListMap、ConcurrentSkipListSet、ArrayBlockingQueue、 - -LinkedBlockingQueue、LinkedBlockingDeque、PriorityBlockingQueue、SynchronousQueue、 - -LinkedTransferQueue、DelayQueue - -#### 72.ConcurrentHashMap - -并发安全版HashMap,java7中采用分段锁技术来提高并发效率,默认分16段。Java8放弃了分段锁,采用CAS,同时当哈希冲突时,当链表的长度到8时,会转化成红黑树。(如需了解细节,见jdk中代码) - -#### 73.ConcurrentLinkedQueue - -基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。它采用cas算法来实现。(如需了解细节,见jdk中代码) - -#### 74.什么是阻塞队列? - -阻塞队列是一个支持两个附加操作的队列,这两个附加操作支持阻塞的插入和移除方法。 - -1、支持阻塞的插入方法:当队列满时,队列会阻塞插入元素的线程,直到队列不满。 - -2、支持阻塞的移除方法:当队列空时,获取元素的线程会等待队列变为非空。 - -#### 75.阻塞队列常用的应用场景? - -常用于生产者和消费者场景,生产者是往队列里添加元素的线程,消费者是从队列里取元素的线程。阻塞队列正好是生产者存放、消费者来获取的容器。 - -#### 76.Java里的阻塞的队列 - -```sqlite -ArrayBlockingQueue: 数组结构组成的 |有界阻塞队列 -LinkedBlockingQueue: 链表结构组成的|有界阻塞队列 -PriorityBlockingQueue: 支持优先级排序|无界阻塞队列 -DelayQueue: 优先级队列实现|无界阻塞队列 -SynchronousQueue: 不存储元素| 阻塞队列 -LinkedTransferQueue: 链表结构组成|无界阻塞队列 -LinkedBlockingDeque: 链表结构组成|双向阻塞队列 -``` - -#### 77.Fork/Join - -java7提供的一个用于并行执行任务的框架,把一个大任务分割成若干个小任务,最终汇总每个小任务结果的后得到大任务结果的框架。 - -#### 78.工作窃取算法 - -是指某个线程从其他队列里窃取任务来执行。当大任务被分割成小任务时,有的线程可能提前完成任务,此时闲着不如去帮其他没完成工作线程。此时可以去其他队列窃取任务,为了减少竞争,通常使用双端队列,被窃取的线程从头部拿,窃取的线程从尾部拿任务执行。 - -#### 79.工作窃取算法的有缺点 - -优点:充分利用线程进行并行计算,减少了线程间的竞争。 - -缺点:有些情况下还是存在竞争,比如双端队列中只有一个任务。这样就消耗了更多资源。 - -#### 80.Java中原子操作更新基本类型,Atomic包提供了哪几个类? - -AtomicBoolean:原子更新布尔类型 - -AtomicInteger:原子更新整形 - -AtomicLong:原子更新长整形 - -#### 81.Java中原子操作更新数组,Atomic包提供了哪几个类? - -AtomicIntegerArray: 原子更新整形数据里的元素 - -AtomicLongArray: 原子更新长整形数组里的元素 - -AtomicReferenceArray: 原子更新饮用类型数组里的元素 - -AtomicIntegerArray: 主要提供原子方式更新数组里的整形 - -#### 82.Java中原子操作更新引用类型,Atomic包提供了哪几个类? - -如果原子需要更新多个变量,就需要用引用类型了。 - -AtomicReference : 原子更新引用类型 - -AtomicReferenceFieldUpdater: 原子更新引用类型里的字段。 - -AtomicMarkableReference: 原子更新带有标记位的引用类型。标记位用boolean类型表示,构造方法时AtomicMarkableReference(V initialRef,boolean initialMark) - -#### 83.Java中原子操作更新字段类,Atomic包提供了哪几个类? - -AtomiceIntegerFieldUpdater: 原子更新整形字段的更新器 - -AtomiceLongFieldUpdater: 原子更新长整形字段的更新器 - -AtomiceStampedFieldUpdater: 原子更新带有版本号的引用类型,将整数值 - -#### 84.JDK并发包中提供了哪几个比较常见的处理并发的工具类? - -提供并发控制手段: CountDownLatch、CyclicBarrier、Semaphore - -线程间数据交换: Exchanger - -#### 85.CountDownLatch - -允许一个或多个线程等待其他线程完成操作。 - -CountDownLatch的构造函数接受一个int类型的参数作为计数器,你想等待n个点完成,就传入n。 - -两个重要的方法: - -countDown() : 调用时,n会减1。 - -await() : 调用会阻塞当前线程,直到n变成0。 - -await(long time,TimeUnit unit) : 等待特定时间后,就不会继续阻塞当前线程。 - -tips:计数器必须大于等于0,当为0时,await就不会阻塞当前线程。 - -不提供重新初始化或修改内部计数器的值的功能。 - -#### 86.CyclicBarrier - -可循环使用的屏障。 - -让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。 - -CyclicBarrier默认构造放时CyclicBarrier(int parities) ,其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达屏障,然后当前线程被阻塞。 - -#### 87.CountDownLatch与CyclicBarrier区别 - -CountDownLatch: - -计数器:计数器只能使用一次。 - -等待: 一个线程或多个等待另外n个线程完成之后才能执行。 - -CyclicBarrier: - -计数器:计数器可以重置(通过reset()方法)。 - -等待: n个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。 - -#### 88.Semaphore - -用来控制同时访问资源的线程数量,通过协调各个线程,来保证合理的公共资源的访问。 - -应用场景:流量控制,特别是公共资源有限的应用场景,比如数据链接,限流等。 - -#### 89.Exchanger - -Exchanger是一个用于线程间协作的工具类,它提供一个同步点,在这个同步点上,两个线程可以交换彼此的数据。比如第一个线程执行exchange()方法,它会一直等待第二个线程也执行exchange,当两个线程都到同步点,就可以交换数据了。 - -一般来说为了避免一直等待的情况,可以使用exchange(V x,long timeout,TimeUnit unit),设置最大等待时间。 - -Exchanger可以用于遗传算法。 - -#### 90.为什么使用线程池 - -几乎所有需要异步或者并发执行任务的程序都可以使用线程池。合理使用会给我们带来以下好处。 - -- 降低系统消耗:重复利用已经创建的线程降低线程创建和销毁造成的资源消耗。 -- 提高响应速度: 当任务到达时,任务不需要等到线程创建就可以立即执行。 -- 提供线程可以管理性: 可以通过设置合理分配、调优、监控。 - -#### 91.线程池工作流程 - -1、判断核心线程池里的线程是否都有在执行任务,否->创建一个新工作线程来执行任务。是->走下个流程。 - -2、判断工作队列是否已满,否->新任务存储在这个工作队列里,是->走下个流程。 - -3、判断线程池里的线程是否都在工作状态,否->创建一个新的工作线程来执行任务, - -是->走下个流程。 - -4、按照设置的策略来处理无法执行的任务。 - -#### 92.创建线程池参数有哪些,作用? - -```java - public ThreadPoolExecutor( int corePoolSize, - int maximumPoolSize, - long keepAliveTime, - TimeUnit unit, - BlockingQueue workQueue, - ThreadFactory threadFactory, - RejectedExecutionHandler handler) -``` - -1.corePoolSize:核心线程池大小,当提交一个任务时,线程池会创建一个线程来执行任务,即使其他空闲的核心线程能够执行新任务也会创建,等待需要执行的任务数大于线程核心大小就不会继续创建。 - -2.maximumPoolSize:线程池最大数,允许创建的最大线程数,如果队列满了,并且已经创建的线程数小于最大线程数,则会创建新的线程执行任务。如果是无界队列,这个参数基本没用。 - -3.keepAliveTime: 线程保持活动时间,线程池工作线程空闲后,保持存活的时间,所以如果任务很多,并且每个任务执行时间较短,可以调大时间,提高线程利用率。 - -4.unit: 线程保持活动时间单位,天(DAYS)、小时(HOURS)、分钟(MINUTES、毫秒MILLISECONDS)、微秒(MICROSECONDS)、纳秒(NANOSECONDS) - -5.workQueue: 任务队列,保存等待执行的任务的阻塞队列。 - -一般来说可以选择如下阻塞队列: - -ArrayBlockingQueue:基于数组的有界阻塞队列。 - -LinkedBlockingQueue:基于链表的阻塞队列。 - -SynchronizedQueue:一个不存储元素的阻塞队列。 - -PriorityBlockingQueue:一个具有优先级的阻塞队列。 - -6.threadFactory:设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。 - -7. handler: 饱和策略也叫拒绝策略。当队列和线程池都满了,即达到饱和状态。所以需要采取策略来处理新的任务。默认策略是AbortPolicy。 - - AbortPolicy:直接抛出异常。 - - CallerRunsPolicy: 调用者所在的线程来运行任务。 - - DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。 - - DiscardPolicy:不处理,直接丢掉。 - - 当然可以根据自己的应用场景,实现RejectedExecutionHandler接口自定义策略。 - -#### 93.向线程池提交任务 - -可以使用execute()和submit() 两种方式提交任务。 - -execute():无返回值,所以无法判断任务是否被执行成功。 - -submit():用于提交需要有返回值的任务。线程池返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()来获取返回值,get()方法会阻塞当前线程知道任务完成。get(long timeout,TimeUnit unit)可以设置超市时间。 - -#### 94.关闭线程池 - -可以通过shutdown()或shutdownNow()来关闭线程池。它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt来中断线程,所以无法响应终端的任务可以能永远无法停止。 - -shutdownNow首先将线程池状态设置成STOP,然后尝试停止所有的正在执行或者暂停的线程,并返回等待执行任务的列表。 - -shutdown只是将线程池的状态设置成shutdown状态,然后中断所有没有正在执行任务的线程。 - -只要调用两者之一,isShutdown就会返回true,当所有任务都已关闭,isTerminaed就会返回true。 - -一般来说调用shutdown方法来关闭线程池,如果任务不一定要执行完,可以直接调用shutdownNow方法。 - -#### 95.线程池如何合理设置 - -配置线程池可以从以下几个方面考虑。 - -- 任务是cpu密集型、IO密集型或者混合型 - -- 任务优先级,高中低。 - -- 任务时间执行长短。 - -- 任务依赖性:是否依赖其他系统资源。 - - cpu密集型可以配置可能小的线程,比如 n + 1个线程。 - - io密集型可以配置较多的线程,如 2n个线程。 - - 混合型可以拆成io密集型任务和cpu密集型任务, - - 如果两个任务执行时间相差大,否->分解后执行吞吐量将高于串行执行吞吐量。 - - 否->没必要分解。 - - 可以通过Runtime.getRuntime().availableProcessors()来获取cpu个数。 - - 建议使用有界队列,增加系统的预警能力和稳定性。 - -#### 96.Executor - -从JDK5开始,把工作单元和执行机制分开。工作单元包括Runnable和Callable,而执行机制由Executor框架提供。 - -#### 97.Executor框架的主要成员 - -ThreadPoolExecutor :可以通过工厂类Executors来创建。 - -可以创建3种类型的ThreadPoolExecutor:SingleThreadExecutor、FixedThreadPool、CachedThreadPool。 - -ScheduledThreadPoolExecutor :可以通过工厂类Executors来创建。 - -可以创建2中类型的ScheduledThreadPoolExecutor:ScheduledThreadPoolExecutor、SingleThreadScheduledExecutor - -Future接口:Future和实现Future接口的FutureTask类来表示异步计算的结果。 - -Runnable和Callable:它们的接口实现类都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行。Runnable不能返回结果,Callable可以返回结果。 - -#### 98.FixedThreadPool - -可重用固定线程数的线程池。 - -查看源码: - -```java -public static ExecutorService newFixedThreadPool(int nThreads) { - return new ThreadPoolExecutor(nThreads, nThreads, - 0L, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue());} -``` - -corePoolSize 和maxPoolSize都被设置成我们设置的nThreads。 - -当线程池中的线程数大于corePoolSize ,keepAliveTime为多余的空闲线程等待新任务的最长时间,超过这个时间后多余的线程将被终止,如果设为0,表示多余的空闲线程会立即终止。 - -工作流程: - -1.当前线程少于corePoolSize,创建新线程执行任务。 - -2.当前运行线程等于corePoolSize,将任务加入LinkedBlockingQueue。 - -3.线程执行完1中的任务,会循环反复从LinkedBlockingQueue获取任务来执行。 - -LinkedBlockingQueue作为线程池工作队列(默认容量Integer.MAX_VALUE)。因此可能会造成如下赢下。 - -1.当线程数等于corePoolSize时,新任务将在队列中等待,因为线程池中的线程不会超过corePoolSize。 - -2.maxnumPoolSize等于说是一个无效参数。 - -3.keepAliveTime等于说也是一个无效参数。 - -4.运行中的FixedThreadPool(未执行shundown或shundownNow))则不会调用拒绝策略。 - -5.由于任务可以不停的加到队列,当任务越来越多时很容易造成OOM。 - -#### 99.SingleThreadExecutor - -是使用单个worker线程的Executor。 - -查看源码: - -```java -public static ExecutorService newSingleThreadExecutor() { - return new FinalizableDelegatedExecutorService - (new ThreadPoolExecutor(1, 1, - 0L, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue())); - } -``` - -corePoolSize和maxnumPoolSize被设置为1。其他参数和FixedThreadPool相同。 - -执行流程以及造成的影响同FixedThreadPool. - -#### 100.CachedThreadPool - -根据需要创建新线程的线程池。 - -查看源码: - -```java - public static ExecutorService newCachedThreadPool() { - return new ThreadPoolExecutor(0, Integer.MAX_VALUE, - 60L, TimeUnit.SECONDS, - new SynchronousQueue()); -``` - -corePoolSize设置为0,maxmumPoolSize为Integer.MAX_VALUE。keepAliveTime为60秒。 - -工作流程: - -1.首先执行SynchronousQueue.offer (Runnable task)。如果当前maximumPool 中有空闲线程正在执行S ynchronousQueue.poll(keepAliveTIme,TimeUnit.NANOSECONDS),那么主线程执行offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行,execute方 法执行完成;否则执行下面的步骤2。 - -2. 当初始maximumPool为空或者maximumPool中当前没有空闲线程时,将没有线程执行 SynchronousQueue.poll (keepAliveTime,TimeUnit.NANOSECONDS)。这种情况下,步骤 1将失 败。此时CachedThreadPool会创建一个新线程执行任务,execute()方法执行完成。 - -3.在步骤2中新创建的线程将任务执行完后,会执行SynchronousQueue.poll (keepAliveTime,TimeUnit.NANOSECONDS)。这个poll操作会让空闲线程最多在SynchronousQueue中等待60秒钟。如果60秒钟内主线程提交了一个新任务(主线程执行步骤1),那么这个空闲线程将执行主线程提交的新任务;否则,这个空闲线程将终止。由于空闲60秒的空闲线程会被终止,因此长时间保持空闲的CachedThreadPool不会使用任何资源。 - -一般来说它适合处理时间短、大量的任务。 - -参考: - -- 《Java多线程编程核心技术》 - -- 《Java高并发编程详解》 - -- 《Java 并发编程的艺术》 - - ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) - From 5be9702ebc22c7a3a57952b0d3e9d48e24d4dcf5 Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:39:41 +0800 Subject: [PATCH 17/19] =?UTF-8?q?Delete=20=E7=AE=80=E5=8E=86.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "Docs/\347\256\200\345\216\206.md" | 169 ----------------------------- 1 file changed, 169 deletions(-) delete mode 100644 "Docs/\347\256\200\345\216\206.md" diff --git "a/Docs/\347\256\200\345\216\206.md" "b/Docs/\347\256\200\345\216\206.md" deleted file mode 100644 index 6ce6348..0000000 --- "a/Docs/\347\256\200\345\216\206.md" +++ /dev/null @@ -1,169 +0,0 @@ -## 简历 - -几个制作简历不错的网站: - -https://mp.weixin.qq.com/s/z0k922U6jwXe5VYlz33gaA - -原文链接:https://www.zhihu.com/question/25002833 ThoughtWorks中国回答 - -大家伙让一让,这个问题让老司机先答!作为一个潜入IT圈五年之久、看过数万份简历的HR,在这个问题上还是有点发言权的。HR在筛选简历时主要从公司需求出发,重点不一,不过还是有很多“通用”的套路,为了在30秒内判断出这份简历是否值得跟进,我认为程序员写简历的正确姿势是这样的: - -#### **一、基本格调** - -即打开简历之后的第一印象。就好比我们看见一个人,会有一个整体的感觉,他是fashion的、小清新的还是老道的?有了第一印象之后再慢慢分解来看。 - -**加分写法:** - -- 简洁明了,逻辑结构清晰。 -- 字体,排版,顺畅,清晰整齐就好。 -- 最好是PDF格式,兼容性强且不易乱序。 - -**减分写法:** - -- 设计的过于浮夸或者过于简单的。(eg.有的简历五颜六色、非常酷炫,却半天找不到联系方式,抑或是只有个人基本信息和公司名称) -- 写了十几页,半天打不开的,或者加载了半天,打开还乱码。 - -#### **二、基本信息(姓名/性别/毕业院校/电话/邮箱/居住地/期望地)** - -**加分写法:** - -- 清晰罗列出以上信息,这样HR就不用在接下来的电话沟通或面试中再去追问这些内容,建立我们接下来电话沟通对你的熟悉度。 -- 再额外能加上QQ或者微信就更好了(以防有时候电话打不通哦,时不时会遇到这种情况) - -**减分写法:** - -- 大部分的基本信息没有写 -- 甩给我一个Github链接,极致简洁的几句描述,需要通过你的链接来找你的联系方式。(如果不是博客写的特别好,基本是要放弃你了) - -#### **三、工作经历&项目经历** - -**加分写法:** - -- 工作经历项目经历可参照万能的STAR法则来写,STAR不清楚的童鞋点[这里](https://link.zhihu.com/?target=http%3A//www.xuexila.com/success/story/509956.html)啦 -- 效力过哪些公司,我们匹配的公司? BAT? 知名大型互联网公司? -- 做过什么行业领域,和我们目前的行业是否匹配 -- 擅长的技术语言,应用了哪些技术栈,(Java, Scala,Ruby, React, Vue, Microservice…) -- 经历的项目复杂度,及在项目中承担什么样的角色(人的变化/技术的变化/环境的变化/不同工作经历相同角色的不同点) -- 时间节点(空档期) - -**减分写法:** - -- 看了半天,不知所云,没有任何亮点,没有让人有去和你聊一聊深扒的信息。 - -**来几个栗子** - -栗子1错误打开方式: - -- XX(全栈工程师)2013.06 — 至今 -- 参与需求分析及实现方案设计。 -- 设计数据库表结构,实现后台功能及web页面展示。 -- 产品线上部署及运维。 -- ay 配置管理工程师 2010.03 — 2013.03 -- 负责公司产品性能测试,及线上数据分析 -- 负责公司配置管理,环境维护等工作 - -**点评:看不出来他做的什么事情,没有逻辑性,甚至不知道他做的什么技术语言。** - -**栗子2正确打开方式:** - -**西安XXX公司 Java工程师 — 2016.2月-2017.2月** - -1、MOGU推荐架构数据与缓存层设计开发 - -- MOGU是一款时尚资讯app,负责推荐页面资讯feed流的展示及用户历史的展示 -- 负责数据层,处理前端逻辑整个开发工作,分布式rpc服务搭建 -- 负责进行压测监测、缓存处理,对接又进行改进优化,主用redis缓存 - -2、基于JAVA的电商爬虫开发 - -- 使用java搭建爬虫server平台,进行配置和开发,进行网页改版监测功能开发 -- 爬取淘宝时尚品牌与其他电商网站商品品牌与详情等 -- 通过频率、ip池、匿名代理等应对一些网站的反爬 - -3、同图搜索Solr服务开发 - -基于算法组的同图策略,使用solr做java接又实现rpc服务搭建,进行索引构建和solr实现 - -**北京XXX** - -java大数据工程师— 2013.4月-2015.12月 - -1、负责实时流消息处理应用系统构建和实现 - -- 在调研了kafka的优势和我们的具体需求之后,用kafka作为消费者,保证高吞吐处理消息,并持久化消息的同时供其它服务使用,进行了系统的设计和搭建使用。 本地日志保证消息不丢失,并通过记录游标滑动重复读取数据。 -- 使用storm 负责搭建消息处理架构,并完成基于业务的消息落地,提供后续的数据 统计分析实时和离线任务,诸如pv、uv等数据,为运营做决策 -- 网站用户行为埋点和基于js的日志收集器开发,定义接又和前端部门配合。主用go 2、hadoop集群搭建和数据分析处理 - -2、基于CDH的集群搭建工作,后期进行维护 - -编写MapReduce程序,能将复杂工作逻辑化,尽最大能力发挥大数据应用的特点, 对程序高要求,监控自己程序运行情况,使用内存合理,注重增量和全量运算的利弊 - -3、调度系统设计与实现 基于quartz2搭建调度平台,带徒弟实现相关功能并定期review代码 - -4、数据库调优 负责主从搭建,并掌握主从搭建的利弊,了解业界mycat原理,有数据库优化经验,能 正确并擅长使用索引,对锁有深刻的认识 - -5、网站开发 java web网站业务开发,并能很好的使用缓存技术,对重构有实际的经验,并对面向对 象开发有全面的实战经验。了解java数据结构的使用场景,虽然对于大并发没有太大的 发挥余地,但是掌握了数据结构,对于并发和阻塞等有自己的见解。 - -**点评:非常清晰的告诉简历阅读者自己做了什么事情,负责了什么样的事情,用了什么技术栈,且逻辑连贯。** - -#### **四、工作期望&个人评价** - -加分写法: - -- 对自己有一个全方位的一个描述总结,让别人更好的解读你。或者在此处,高亮你的优点特长有哪些。 -- 即使不写个人评价,也一定记得写上工作期望。 - -减分写法: - -完全看不出个性特点,写和没写没什么区别。 来几个栗子 - -**栗子1 错误打开方式** - -为人性格,诚实谦虚,勤奋,能吃苦耐劳,有耐心,有团队意识,能和同学和谐相处,能虚心接受别人的建议的人。 - -责任心强,善于沟通,具有良好的团队合作精神;专业扎实,具有较强的钻研精神和学习能力;性格比较乐观外向,喜欢打羽毛球。 - -**栗子2正确打开方式** - -- 我对自己的定位: 主攻前端,同时在其他方面打打辅助。我不希望过于依赖别人,即使没有后端没有设计没有产品经理,我依然想要把这个产品做到完美。毕竟全栈才能最高效地解决问题。 -- 我对工作的态度: 第一,要高效完成自己的本职工作。第二,要在完成的基础上寻找完美。第三,要在完美的基础上,与其他同事 互相交流学习,互相提升。工作是一种生活方式,不是一份养家糊口的差事。 -- 我怎样克服困难: 不用百度是第一原则,在遇到技术问题时我往往会去Google、Stack over flow上寻找答案。但通常很多问题 并不一定已经被人解决,所以熟练地阅读源码、在手册、规范甚至 REPL的环境自己做实验才是最终解决问题的办法。相信事实的结果,自己动手去做。 -- 怎样保持自己的视野:我一直认为软件开发中视野极其重要,除了在 Twitter 上关注业界大牛,Github Trending 也是每周必刷。 另外 Podcast、Hacker News、Reddit 以及TechRadar 也是重要的一手资料。保持开阔视野才能找到更酷的解决方案。 -- 我的优势: 热爱技术、自学能力强,有良好的自我认知。全面的技能树与开阔的视野,良好的心态、情商与沟通能力。 -- 我的劣势: 非科班出身没有科班同学对算法的熟练掌握,但我决定死磕技术,弥补不足。 - -**栗子3正确打开方式** - -- 极客、热爱技术、热爱开源 -- Ruby on Rails:精通 -- Agile/Lean:精通 -- ReactJS:掌握 -- Docker:掌握 -- AWS:掌握 - -#### **五、 是否有博客,个人技术栈点等** - -1. 看到有这项的HR两眼已经放光了,加分加分项,说明你真正的热爱技术,善于学习总结,乐于分享,且有投入自己的业余时间到软件事业中。 -2. 我喜欢的书籍:《重构》《卓有成效的程序员》《代码整洁之道》等 -3. 我喜欢的社区: 图灵社区,知乎,博客园,Stack Over flow,Google Developer Group等 -4. 我的博客链接、个人作品链接如下: - -- [https://github.com/github](https://link.zhihu.com/?target=https%3A//github.com/github) -- [http://www.oschina.net/](https://link.zhihu.com/?target=http%3A//www.oschina.net/) -- [https://www.cnblogs.com/](https://link.zhihu.com/?target=https%3A//www.cnblogs.com/) -- [https://itunes.apple.com/app/battle-of-crab/id1121917063?l=en&amp;mt=8](https://link.zhihu.com/?target=https%3A//itunes.apple.com/app/battle-of-crab/id1121917063%3Fl%3Den%26mt%3D8) - -#### **六、简历内容真实性** - -老司机提醒你,你简历的任意一个细节将会是后面面试中的呈堂证供。 - -基本就这些了,希望对大家能有帮助,看起简历来几十秒,码字还是个体力活。 - -​ 扫码回复“404”获取404份简历模版 - -![getqrcode](https://gitee.com/yizhibuerdai/Imagetools/raw/85449e4e4b14d08e02a5c7683d06518a869880f4/images/getqrcode.jpg) - -写在最后: 希望大家都能拿到自己心仪的offer。面试笔记会继续更新优化。 - -![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) - From 8d05c14be7603e3941ca3f0a7b669bc23d218a63 Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:41:28 +0800 Subject: [PATCH 18/19] Add files via upload --- Dubbo.md | 271 ++++ Elasticsearch.md | 294 +++++ Git.md | 268 ++++ IO&NIO.md | 209 ++++ JVM.md | 395 ++++++ Java8.md | 187 +++ "Java\345\237\272\347\241\200.md" | 933 ++++++++++++++ ...00\357\274\210\344\270\213\357\274\211.md" | 438 +++++++ "Java\345\244\232\347\272\277\347\250\213.md" | 1113 +++++++++++++++++ "Java\351\233\206\345\220\210.md" | 676 ++++++++++ Jsp.md | 417 ++++++ Kafka.md | 178 +++ Linux.md | 786 ++++++++++++ Maven.md | 318 +++++ MongoDB.md | 609 +++++++++ "MySql-\344\270\212\345\215\267.md" | 582 +++++++++ "MySql-\344\270\213\345\215\267.md" | 584 +++++++++ Mybatis.md | 255 ++++ Netty.md | 587 +++++++++ Nginx.md | 226 ++++ RabbitMQ.md | 215 ++++ "Redis-\344\270\212\345\215\267.md" | 433 +++++++ "Redis-\344\270\213\345\215\267.md" | 359 ++++++ Servlet.md | 247 ++++ Spring.md | 344 +++++ SpringBoot.md | 285 +++++ SpringCloud.md | 235 ++++ Tomcat.md | 440 +++++++ "Web\345\256\211\345\205\250.md" | 602 +++++++++ Zookeeper.md | 448 +++++++ ...2\345\270\270&\345\217\215\345\260\204.md" | 167 +++ "\347\256\200\345\216\206.md" | 167 +++ ...27\346\234\272\345\237\272\347\241\200.md" | 287 +++++ 33 files changed, 13555 insertions(+) create mode 100644 Dubbo.md create mode 100644 Elasticsearch.md create mode 100644 Git.md create mode 100644 IO&NIO.md create mode 100644 JVM.md create mode 100644 Java8.md create mode 100644 "Java\345\237\272\347\241\200.md" create mode 100644 "Java\345\237\272\347\241\200\357\274\210\344\270\213\357\274\211.md" create mode 100644 "Java\345\244\232\347\272\277\347\250\213.md" create mode 100644 "Java\351\233\206\345\220\210.md" create mode 100644 Jsp.md create mode 100644 Kafka.md create mode 100644 Linux.md create mode 100644 Maven.md create mode 100644 MongoDB.md create mode 100644 "MySql-\344\270\212\345\215\267.md" create mode 100644 "MySql-\344\270\213\345\215\267.md" create mode 100644 Mybatis.md create mode 100644 Netty.md create mode 100644 Nginx.md create mode 100644 RabbitMQ.md create mode 100644 "Redis-\344\270\212\345\215\267.md" create mode 100644 "Redis-\344\270\213\345\215\267.md" create mode 100644 Servlet.md create mode 100644 Spring.md create mode 100644 SpringBoot.md create mode 100644 SpringCloud.md create mode 100644 Tomcat.md create mode 100644 "Web\345\256\211\345\205\250.md" create mode 100644 Zookeeper.md create mode 100644 "\345\274\202\345\270\270&\345\217\215\345\260\204.md" create mode 100644 "\347\256\200\345\216\206.md" create mode 100644 "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200.md" diff --git a/Dubbo.md b/Dubbo.md new file mode 100644 index 0000000..f258442 --- /dev/null +++ b/Dubbo.md @@ -0,0 +1,271 @@ +## Dubbo + +#### 1.什么是Dubbo? + +Dubbo是基于Java的高性能轻量级的RPC分布式服务框架,现已成为 Apache 基金会孵化项目。 + +官网:http://dubbo.apache.org/en-us/ + +#### 2.为什么要使用Dubbo? + +背景: + +随着互联网的快速发展,Web应用程序的规模不断扩大,最后我们发现传统的垂直体系结构(整体式)已无法解决。分布式服务体系结构和流计算体系结构势在必行,迫切需要一个治理系统来确保体系结构的有序发展。 + +- 开源免费 +- 一些核心业务被提取并作为独立的服务提供服务,逐渐形成一个稳定的服务中心,这样前端应用程序就可以更好地响应变化多端的市场需求 +- 分布式框架能承受更大规模的流量 +- 内部基于netty性能高 + +#### 3.Dubbo提供了哪3个关键功能? + +基于接口的远程调用 + +容错和负载均衡 + +自动服务注册和发现 + +#### 4.你知道哪些机构在用Dubbo吗? + +![image-20200423105332840](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423105332840.png) + +#### 5.Dubbo服务的关键节点有哪些? + +![image-20200423105925624](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423105925624.png) + +#### 6.说一下Dubbo服务注册流程? + +1. 服务容器负责启动,加载,运行服务提供者。 +2. 服务提供者在启动时,向注册中心注册自己提供的服务。 +3. 服务消费者在启动时,向注册中心订阅自己所需的服务。 +4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。 +5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。 +6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。 + +#### 7.能画一下服务注册流程图吗? + +![image-20200423110344448](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423110344448.png) + +#### 8.Dubbo架构的特点? + +连通性、健壮性、伸缩性、以及向未来架构的升级性。 + +#### 9.对jdk的最小版本需求? + +jdk1.6+ + +#### 10.注册中心的选择? + +一般来说选中Zookeeper更稳定更合适。 + +除了Zookeeper还有Redis注册中心、Multicast注册中心、Simple注册中心。 + +#### 11.Dubbo的核心配置?用途? + +![image-20200423111538534](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423111538534.png) + +#### 12.配置优先级规则? + +![image-20200423112118958](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423112118958.png) + +优先级从高到低: + +- JVM -D参数,当你部署或者启动应用时,它可以轻易地重写配置,比如,改变dubbo协议端口; +- XML, XML中的当前配置会重写dubbo.properties中的; +- Properties,默认配置,仅仅作用于以上两者没有配置时。 + +#### 13.如何用代码方式绕过注册中心点对点直连? + +```java +… + +ReferenceConfig reference = new ReferenceConfig(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏 +// 如果点对点直连,可以用reference.setUrl()指定目标地址,设置url后将绕过注册中心, +// 其中,协议对应provider.setProtocol()的值,端口对应provider.setPort()的值, +// 路径对应service.setPath()的值,如果未设置path,缺省path为接口名 +reference.setUrl("dubbo://10.20.130.230:20880/com.xxx.XxxService"); + +… +``` + +#### 14.Dubbo配置来源有几种?分别是? + +4种 + +- JVM System Properties,-D参数 +- Externalized Configuration,外部化配置 +- ServiceConfig、ReferenceConfig等编程接口采集的配置 +- 本地配置文件dubbo.properties + +#### 15.如何禁用某个服务的启动检查? + +```xml + +``` + +#### 16.Dubbo 负载均衡策略?默认是? + +- 随机负载平衡(默认) + +- RoundRobin负载平衡 + +- 最小活动负载平衡 + +- 一致的哈希负载平衡 + +#### 17.上线兼容老版本? + +多版本号(version) + +#### 18.开发测试环境,想绕过注册中心如何配置? + +- xml + +```xml + + +``` + +- -D + + ```powershell + java -Dcom.alibaba.xxx.XxxService=dubbo://localhost:20890 + ``` + +- .properties + + ```properties + java -Ddubbo.resolve.file=xxx.properties + ``` + +```properties +com.alibaba.xxx.XxxService=dubbo://localhost:20890 +``` + +#### 19.集群容错几种方法? + +![image-20200423121735540](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423121735540.png) + +#### 20.Dubbo有几种配置方式? + +1. Spring +2. Java API + +#### 21.Dubbo有哪些协议?推荐? + +- dubbo://(推荐) +- rmi:// +- hessian:// +- http:// +- webservice:// +- thrift:// +- memcached:// +- redis:// +- rest:// + +#### 22.Dubbo使用什么通信框架? + +dubbo使用netty。 + +#### 23.dubbo协议默认端口号?http协议默认端口?hessian?rmi? + +- dubbo:20880 +- http:80 +- hessian:80 +- rmi:80 + +#### 24.Dubbo默认序列化框架?其他的你还知道? + +- dubbo协议缺省为hessian2 +- rmi协议缺省为java +- http协议缺省为json + +#### 25.一个服务有多重实现时,如何处理? + +可以用group分组,服务提供方和消费放都指定同一个group。 + +#### 26.Dubbo服务调用默认是阻塞的?还有其他的? + +默认是同步等待结果阻塞的,同时也支持异步调用。 + +Dubbo 是基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小,异步调用会返回一个 Future 对象。 + +#### 27.Dubbo服务追踪解决方案? + +- Zipkin +- Pinpoint +- SkyWalking + +#### 28.Dubbo不维护了吗?Dubbo和Dubbox有什么区别? + +现在进入了Apache,由apache维护。 + +Dubbox是当当的扩展项目。 + +#### 29.Dubbox有什么新功能? + +- 支持REST风格远程调用(HTTP + JSON/XML) + +- 支持基于Kryo和FST的Java高效序列化实现 + +- 支持基于嵌入式Tomcat的HTTP remoting体系 +- 升级Spring +- 升级ZooKeeper客户端 + +#### 30.io线程池大小默认? + +cpu个数 + 1 + +#### 31.dubbo://协议适合什么样的服务调用? + +采用单一长链接和NIO异步通讯,适用于小数量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。 + +不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。 + +![image-20200423154308365](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423154308365.png) + +#### 32.自动剔除服务什么原理? + +zookeeper临时节点,会话保持原理。 + +#### 33.从 `2.0.5` 版本开始,dubbo支持通过x命令来进行服务治理? + +telnet + +#### 34.如何用命令查看服务列表? + +```powershell +telnet localhost 20880 +``` + +进入命令行。然后执行 ls相关命令: + +- `ls`: 显示服务列表 +- `ls -l`: 显示服务详细信息列表 +- `ls XxxService`: 显示服务的方法列表 +- `ls -l XxxService`: 显示服务的方法详细信息列表 + +#### 35.Dubbo框架设计是怎样的? + +![image-20200423162348971](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200423162348971.png) + +各层说明: + +- **config 配置层**:对外配置接口,以 `ServiceConfig`, `ReferenceConfig` 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类 +- **proxy 服务代理层**:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 `ServiceProxy` 为中心,扩展接口为 `ProxyFactory` +- **registry 注册中心层**:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 `RegistryFactory`, `Registry`, `RegistryService` +- **cluster 路由层**:封装多个提供者的路由及负载均衡,并桥接注册中心,以 `Invoker` 为中心,扩展接口为 `Cluster`, `Directory`, `Router`, `LoadBalance` +- **monitor 监控层**:RPC 调用次数和调用时间监控,以 `Statistics` 为中心,扩展接口为 `MonitorFactory`, `Monitor`, `MonitorService` +- **protocol 远程调用层**:封装 RPC 调用,以 `Invocation`, `Result` 为中心,扩展接口为 `Protocol`, `Invoker`, `Exporter` +- **exchange 信息交换层**:封装请求响应模式,同步转异步,以 `Request`, `Response` 为中心,扩展接口为 `Exchanger`, `ExchangeChannel`, `ExchangeClient`, `ExchangeServer` +- **transport 网络传输层**:抽象 mina 和 netty 为统一接口,以 `Message` 为中心,扩展接口为 `Channel`, `Transporter`, `Client`, `Server`, `Codec` +- **serialize 数据序列化层**:可复用的一些工具,扩展接口为 `Serialization`, `ObjectInput`, `ObjectOutput`, `ThreadPool` + +#### 36.你读过Dubbo的源码吗? + +这个问题其实面试中如果问dubbo的话,基本就会带这个问题。有时间的话,大家可以下载源码,读一读,如果大家有兴趣的话,我会出后续文章。 + +参考:http://dubbo.apache.org/en-us/ + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) + diff --git a/Elasticsearch.md b/Elasticsearch.md new file mode 100644 index 0000000..27e6a25 --- /dev/null +++ b/Elasticsearch.md @@ -0,0 +1,294 @@ +## Elasticsearch + +#### 1.简单介绍下ES? + +ES是一种存储和管理基于文档和半结构化数据的数据库(搜索引擎)。它提供实时搜索(ES最近几个版本才提供实时搜索,以前都是准实时)和分析结构化、半结构化文档、数据和地理空间信息数据。 + +#### 2.简单介绍当前可以下载的ES稳定版本? + +最新的稳定版本是7.10.0 + +#### 3.安装ES前需要安装哪种软件? + +JDK 8或者 Java 1.8.0 + +#### 4.请介绍启动ES服务的步骤? + +**A:**启动步骤如下 + +Windows下进入ES文件夹的bin目录下,点击ElasticSearch.bat开始运行 + +打开本地9200端口http://localhost:9200, 就可以使用ES了 + +#### 5.ES中的倒排索引是什么? + +传统的检索方式是通过文章,逐个遍历找到对应关键词的位置。 +倒排索引,是通过分词策略,形成了词和文章的映射关系表,也称倒排表,这种词典 + 映射表即为**倒排索引**。 + +其中词典中存储词元,倒排表中存储该词元在哪些文中出现的位置。 +有了倒排索引,就能实现 O(1) 时间复杂度的效率检索文章了,极大的提高了检索效率。 + +加分项: +倒排索引的底层实现是基于:FST(Finite State Transducer)数据结构。 + +Lucene 从 4+ 版本后开始大量使用的数据结构是 FST。FST 有两个优点: +1)空间占用小。通过对词典中单词前缀和后缀的重复利用,压缩了存储空间; +2)查询速度快。O(len(str)) 的查询时间复杂度。 + +#### 6. ES是如何实现master选举的? + +前置条件: +1)只有是候选主节点(master:true)的节点才能成为主节点。 +2)最小主节点数(min_master_nodes)的目的是防止脑裂。 + +Elasticsearch 的选主是 ZenDiscovery 模块负责的,主要包含 Ping(节点之间通过这个RPC来发现彼此)和 Unicast(单播模块包含一个主机列表以控制哪些节点需要 ping 通)这两部分; +获取主节点的核心入口为 findMaster,选择主节点成功返回对应 Master,否则返回 null。 + +选举流程大致描述如下: +第一步:确认候选主节点数达标,elasticsearch.yml 设置的值 discovery.zen.minimum_master_nodes; +第二步:对所有候选主节点根据nodeId字典排序,每次选举每个节点都把自己所知道节点排一次序,然后选出第一个(第0位)节点,暂且认为它是master节点。 +第三步:如果对某个节点的投票数达到一定的值(候选主节点数n/2+1)并且该节点自己也选举自己,那这个节点就是master。否则重新选举一直到满足上述条件。 + +- 补充: + - 这里的 id 为 string 类型。 + - master 节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理;data 节点可以关闭 http 功能。 + +#### 7. 如何解决ES集群的脑裂问题 + +所谓集群脑裂,是指 Elasticsearch 集群中的节点(比如共 20 个),其中的 10 个选了一个 master,另外 10 个选了另一个 master 的情况。 + +当集群 master 候选数量不小于 3 个时,可以通过设置最少投票通过数量(discovery.zen.minimum_master_nodes)超过所有候选节点一半以上来解决脑裂问题; +当候选数量为两个时,只能修改为唯一的一个 master 候选,其他作为 data 节点,避免脑裂问题。 + +#### 8. 详细描述一下ES索引文档的过程? + +这里的索引文档应该理解为文档写入 ES,创建索引的过程。 + +第一步:客户端向集群某节点写入数据,发送请求。(如果没有指定路由/协调节点,请求的节点扮演协调节点的角色。) +第二步:协调节点接受到请求后,默认使用文档 ID 参与计算(也支持通过 routing),得到该文档属于哪个分片。随后请求会被转到另外的节点。 + +```java +bash# 路由算法:根据文档id或路由计算目标的分片id +shard = hash(document_id) % (num_of_primary_shards) +``` + +第三步:当分片所在的节点接收到来自协调节点的请求后,会将请求写入到 Memory Buffer,然后定时(默认是每隔 1 秒)写入到F ilesystem Cache,这个从 Momery Buffer 到 Filesystem Cache 的过程就叫做 refresh; +第四步:当然在某些情况下,存在 Memery Buffer 和 Filesystem Cache 的数据可能会丢失,ES 是通过 translog 的机制来保证数据的可靠性的。其实现机制是接收到请求后,同时也会写入到 translog 中,当 Filesystem cache 中的数据写入到磁盘中时,才会清除掉,这个过程叫做 flush; +第五步:在 flush 过程中,内存中的缓冲将被清除,内容被写入一个新段,段的 fsync 将创建一个新的提交点,并将内容刷新到磁盘,旧的 translog 将被删除并开始一个新的 translog。 +第六步:flush 触发的时机是定时触发(默认 30 分钟)或者 translog 变得太大(默认为 512 M)时。 + +![elasticsearch_index_process.jpg](https://www.wenyuanblog.com/medias/blogimages/elasticsearch_index_process.jpg) + + + +- 补充:关于 Lucene 的 Segement + - Lucene 索引是由多个段组成,段本身是一个功能齐全的倒排索引。 + - 段是不可变的,允许 Lucene 将新的文档增量地添加到索引中,而不用从头重建索引。 + - 对于每一个搜索请求而言,索引中的所有段都会被搜索,并且每个段会消耗 CPU 的时钟周、文件句柄和内存。这意味着段的数量越多,搜索性能会越低。 + - 为了解决这个问题,Elasticsearch 会合并小段到一个较大的段,提交新的合并段到磁盘,并删除那些旧的小段。(段合并) + +#### 9. 详细描述一下ES更新和删除文档的过程? + +删除和更新也都是写操作,但是 Elasticsearch 中的文档是不可变的,因此不能被删除或者改动以展示其变更。 + +磁盘上的每个段都有一个相应的 .del 文件。当删除请求发送后,文档并没有真的被删除,而是在 .del 文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在 .del 文件中被标记为删除的文档将不会被写入新段。 + +在新的文档被创建时,Elasticsearch 会为该文档指定一个版本号,当执行更新时,旧版本的文档在 .del 文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。 + +#### 10. 详细描述一下ES搜索的过程? + +搜索被执行成一个两阶段过程,即 Query Then Fetch; +Query阶段: +查询会广播到索引中每一个分片拷贝(主分片或者副本分片)。每个分片在本地执行搜索并构建一个匹配文档的大小为 from + size 的优先队列。PS:在搜索的时候是会查询Filesystem Cache的,但是有部分数据还在Memory Buffer,所以搜索是近实时的。 +每个分片返回各自优先队列中 **所有文档的 ID 和排序值** 给协调节点,它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。 +Fetch阶段: +协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。每个分片加载并 丰富 文档,如果有需要的话,接着返回文档给协调节点。一旦所有的文档都被取回了,协调节点返回结果给客户端。 + +#### 11.索引是什么? + +ES集群包含多个索引,每个索引包含一种表,表包含多个文档,并且每个文档包含不同的属性。 + +#### 12.请解释什么是分片(SHARDs)? + +随着索引文件的增加,磁盘容量、处理能力都会变得不够,在这种情况下,将索引数据切分成小段,这就叫分片(SHARDS)。它的出现大大改进了数据查询的效率。 + +#### 13.什么是副本(REPLICA), 他的作用是什么? + +副本是分片的完整拷贝,副本的作用是增加了查询的吞吐率和在极端负载情况下获得高可用的能力。副本有效的帮助处理用户请求。 + +#### 14.在ES集群中增加和创建索引的步骤是什么? + +可以在Kibana中配置新的索引,进行Fields Mapping,设置索引别名。也可以通过HTTP请求来创建索引。 + +#### 15.ES支持哪些类型的查询? + +主要分为匹配(文本)查询和基于Term的查询。 + +文本查询包括基本匹配,match phrase, multi-match, match phrase prefix, common terms, query-string, simple query string. + +Term查询,比如term exists, type, term set, range, prefix, ids, wildcard, regexp, and fuzzy。 + +#### 16.Elasticsearch在部署时,对Linux的设置有哪些优化方法 + +`面试官`:想了解对ES集群的运维能力。 `解答`: + +- 1)关闭缓存swap; +- 2)堆内存设置为:Min(节点内存/2, 32GB); +- 3)设置最大文件句柄数; +- 4)线程池+队列大小根据业务需要做调整; +- 5)磁盘存储raid方式——存储有条件使用RAID10,增加单节点性能以及避免单节点存储故障。 + +#### 17.什么是ElasticSearch中的编译器? + +编译器用于将字符串分解为术语或标记流。一个简单的编译器可能会将字符串拆分为任何遇到空格或标点的地方。Elasticsearch有许多内置标记器,可用于构建自定义分析器。 + +#### 18.拼写纠错是如何实现的? + +1、拼写纠错是基于编辑距离来实现;编辑距离是一种标准的方法,它用来表示经过插入、删除和替换操作从一个字符串转换到另外一个字符串的最小操作步数; + + + +2、编辑距离的计算过程:比如要计算 batyu 和 beauty 的编辑距离,先创建一个7×8 的表(batyu 长度为 5,coffee 长度为 6,各加 2),接着,在如下位置填入黑色数字。其他格的计算过程是取以下三个值的最小值: + + + +如果最上方的字符等于最左方的字符,则为左上方的数字。否则为左上方的数字+1。(对于 3,3 来说为 0) + +左方数字+1(对于 3,3 格来说为 2) + +上方数字+1(对于 3,3 格来说为 2) + + + +最终取右下角的值即为编辑距离的值 3。 + + + +![img](http://res.mianshigee.com/upload/article/20200307/v2-1f5084b94e47d417b3cebd615ef04647_1440w.jpg) + + + +对于拼写纠错,我们考虑构造一个度量空间(Metric Space),该空间内任何关 + +系满足以下三条基本条件: + + + +d(x,y) = 0 -- 假如 x 与 y 的距离为 0,则 x=y + +d(x,y) = d(y,x) -- x 到 y 的距离等同于 y 到 x 的距离 + +d(x,y) + d(y,z) >= d(x,z) -- 三角不等式 + + + +1、根据三角不等式,则满足与 query 距离在 n 范围内的另一个字符转 B,其与 A + +的距离最大为 d+n,最小为 d-n。 + + + +2、BK 树的构造就过程如下:每个节点有任意个子节点,每条边有个值表示编辑距离。所有子节点到父节点的边上标注 n 表示编辑距离恰好为 n。比如,我们有棵树父节点是”book”和两个子节点”cake”和”books”,”book”到”books”的边标号 1,”book”到”cake”的边上标号 4。从字典里构造好树后,无论何 + +时你想插入新单词时,计算该单词与根节点的编辑距离,并且查找数值为d(neweord, root)的边。递归得与各子节点进行比较,直到没有子节点,你就可以创建新的子节点并将新单词保存在那。比如,插入”boo”到刚才上述例子的树中,我们先检查根节点,查找 d(“book”, “boo”) = 1 的边,然后检查标号为1 的边的子节点,得到单词”books”。我们再计算距离 d(“books”, “boo”)=2,则将新单词插在”books”之后,边标号为 2。 + + + +3、查询相似词如下:计算单词与根节点的编辑距离 d,然后递归查找每个子节点标号为 d-n 到 d+n(包含)的边。假如被检查的节点与搜索单词的距离 d 小于 n,则返回该节点并继续查询。比如输入 cape 且最大容忍距离为 1,则先计算和根的编辑距离 d(“book”, “cape”)=4,然后接着找和根节点之间编辑距离为 3 到5 的,这 + +个就找到了 cake 这个节点,计算 d(“cake”, “cape”)=1,满足条件所以返回 **cake**,然后再找和 cake 节点编辑距离是 0 到 2 的,分别找到 cape 和cart 节点,这样就得到 **cape** 这个满足条件的结果。 + + + + + +![img](http://res.mianshigee.com/upload/article/20200307/v2-d5426155b3c3c0a7e49123954f96e347_1440w.jpg) + + + +#### 19.ElasticSearch中的分析器是什么? + +在ElasticSearch中索引数据时,数据由为索引定义的Analyzer在内部进行转换。 分析器由一个Tokenizer和零个或多个TokenFilter组成。编译器可以在一个或多个CharFilter之前。分析模块允许您在逻辑名称下注册分析器,然后可以在映射定义或某些API中引用它们。 + +Elasticsearch附带了许多可以随时使用的预建分析器。或者,您可以组合内置的字符过滤器,编译器和过滤器器来创建自定义分析器。 + +#### 20.是否了解字典树? + +常用字典数据结构如下所示: + + + +![img](http://res.mianshigee.com/upload/article/20200307/v2-aa1a57bbbcbbf04ef089d6681d662ffe_1440w.jpg) + + + +Trie 的核心思想是空间换时间,利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。它有 3 个基本性质: + +1、根节点不包含字符,除根节点外每一个节点都只包含一个字符。 + +2、从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。 + +3、每个节点的所有子节点包含的字符都不相同。 + + + +![img](http://res.mianshigee.com/upload/article/20200307/v2-df4c8cd2e2b1dad444a50bab3f6d9bb2_1440w.jpg) + +1、可以看到,trie 树每一层的节点数是 26^i 级别的。所以为了节省空间,我们还可以用动态链表,或者用数组来模拟动态。而空间的花费,不会超过单词数×单词长度。 + +2、实现:对每个结点开一个字母集大小的数组,每个结点挂一个链表,使用左儿子右兄弟表示法记录这棵树; + +3、对于中文的字典树,每个节点的子节点用一个哈希表存储,这样就不用浪费太大的空间,而且查询速度上可以保留哈希的复杂度 O(1)。 + +#### 21. 在并发情况下,ES如果保证读写一致? + +可以通过版本号使用乐观并发控制,以确保新版本不会被旧版本覆盖,由应用层来处理具体的冲突; +另外对于写操作,一致性级别支持quorum/one/all,默认为quorum,即只有当大多数分片可用时才允许写操作。但即使大多数可用,也可能存在因为网络等原因导致写入副本失败,这样该副本被认为故障,分片将会在一个不同的节点上重建。 +对于读操作,可以设置replication为sync(默认),这使得操作在主分片和副本分片都完成后才会返回;如果设置replication为async时,也可以通过设置搜索请求参数_preference为primary来查询主分片,确保文档是最新版本。 + +#### 22. ES对于大数据量(上亿量级)的聚合如何实现? + +Elasticsearch 提供的首个近似聚合是cardinality 度量。它提供一个字段的基数,即该字段的distinct或者unique值的数目。它是基于HLL算法的。HLL 会先对我们的输入作哈希运算,然后根据哈希运算的结果中的 bits 做概率估算从而得到基数。其特点是:可配置的精度,用来控制内存的使用(更精确 = 更多内存);小的数据集精度是非常高的;我们可以通过配置参数,来设置去重需要的固定内存使用量。无论数千还是数十亿的唯一值,内存使用量只与你配置的精确度相关。 + +#### 23. 对于GC方面,在使用ES时要注意什么? + +1)倒排词典的索引需要常驻内存,无法GC,需要监控data node上segment memory增长趋势。 +2)各类缓存,field cache, filter cache, indexing cache, bulk queue等等,要设置合理的大小,并且要应该根据最坏的情况来看heap是否够用,也就是各类缓存全部占满的时候,还有heap空间可以分配给其他任务吗?避免采用clear cache等“自欺欺人”的方式来释放内存。 +3)避免返回大量结果集的搜索与聚合。确实需要大量拉取数据的场景,可以采用scan & scroll api来实现。 +4)cluster stats驻留内存并无法水平扩展,超大规模集群可以考虑分拆成多个集群通过tribe node连接。 +5)想知道heap够不够,必须结合实际应用场景,并对集群的heap使用情况做持续的监控。 + +#### 24. 说说你们公司ES的集群架构,索引数据大小,分片有多少,以及一些调优手段? + +根据实际情况回答即可,如果是我的话会这么回答: +我司有多个ES集群,下面列举其中一个。该集群有20个节点,根据数据类型和日期分库,每个索引根据数据量分片,比如日均1亿+数据的,控制单索引大小在200GB以内。  +下面重点列举一些调优策略,仅是我做过的,不一定全面,如有其它建议或者补充欢迎留言。 +部署层面: +1)最好是64GB内存的物理机器,但实际上32GB和16GB机器用的比较多,但绝对不能少于8G,除非数据量特别少,这点需要和客户方面沟通并合理说服对方。 +2)多个内核提供的额外并发远胜过稍微快一点点的时钟频率。 +3)尽量使用SSD,因为查询和索引性能将会得到显著提升。 +4)避免集群跨越大的地理距离,一般一个集群的所有节点位于一个数据中心中。 +5)设置堆内存:节点内存/2,不要超过32GB。一般来说设置export ES_HEAP_SIZE=32g环境变量,比直接写-Xmx32g -Xms32g更好一点。 +6)关闭缓存swap。内存交换到磁盘对服务器性能来说是致命的。如果内存交换到磁盘上,一个100微秒的操作可能变成10毫秒。 再想想那么多10微秒的操作时延累加起来。不难看出swapping对于性能是多么可怕。 +7)增加文件描述符,设置一个很大的值,如65535。Lucene使用了大量的文件,同时,Elasticsearch在节点和HTTP客户端之间进行通信也使用了大量的套接字。所有这一切都需要足够的文件描述符。 +8)不要随意修改垃圾回收器(CMS)和各个线程池的大小。 +9)通过设置gateway.recover_after_nodes、gateway.expected_nodes、gateway.recover_after_time可以在集群重启的时候避免过多的分片交换,这可能会让数据恢复从数个小时缩短为几秒钟。 +索引层面: +1)使用批量请求并调整其大小:每次批量数据 5–15 MB 大是个不错的起始点。 +2)段合并:Elasticsearch默认值是20MB/s,对机械磁盘应该是个不错的设置。如果你用的是SSD,可以考虑提高到100-200MB/s。如果你在做批量导入,完全不在意搜索,你可以彻底关掉合并限流。另外还可以增加 index.translog.flush_threshold_size 设置,从默认的512MB到更大一些的值,比如1GB,这可以在一次清空触发的时候在事务日志里积累出更大的段。 +3)如果你的搜索结果不需要近实时的准确度,考虑把每个索引的index.refresh_interval 改到30s。 +4)如果你在做大批量导入,考虑通过设置index.number_of_replicas: 0 关闭副本。 +5)需要大量拉取数据的场景,可以采用scan & scroll api来实现,而不是from/size一个大范围。 +存储层面: +1)基于数据+时间滚动创建索引,每天递增数据。控制单个索引的量,一旦单个索引很大,存储等各种风险也随之而来,所以要提前考虑+及早避免。 +2)冷热数据分离存储,热数据(比如最近3天或者一周的数据),其余为冷数据。对于冷数据不会再写入新数据,可以考虑定期force_merge加shrink压缩操作,节省存储空间和检索效率。 + + + +#### 参考 + +https://zhuanlan.zhihu.com/p/265399976 + +https://www.wenyuanblog.com/blogs/elasticsearch-interview-questions.html + +http://www.mianshigee.com/question \ No newline at end of file diff --git a/Git.md b/Git.md new file mode 100644 index 0000000..272dcf9 --- /dev/null +++ b/Git.md @@ -0,0 +1,268 @@ +## Git + +#### 1.什么是Git? + +我建议你先通过了解 git 的架构再来回答这个问题,如下图所示,试着解释一下这个图: + +- Git 是分布式版本控制系统(DVCS)。它可以跟踪文件的更改,并允许你恢复到任何特定版本的更改。 +- 与 SVN 等其他版本控制系统(VCS)相比,其分布式架构具有许多优势,一个主要优点是它不依赖于中央服务器来存储项目文件的所有版本。 +- 每个开发人员都可以“克隆”我在图中用“Local repository”标注的存储库的副本,并且在他的硬盘驱动器上具有项目的完整历史记录,因此当服务器中断时,你需要的所有恢复数据都在你队友的本地 Git 存储库中。 +- 还有一个中央云存储库,开发人员可以向其提交更改,并与其他团队成员进行共享,如图所示,所有协作者都在提交更改“远程存储库”。 + +#### 2.Git 工作流程 + +本章节我们将为大家介绍 Git 的工作流程。 + +一般工作流程如下: + +- 克隆 Git 资源作为工作目录。 +- 在克隆的资源上添加或修改文件。 +- 如果其他人修改了,你可以更新资源。 +- 在提交前查看修改。 +- 提交修改。 +- 在修改完成后,如果发现错误,可以撤回提交并再次修改并提交。 + +下图展示了 Git 的工作流程: + +![img](https://www.runoob.com/wp-content/uploads/2015/02/git-process.png) + +#### 3.在 Git 中提交的命令是什么? + +用于写入提交的命令是 **`git commit -a`**。 + +现在解释一下 `-a` 标志, 通过在命令行上加 `-a` 指示 git 提交已修改的所有被跟踪文件的新内容。还要提一下,如果你是第一次需要提交新文件,可以在在 `git commit -a` 之前先 **`git add `**。 + +#### 4.什么是 Git 中的“裸存储库”? + +你应该说明 “工作目录” 和 “裸存储库” 之间的区别。 + +Git 中的 “裸” 存储库只包含版本控制信息而没有工作文件(没有工作树),并且它不包含特殊的 `.git` 子目录。相反,它直接在主目录本身包含 `.git` 子目录中的所有内容,其中工作目录包括: + +1. 一个 `.git` 子目录,其中包含你的仓库所有相关的 Git 修订历史记录。 +2. 工作树,或签出的项目文件的副本。 + +#### 5.Git 是用什么语言编写的? + +你需要说明使用它的原因,而不仅仅是说出语言的名称。我建议你这样回答: + +Git使用 C 语言编写。 GIT 很快,C 语言通过减少运行时的开销来做到这一点。 + +#### 6.在Git中,你如何还原已经 push 并公开的提交? + +There can be two answers to this question and make sure that you include both because any of the below options can be used depending on the situation: 1 +这个问题可以有两个答案,你回答时也要保包含这两个答案,因为根据具体情况可以使用以下选项: + +- 删除或修复新提交中的错误文件,并将其推送到远程存储库。这是修复错误的最自然方式。对文件进行必要的修改后,将其提交到我将使用的远程存储库 + +``` +git commit -m "commit message" +``` + +- 创建一个新的提交,撤消在错误提交中所做的所有更改。可以使用命令: + +``` +git revert +``` + + + +#### 7.git pull 和 git fetch 有什么区别? + +`git pull` 命令从中央存储库中提取特定分支的新更改或提交,并更新本地存储库中的目标分支。 + +`git fetch` 也用于相同的目的,但它的工作方式略有不同。当你执行 `git fetch` 时,它会从所需的分支中提取所有新提交,并将其存储在本地存储库中的新分支中。如果要在目标分支中反映这些更改,必须在 `git fetch` 之后执行`git merge`。只有在对目标分支和获取的分支进行合并后才会更新目标分支。为了方便起见,请记住以下等式: + +
git pull = git fetch + git merge
+ + + +#### 8.git中的“staging area”或“index”是什么? + +For this answer try to explain the below diagram as you can see: +可以通过下图进行解释: + +在完成提交之前,可以在称为“staging area”或“index”的中间区域中对其进行格式化和审查。从图中可以看出,每个更改首先在暂存区域中进行验证,我将其称为“stage file”,然后将更改提交到存储库。 + +![clipboard.png](https://segmentfault.com/img/bVbtc0c?w=655&h=645) + +### 9.什么是 git stash? + +首先应该解释 git stash 的必要性。 + +通常情况下,当你一直在处理项目的某一部分时,如果你想要在某个时候切换分支去处理其他事情,事情会处于混乱的状态。问题是,你不想把完成了一半的工作的提交,以便你以后就可以回到当前的工作。解决这个问题的答案是 git stash。 + +再解释什么是git stash。 + +stash 会将你的工作目录,即修改后的跟踪文件和暂存的更改保存在一堆未完成的更改中,你可以随时重新应用这些更改。 + +#### 10.什么是git stash drop? + +通过说明我们使用 `git stash drop` 的目的来回答这个问题。 + +`git stash drop` 命令用于删除隐藏的项目。默认情况下,它将删除最后添加的存储项,如果提供参数的话,它还可以删除特定项。 + +下面举个例子。 + +如果要从隐藏项目列表中删除特定的存储项目,可以使用以下命令: + +**git stash list:**它将显示隐藏项目列表,如: + +stash@{0}: WIP on master: 049d078 added the index file +stash@{1}: WIP on master: c264051 Revert “added file_size” +stash@{2}: WIP on master: 21d80a5 added number to log + +如果要删除名为 stash@{0} 的项目,请使用命令 **git stash drop stash@{0}**。 + +#### 11.如何找到特定提交中已更改的文件列表? + +对于这个问题,不能仅仅是提供命令,还要解释这个命令究竟做了些什么。 + +要获取特定提交中已更改的列表文件,请使用以下命令: + +**git diff-tree -r {hash}** + +给定提交哈希,这将列出在该提交中更改或添加的所有文件。 `-r` 标志使命令列出单个文件,而不是仅将它们折叠到根目录名称中。 + +你还可以包括下面提到的内容,虽然它是可选的,但有助于给面试官留下深刻印象。 + +输出还将包含一些额外信息,可以通过包含两个标志把它们轻松的屏蔽掉: + +**git diff-tree –no-commit-id –name-only -r {hash}** + +这里 `-no-commit-id` 将禁止提交哈希值出现在输出中,而 `-name-only` 只会打印文件名而不是它们的路径。 + +#### 12.git config 的功能是什么? + +首先说明为什么我们需要 `git config`。 + +git 使用你的用户名将提交与身份相关联。 `git config` 命令可用来更改你的 git 配置,包括你的用户名。 + +下面用一个例子来解释。 + +假设你要提供用户名和电子邮件 ID 用来将提交与身份相关联,以便你可以知道是谁进行了特定提交。为此,我将使用: + +**git config –global user.name "Your Name":** 此命令将添加用户名。 + +**git config –global user.email "Your E-mail Address":** 此命令将添加电子邮件ID。 + +#### 13.提交对象包含什么? + +Commit 对象包含以下组件,你应该提到以下这三点: + +- 一组文件,表示给定时间点的项目状态 +- 引用父提交对象 +- SHAI 名称,一个40个字符的字符串,提交对象的唯一标识。 + +#### 14.Git的工作区域 + +对于任何一个文件,在 Git 内都只有三种区域:工作区,暂存区和本地仓库。 + +`工作区:表示新增或修改了某个文件,但还没有提交保存;` + +`暂存区:表示把已新增或修改的文件,放在下次提交时要保存的清单中;` + +`本地仓库:文件已经被安全地保存在本地仓库中了。` + +#### 15.如果分支是否已合并为master,你可以通过什么手段知道? + +要知道某个分支是否已合并为master,你可以使用以下命令: + +`git branch –merged` 它列出了已合并到当前分支的分支。 + +`git branch –no-merged` 它列出了尚未合并的分支。 + +#### 16.什么是SubGit? + +SubGit 是将 SVN 到 Git迁移的工具。它创建了一个可写的本地或远程 Subversion 存储库的 Git 镜像,并且只要你愿意,可以随意使用 Subversion 和 Git。 + +这样做有很多优点,比如你可以从 Subversion 快速一次性导入到 Git 或者在 Atlassian Bitbucket Server 中使用SubGit。我们可以用 SubGit 创建现有 Subversion 存储库的双向 Git-SVN 镜像。你可以在方便时 push 到 Git 或提交 Subversion。同步由 SubGit 完成。 + +#### 17. 如何把本地仓库的内容推向一个空的远程仓库? + +首先确保本地仓库与远程之间是连同的。如果提交失败,则需要进行下面的命令进行连通: + +``` +git remote add origin XXXX +``` + +注意:XXXX是你的远程仓库地址。 如果是第一次推送,则进行下面命令: + +``` +git push -u origin master +``` + +注意:-u 是指定origin为默认主分支 之后的提交,只需要下面的命令: + +``` +git push origin master +``` + +#### 18.描述一下你所使用的分支策略? + +这个问题被要求用Git来测试你的分支经验,告诉他们你在以前的工作中如何使用分支以及它的用途是什么,你可以参考以下提到的要点: + +- 功能分支(Feature branching) + + 要素分支模型将特定要素的所有更改保留在分支内。当通过自动化测试对功能进行全面测试和验证时,该分支将合并到主服务器中。 + +- 任务分支(Task branching) + + 在此模型中,每个任务都在其自己的分支上实现,任务键包含在分支名称中。很容易看出哪个代码实现了哪个任务,只需在分支名称中查找任务键。 + +- 发布分支(Release branching) + + 一旦开发分支获得了足够的发布功能,你就可以克隆该分支来形成发布分支。创建该分支将会启动下一个发布周期,所以在此之后不能再添加任何新功能,只有错误修复,文档生成和其他面向发布的任务应该包含在此分支中。一旦准备好发布,该版本将合并到主服务器并标记版本号。此外,它还应该再将自发布以来已经取得的进展合并回开发分支。 + +最后告诉他们分支策略因团队而异,所以我知道基本的分支操作,如删除、合并、检查分支等。 + +#### 19.Git 工作区、暂存区和版本库 + +我们先来理解下 Git 工作区、暂存区和版本库概念: + +- **工作区:**就是你在电脑里能看到的目录。 +- **暂存区:**英文叫 stage 或 index。一般存放在 **.git** 目录下的 index 文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。 +- **版本库:**工作区有一个隐藏目录 **.git**,这个不算工作区,而是 Git 的版本库。 + +下面这个图展示了工作区、版本库中的暂存区和版本库之间的关系: + +![img](https://www.runoob.com/wp-content/uploads/2015/02/1352126739_7909.jpg) + +- 图中左侧为工作区,右侧为版本库。在版本库中标记为 "index" 的区域是暂存区(stage/index),标记为 "master" 的是 master 分支所代表的目录树。 +- 图中我们可以看出此时 "HEAD" 实际是指向 master 分支的一个"游标"。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。 +- 图中的 objects 标识的区域为 Git 的对象库,实际位于 ".git/objects" 目录下,里面包含了创建的各种对象及内容。 +- 当对工作区修改(或新增)的文件执行 **git add** 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。 +- 当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。 +- 当执行 **git reset HEAD** 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。 +- 当执行 **git rm --cached ** 命令时,会直接从暂存区删除文件,工作区则不做出改变。 +- 当执行 **git checkout .** 或者 **git checkout -- ** 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区的改动。 +- 当执行 **git checkout HEAD .** 或者 **git checkout HEAD ** 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。 + +#### 20.打标签 + + git tag -a 2.27 -m "release version 201606" + git push origin --tags + 详解:git tag 是命令 + -a 0.1.3是增加 名为0.1.3的标签 + -m 后面跟着的是标签的注释 + + 打标签的操作发生在我们commit修改到本地仓库之后。完整的例子 + git add . + git commit -m “fixed some bugs” + git tag -a 0.1.3 -m “Release version 0.1.3″ + + 分享提交标签到远程服务器上 + git push origin master + git push origin --tags + –tags参数表示提交所有tag至服务器端,普通的git push origin master操作不会推送标签到服务器端。 + + 删除标签的命令 + git push origin local_branch:remote_branch +#### 参考 + +https://segmentfault.com/a/1190000019315509 + +https://www.runoob.com/git/ + +https://www.cnblogs.com/rebackl/p/12990881.html + +https://blog.csdn.net/junwua/article/details/82906002 \ No newline at end of file diff --git a/IO&NIO.md b/IO&NIO.md new file mode 100644 index 0000000..d15183f --- /dev/null +++ b/IO&NIO.md @@ -0,0 +1,209 @@ +## IO&NIO + + + +![image-20200414192748596](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414192748596.png) + +#### 1.**什么是IO流?** + +它是一种数据的流从源头流到目的地。比如文件拷贝,输入流和输出流都包括了。输入流从文件中读取数据存储到进程(process)中,输出流从进程中读取数据然后写入到目标文件。 + +#### 2.java中有几种类型的流? + +按照单位大小:字符流、字节流。按照流的方向:输出流、输入流。 + +#### 3.字节流和字符流哪个好?怎么选择? + +1. 缓大多数情况下使用字节流会更好,因为字节流是字符流的包装,而大多数时候 IO 操作都是直接操作磁盘文件,所以这些流在传输时都是以字节的方式进行的(图片等都是按字节存储的) +2. 如果对于操作需要通过 IO 在内存中频繁处理字符串的情况使用字符流会好些,因为字符流具备缓冲区,提高了性能 + +#### 4.读取数据量大的文件时,速度会很慢,如何选择流? + +字节流时,选择BufferedInputStream和BufferedOutputStream。 +字符流时,选择BufferedReader 和 BufferedWriter + +#### 5. IO模型有几种? + +阻塞IO、非阻塞IO、多路复用IO、信号驱动IO以及异步IO。 + +#### 6.阻塞IO**(blocking IO)** + + 应用程序调用一个IO函数,导致应用程序阻塞,如果数据已经准备好,从内核拷贝到用户空间,否则一直等待下去。一个典型的读操作流程大致如下图,当用户进程调用recvfrom这个系统调用时,kernel就开始了IO的第一个阶段:准备数据,就是数据被拷贝到内核缓冲区中的一个过程(很多网络IO数据不会那么快到达,如没收一个完整的UDP包),等数据到操作系统内核缓冲区了,就到了第二阶段:将数据从内核缓冲区拷贝到用户内存,然后kernel返回结果,用户进程才会解除block状态,重新运行起来。**blocking IO的特点就是在IO执行的两个阶段用户进程都会block住;** + +![image-20200416135005801](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416135005801.png) + +#### 7.**非阻塞I/O(nonblocking IO)** + + 非阻塞I/O模型,我们把一个套接口设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,**会大量的占用CPU的时间**。 + +​ 当用户进程发出read操作时,如果kernel中数据还没准备好,那么并不会block用户进程,而是立即返回error,用户进程判断结果是error,就知道数据还没准备好,用户可以再次发read,直到kernel中数据准备好,并且用户再一次发read操作,产生system call,那么kernel 马上将数据拷贝到用户内存,然后返回;所以**nonblocking IO的特点是用户进程需要不断的主动询问kernel数据好了没有。** + +  阻塞IO一个线程只能处理一个IO流事件,要想同时处理多个IO流事件要么多线程要么多进程,这样做效率显然不会高,而非阻塞IO可以一个线程处理多个流事件,只要不停地询所有流事件即可,当然这个方式也不好,当大多数流没数据时,也是会大量浪费CPU资源;为了避免CPU空转,引进代理(select和poll,两种方式相差不大),代理可以观察多个流I/O事件,空闲时会把当前线程阻塞掉,当有一个或多个I/O事件时,就从阻塞态醒过来,把所有IO流都轮询一遍,于是没有IO事件我们的程序就阻塞在select方法处,即便这样依然存在问题,我们从select出只是知道有IO事件发生,却不知道是哪几个流,还是只能轮询所有流,**epoll**这样的代理就可以把哪个流发生怎样的IO事件通知我们;  + +![image-20200416135208788](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416135208788.png) + +#### 8.**I/O多路复用模型(IO multiplexing)** + + I/O多路复用就在于单个进程可以同时处理多个网络连接IO,基本原理就是select,poll,epoll这些个函数会不断轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程,这三个functon会阻塞进程,但和IO阻塞不同,这些函数可以同时阻塞多个IO操作,而且可以同时对多个读操作,写操作IO进行检验,直到有数据到达,才真正调用IO操作函数,调用过程如下图;**所以IO多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中任意一个进入就绪状态,select函数就可以返回。** + +  IO多路复用的优势在于并发数比较高的IO操作情况,可以同时处理多个连接,和bloking IO一样socket是被阻塞的,只不过在多路复用中socket是被select阻塞,而在阻塞IO中是被socket IO给阻塞。 + +![image-20200416135308008](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416135308008.png) + +#### 9.信号驱动I/O模型 + +可以用信号,让内核在描述符就绪时发送SIGIO信号通知我们,通过sigaction系统调用安装一个信号处理函数。该系统调用将立即返回,我们的进程继续工作,也就是说它没有被阻塞。当数据报准备好读取时,内核就为该进程产生一个SIGIO信号。我们随后既可以在信号处理函数中调用recvfrom读取数据报,并通知主循环数据已经准备好待处理。**特点:等待数据报到达期间进程不被阻塞。主循环可以继续执行,只要等待来自信号处理函数的通知:既可以是数据已准备好被处理,也可以是数据报已准备好被读取** + +![image-20200416140944940](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416140944940.png) + +#### 10.异步 I/O(asynchronous IO) + +异步IO告知内核启动某个操作,并让内核在整个操作(包括将内核数据复制到我们自己的缓冲区)完成后通知我们,调用aio_read(Posix异步I/O函数以aio_或lio_开头)函数,给内核传递描述字、缓冲区指针、缓冲区大小(与read相同的3个参数)、文件偏移以及通知的方式,然后系统立即返回。我们的进程不阻塞于等待I/0操作的完成。当内核将数据拷贝到缓冲区后,再通知应用程序。 + +用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了 + +![image-20200416141152742](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416141152742.png) + +#### 11.NIO与IO的区别? + + NIO即New IO,这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多。在Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO。 + +![image-20200416143304767](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416143304767.png) + +#### 12.NIO和IO适用场景 + + NIO是为弥补传统IO的不足而诞生的,但是尺有所短寸有所长,**NIO也有缺点,因为NIO是面向缓冲区的操作,每一次的数据处理都是对缓冲区进行的,那么就会有一个问题,在数据处理之前必须要判断缓冲区的数据是否完整或者已经读取完毕,如果没有,假设数据只读取了一部分,那么对不完整的数据处理没有任何意义。**所以每次数据处理之前都要检测缓冲区数据。 +  那么NIO和IO各适用的场景是什么呢? +  如果需要管理同时打开的成千上万个连接,这些**连接每次只是发送少量的数据**,例如聊天服务器,这时候用NIO处理数据可能是个很好的选择。 +  而如果只**有少量的连接**,而这些连接每次要发送大量的数据,这时候传统的IO更合适。使用哪种处理数据,需要在数据的响应等待时间和检查缓冲区数据的时间上作比较来权衡选择。 + +#### 13.NIO核心组件 + +channel、buffer、selector + +#### 14.什么是channel + +一个Channel(通道)代表和某一实体的连接,这个实体可以是文件、网络套接字等。也就是说,通道是Java NIO提供的一座桥梁,用于我们的程序和操作系统底层I/O服务进行交互。 + +通道是一种很基本很抽象的描述,和不同的I/O服务交互,执行不同的I/O操作,实现不一样,因此具体的有FileChannel、SocketChannel等。 + +通道使用起来跟Stream比较像,可以读取数据到Buffer中,也可以把Buffer中的数据写入通道。 + +![image-20200416150623938](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416150623938.png) + +当然,也有区别,主要体现在如下两点: + +- 一个通道,既可以读又可以写,而一个Stream是单向的(所以分 InputStream 和 OutputStream) +- 通道有非阻塞I/O模式 + +#### 15.Java NIO中最常用的通道实现? + +- FileChannel:读写文件 +- DatagramChannel: UDP协议网络通信 +- SocketChannel:TCP协议网络通信 +- ServerSocketChannel:监听TCP连接 + +#### 16.Buffer是什么? + +NIO中所使用的缓冲区不是一个简单的byte数组,而是封装过的Buffer类,通过它提供的API,我们可以灵活的操纵数据。 + +与Java基本类型相对应,NIO提供了多种 Buffer 类型,如ByteBuffer、CharBuffer、IntBuffer等,区别就是读写缓冲区时的单位长度不一样(以对应类型的变量为单位进行读写)。 + +#### 17.核心Buffer实现有哪些? + +核心的buffer实现有这些:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer,涵盖了所有的基本数据类型(4类8种,除了Boolean)。也有其他的buffer如MappedByteBuffer。 + +#### 18.buffer读写数据基本操作 + +1)、将数据写入buffer +2)、调用buffer.flip() +3)、将数据从buffer中读取出来 +4)、调用buffer.clear()或者buffer.compact() + +在写buffer的时候,buffer会跟踪写入了多少数据,需要读buffer的时候,需要调用flip()来将buffer从写模式切换成读模式,读模式中只能读取写入的数据,而非整个buffer。 +  当数据都读完了,你需要清空buffer以供下次使用,可以有2种方法来操作:调用clear() 或者 调用compact()。 +  区别:clear方法清空整个buffer,compact方法只清除你已经读取的数据,未读取的数据会被移到buffer的开头,此时写入数据会从当前数据的末尾开始。 + +```java +// 创建一个容量为48的ByteBuffer +ByteBuffer buf = ByteBuffer.allocate(48); +// 从channel中读(取数据然后写)入buffer +int bytesRead = inChannel.read(buf); +// 下面是读取buffer +while (bytesRead != -1) { + buf.flip(); // 转换buffer为读模式 + System.out.print((char) buf.get()); // 一次读取一个byte + buf.clear(); //清空buffer准备下一次写入 +} + +``` + +#### 19.Selector是什么? + +Selector(选择器)是一个特殊的组件,用于采集各个通道的状态(或者说事件)。我们先将通道注册到选择器,并设置好关心的事件,然后就可以通过调用select()方法,静静地等待事件发生。 + +#### 20.通道可以监听那几个事件? + +通道有如下4个事件可供我们监听: + +- Accept:有可以接受的连接 +- Connect:连接成功 +- Read:有数据可读 +- Write:可以写入数据了 + +#### 21.为什么要用Selector? + +如果用阻塞I/O,需要多线程(浪费内存),如果用非阻塞I/O,需要不断重试(耗费CPU)。Selector的出现解决了这尴尬的问题,非阻塞模式下,通过Selector,我们的线程只为已就绪的通道工作,不用盲目的重试了。比如,当所有通道都没有数据到达时,也就没有Read事件发生,我们的线程会在select()方法处被挂起,从而让出了CPU资源。 + +#### 22.Selector处理多Channel图文说明 + +![image-20200416161430793](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200416161430793.png) + + + +要使用一个Selector,你要先注册这个Selector的Channels。然后你调用Selector的select()方法。这个方法会阻塞,直到它注册的Channels当中有一个准备好了的事件发生了。当select()方法返回的时候,线程可以处理这些事件,如新的连接的到来,数据收到了等。 + +#### 23.代码示例:如何使用流的基本接口来读写文件内容 + +``` +try { + +DataInputStream in = + + new DataInputStream( + + new BufferedInputStream( + + new FileInputStream("Test.java") + + ) + +); + +while ((currentLine = in.readLine()) != null){ + + System.out.println(currentLine); + +} + +} catch (IOException e){ + + System.err.println("Error: " + e); + +} +``` + + + +#### 参考: + +https://www.cnblogs.com/sharing-java/p/10791802.html + +https://blog.csdn.net/zengxiantao1994/article/details/88094910 + +https://www.cnblogs.com/xueSpring/p/9513266.html + +https://zhuanlan.zhihu.com/p/163506337 + + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file diff --git a/JVM.md b/JVM.md new file mode 100644 index 0000000..5aee119 --- /dev/null +++ b/JVM.md @@ -0,0 +1,395 @@ +## JVM + + + +#### 1.JDK、JRE、JVM关系? + +Jdk (Java Development Kit) : java语言的软件开发包。包括Java运行时环境Jre。 + +Jre (Java Runtime Environment) :Java运行时环境,包括Jvm。 + +Jvm (Java Virtual Machine) : + +- 一种用于计算机设备的规范。 +- Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。 + +Jdk包括Jre,Jre包括jvm。 + +#### 2.启动程序如何查看加载了哪些类,以及加载顺序? + +java -XX:+TraceClassLoading 具体类 + +Java -verbose 具体类 + +#### 3. class字节码文件10个主要组成部分? + +- MagicNumber +- Version +- Constant_pool +- Access_flag +- This_class +- Super_class +- Interfaces +- Fields +- Methods +- Attributes + +#### 4.画一下jvm内存结构图? + +![image-20200424132830267](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200424132830267.png) + +#### 5.程序计数器 + +属于线程私有内存。占用一块非常小的空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的指令的字节码,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器来完成。 + +#### 6.Java虚拟机栈 + +属于线程私有内存。它的生命周期与线程相同,虚拟机栈描述的是Java方法执行内存模型;每个方法被执行的时候都会同时创建一个栈桢用于存储局部变量表、操作栈、动态链接、方法出口信息等。每一个方法被调用直至执行完成的过程,就对应着一个栈帧再虚拟机中从入栈到出栈的过程。 + +#### 7.本地方法栈 + +本地方法栈与虚拟机栈所发挥的作用是非常相似的,只不过虚拟机栈对虚拟机执行Java方法服务,而本地栈是为虚拟机使用到Native方法服务。 + +#### 8.Java堆 + +是Java虚拟机所管理的内存中最大的一块。Java堆事被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。 + +Tips:但随着JIT编译器的发展与逃逸分析技术的逐渐成熟,栈上分配、标亮替换优化技术将会导师一些微妙的变化发生,所有的对象都分配在堆上就不那么绝对了。 + +#### 9.方法区 + +是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 + +#### 10.运行时常量池? + +是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息,还有一项是常量池(Constant PoolTable)用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放道方法区的运行时常量池中。 + +#### 11.什么时候抛出StackOverflowError? + +如果线程请求的栈深度大于虚拟机所允许的深度,则抛出StackOverflowError。 + +#### 12.Java7和Java8在内存模型上有什么区别? + +Java8取消了永久代,用元空间(Metaspace)代替了,元空间是存在本地内存(Native memory)中。 + +#### 13.程序员最关注的两个内存区域? + +堆(Heap)和栈(Stack),一般大家会把Java内存分为堆内存和栈内存,这是一种比较粗糙的划分方式,但实际上Java内存区域是很复杂的。 + +#### 14.直接内存是什么? + +直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但这部分内存也频繁被实用,也有OutOfMemoryError异常的出现的可能。 + +Tips:JDK1.4中加入了NIO(new input/output)类,引入了一种基于通道(Channe)与缓冲区(Buffer)的I/O方式,也可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer的对象作为这块内存的引用进行操作。 + +#### 15.除了哪个区域外,虚拟机内存其他运行时区域都会发生OutOfMemoryError? + +程序计数器。 + +#### 16.什么情况下会出现堆内存溢出? + +堆内存存储对象实例。我们只要不断地创建对象。并保证gc roots到对象之间有可达路径来避免垃圾回收机制清除这些对象。就会在对象数量到达最大。堆容量限制后,产生内存溢出异常。 + +#### 17.如何实现一个堆内存溢出? + +```java +public class Cat { + + public static void main(String[] args) { + List list = new ArrayList(); + while (true) { + list.add(new Cat()); + } + } + +} +``` + + + +#### 18.空间什么情况下会抛出OutOfMemoryError? + +如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError。 + +#### 19.如何实现StrackOverflowError? + +```java + public static void main(String[] args) { + eat(); + } + + public static void eat () { + eat(); + } +``` + +#### 20.如何设置直接内存容量? + +通过 -XX:MaxDirectMemorySize指定,如果不指定,则默认与Java堆的最大值一样。 + +#### 21.Java堆内存组成? + +![image-20200424170643045](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200424170643045.png) + +堆大小 = 新生代 + 老年代。如果是Java8则没有Permanent Generation。 + +其中新生代(Young) 被分为 Eden和S0(from)和S1(to)。 + +#### 22.Edem : from : to默认比例是? + +Edem : from : to = 8 : 1 : 1 + +此比例可以通过 –XX:SurvivorRatio 来设定 + +#### 23.垃圾标记阶段? + +在GC执行垃圾回收之前,为了区分对象存活与否,当对象被标记为死亡时,GC才回执行垃圾回收,这个过程就是垃圾标记阶段。 + +#### 24.引用计数法? + +比如对象a,只要任何一个对象引用了a,则a的引用计数器就加1,当引用失效时,引用计数器就减1,当计数器为0时,就可以对其回收。 + +但是无法解决循环引用的问题。 + +#### 25.根搜索算法? + +跟搜索算法是以跟为起始点,按照从上到下的方式搜索被根对象集合所连接的目标对象是否可达(使用根搜索算法后,内存中 的存活对象都会被根对象集合直接或间接连接着),如果目标对象不可达,就意味着该对象己经死亡,便可以在 instanceOopDesc的 Mark World 中将其标记为垃圾对象。 + +在根搜索算法中, 只有能够被根对象集合直接或者间接连接的对象才是存活对象。 + +#### 26.JVM中三种常见的垃圾收集算法? + +标记-清除算法(Mark_Sweep) + +复制算法(Copying) + +标记-压缩算法(Mark-Compact) + +#### 27.标记-清除算法? + +首先标记出所有需要回收的对象,在标记完成后统一回收掉所有的被标记对象。 + +![image-20200424181634238](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200424181634238.png) + +缺点: + +- 标记和清除的效率都不高。 +- 空间问题,清除后产生大量不连续的内存随便。如果有大对象会出现空间不够的现象从而不得不提前触发另一次垃圾收集动作。 + +#### 28.复制算法? + + 他将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块内存用完了,就将还存活的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。 + +![image-20200424183117930](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200424183117930.png) + +优点: + +解决了内存碎片问题。 + +缺点: + +将原来的内存缩小为原来的一半,存活对象越多效率越低。 + +#### 29.标记-整理算法? + +先标记出要被回收的对象,然后让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。解决了复制算法和标记清理算法的问题。 + +![image-20200424184054165](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200424184054165.png) + +#### 30.分代收集算法? + +当前商业虚拟机的垃圾收集都采用“分代手机算法”,其实就根据对象存活周期的不同将内存划分为几块,一般是新老年代。根据各个年代的特点采用最适当的收集算法。 + +#### 31.垃圾收集器? + +如果说垃圾收集算法是方法论,那么垃圾收集器就是具体实现。连线代表可以搭配使用。 + +![image-20200424191253710](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200424191253710.png) + + + +#### 32.Stop The World? + +进行垃圾收集时,必须暂停其他所有工作线程,Sun将这种事情叫做"Stop The World"。 + +#### 33.Serial收集器? + +单线程收集器,单线程的含义在于它会 stop the world。垃圾回收时需要stop the world ,直到它收集结束。所以这种收集器体验比较差。 + +#### 34.PartNew收集器? + +Serial收集器的多线程版本,除了使用采用并行收回的方式回收内存外,其他行为几乎和Serial没区别。 + +可以通过选项“-XX:+UseParNewGC”手动指定使用 ParNew收集器执行内存回收任务。 + +#### 36.Parallel Scavenge? + +是一个新生代收集器,也是复制算法的收集器,同时也是多线程并行收集器,与PartNew 不同是,它重点关注的是程序达到一个可控制的吞吐量(Thoughput,CPU 用于运行用户代码 的时间/CPU 总消耗时间,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)), 高吞吐量可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务,主要适用于在后台运算而不需要太多交互的任务。 + +他可以通过2个参数精确的控制吞吐量,更高效的利用cpu。 + +分别是: -XX:MaxCcPauseMillis 和 -XX:GCTimeRatio + +#### 37.Parallel Old收集器? + +Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法。JDK 1.6中才开始提供。 + +#### 38.CMS 收集器? + +Concurrent Mar Sweep 收集器是一种以获取最短回收停顿时间为目标的收集器。重视服务的响应速度,希望系统停顿时间最短。采用标记-清除的算法来进行垃圾回收。 + +#### 39.CMS垃圾回收的步骤? + +1. 初始标记 (stop the world) + +2. 并发标记 + +3. 重新标记 (stop the world) + +4. 并发清除 + + 初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快。 + + 并发标记就是进行Gc Roots Tracing的过程。 + + 重新标记则是为了修正并发标记期间,因用户程序继续运行而导致的标记产生变动的那一部分对象的标记记录,这个阶段停顿时间一般比初始标记时间长,但是远比并发标记时间短。 + + 整个过程中并发标记时间最长,但此时可以和用户线程一起工作。 + +#### 41.CMS收集器优点?缺点? + +优点: + +并发收集、低停顿 + +缺点: + +- 对cpu资源非常敏感。 +- 无法处理浮动垃圾。 +- 内存碎片问题。 + +#### 42.G1收集器? + +Garbage First 收集器是当前收集器技术发展的最前沿成果。jdk 1.6_update14中提供了 g1收集器。 + + G1收集器是基于标记-整理算法的收集器,它避免了内存碎片的问题。 + +可以非常精确控制停顿时间,既能让使用者明确指定一个长度为 M毫秒的时间片段内,消耗在垃圾收集上的时间不多超过N毫秒,这几乎已经是实时java(rtsj)的垃圾收集器特征了。 + +#### 42. G1收集器是如何改进收集方式的? + +极力避免全区域垃圾收集,之前的收集器进行收集的范围都是整个新生代或者老年代。而g1将整个Java堆(包括新生代、老年代)划分为多个大小固定的独立区域,并且跟踪这些区域垃圾堆积程度,维护一个优先级李彪,每次根据允许的收集时间,优先回收垃圾最多的区域。从而获得更高的效率。 + +#### 43.虚拟机进程状况工具? + +jps (Jvm process status tool ),他的功能与ps类似。 + +可以列出正在运行的虚拟机进程,并显示执行主类(Main Class,main()函数所在的类)的名称,以及浙西进程的本地虚拟机的唯一ID。 + +语法 : jps [options] [hostid] + +-q 主输出lvmid,省略主类的名称 + +-m 输出虚拟机进程启动时传递给主类main()函数的参数 + +-l 输出主类全名,如果进程执行是Jar包,输出Jar路径 + +-v 输出虚拟机进程启动时JVM参数 + +#### 44.虚拟机统计信息工具? + +jstat(JVM Statistics Montoring Tool)是用于监视虚拟机各种运行状态信息命令行工机具。他可以显示本地或远程虚拟机进程中的类装载、内存、垃圾收集、jit编译等运行数据。 + +jstat [option vmid [interval[s|ms] [count]] ] + +interval 查询间隔 + +count 查询次数 + +如果不用这两个参数,就默认查询一次。 + +option代表用户希望查询的虚拟机信息,主要分3类: + +- 类装载 +- 垃圾收集 +- 运行期编译状况 + +#### 45.jstat 工具主要选项? + +-class 监视类装载、卸载数量、总空间及类装载锁消耗的时间 + +-gc 监视Java堆状况,包括Eden区,2个survivor区、老年代 + +-gccapacity 监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用的最大和最小空间 + +-gcutil 监视内容与-gc基本相同,主要关注已经使用空间站空间百分比 + +-gccause 与-gcutil 功能一样,但是会额外输出导致上一次GC产生的原因 + +-gcnew 监视新生代的GC的状况 + +-gcnewcapacity 监视内容与 -gcnew基本相同,输出主要关注使用到的最大最小空间 + +-gcold 监视老年代的GC情况 + +-gcoldcapacity 监控内容与 -gcold基本相同,主要关注使用到的最大最小空间 + +-compiler 输出jit 编译器编译过的方法、耗时等信息 + +#### 45.配置信息工具? + +jinfo(Configuration Info for Java)的作用是实时地查看和调整虚拟机的各项参数。 + +使用jps 命令的 -v 参数可以查看虚拟机启动时显示指定的参数列表。 + +jinfo 语法: jinfo [option] pid + +#### 46.内存映像工具? + +jmap(Memory Map for Java) 命令用于生成堆转储快照(一般称为heapdump或dump文件)。 + + 语法 :jmap [option] vmid + +它还可以查询finalize执行队列,Java堆和永久代的详细信息,如果空间使用率、当前用的是哪种收集器等。 + +- -dump 生成Java堆转储快照,其中live自参数说明是否只dump出存活对象 +- -finalizerinfo 显示在F -Queue 中等待Finalizer线程执行finalize方法的对象。只在Linux/Solaris平台下有效 +- -heap 显示Java堆详细信息,如使用哪种回收器、参数配置、分代状况。 +- -histo 显示堆中对象统计信息、包括类、实例数量和合计容量。 +- -F 当虚拟机进程对-dump选项没有响应时,可使用这个选项强制生成dump快照。 + +#### 47.虚拟机堆转存储快照分析工具? + +jhat ( JVM Heap Analysis Tool) 用来分析jmap生成的堆转储快照。 + +#### 48.堆栈跟踪工具? + +jstack(Stack Trace for Java) 命令用于生成虚拟机当前时刻的线程快照(一般称为thread dump 或javacore文件)。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因。 + +jstack [option] vmid + +-F 当正常输出的请求不被响应时,强制输出线程堆栈 + +-l 除堆栈外,显示关于锁的附加信息 + +-m 如果调用本地方法的花,可以显示C/C++ 的堆栈 + +#### 49.除了命令行,还有什么可视化工具? + +JConsole 和 VisualVM,这两个工具是JDK的正式成员。 + +#### 50.类加载过程? + +加载-》验证-》准备-》解析-》初始化-》使用-》卸载 + +参考: + +- 《深入理解JVM & G1 GC》 + +- 《深入理解Java虚拟机:JVM高级特性与最佳实践》 + +- 《实战Java虚拟机:JVM故障诊断与性能优化》 + + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) + diff --git a/Java8.md b/Java8.md new file mode 100644 index 0000000..935e857 --- /dev/null +++ b/Java8.md @@ -0,0 +1,187 @@ +#### Java8 + +#### 1.阐述 Java 7 和 Java 8 的区别 + +实话说,两者有很多不同。如果你能列出最重要的,应该就足够了。你应该解释 Java 8 中的新功能。想要获得完整清单,请访问官网:Java 8 JDK。 + +你应该知道以下几个重点: + +- **lambda 表达式**,Java 8 版本引入的一个新特性。lambda 表达式允许你将功能当作方法参数或将代码当作数据。lambda 表达式还能让你以更简洁的方式表示只有一个方法的接口 (称为函数式接口) 的实例。 +- **方法引用**,为已命名方法提供了易于阅读的 lambda 表达式。 +- 默认方法,支持将新功能添加到类库中的接口,并确保与基于这些接口的旧版本的代码的二进制兼容性。 +- **重复注解**,支持在同一声明或类型上多次应用同一注解类型。 +- **类型注解**,支持在任何使用类型的地方应用注解,而不仅限于声明。此特性与可插入型系统一起使用时,可增强对代码的类型检查。 + +#### 2.Java SE 8中最流行和最著名的最新功能是什么? + +Java SE 8最受欢迎和最著名的最新功能包括以下内容: + +功能接口。集合API增强功能。Lambda表达式。分流器。流API等。 + +#### 3.是什么使Java SE 8优于其他? + +Java SE 8具有以下功能,使其优于其他功能: + +它编写并行代码。它提供了更多可用的代码。它具有改进的性能应用程序。它具有更易读和简洁的代码。它支持编写包含促销的数据库。 + +#### 4.在Java SE 8中定义Lambda表达式? + +Lambda表达式是Java SE 8,是匿名函数的名称,该匿名函数有助于接受一组不同的输入参数,并提供各种结果结果。 + +#### 5.为什么将Lambda Expression创造为代码块? + +Lambda表达式是作为代码块创造的,因为它没有名称,可以带有或不带有参数和结果。 + +#### 6.Lambda表达式和功能接口之间有什么联系? + +当我们使用Lambda表达式时,这意味着我们正在使用功能接口。因此,它们都是相互关联的。这意味着Lambda表达式是Functional接口的一部分,Functional接口是一个承载各种其他功能和表达式的更大平台。 + +#### 7.在Java SE 8中定义Nashorn? + +Nashorn是在Java SE 8的Java平台上使用的最新Javascript处理引擎。 + +#### 8.Map和FlatMap流操作之间的主要区别是什么? + +Map和FlatMap流操作之间的主要区别在于,前者将返回值包装在其序数类型内,而后者则没有。 + +#### 9.Map和Flat map流操作之间的相似之处是什么? + +Map和FlatMap流操作都是中间流操作,它们接收一个函数并将这些函数应用于流的不同元素。 + +#### 10.定义流管道? + +Java SE 8中的流管道用于通过拆分可能在一个流上发生的操作来将操作链接在一起。 + +#### 11.什么是使用Stream Pipeline的强制性? + +使用Stream Pipeline的强制性在于存在终端操作,该操作有助于返回最终值并支持管道的终止。 + +#### 12.新日期和时间API的作用是什么? + +新的日期和时间API是在Java SE 8中的java time软件包下设计的,因此可以避免与JDK或Java.util.date相关的问题。 + +#### 13.Java SE 8的核心API类是什么? + +Java SE 8的核心API类包括LocalDate,LocalTime和LocalDateTime。 + +#### 14.Metaspace与PermGen相比有什么优势? + +PerGen的大小是固定的,不能动态增长,而Metaspace可以动态增长,并且确实具有任何类型的大小约束。 + +#### 15.功能接口和SAM接口之间有什么区别吗? + +不,功能接口和SAM接口之间没有区别。 SAM接口或单一抽象方法接口是Java SE 8 API中定义的一种功能接口。 + +#### 16.接口默认方法和静态方法 + + interface IDefaultFunction{ + default void study(){ + System.out.println("什么时候能找到工作"); + } + static void finJob(){ + System.out.println("开始找工作"); + } + } + + + class DefaultFunction implements IDefaultFunction{ + + } + +Java 8用默认方法与静态方法这两个新概念来扩展接口的声明。默认方法与抽象方法不同之处在于抽象方法必须要求实现,但是默认方法则没有这个要求,就是接口可以有实现方法,而且不需要实现类去实现其方法。我们只需在方法名前面加个default关键字即可实现默认方法。为什么要有这个特性?以前当需要修改接口的时候,需要修改全部实现该接口的类。而引进的默认方法的目的是为了解决接口的修改与现有的实现不兼容的问题。 + +#### 17.引入了流Stream + +Stream可以链式书写代码,需要几行搞定的代码,Stream可以一行搞定,Stream是使用内部迭代,而且Stream支持并行操作 + + @Test + public void streamTest(){ + Listlist = new ArrayList<>(); + list.add(2); + list.add(4); + list.add(5); + list.forEach(integer -> { + System.out.println(integer); + }); + System.out.println("=======分割线========="); + List list2 = list.stream().filter((i)-> i>2).collect(Collectors.toList()); + list2.forEach(i-> System.out.println(i)); + } +输出 + +``` +2 +4 +5 +=======分割线========= +4 +5 +``` + +filter:接受lamada表达式,从中截取一符合条件的元素 +limit:截流limit(5),表示截取五个元素 +skip(n):跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补 +distinct:筛选,通过流所生成元素的hashCode()和equals()去除重复元素 +map--接收Lambda,将元素转换成其他形式或提取信息 + +#### 18.可以重复注解 + +@Repeatable注解 + +#### 19.集合引入了很多parallel开头的并行操作的方法 + +特别是parallelSort,这个将排序拆分很多小的集合,运用多线程进行排序,最后合并,得到最终想要的结果。 + +#### 20.日期时间 + +Clock + +LocalDate 只保存有ISO-8601日期系统的日期部分,有时区信息 + +LocalTime 只保存ISO-8601日期系统的时间部分,没有时区信息 + +LocalDateTime类合并了LocalDate和LocalTime,它保存有ISO-8601日期系统的日期和时间,但是没有时区信息 + +ZonedDateTime,它保存有ISO-8601日期系统的日期和时间,而且有时区信息 + +Duration类,Duration持有的时间精确到纳秒。它让我们很容易计算两个日期中间的差异 + +#### 21.Nashorn javascript 引擎 + +Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用 + +#### 22.Base64 + +新的Base64API也支持URL和MINE的编码解码 + +#### 23.并行数组 + +Java 8新增加了很多方法支持并行的数组处理**parallelSort()** + +#### 24.并发 + +java.util.concurrent.ConcurrentHashMap中加入了一些新方法来支持聚集操作 + +java.util.concurrent.ForkJoinPool类中加入了一些新方法来支持共有资源池 + +java.util.concurrent.locks.StampedLock类提供一直基于容量的锁,这种锁有三个模型来控制读写操作(它被认为是不太有名的 + +java.util.concurrent.locks.ReadWriteLock类的替代者) + +#### 25.什么是Lambda表达式? + +Lambda Expression可以定义为允许用户将方法作为参数传递的匿名函数。这有助于删除大量的样板代码。Lambda函数没有访问修饰符(私有,公共或受保护),没有返回类型声明和没有名称。 + +Lambda表达式允许用户将“函数”传递给代码。所以,与以前需要一整套的接口/抽象类想必,我们可以更容易地编写代码。例如,假设我们的代码具有一些复杂的循环/条件逻辑或工作流程。使用lambda表达式,在那些有难度的地方,可以得到很好的解决。 + + + +#### 参考 + +https://zhuanlan.zhihu.com/p/215758969 + +https://baijiahao.baidu.com/s?id=1667297300847848263&wfr=spider&for=pc + +https://blog.csdn.net/wangbiao007/article/details/103229950 + +https://www.cnblogs.com/wangwanchao/p/5269648.html \ No newline at end of file diff --git "a/Java\345\237\272\347\241\200.md" "b/Java\345\237\272\347\241\200.md" new file mode 100644 index 0000000..9aa4a38 --- /dev/null +++ "b/Java\345\237\272\347\241\200.md" @@ -0,0 +1,933 @@ +## Java基础 + +#### 1.Java语言的三大特性 + +##### 1.封装: + +首先,属性可用来描述同一类事物的特征,方法可描述一类事物可做的操作。封装就是把属于同一类事物的共性(包括属性与方法)归到一个类中,以方便使用。 + + + +概念:封装也称为信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。系统的其他部分只有通过包裹在数据外面的被授权的操作来与这个抽象数据类型交流与交互。也就是说,用户无需知道对象内部方法的实现细节,但可以根据对象提供的外部接口(对象名和参数)访问该对象。 +好处: + +(1)实现了专业的分工。将能实现某一特定功能的代码封装成一个独立的实体后,各程序员可以在需要的时候调用,从而实现了专业的分工。 + +(2)隐藏信息,实现细节。通过控制访问权限可以将可以将不想让客户端程序员看到的信息隐藏起来,如某客户的银行的密码需要保密,只能对该客户开发权限。 + +##### 2.继承: + +就是个性对共性的属性与方法的接受,并加入个性特有的属性与方法 +1.概念:一个类继承另一个类,则称继承的类为子类,被继承的类为父类。 +2.目的:实现代码的复用。 +3.理解:子类与父类的关系并不是日常生活中的父子关系,子类与父类而是一种特殊化与一般化的关系,是is-a的关系,子类是父类更加详细的分类。如class dog extends animal,就可以理解为dog is a animal.注意设计继承的时候,若要让某个类能继承,父类需适当开放访问权限,遵循里氏代换原则,即向修改关闭对扩展开放,也就是开-闭原则。 +4.结果:继承后子类自动拥有了父类的属性和方法,但特别注意的是,父类的私有属性和构造方法并不能被继承。 +另外子类可以写自己特有的属性和方法,目的是实现功能的扩展,子类也可以复写父类的方法即方法的重写。 + +##### 3.多态: + +多态的概念发展出来,是以封装和继承为基础的。 +多态就是在抽象的层面上实施一个统一的行为,到个体(具体)的层面上时,这个统一的行为会因为个体(具体)的形态特征而实施自己的特征行为。(针对一个抽象的事,对于内部个体又能找到其自身的行为去执行。) +1.概念:相同的事物,调用其相同的方法,参数也相同时,但表现的行为却不同。 +2.理解:子类以父类的身份出现,但做事情时还是以自己的方法实现。子类以父类的身份出现需要向上转型(upcast),其中向上转型是由JVM自动实现的,是安全的,但向下转型(downcast)是不安全的,需要强制转换。子类以父类的身份出现时自己特有的属性和方法将不能使用。 + +#### 2.Java语言主要特性 + +1. **Java语言是易学的。**Java语言的语法与C语言和C++语言很接近,使得大多数程序员很容易学习和使用Java。 + +2. **Java语言是强制面向对象的。**Java语言提供类、接口和继承等原语,为了简单起见,只支持类之间的单继承,但支持接口之间的多继承,并支持类与接口之间的实现机制(关键字为implements)。 + +3. **Java语言是分布式的**。Java语言支持Internet应用的开发,在基本的Java应用编程接口中有一个网络应用编程接口(java net),它提供了用于网络应用编程的类库,包括URL、URLConnection、Socket、ServerSocket等。Java的RMI(远程方法激活)机制也是开发分布式应用的重要手段。 + +4. **Java语言是健壮的。**Java的强类型机制、异常处理、垃圾的自动收集等是Java程序健壮性的重要保证。对指针的丢弃是Java的明智选择。 + +5. **Java语言是安全的。**Java通常被用在网络环境中,为此,Java提供了一个安全机制以防恶意代码的攻击。如:安全防范机制(类ClassLoader),如分配不同的名字空间以防替代本地的同名类、字节代码检查。 + +6. **Java语言是体系结构中立的。**Java程序(后缀为java的文件)在Java平台上被编译为体系结构中立的字节码格式(后缀为class的文件),然后可以在实现这个Java平台的任何系统中运行。 + +7. **Java语言是解释型的。**如前所述,Java程序在Java平台上被编译为字节码格式,然后可以在实现这个Java平台的任何系统的解释器中运行。(一次编译,到处运行) + +8. **Java是性能略高的**。与那些解释型的高级脚本语言相比,Java的性能还是较优的。 + +9. **Java语言是原生支持多线程的。**在Java语言中,线程是一种特殊的对象,它必须由Thread类或其子(孙)类来创建。 + +#### 3. JDK 和 JRE 有什么区别 + +- JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境。 +- JRE:Java Runtime Environment 的简称,Java 运行环境,为 Java 的运行提供了所需环境。 + +具体来说 JDK 其实包含了 JRE,同时还包含了编译 Java 源码的编译器 Javac,还包含了很多 Java 程序调试和分析的工具。简单来说:如果你需要运行 Java 程序,只需安装 JRE 就可以了,如果你需要编写 Java 程序,需要安装 JDK。 + +#### 4.Java基本数据类型及其封装类 + +![image-20200413145454097](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200413145454097.png) + +Tips:boolean类型占了单独使用是4个字节,在数组中又是1个字节 +基本类型所占的存储空间是不变的。这种不变性也是Java具有可移植性的原因之一。 +基本类型放在栈中,直接存储值。 +所有数值类型都有正负号,没有无符号的数值类型。 + +为什么需要封装类? +因为泛型类包括预定义的集合,使用的参数都是对象类型,无法直接使用基本数据类型,所以Java又提供了这些基本类型的封装类。 + +基本类型和对应的封装类由于本质的不同。具有一些区别: +1.基本类型只能按值传递,而封装类按引用传递。 +2.基本类型会在栈中创建,而对于对象类型,对象在堆中创建,对象的引用在栈中创建,基本类型由于在栈中,效率会比较高,但是可能存在内存泄漏的问题。 + +#### 5.如果main方法被声明为private会怎样? + +能正常编译,但运行的时候会提示”main方法不是public的”。在idea中如果不用public修饰,则会自动去掉可运行的按钮。 + +#### 6.说明一下public static void main(String args[])这段声明里每个关键字的作用 + +public: main方法是Java程序运行时调用的第一个方法,因此它必须对Java环境可见。所以可见性设置为pulic. + +static: Java平台调用这个方法时不会创建这个类的一个实例,因此这个方法必须声明为static。 + +void: main方法没有返回值。 + +String是命令行传进参数的类型,args是指命令行传进的字符串数组。 + +#### 7.==与equals的区别 + +==比较两个对象在内存里是不是同一个对象,就是说在内存里的存储位置一致。两个String对象存储的值是一样的,但有可能在内存里存储在不同的地方 。 + +==比较的是引用而equals方法比较的是内容。public boolean equals(Object obj) 这个方法是由Object对象提供的,可以由子类进行重写。默认的实现只有当对象和自身进行比较时才会返回true,这个时候和==是等价的。String, BitSet, Date, 和File都对equals方法进行了重写,对两个String对象 而言,值相等意味着它们包含同样的字符序列。对于基本类型的包装类来说,值相等意味着对应的基本类型的值一样。 + +```java +public class EqualsTest { + public static void main(String[] args) { + String s1 = “abc”; + String s2 = s1; + String s5 = “abc”; + String s3 = new String(”abc”); + String s4 = new String(”abc”); + System.out.println(”== comparison : ” + (s1 == s5)); + System.out.println(”== comparison : ” + (s1 == s2)); + System.out.println(”Using equals method : ” + s1.equals(s2)); + System.out.println(”== comparison : ” + s3 == s4); + System.out.println(”Using equals method : ” + s3.equals(s4)); + } +} +``` + +结果: + +```tex +== comparison : true +== comparison : true +Using equals method : true +false +Using equals method :true +``` + +#### 8.Object有哪些公用方法 + +**Object是所有类的父类,任何类都默认继承Object** + +**clone** 保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。 + +**equals** 在Object中与==是一样的,子类一般需要重写该方法。 + +**hashCode** 该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。 + +**getClass** final方法,获得运行时类型 + +**wait** 使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。 **wait()** 方法一直等待,直到获得锁或者被中断。 **wait(long timeout)** 设定一个超时间隔,如果在规定时间内没有获得锁就返回。 + +**调用该方法后当前线程进入睡眠状态,直到以下事件发生** + +1、其他线程调用了该对象的notify方法。 2、其他线程调用了该对象的notifyAll方法。 3、其他线程调用了interrupt中断该线程。 4、时间间隔到了。 5、此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。 + +**notify** 唤醒在该对象上等待的某个线程。 + +**notifyAll** 唤醒在该对象上等待的所有线程。 + +**toString** 转换成字符串,一般子类都有重写,否则打印句柄。 + +#### 9.为什么Java里没有全局变量? + +全局变量是全局可见的,Java不支持全局可见的变量,因为:全局变量破坏了引用透明性原则。全局变量导致了命名空间的冲突。 + +#### 10.while循环和do循环有什么不同? + +while结构在循环的开始判断下一个迭代是否应该继续。do/while结构在循环的结尾来判断是否将继续下一轮迭代。do结构至少会执行一次循环体。 + +#### 11.char型变量中能不能存储一个中文汉字?为什么? + +可以。Java默认Unicode编码。Unicode码占16位。 char两个字节刚好16位。 + +#### 12.public,private,protected的区别,继承方法与访问权限 + +![image-20200413174229242](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200413174229242.png) + + Tips:不写默认default + +#### 13.**float f=3.4;是否正确?** + +不正确。3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换float f =(float)3.4; 或者写成float f =3.4F。 + +#### 14.**short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?** + +对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型。而short s1 = 1; s1 += 1;+=操作符会进行**隐式自动类型转换**,是 **Java 语言规定的运算符**;Java编译器会对它进行特殊处理,因此可以正确编译。因为s1+= 1;相当于s1 = (short)(s1 + 1)。 + +#### 15.**&和&&的区别?** + +1. &:(1)按位与;(2)逻辑与。 + + 按位与: 0 & 1 = 0 ; 0 & 0 = 0; 1 & 1 = 1 + + 逻辑与: a == b & b ==c (即使a==b已经是 false了,程序还会继续判断b是否等于c) + +2.&&: 短路与 + +​ a== b && b== c (当a==b 为false则不会继续判断b是否等与c) + +比如判断某对象中的属性是否等于某值,则必须用&&,否则会出现空指针问题。 + +#### 16.IntegerCache + +```Java +public class IntegerTest { + + public static void main(String[] args) { + + Integer a = 100, b = 100 ,c = 129,d = 129; + + System.out.println(a==b); + + System.out.println(c==d); + } +} + +``` + + 结果: + +```tex +true +false +``` + +小朋友,你是否有很多问号? + +来解释一下: + +```java + /** + * Cache to support the object identity semantics of autoboxing for values between + * -128 and 127 (inclusive) as required by JLS. + * + * The cache is initialized on first usage. The size of the cache + * may be controlled by the {@code -XX:AutoBoxCacheMax=} option. + * During VM initialization, java.lang.Integer.IntegerCache.high property + * may be set and saved in the private system properties in the + * sun.misc.VM class. + */ + + private static class IntegerCache { + static final int low = -128; + static final int high; + static final Integer cache[]; + + static { + // high value may be configured by property + int h = 127; + String integerCacheHighPropValue = + sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); + if (integerCacheHighPropValue != null) { + try { + int i = parseInt(integerCacheHighPropValue); + i = Math.max(i, 127); + // Maximum array size is Integer.MAX_VALUE + h = Math.min(i, Integer.MAX_VALUE - (-low) -1); + } catch( NumberFormatException nfe) { + // If the property cannot be parsed into an int, ignore it. + } + } + high = h; + + cache = new Integer[(high - low) + 1]; + int j = low; + for(int k = 0; k < cache.length; k++) + cache[k] = new Integer(j++); + + // range [-128, 127] must be interned (JLS7 5.1.7) + assert IntegerCache.high >= 127; + } + + private IntegerCache() {} + } +``` + + + +```Java + public static Integer valueOf(int i) { + assert IntegerCache.high >= 127; + if (i >= IntegerCache.low && i <= IntegerCache.high) + return IntegerCache.cache[i + (-IntegerCache.low)]; + return new Integer(i); + } +``` + +通过源码,我们可以看出, -128~127之间做了缓存。考虑到高频数值的复用场景,这样做还是很合理的,合理优化。最大边界可以通过-XX:AutoBoxCacheMax进行配置。 + +#### 17.Locale类是什么? + +Locale类用来根据语言环境来动态调整程序的输出。 + +#### 18.Java中final、finally、finalize的区别与用法 + +1. final + final是一个修饰符也是一个关键字。 + +- 被final修饰的类无法被继承 +- 对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改; + 如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。但是它*指向的对象的内容是可变的*。 +- 被final修饰的方法将无法被重写,但*允许重载* + 注意:类的private方法会隐式地被指定为final方法。 + +2. finally + finally是一个关键字。 + +- finally在异常处理时提供finally块来执行任何清除操作。不管有没有异常被抛出或者捕获,finally块都会执行,通常用于释放资源。 +- finally块正常情况下一定会被执行。但是有至少两个极端情况: + 如果对应的try块没有执行,则这个try块的finally块并不会被执行 + 如果在try块中jvm关机,例如system.exit(n),则finally块也不会执行(都拔电源了,怎么执行) +- finally块中如果有return语句,则会覆盖try或者catch中的return语句,导致二者无法return,所以强烈建议finally块中不要存在return关键字 + +3. finalize + finalize()是Object类的protected方法,子类可以覆盖该方法以实现资源清理工作。 + *GC在回收对象之前都会调用该方法* + finalize()方法是存在很多问题的: + +- java语言规范并不保证finalize方法会被及时地执行,更根本不会保证它们一定会被执行 +- finalize()方法可能带来性能问题,因为JVM通常在单独的低优先级线程中完成finalize的执行 +- finalize()方法中,可将待回收对象赋值给GC Roots可达的对象引用,从而达到对象再生的目的 +- finalize方法最多由GC执行一次(但可以手动调用对象的finalize方法) + +#### 19.hashCode()和equals()的区别 + +下边从两个角度介绍了他们的区别:一个是性能,一个是可靠性。他们之间的主要区别也基本体现在这里。 + +1.equals()既然已经能实现对比的功能了,为什么还要hashCode()呢? +因为重写的equals()里一般比较的比较全面比较复杂,这样效率就比较低,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率很高。 + +2.hashCode()既然效率这么高为什么还要equals()呢? +因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题),所以hashCode()只能说是大部分时候可靠,并不是绝对可靠,所以我们可以得出(PS:以下两条结论是重点,很多人面试的时候都说不出来): + +equals()相等的两个对象他们的hashCode()肯定相等,也就是用equals()对比是绝对可靠的。 + +hashCode()相等的两个对象他们的equals()不一定相等,也就是hashCode()不是绝对可靠的。 +**扩展** + +1.阿里巴巴开发规范明确规定: + +只要重写 equals,就必须重写 hashCode; + +因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须重写这两个方法; + +如果自定义对象做为 Map 的键,那么必须重写 hashCode 和 equals; + +String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象作为 key 来使用; + +2、什么时候需要重写? +一般的地方不需要重载hashCode,只有当类需要放在HashTable、HashMap、HashSet等等hash结构的集合时才会重载hashCode。 + +3、那么为什么要重载hashCode呢? +如果你重写了equals,比如说是基于对象的内容实现的,而保留hashCode的实现不变,那么很可能某两个对象明明是“相等”,而hashCode却不一样。 + +这样,当你用其中的一个作为键保存到hashMap、hasoTable或hashSet中,再以“相等的”找另一个作为键值去查找他们的时候,则根本找不到。 + +4、为什么equals()相等,hashCode就一定要相等,而hashCode相等,却不要求equals相等? + +因为是按照hashCode来访问小内存块,所以hashCode必须相等。 +HashMap获取一个对象是比较key的hashCode相等和equals为true。 +之所以hashCode相等,却可以equal不等,就比如ObjectA和ObjectB他们都有属性name,那么hashCode都以name计算,所以hashCode一样,但是两个对象属于不同类型,所以equals为false。 + +5、为什么需要hashCode? + +通过hashCode可以很快的查到小内存块。 +通过hashCode比较比equals方法快,当get时先比较hashCode,如果hashCode不同,直接返回false。 + +#### 20.深拷贝和浅拷贝的区别是什么? + +浅拷贝 + +**(1)、定义** + +被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。即对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。”里面的对象“会在原来的对象和它的副本之间共享。 + +简而言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象 +**(2)、浅拷贝实例** + +```java +package com.test; + +public class ShallowCopy { + public static void main(String[] args) throws CloneNotSupportedException { + Teacher teacher = new Teacher(); + teacher.setName("riemann"); + teacher.setAge(27); + + Student2 student1 = new Student2(); + student1.setName("edgar"); + student1.setAge(18); + student1.setTeacher(teacher); + + Student2 student2 = (Student2) student1.clone(); + System.out.println("拷贝后"); + System.out.println(student2.getName()); + System.out.println(student2.getAge()); + System.out.println(student2.getTeacher().getName()); + System.out.println(student2.getTeacher().getAge()); + + System.out.println("修改老师的信息后——————"); + // 修改老师的信息 + teacher.setName("Games"); + System.out.println(student1.getTeacher().getName()); + System.out.println(student2.getTeacher().getName()); + + } +} + +class Teacher implements Cloneable { + private String name; + private int age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } +} + +class Student2 implements Cloneable { + private String name; + private int age; + private Teacher teacher; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Teacher getTeacher() { + return teacher; + } + + public void setTeacher(Teacher teacher) { + this.teacher = teacher; + } + + public Object clone() throws CloneNotSupportedException { + Object object = super.clone(); + return object; + } +} + + +``` + +输出结果: + +```sqlite +拷贝后 +edgar +18 +riemann +27 +修改老师的信息后—————— +Games +Games +``` + +结果分析: 两个引用student1和student2指向不同的两个对象,但是两个引用student1和student2中的两个teacher引用指向的是同一个对象,所以说明是浅拷贝。 + +![image-20200413210910649](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200413210910649.png) + +深拷贝 + +**(1)、定义** + +深拷贝是一个整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。 + +简而言之,深拷贝把要复制的对象所引用的对象都复制了一遍。 + +**(2)、深拷贝实例** + +```java +package com.test; + +public class DeepCopy { + public static void main(String[] args) throws CloneNotSupportedException { + Teacher2 teacher = new Teacher2(); + teacher.setName("riemann"); + teacher.setAge(27); + + Student3 student1 = new Student3(); + student1.setName("edgar"); + student1.setAge(18); + student1.setTeacher(teacher); + + Student3 student2 = (Student3) student1.clone(); + System.out.println("拷贝后"); + System.out.println(student2.getName()); + System.out.println(student2.getAge()); + System.out.println(student2.getTeacher().getName()); + System.out.println(student2.getTeacher().getAge()); + + System.out.println("修改老师的信息后——————"); + // 修改老师的信息 + teacher.setName("Games"); + System.out.println(student1.getTeacher().getName()); + System.out.println(student2.getTeacher().getName()); + } +} + +class Teacher2 implements Cloneable { + private String name; + private int age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } +} + +class Student3 implements Cloneable { + private String name; + private int age; + private Teacher2 teacher; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Teacher2 getTeacher() { + return teacher; + } + + public void setTeacher(Teacher2 teacher) { + this.teacher = teacher; + } + + public Object clone() throws CloneNotSupportedException { + // 浅复制时: + // Object object = super.clone(); + // return object; + + // 改为深复制: + Student3 student = (Student3) super.clone(); + // 本来是浅复制,现在将Teacher对象复制一份并重新set进来 + student.setTeacher((Teacher2) student.getTeacher().clone()); + return student; + + } +} +``` + +输出结果: + +```sqlite +拷贝后 +edgar +18 +riemann +27 +修改老师的信息后—————— +Games +riemann +``` + +结果分析: +两个引用student1和student2指向不同的两个对象,两个引用student1和student2中的两个teacher引用指向的是两个对象,但对teacher对象的修改只能影响student1对象,所以说是深拷贝。 + +![image-20200413211228832](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200413211228832.png) + +#### 21.Java 中操作字符串都有哪些类?它们之间有什么区别? + +String、StringBuffer、StringBuilder。 + +String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。 + +StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。 + +#### 22.String str="a"与 String str=new String("a")一样吗? + +不一样,因为内存的分配方式不一样。 + +String str="a"; -> 常量池 String str=new String("a") -> 堆内存 + +#### 23.**抽象类能使用 final 修饰吗?** + +不能。定义抽象类就是让其他类继承的,而 final修饰的类不能被继承。 + +#### 24.static关键字5连问 + +(1)抽象的(abstract)方法是否可同时是静态的(static)? + +​ 抽象方法将来是要被重写的,而静态方法是不能重写的,所以这个是错误的。 + + (2)是否可以从一个静态(static)方法内部发出对非静态方法的调用? + +​ 不可以,静态方法只能访问静态成员,非静态方法的调用要先创建对象。 + + (3) static 可否用来修饰局部变量? + +​ static 不允许用来修饰局部变量 + +(4)内部类与静态内部类的区别? + +静态内部类相对与外部类是独立存在的,在静态内部类中无法直接访问外部类中变量、方法。如果 要访问的话,必须要new一个外部类的对象,使用new出来的对象来访问。但是可以直接访问静态的变量、调用静态的方法; + +普通内部类作为外部类一个成员而存在,在普通内部类中可以直接访问外部类属性,调用外部类的方法。 + +如果外部类要访问内部类的属性或者调用内部类的方法,必须要创建一个内部类的对象,使用该对象访问属性或者调用方法。 + +如果其他的类要访问普通内部类的属性或者调用普通内部类的方法,必须要在外部类中创建一个普通内部类的对象作为一个属性,外同类可以通过该属性调用普通内部类的方法或者访问普通内部类的属性 + +如果其他的类要访问静态内部类的属性或者调用静态内部类的方法,直接创建一个静态内部类对象即可。 + +(5)Java中是否可以覆盖(override) 一个private或者是static的方法? + +Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,所以概念上不适用。 + +#### 25. 重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分? + +方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求,不能根据返回类型进行区分。 + +#### 26.Java的四种引用 + +**1、强引用** + +最普遍的一种引用方式,如String s = "abc",变量s就是字符串“abc”的强引用,只要强引用存在,则垃圾回收器就不会回收这个对象。 + +**2、软引用(SoftReference)** + +用于描述还有用但非必须的对象,如果内存足够,不回收,如果内存不足,则回收。一般用于实现内存敏感的高速缓存,软引用可以和引用队列ReferenceQueue联合使用,如果软引用的对象被垃圾回收,JVM就会把这个软引用加入到与之关联的引用队列中。 + +**3、弱引用(WeakReference)** + +弱引用和软引用大致相同,弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。 + +**4、虚引用(PhantomReference)** + +就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。 虚引用主要用来跟踪对象被垃圾回收器回收的活动。 + +**虚引用与软引用和弱引用的一个区别在于:** + +虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。 + +#### 27.**Java 中Comparator 与Comparable 有什么不同?** + +Comparable 接口用于定义对象的自然顺序,是排序接口,而 comparator 通常用于定义用户定制的顺序,是比较接口。我们如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序。Comparable 总是只有一个,但是可以有多个 comparator 来定义对象的顺序。 + +#### 28. Java 序列化,反序列化? + +Java 序列化就是指将对象转换为字节序列的过程,反序列化是指将字节序列转换成目标对象的过程。 + +#### 29.什么情况需要Java序列化? + +当 Java 对象需要在网络上传输 或者 持久化存储到文件中时。 + +#### 30.序列化的实现? + +让类实现Serializable接口,标注该类对象是可被序列。 + +#### 31.如果某些数据不想序列化,如何处理? + +在字段面前加 transient 关键字,例如: + +```java + transient private String phone;//不参与序列化 +``` + +#### 32.**Java泛型和类型擦除?** + +泛型即参数化类型,在创建集合时,指定集合元素的类型,此集合只能传入该类型的参数。 + +类型擦除:java编译器生成的字节码不包含泛型信息,所以在编译时擦除:1.泛型用最顶级父类替换;2.移除。 + +#### 33. 如何将字符串反转? + +使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。 + +示例代码: + +```java +// StringBuffer reverse +StringBuffer stringBuffer = new StringBuffer(); +stringBuffer. append("abcdefg"); +System. out. println(stringBuffer. reverse()); // gfedcba +// StringBuilder reverse +StringBuilder stringBuilder = new StringBuilder(); +stringBuilder. append("abcdefg"); +System. out. println(stringBuilder. reverse()); // gfedcba +``` + +#### 34.String 类的常用方法都有那些? + +- indexOf():返回指定字符的索引。 +- charAt():返回指定索引处的字符。 +- replace():字符串替换。 +- trim():去除字符串两端空白。 +- split():分割字符串,返回一个分割后的字符串数组。 +- getBytes():返回字符串的 byte 类型数组。 +- length():返回字符串长度。 +- toLowerCase():将字符串转成小写字母。 +- toUpperCase():将字符串转成大写字符。 +- substring():截取字符串。 +- equals():字符串比较。 + +#### 35.抽象类必须要有抽象方法吗? + +不需要,抽象类不一定非要有抽象方法。 + +示例代码: + +```java +abstract class Cat { + public static void sayHi() { + System. out. println("hi~"); + } +} +``` + +上面代码,抽象类并没有抽象方法但完全可以正常运行。 + +#### 36. 普通类和抽象类有哪些区别? + +- 普通类不能包含抽象方法,抽象类可以包含抽象方法。 +- 抽象类不能直接实例化,普通类可以直接实例化。 + +#### 37.接口和抽象类有什么区别? + +- 实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。 +- 构造函数:抽象类可以有构造函数;接口不能有。 +- 实现数量:类可以实现很多个接口;但是只能继承一个抽象类。 +- 访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。 + +#### 38.以下代码中,s5`==`s2返回值是什么? + +``` +String s1="ab"; +String s2="a"+"b"; +String s3="a"; +String s4="b"; +String s5=s3+s4; +``` + +返回false.在编译过程中,编译器会将s2直接优化为"ab",将其放置在常量池当中;而s5则是被创建在堆区,相当于s5=new String(“ab”); + +#### 39.String 中的 intern() + +Stirng中的intern()是个Native方法,它会首先从常量池中查找是否存在该常量值的字符串,若不存在则先在常量池中创建,否则直接返回常量池已经存在的字符串的引用. 比如 + +``` + String s1="aa"; + String s2=s1.intern(); + System.out.print(s1==s2); +``` + + +上述代码将返回true.因为在"aa"会在编译阶段确定下来,并放置字符串常量池中,因此最终s1和s2引用的是同一个字符串常量对象。 + +#### 40.什么是编译器常量?使用它有什么风险? + +公共静态不可变,即public static final修饰的变量就是我们所说的编译期常量.这里的public可选的.实际上这些变量在编译时会被替换掉,因为编译器明确的能推断出这些变量的值(如果你熟悉C++,那么这里就相当于宏替换). + +编译器常量虽然能够提升性能,但是也存在一定问题:你使用了一个内部的或第三方库中的公有编译时常量,但是这个值后面被其他人改变了,但是你的客户端没有重新编译,这意味着你仍然在使用被修改之前的常量值. + +#### 41.3*0.1`==`0.3返回值是什么 + +false,因为有些浮点数不能完全精确的表示出来. + +#### 42.a=a+b与a+=b有什么区别吗? + ++=操作符会进行隐式自动类型转换,此处a+=b隐式的将加操作的结果类型强制转换为持有结果的类型,而a=a+b则不会自动进行类型转换.如: + +``` +byte a = 127; +byte b = 127; +b = a + b; // 报编译错误:cannot convert from int to byte +b += a; +``` + +#### 43.this与super的区别 + +- super:它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参) +- this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名) +- super()和this()类似,区别是,super()在子类中调用父类的构造方法,this()在本类内调用本类的其它构造方法。 +- super()和this()均需放在构造方法内第一行。 +- 尽管可以用this调用一个构造器,但却不能调用两个。 +- this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。 +- this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。 +- 从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。 + +#### 44.static存在的主要意义 + + static的主要意义是在于创建独立于具体对象的域变量或者方法。 **以致于即使没有创建对象,也能使用属性和调用方法** ! + +static关键字还有一个比较关键的作用就是 **用来形成静态代码块以优化程序性能** 。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。 + +为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。 + + + +#### 45.static的独特之处 + +1、被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法 **不属于任何一个实例对象,而是被类的实例对象所共享** 。 + +怎么理解 “被类的实例对象所共享” 这句话呢?就是说,一个类的静态成员,它是属于大伙的【大伙指的是这个类的多个对象实例,我们都知道一个类可以创建多个实例!】,所有的类对象共享的,不像成员变量是自个的【自个指的是这个类的单个实例对象】…我觉得我已经讲的很通俗了,你明白了咩? + +2、在该类被第一次加载的时候,就会去加载被static修饰的部分,而且只在类第一次使用时加载并进行初始化,注意这是第一次用就要初始化,后面根据需要是可以再次赋值的。 + +3、static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。赋值的话,是可以任意赋值的! + +4、被static修饰的变量或者方法是优先于对象存在的,也就是说当一个类加载完毕之后,即便没有创建对象,也可以去访问。 + +#### 46.static应用场景 + +因为static是被类的实例对象所共享,因此如果 某个成员变量是被所有对象所共享的,那么这个成员变量就应该定义为静态变量 。 + +因此比较常见的static应用场景有: + +1、修饰成员变量 + +2、修饰成员方法 + + 3、静态代码块 + + 4、修饰类【只能修饰内部类也就是静态内部类】 + +5、静态导包 + +#### 47.break ,continue ,return 的区别及作用 + +break 跳出总上一层循环,不再执行循环(结束当前的循环体) + +continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件) + +return 程序返回,不再执行下面的代码(结束当前的方法 直接返回) + +#### 48.Java 中,如何跳出当前的多重嵌套循环 + +在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break 语句,即可跳出外层循环。例如: + +``` +public static void main(String[] args) { + ok: + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + System.out.println("i=" + i + ",j=" + j); + if (j == 5) { + break ok; + } + + } + } +} +``` + +#### 49.在Java中定义一个不做事且没有参数的构造方法的作用 + +Java程序在执行子类的构造方法之前,如果没有用super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中特定的构造方法,则编译时将发生错误,因为Java程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。 + +#### 50.在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是? + +帮助子类做初始化工作。 + +#### 51.一个类的构造方法的作用是什么?若一个类没有声明构造方法,改程序能正确执行吗?为什么? + +主要作用是完成对类对象的初始化工作。可以执行。因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。 + +#### 52.构造方法有哪些特性? + +名字与类名相同; + +没有返回值,但不能用void声明构造函数; + +生成类的对象时自动执行,无需调用。 + +#### 53.静态变量和实例变量区别 + +静态变量: 静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间。 + +实例变量: 每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。 + +#### 54.静态变量与普通变量区别 + +static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。 + +还有一点就是static成员变量的初始化顺序按照定义的顺序进行初始化。 + +#### 55.静态方法和实例方法有何不同? + +静态方法和实例方法的区别主要体现在两个方面: + +1. 在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。 +2. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制 + +#### 56.在一个静态方法内调用一个非静态成员为什么是非法的? + +由于静态方法可以不通过对象进行调用,因此在静态方法里,不能调用其他非静态变量,也不可以访问非静态变量成员。 + + + +#### 参考: + +https://blog.csdn.net/qq_41701956/article/details/86773940 + +https://zhuanlan.zhihu.com/p/94095050 + +https://www.cnblogs.com/Java-JJ/p/12625888.html \ No newline at end of file diff --git "a/Java\345\237\272\347\241\200\357\274\210\344\270\213\357\274\211.md" "b/Java\345\237\272\347\241\200\357\274\210\344\270\213\357\274\211.md" new file mode 100644 index 0000000..5e8790a --- /dev/null +++ "b/Java\345\237\272\347\241\200\357\274\210\344\270\213\357\274\211.md" @@ -0,0 +1,438 @@ +## 基础(下) + +#### 1.Java和C++的区别? + +我知道很多人没学过 C++,但是面试官就是没事喜欢拿咱们 Java 和 C++ 比呀!没办法!!!就算没学过C++,也要记下来! + +都是面向对象的语言,都支持封装、继承和多态 + +Java 不提供指针来直接访问内存,程序内存更加安全 + +Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。 + +Java 有自动内存管理机制,不需要程序员手动释放无用内存 + +#### 2.什么是 Java 程序的主类 应用程序和小程序的主类有何不同? + +一个程序中可以有多个类,但只能有一个类是主类。在 Java 应用程序中,这个主类是指包含 main()方法的类。而在 Java 小程序中,这个主类是一个继承自系统类 JApplet 或 Applet 的子类。应用程序的主类不一定要求是 public 类,但小程序的主类要求必须是 public 类。主类是 Java 程序执行的入口点。 + +#### 3.Java 应用程序与小程序之间有哪些差别? + +简单说应用程序是从主线程启动(也就是 `main()` 方法)。applet 小程序没有 `main()` 方法,主要是嵌在浏览器页面上运行(调用`init()`或者`run()`来启动),嵌入浏览器这点跟 flash 的小游戏类似。 + +#### 4.import java和javax有什么区别? + +刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来使用。然而随着时间的推移,javax 逐渐地扩展成为 Java API 的组成部分。但是,将扩展从 javax 包移动到 java 包确实太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准API的一部分。 所以,实际上java和javax没有区别。这都是一个名字。 + +#### 5.object-c中的协议和java中的接口概念有何不同? + +OC中的协议有2层含义,官方定义为 formal和informal protocol。前者和Java接口一样。 informal protocol中的方法属于设计模式考虑范畴,不是必须实现的,但是如果有实现,就会改变类的属性。 其实关于正式协议,类别和非正式协议我很早前学习的时候大致看过,也写在了学习教程里 “非正式协议概念其实就是类别的另一种表达方式“这里有一些你可能希望实现的方法,你可以使用他们更好的完成工作”。 这个意思是,这些是可选的。比如我门要一个更好的方法,我们就会申明一个这样的类别去实现。然后你在后期可以直接使用这些更好的方法。 这么看,总觉得类别这玩意儿有点像协议的可选协议。" 现在来看,其实protocal已经开始对两者都统一和规范起来操作,因为资料中说“非正式协议使用interface修饰“, 现在我们看到协议中两个修饰词:“必须实现(@requied)”和“可选实现(@optional)”。 + +#### 6.Javascipt的本地对象,内置对象和宿主对象 + +本地对象: Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError, 简单来说,本地对象就是 ECMA一262 定义的类. + +内置对象: ECMA一262 把内置对象(built一in object)定义为“由 ECMAScript 实现提供的、独立于宿主环境的所有对象,在 ECMAScript 程序开始执行时出现”。这意味着开发者不必明确实例化内置对象,它已被实例化了。 同样是“独立于宿主环境”。根据定义我们似乎很难分清“内置对象”与“本地对象”的区别。而ECMA一262 只定义了两个内置对象,即 Global 和 Math (它们也是本地对象,根据定义,每个内置对象都是本地对象)。 如此就可以理解了。内置对象是本地对象的一种。而其包含的两种对象中,Math对象我们经常用到,可这个Global对象是啥东西呢? Global对象是ECMAScript中最特别的对象,因为实际上它根本不存在,有点玩人的意思。大家要清楚,在ECMAScript中,不存在独立的函数,所有函数都必须是某个对象的方法。 类似于isNaN()、parseInt()和parseFloat()方法等,看起来都是函数,而实际上,它们都是Global对象的方法。而且Global对象的方法还不止这些. + +宿主对象: ECMAScript中的“宿主”就是我们网页的运行环境,即“操作系统”和“浏览器”。所有非本地对象都是宿主对象(host object),即由 ECMAScript 实现的宿主环境提供的对象。所有的BOM和DOM对象都是宿主对象。因为其对于不同的“宿主”环境所展示的内容不同。其实说白了就是,ECMAScript官方未定义的对象都属于宿主对象,因为其未定义的对象大多数是自己通过ECMAScript程序创建的对象。自定义的对象也是宿主对象。 + +#### 7.在javascript中什么是伪数组,如何将伪数组转化为标准数组 + +这里把符合以下条件的对象称为伪数组: + +1,具有length属性 + +2,按索引方式存储数据 + +3,不具有数组的push,pop等方法 伪数组(类数组):无法直接调用数组方法或期望length属性有什么特殊的行为,不具有数组的push,pop等方法,但仍可以对真正数组遍历方法来遍历它们。典型的是函数的argument参数,还有像调用document.getElementsByTagName, document.childNodes之类的,它们返回的NodeList对象都属于伪数组。 + +#### 8.请问EJB与JAVA BEAN的区别是什么? + +Java Bean 是可复用的组件,对Java Bean并没有严格的规范,理论上讲,任何一个Java类都可以是一个Bean。但通常情况下,由于Java Bean是被容器所创建(如Tomcat)的,所以Java Bean应具有一个无参的构造器,另外,通常Java Bean还要实现Serializable接口用于实现Bean的持久性。Java Bean实际上相当于微软COM模型中的本地进程内COM组件,它是不能被跨进程访问的。EnterpriseJava Bean 相当于DCOM,即分布式组件。它是基于Java的远程方法调用(RMI)技术的,所以EJB可以被远程访问(跨进程、跨计算机)。但EJB必须被布署在诸如Webspere、WebLogic这样的容器中,EJB客户从不直接访问真正的EJB组件,而是通过其容器访问。EJB容器是EJB组件的代理, EJB组件由容器所创建和管理。客户通过容器来访问真正的EJB组件。 + +#### 9.请你说说Java和PHP的区别? + +PHP暂时还不支持像Java那样JIT运行时编译热点代码,但是PHP具有opcache机制,能够把脚本对应的opcode缓存在内存,PHP7中还支持配置opcache.file_cache导出opcode到文件.第三方的Facebook HHVM也支持JIT.另外PHP官方基于LLVM围绕opcache机制构建的Zend JIT分支也正在开发测试中.在php-src/Zend/bench.php测试显示,PHP JIT分支速度是PHP 5.4的10倍. PHP的库函数用C实现,而Java核心运行时类库(jdk/jre/lib/rt.jar,大于60MB)用Java编写(jdk/src.zip), 所以Java应用运行的时候,用户编写的代码以及引用的类库和框架都要在JVM上解释执行. Java的HotSpot机制,直到有方法被执行10000次才会触发JIT编译, 在此之前运行在解释模式下,以避免出现JIT编译花费的时间比方法解释执行消耗的时间还要多的情况. + +PHP内置模板引擎,自身就是模板语言.而Java Web需要使用JSP容器如Tomcat或第三方模板引擎. + +PHP也可以运行在多线程模式下,比如Apache的event MPM和Facebook的HHVM都是多线程架构.不管是多进程还是多线程的PHP Web运行模式,都不需要PHP开发者关心和控制,也就是说PHP开发者不需要写代码参与进程和线程的管理,这些都由PHP-FPM/HHVM/Apache实现.PHP-FPM进程管理和并发实现并不需要PHP开发者关心,而Java多线程编程需要Java开发者编码参与.PHP一个worker进程崩溃,master进程会自动新建一个新的worker进程,并不会导致PHP服务崩溃.而Java多线程编程稍有不慎(比如没有捕获异常)就会导致JVM崩溃退出.对于PHP-FPM和Apache MOD_PHP来说,服务进程常驻内存,但一次请求释放一次资源,这种内存释放非常彻底. PHP基于引用计数的GC甚至都还没发挥作用程序就已经结束了。 + +#### 10.请你谈谈Java中是如何支持正则表达式操作的? + +Java中的String类提供了支持正则表达式操作的方法,包括:matches()、replaceAll()、replaceFirst()、split()。此外,Java中可以用Pattern类表示正则表达式对象,它提供了丰富的API进行各种正则表达式操作,如: + +```java +import java.util.regex.Matcher; +import java.util.regex.Pattern; +class RegExpTest { + public static void main(String[] args) { + String str = "成都市(成华区)(武侯区)(高新区)"; + Pattern p = Pattern.compile(".*?(?=\\()"); + Matcher m = p.matcher(str); + if(m.find()) { + System.out.println(m.group()); + } + } +} +``` + +#### 11.请你说明一下,在Java中如何跳出当前的多重嵌套循环? + +在最外层循环前加一个标记如A,然后用break A;可以跳出多重循环。(Java中支持带标签的break和continue语句,作用有点类似于C和C++中的goto语句,但是就像要避免使用goto一样,应该避免使用带标签的break和continue,因为它不会让你的程序变得更优雅,很多时候甚至有相反的作用,所以这种语法其实不知道更好),根本不能进行字符串的equals比较,否则会产生NullPointerException异常。 + +#### 12.请说明Java的接口和C++的虚类的相同和不同处 + +由于Java不支持多继承,而有可能某个类或对象要使用分别在几个类或对象里面的方法或属性,现有的单继承机制就不能满足要求。 与继承相比,接口有更高的灵活性,因为接口中没有任何实现代码。当一个类实现了接口以后,该类要实现接口里面所有的方法和属性,并且接口里面的属性在默认状态下面都是public static,所有方法默认情况下是public.一个类可以实现多个接口。 + +#### 13.请说明Java是否支持多继承? + +Java中类不支持多继承,只支持单继承(即一个类只有一个父类)。 但是java中的接口支持多继承,,即一个子接口可以有多个父接口。(接口的作用是用来扩展对象的功能,一个子接口继承多个父接口,说明子接口扩展了多个功能,当类实现接口时,类就扩展了相应的功能)。 + +#### 14.请讲讲Java有哪些特性,并举一个和多态有关的例子。 + +封装、继承、多态。多态:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用) + +#### 15.什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? + +Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。 Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。 + +#### 16.请列举一下,在JAVA虚拟机中,哪些对象可作为ROOT对象? + +虚拟机栈中的引用对象 + +方法区中类静态属性引用的对象 + +方法区中常量引用对象 + +本地方法栈中JNI引用对象 + +#### 17.C++,Java,JavaScript这三种语言的区别 + +参考回答: + +从静态类型还是动态类型来看 + +静态类型,编译的时候就能够知道每个变量的类型,编程的时候也需要给定类型,如Java中的整型int,浮点型float等。C、C++、Java都属于静态类型语言。 + +动态类型,运行的时候才知道每个变量的类型,编程的时候无需显示指定类型,如JavaScript中的var、PHP中的$。JavaScript、Ruby、Python都属于动态类型语言。 + +静态类型还是动态类型对语言的性能有很大影响。 + +对于静态类型,在编译后会大量利用已知类型的优势,如int类型,占用4个字节,编译后的代码就可以用内存地址加偏移量的方法存取变量,而地址加偏移量的算法汇编很容易实现。 + +对于动态类型,会当做字符串通通存下来,之后存取就用字符串匹配。 + +从编译型还是解释型来看 + +编译型语言,像C、C++,需要编译器编译成本地可执行程序后才能运行,由开发人员在编写完成后手动实施。用户只使用这些编译好的本地代码,这些本地代码由系统加载器执行,由操作系统的CPU直接执行,无需其他额外的虚拟机等。 + +源代码=》抽象语法树=》中间表示=》本地代码 + +解释性语言,像JavaScript、Python,开发语言写好后直接将代码交给用户,用户使用脚本解释器将脚本文件解释执行。对于脚本语言,没有开发人员的编译过程,当然,也不绝对。 + +源代码=》抽象语法树=》解释器解释执行。 + +对于JavaScript,随着Java虚拟机JIT技术的引入,工作方式也发生了改变。可以将抽象语法树转成中间表示(字节码),再转成本地代码,如JavaScriptCore,这样可以大大提高执行效率。也可以从抽象语法树直接转成本地代码,如V8 + +Java语言,分为两个阶段。首先像C++语言一样,经过编译器编译。和C++的不同,C++编译生成本地代码,Java编译后,生成字节码,字节码与平台无关。第二阶段,由Java的运行环境也就是Java虚拟机运行字节码,使用解释器执行这些代码。一般情况下,Java虚拟机都引入了JIT技术,将字节码转换成本地代码来提高执行效率。 + +注意,在上述情况中,编译器的编译过程没有时间要求,所以编译器可以做大量的代码优化措施。 + +对于JavaScript与Java它们还有的不同: + +对于Java,Java语言将源代码编译成字节码,这个同执行阶段是分开的。也就是从源代码到抽象语法树到字节码这段时间的长短是无所谓的。 + +对于JavaScript,这些都是在网页和JavaScript文件下载后同执行阶段一起在网页的加载和渲染过程中实施的,所以对于它们的处理时间有严格要求。 + +#### 18.java nextLine 与 next 两个方法的区别? + +1. next 不会接收回车符,tab 或者空格键,在接收有效数据之前会忽略这些符号,若已经读取了有效数据,遇到这些符号会直接退出 +2. nextLine 可以接收空格或者 tab 键,其输入以 enter 键结束 + +#### 19.什么是 JavaConfig? + +Spring JavaConfig 是 Spring 社区的产品,它提供了配置 Spring IoC 容器的纯Java 方法。因此它有助于避免使用 XML 配置。使用 JavaConfig 的优点在于: + +(1)面向对象的配置。由于配置被定义为 JavaConfig 中的类,因此用户可以充分利用 Java 中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean 方法等。 + +(2)减少或消除 XML 配置。基于依赖注入原则的外化配置的好处已被证明。但是,许多开发人员不希望在 XML 和 Java 之间来回切换。JavaConfig 为开发人员提供了一种纯 Java 方法来配置与 XML 配置概念相似的 Spring 容器。从技术角度来讲,只使用 JavaConfig 配置类来配置容器是可行的,但实际上很多人认为将JavaConfig 与 XML 混合匹配是理想的。 + +(3)类型安全和重构友好。JavaConfig 提供了一种类型安全的方法来配置 Spring容器。由于 Java 5.0 对泛型的支持,现在可以按类型而不是按名称检索 bean,不需要任何强制转换或基于字符串的查找。 + +#### 20.停止非循环Java线程 + +这可能是我误读了我所读内容的一种情况,但是在Java中杀死线程的所有示例似乎都表明您必须发出信号以杀死自己。您不能在没有严重风险的情况下从外面杀死它。问题是,所有有关如何“礼貌地”要求线程死亡的示例都有某种循环,因此您要做的就是观察每次迭代中的标志。 + +因此,我得到的是一个线程,该线程执行的操作仅需要一段时间(一系列SQL查询)。我当然可以在每个步骤之后进行检查,但是它们并没有处于循环中,并且我没有一种非常优雅的方式可以解决此问题。这是我正在做的事的一个例子: + +``` +new Thread(new Runnable(){ + public void run(){ + //query 1 + Connection conn = db.getConnection(); + Statement s = conn.createStatement(); + ResultSet rs = s.executeQuery("SELECT ..."); + while(rs.next()){ + //do stuff + } + + //query 2 + rs = s.executeQuery("SELECT ..."); + while(rs.next()){ + //do stuff + } + + //query 3 + rs = s.executeQuery("SELECT ..."); + while(rs.next()){ + //do stuff + } + } +}).start(); +``` + +这是一个示例,我没有使用匿名内部类,但是它说明了我的run()方法无法优雅地停止自身。此外,即使我在每个步骤之后都进行检查,如果特定查询需要很长时间才能运行,则该代码将无法在查询完成后停止。 + +这段代码是针对GUI应用程序的,我真的很想找到一种无需使用Thread.stop()即可快速杀死线程的好方法。 + +**编辑** +-yshavit的回答很有帮助,因为我不知道它的`Statement.cancel()`存在。如果您感到好奇,那么对我的特定问题的答案是建立一个更抽象的数据库访问类。该类必须创建一个子线程以在运行时执行查询和循环,并检查每次迭代是否当前线程(不是子线程)被中断。如果确实被中断,它将仅调用Statement.cancel(),并且子线程将引发异常并死亡。并非所有的JDBC驱动程序都支持`Statement.cancel()`,但是Oracle +11g支持。 + + + +#### 21.在java中使用最简单的方法打印数组内容? + +从Java 5开始,你可以将Arrays.toString(arr)或Arrays.deepToString(arr)用于数组中的数组。 +请注意,Object[]版本会调用.toString()数组中的每个对象。输出甚至按照您的要求进行修饰。 + +**例子:** + +简单数组: + +``` +String[] array = new String[] {"John", "Mary", "Bob"}; +System.out.println(Arrays.toString(array)); +``` + +输出: + +``` +[John, Mary, Bob] +``` + +嵌套数组: + +``` +String[][] deepArray = new String[][] {{"John", "Mary"}, {"Alice", "Bob"}}; +System.out.println(Arrays.toString(deepArray)); +//output: [[Ljava.lang.String;@106d69c, [Ljava.lang.String;@52e922] +System.out.println(Arrays.deepToString(deepArray)); +``` + +输出: + +``` +[[John, Mary], [Alice, Bob]] +``` + +double 数组: + +``` +double[] doubleArray = { 7.0, 9.0, 5.0, 1.0, 3.0 }; +System.out.println(Arrays.toString(doubleArray)); +``` + +输出: + +``` +[7.0, 9.0, 5.0, 1.0, 3.0 ] +``` + +int 数组: + +``` +int[] intArray = { 7, 9, 5, 1, 3 }; +System.out.println(Arrays.toString(intArray)); +``` + +输出: + +``` +[7, 9, 5, 1, 3 ] +``` + +#### 22.为什么打印java对象得到SomeType@2f92e0f4这样的结果? + +##### 背景 + +所有Java对象都有一个`toString()`方法,当你尝试打印该对象时会调用该方法。 + +``` +System.out.println(myObject); // invokes myObject.toString() +``` + +此方法在`Object`类(所有Java对象的超类)中定义。该`Object.toString()`方法返回一个看起来很难看的字符串,该字符串由类的名称,`@`符号和对象的哈希码(十六进制)组成。此代码如下所示: + +``` +// Code of Object.toString() +public String toString() { + return getClass().getName() + "@" + Integer.toHexString(hashCode()); +} +``` + +这样的结果`com.foo.MyType@2f92e0f4`可以解释为: + +- com.foo.MyType -类的名称,即类MyType在package中com.foo。 +- @ -将字符串连接在一起 +- 2f92e0f4 对象的哈希码。 + +数组类的名称看起来有些不同,这在Javadocs for中得到了很好的解释`Class.getName()`。例如,`[Ljava.lang.String`表示: + +- [-一维数组(相对于[[或[[[等) +- L -数组包含一个类或接口 +- java.lang.String -数组中对象的类型 + +##### 自定义输出 + +要在调用时打印不同的内容`System.out.println(myObject)`,必须重写`toString()`自己类中的方法。这是一个简单的例子: + +``` +public class Person { + private String name; + // constructors and other methods omitted + @Override + public String toString() { + return name; + } +} +``` + +现在,如果我们打印一个Person,我们将看到它们的名称,而不是`com.foo.Person@12345678`。 + +请记住,这toString()只是将对象转换为字符串的一种方法。通常,此输出应以简洁明了的方式完全描述你的对象。toString()对于我们的Person班级来说,更好的选择可能是: + +``` +@Override +public String toString() { + return getClass().getSimpleName() + "[name=" + name + "]"; +} +``` + +将打印,例如`Person[name=Henry]`。这对于调试/测试来说是非常有用的数据。 + +如果你只想关注对象的一个方面或包含许多爵士乐的格式,则最好定义一个单独的方法,例如String toElegantReport() {…}。 + +##### 自动生成输出 + +许多IDEtoString()基于类中的字段提供了对自动生成方法的支持。例如,请参阅Eclipse和IntelliJ的文档。 + +一些流行的Java库也提供此功能。一些示例包括: + +- ToStringBuilder来自Apache Commons Lang +- MoreObjects.ToStringHelper来自Google Guava +- @ToString龙目岛项目的注释 + +##### 打印对象组 + +因此,你已经`toString()`为课程创建了一个不错的选择。如果将该类放入数组或集合,会发生什么情况? + +##### 数组 + +如果你有一个对象数组,则可以调用Arrays.toString()以生成该数组内容的简单表示。例如,考虑以下Person对象数组: + +``` +Person[] people = { new Person("Fred"), new Person("Mike") }; +System.out.println(Arrays.toString(people)); +``` + +// Prints: [Fred, Mike] + +注意:这是对Arrays类中调用的静态方法的调用toString(),这与我们上面讨论的内容不同。 + +如果你具有多维数组,则可以用于Arrays.deepToString()实现相同类型的输出。 + +##### 集合 + +大多数集合都会基于.toString()对每个元素的调用而产生漂亮的输出。 + +``` +List people = new ArrayList<>(); +people.add(new Person("Alice")); +people.add(new Person("Bob")); +System.out.println(people); +``` + +// Prints [Alice, Bob] +因此,你只需要确保列表元素定义一个`toString()`如上所述的尼斯。 + +#### 23.如何理解和使用Java中的增强型for循环foreach? + +``` +for (Iterator i = someIterable.iterator(); i.hasNext();) { + String item = i.next(); + System.out.println(item); +} +``` + +请注意,如果你需要`i.remove()`;在循环中使用或以某种方式访问实际的迭代器,则不能使用该`for(:)`惯用语,因为实际的迭代器只是推断出来的。 + +正如Denis Bueno所指出的那样,此代码适用于实现该Iterable接口的任何对象。 + +同样,如果`for(:)`习惯用法的右侧是一个`array`而不是一个`Iterable`对象,则内部代码将使用一个`int`索引计数器并进行检查`array.length`。请参阅Java语言规范。 + +#### 24.在java中为什么 1/3 == 0? + +运行下面的代码结果为0? + +``` +public static void main(String[] args) { + double g = 1 / 3; + System.out.printf("%.2f", g); +} +``` + +#### 25.Java 7中的菱形运算符(<>)有什么意义? + +Java 7中的菱形运算符允许如下代码: + +``` +List list = new LinkedList<>(); +``` + +但是,在Java 5/6中,我可以简单地编写: + +``` +List list = new LinkedList(); +``` + +我对类型擦除的理解是这些完全相同。(无论如何,泛型都会在运行时删除)。 + +``` +List list = new LinkedList(); +``` + +是在左侧,你使用的是通用类型`List`,而在右侧,你使用的是原始类型LinkedList。Java中的原始类型实际上仅存在于与前泛型代码的兼容性,并且除非绝对必要,否则绝对不能在新代码中使用。 + +现在,如果Java从一开始就具有泛型,并且没有LinkedList最初在具有泛型之前创建的类型(例如),则它可能已经做到了,这样泛型类型的构造函数会自动从左侧推断出其类型参数-尽可能在作业的另一侧。但事实并非如此,为了向后兼容,必须对原始类型和泛型类型进行不同的处理。这使得他们需要采取一种稍微不同但同样方便的方式来声明泛型对象的新实例,而不必重复其类型参数……菱形运算符。 + +就你的原始示例而言`List list = new LinkedList()`,编译器会为该分配生成警告,因为它必须这样做。考虑一下: + +``` +List strings = ... // some list that contains some strings + +// Totally legal since you used the raw type and lost all type checking! +List integers = new LinkedList(strings); +``` + +存在泛型以提供编译时保护以防止做错事。在上面的示例中,使用原始类型意味着你没有获得此保护,并且在运行时会收到错误消息。这就是为什么你不应该使用原始类型的原因。 + +``` +// Not legal since the right side is actually generic! +List integers = new LinkedList<>(strings); +``` + +但是,菱形运算符允许将赋值的右侧定义为具有与左侧相同类型参数的真实泛型实例,而不必再次键入这些参数。它使你可以与使用原始类型几乎相同的工作来保持泛型的安全。 + +我认为关键要理解的是,原始类型(不带`<>`)不能与泛型类型相同。声明原始类型时,不会获得任何好处和泛型的类型检查。你还必须记住,泛型是Java语言的通用组成部分 ……它们不仅仅适用于Collections 的无参数构造函数! \ No newline at end of file diff --git "a/Java\345\244\232\347\272\277\347\250\213.md" "b/Java\345\244\232\347\272\277\347\250\213.md" new file mode 100644 index 0000000..81adb9a --- /dev/null +++ "b/Java\345\244\232\347\272\277\347\250\213.md" @@ -0,0 +1,1113 @@ +## Java多线程 + +#### 1.**什么是进程**? + +进程是系统中正在运行的一个程序,程序一旦运行就是进程。 + +进程可以看成程序执行的一个实例。进程是系统资源分配的独立实体,每个进程都拥有独立的地址空间。一个进程无法访问另一个进程的变量和数据结构,如果想让一个进程访问另一个进程的资源,需要使用进程间通信,比如管道,文件,套接字等。 + +#### 2.什么是线程? + +是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 + +#### 3.线程的实现方式? + +1.继承Thread类 + +2.实现Runnable接口 + +3.使用Callable和Future + +#### 4.**Thread 类中的start() 和 run() 方法有什么区别**? + +1.start()方法来启动线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;通过调用Thread类的start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。 +2.run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。 + +#### 5.线程NEW状态 + +new创建一个Thread对象时,并没处于执行状态,因为没有调用start方法启动改线程,那么此时的状态就是新建状态。 + +#### 6.线程RUNNABLE状态 + +线程对象通过start方法进入runnable状态,启动的线程不一定会立即得到执行,线程的运行与否要看cpu的调度,我们把这个中间状态叫可执行状态(RUNNABLE)。 + +#### 7.线程的RUNNING状态 + +一旦cpu通过轮询货其他方式从任务可以执行队列中选中了线程,此时它才能真正的执行自己的逻辑代码。 + +#### 8.线程的BLOCKED状态 + +线程正在等待获取锁。 + +- 进入BLOCKED状态,比如调用了sleep,或者wait方法 +- 进行某个阻塞的io操作,比如因网络数据的读写进入BLOCKED状态 +- 获取某个锁资源,从而加入到该锁的阻塞队列中而进入BLOCKED状态 + +#### 9.线程的TERMINATED状态 + +TERMINATED是一个线程的最终状态,在该状态下线程不会再切换到其他任何状态了,代表整个生命周期都结束了。 + +下面几种情况会进入TERMINATED状态: + +- 线程运行正常结束,结束生命周期 +- 线程运行出错意外结束 +- JVM Crash 导致所有的线程都结束 + +#### 10.线程状态转化图 + +![image-20200501131131886](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200501131131886.png) + +#### 11.i——————与System.out.println()的异常 + +示例代码: + +```java +public class XkThread extends Thread { + + private int i = 5; + + @Override + public void run() { + System.out.println("i=" + (i——————) + " threadName=" + Thread.currentThread().getName()); + } + + public static void main(String[] args) { + XkThread xk = new XkThread(); + Thread t1 = new Thread(xk); + Thread t2 = new Thread(xk); + Thread t3 = new Thread(xk); + Thread t4 = new Thread(xk); + Thread t5 = new Thread(xk); + + t1.start(); + t2.start(); + t3.start(); + t4.start(); + t5.start(); + } +} + +``` + +结果: + +```sqlite +i=5 threadName=Thread-1 +i=2 threadName=Thread-5 +i=5 threadName=Thread-2 +i=4 threadName=Thread-3 +i=3 threadName=Thread-4 +``` + +虽然println()方法在内部是同步的,但i——————的操作却是在进入println()之前发生的,所以有发生非线程安全的概率。 + + println()源码: + +```java + public void println(String x) { + synchronized (this) { + print(x); + newLine(); + } + } +``` + +#### 12.如何知道代码段被哪个线程调用? + +```java + System.out.println(Thread.currentThread().getName()); +``` + +#### 13.线程活动状态? + +```java +public class XKThread extends Thread { + + @Override + public void run() { + System.out.println("run run run is " + this.isAlive() ); + } + + public static void main(String[] args) { + XKThread xk = new XKThread(); + System.out.println("begin ——— " + xk.isAlive()); + xk.start(); + System.out.println("end ————— " + xk.isAlive()); + + } +} + +``` + +#### 14.sleep()方法 + +方法sleep()的作用是在指定的毫秒数内让当前的“正在执行的线程”休眠(暂停执行)。 + +#### 15.如何优雅的设置睡眠时间? + +jdk1.5 后,引入了一个枚举TimeUnit,对sleep方法提供了很好的封装。 + +比如要表达2小时22分55秒899毫秒。 + +```java +Thread.sleep(8575899L); +TimeUnit.HOURS.sleep(3); +TimeUnit.MINUTES.sleep(22); +TimeUnit.SECONDS.sleep(55); +TimeUnit.MILLISECONDS.sleep(899); +``` + +可以看到表达的含义更清晰,更优雅。 + +#### 16.停止线程 + +run方法执行完成,自然终止。 + +stop()方法,suspend()以及resume()都是过期作废方法,使用它们结果不可预期。 + +大多数停止一个线程的操作使用Thread.interrupt()等于说给线程打一个停止的标记, 此方法不回去终止一个正在运行的线程,需要加入一个判断才能可以完成线程的停止。 + +#### 17.interrupted 和 isInterrupted + +interrupted : 判断当前线程是否已经中断,会清除状态。 + +isInterrupted :判断线程是否已经中断,不会清除状态。 + +#### 18.yield + +放弃当前cpu资源,将它让给其他的任务占用cpu执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得cpu时间片。 + +测试代码:(cpu独占时间片) + +```java +public class XKThread extends Thread { + + @Override + public void run() { + long beginTime = System.currentTimeMillis(); + int count = 0; + for (int i = 0; i < 50000000; i++) { + count = count + (i + 1); + } + long endTime = System.currentTimeMillis(); + System.out.println("用时 = " + (endTime - beginTime) + " 毫秒! "); + } + + public static void main(String[] args) { + XKThread xkThread = new XKThread(); + xkThread.start(); + } + +} +``` + +结果: + +```sqlite +用时 = 20 毫秒! +``` + +加入yield,再来测试。(cpu让给其他资源导致速度变慢) + +```java +public class XKThread extends Thread { + + @Override + public void run() { + long beginTime = System.currentTimeMillis(); + int count = 0; + for (int i = 0; i < 50000000; i++) { + Thread.yield(); + count = count + (i + 1); + } + long endTime = System.currentTimeMillis(); + System.out.println("用时 = " + (endTime - beginTime) + " 毫秒! "); + } + + public static void main(String[] args) { + XKThread xkThread = new XKThread(); + xkThread.start(); + } + +} + +``` + +结果: + +```sqlite +用时 = 38424 毫秒! +``` + +#### 19.线程的优先级 + +在操作系统中,线程可以划分优先级,优先级较高的线程得到cpu资源比较多,也就是cpu有限执行优先级较高的线程对象中的任务,但是不能保证一定优先级高,就先执行。 + +Java的优先级分为1~10个等级,数字越大优先级越高,默认优先级大小为5。超出范围则抛出:java.lang.IllegalArgumentException。 + +#### 20.优先级继承特性 + +线程的优先级具有继承性,比如a线程启动b线程,b线程与a优先级是一样的。 + +#### 21.谁跑的更快? + +设置优先级高低两个线程,累加数字,看谁跑的快,上代码。 + +```java +public class Run extends Thread{ + + public static void main(String[] args) { + try { + ThreadLow low = new ThreadLow(); + low.setPriority(2); + low.start(); + + ThreadHigh high = new ThreadHigh(); + high.setPriority(8); + high.start(); + + Thread.sleep(2000); + low.stop(); + high.stop(); + System.out.println("low = " + low.getCount()); + System.out.println("high = " + high.getCount()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + } + +} + +class ThreadHigh extends Thread { + private int count = 0; + + public int getCount() { + return count; + } + + + @Override + public void run() { + while (true) { + count++; + } + } +} + +class ThreadLow extends Thread { + private int count = 0; + + public int getCount() { + return count; + } + + + @Override + public void run() { + while (true) { + count++; + } + } +} +``` + +结果: + +```sqlite +low = 1193854568 +high = 1204372373 + +``` + +#### 22.线程种类 + +Java线程有两种,一种是用户线程,一种是守护线程。 + +#### 23.守护线程的特点 + +守护线程是一个比较特殊的线程,主要被用做程序中后台调度以及支持性工作。当Java虚拟机中不存在非守护线程时,守护线程才会随着JVM一同结束工作。 + +#### 24.Java中典型的守护线程 + +GC(垃圾回收器) + +#### 25.如何设置守护线程 + +Thread.setDaemon(true) + +PS:Daemon属性需要再启动线程之前设置,不能再启动后设置。 + +#### 25.Java虚拟机退出时Daemon线程中的finally块一定会执行? + +Java虚拟机退出时Daemon线程中的finally块并不一定会执行。 + +代码示例: + +```java +public class XKDaemon { + public static void main(String[] args) { + Thread thread = new Thread(new DaemonRunner(),"xkDaemonRunner"); + thread.setDaemon(true); + thread.start(); + + } + + static class DaemonRunner implements Runnable { + + @Override + public void run() { + try { + SleepUtils.sleep(10); + } finally { + System.out.println("Java小咖秀 daemonThread finally run …"); + } + + } + } +} +``` + +结果: + +``` + +``` + + 没有任何的输出,说明没有执行finally。 + +#### 26.设置线程上下文类加载器 + +​ 获取线程上下文类加载器 + +```java +public ClassLoader getContextClassLoader() +``` + +​ 设置线程类加载器(可以打破Java类加载器的父类委托机制) + +```java +public void setContextClassLoader(ClassLoader cl) +``` + +#### 27.join + +join是指把指定的线程加入到当前线程,比如join某个线程a,会让当前线程b进入等待,直到a的生命周期结束,此期间b线程是处于blocked状态。 + +#### 28.什么是synchronized? + +synchronized关键字可以时间一个简单的策略来防止线程干扰和内存一致性错误,如果一个对象是对多个线程可见的,那么对该对想的所有读写都将通过同步的方式来进行。 + +#### 29.synchronized包括哪两个jvm重要的指令? + +monitor enter 和 monitor exit + +#### 30.synchronized关键字用法? + +可以用于对代码块或方法的修饰 + +#### 31.synchronized锁的是什么? + +普通同步方法 —————> 锁的是当前实力对象。 + +静态同步方法—————> 锁的是当前类的Class对象。 + +同步方法快 —————> 锁的是synchonized括号里配置的对象。 + +#### 32.Java对象头 + +synchronized用的锁是存在Java对象头里的。对象如果是数组类型,虚拟机用3个字宽(Word)存储对象头,如果对象是非数组类型,用2字宽存储对象头。 + +Tips:32位虚拟机中一个字宽等于4字节。 + +#### 33.Java对象头长度 + +![image-20200418215447539](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200418215447539.png) + +#### 34.Java对象头的存储结构 + + 32位JVM的Mark Word 默认存储结构 + +![image-20200418220122794](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200418220122794.png) + +#### 35.Mark Word的状态变化 + +Mark Word 存储的数据会随着锁标志为的变化而变化。 + +![image-20200418220322880](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200418220322880.png) + +64位虚拟机下,Mark Word是64bit大小的 + +![image-20200418224055558](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200418224055558.png) + +#### 36.锁的升降级规则 + +Java SE 1.6 为了提高锁的性能。引入了“偏向锁”和轻量级锁“。 + +Java SE 1.6 中锁有4种状态。级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。 + +锁只能升级不能降级。 + +#### 37.偏向锁 + +大多数情况,锁不仅不存在多线程竞争,而且总由同一线程多次获得。当一个线程访问同步块并获取锁时,会在对象头和栈帧中记录存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行 cas操作来加锁和解锁,只需测试一下对象头 Mark Word里是否存储着指向当前线程的偏向锁。如果测试成功,表示线程已经获得了锁,如果失败,则需要测试下Mark Word中偏向锁的标示是否已经设置成1(表示当前时偏向锁),如果没有设置,则使用cas竞争锁,如果设置了,则尝试使用cas将对象头的偏向锁只想当前线程。 + +#### 38.关闭偏向锁延迟 + +java6和7中默认启用,但是会在程序启动几秒后才激活,如果需要关闭延迟, + +-XX:BiasedLockingStartupDelay=0。 + +#### 39.如何关闭偏向锁 + +JVM参数关闭偏向锁:-XX:-UseBiasedLocking=false,那么程序默认会进入轻量级锁状态。 + +Tips:如果你可以确定程序的所有锁通常情况处于竞态,则可以选择关闭。 + +#### 40.轻量级锁 + +线程在执行同步块,jvm会现在当前线程的栈帧中创建用于储存锁记录的空间。并将对象头中的Mark Word复制到锁记录中。然后线程尝试使用cas将对象头中的Mark Word替换为之乡锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。 + +#### 41.轻量锁的解锁 + +轻量锁解锁时,会使原子操作cas将 displaced Mark Word 替换回对象头,如果成功则表示没有竞争发生,如果失败,表示存在竞争,此时锁就会膨胀为重量级锁。 + +#### 42.锁的优缺点对比 + +![image-20200419110938271](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200419110938271.png) + +#### 43.什么是原子操作 + +不可被中断的一个或一系列操作 + +#### 44.Java如何实现原子操作 + +Java中通过锁和循环cas的方式来实现原子操作,JVM的CAS操作利用了处理器提供的CMPXCHG指令来实现的。自旋CAS实现的基本思路就是循环进行CAS操作直到成功为止。 + +#### 45.CAS实现原子操作的3大问题 + +ABA问题,循环时间长消耗资源大,只能保证一个共享变量的原子操作 + +#### 46.什么是ABA问题 + +问题: + +因为cas需要在操作值的时候,检查值有没有变化,如果没有变化则更新,如果一个值原来是A,变成了B,又变成了A,那么使用cas进行检测时会发现发的值没有发生变化,其实是变过的。 + +解决: + +添加版本号,每次更新的时候追加版本号,A-B-A —> 1A-2B-3A。 + +从jdk1.5开始,Atomic包提供了一个类AtomicStampedReference来解决ABA的问题。 + +#### 47.CAS循环时间长占用资源大问题 + +如果jvm能支持处理器提供的pause指令,那么效率会有一定的提升。 + +一、它可以延迟流水线执行指令(de-pipeline),使cpu不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,有些处理器延迟时间是0。 + +二、它可以避免在退出循环的时候因内存顺序冲突而引起的cpu流水线被清空,从而提高cpu执行效率。 + +#### 48.CAS只能保证一个共享变量原子操作 + +一、对多个共享变量操作时,可以用锁。 + +二、可以把多个共享变量合并成一个共享变量来操作。比如,x=1,k=a,合并xk=1a,然后用cas操作xk。 + +Tips:java 1.5开始,jdk提供了AtomicReference类来保证饮用对象之间的原子性,就可以把多个变量放在一个对象来进行cas操作。 + +#### 49.volatile关键字 + +volatile 是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性“。 + +Java语言规范第3版对volatile定义如下,Java允许线程访问共享变量,为了保证共享变量能准确和一致的更新,线程应该确保排它锁单独获得这个变量。如果一个字段被声明为volatile,Java线程内存模型所有线程看到这个变量的值是一致的。 + +#### 50.等待/通知机制 + +一个线程修改了一个对象的值,而另一个线程感知到了变化,然后进行相应的操作。 + +#### ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png)51.wait + +方法wait()的作用是使当前执行代码的线程进行等待,wait()是Object类通用的方法,该方法用来将当前线程置入“预执行队列”中,并在 wait()所在的代码处停止执行,直到接到通知或中断为止。 + +在调用wait之前线程需要获得该对象的对象级别的锁。代码体现上,即只能是同步方法或同步代码块内。调用wait()后当前线程释放锁。 + +#### 52.notify + +notify()也是Object类的通用方法,也要在同步方法或同步代码块内调用,该方法用来通知哪些可能灯光该对象的对象锁的其他线程,如果有多个线程等待,则随机挑选出其中一个呈wait状态的线程,对其发出 通知 notify,并让它等待获取该对象的对象锁。 + +#### 53.notify/notifyAll + +notify等于说将等待队列中的一个线程移动到同步队列中,而notifyAll是将等待队列中的所有线程全部移动到同步队列中。 + +#### 54.等待/通知经典范式 + +等待 + +```java +synchronized(obj) { + while(条件不满足) { + obj.wait(); + } + 执行对应逻辑 +} +``` + +通知 + +```java +synchronized(obj) { + 改变条件 + obj.notifyAll(); +} +``` + +##### 55.ThreadLocal + +主要解决每一个线程想绑定自己的值,存放线程的私有数据。 + +##### 56.ThreadLocal使用 + +获取当前的线程的值通过get(),设置set(T) 方式来设置值。 + +```java +public class XKThreadLocal { + + public static ThreadLocal threadLocal = new ThreadLocal(); + + public static void main(String[] args) { + if (threadLocal.get() == null) { + System.out.println("未设置过值"); + threadLocal.set("Java小咖秀"); + } + System.out.println(threadLocal.get()); + } + +} +``` + +输出: + +```sqlite +未设置过值 +Java小咖秀 +``` + +Tips:默认值为null + +#### 57.解决get()返回null问题 + +通过继承重写initialValue()方法即可。 + +代码实现: + +```java +public class ThreadLocalExt extends ThreadLocal{ + + static ThreadLocalExt threadLocalExt = new ThreadLocalExt(); + + @Override + protected Object initialValue() { + return "Java小咖秀"; + } + + public static void main(String[] args) { + System.out.println(threadLocalExt.get()); + } +} + +``` + +输出结果: + +```java +Java小咖秀 +``` + +#### 58.Lock接口 + +锁可以防止多个线程同时共享资源。Java5前程序是靠synchronized实现锁功能。Java5之后,并发包新增Lock接口来实现锁功能。 + +#### 59.Lock接口提供 synchronized不具备的主要特性 + +![image-20200419193439709](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200419193439709.png) + +#### 60.重入锁 ReentrantLock + +支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁。除此之外,该锁的还支持获取锁时的公平和非公平性选择。 + +#### 61.重进入是什么意思? + +重进入是指任意线程在获取到锁之后能够再次获锁而不被锁阻塞。 + +该特性主要解决以下两个问题: + +一、锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是则再次成功获取。 + +二、所得最终释放。线程重复n次是获取了锁,随后在第n次释放该锁后,其他线程能够获取到该锁。 + +#### 62.ReentrantLock默认锁? + +默认非公平锁 + +代码为证: + +```java + final boolean nonfairTryAcquire(int acquires) { + final Thread current = Thread.currentThread(); + int c = getState(); + if (c == 0) { + if (compareAndSetState(0, acquires)) { + setExclusiveOwnerThread(current); + return true; + } + } + else if (current == getExclusiveOwnerThread()) { + int nextc = c + acquires; + if (nextc < 0) // overflow + throw new Error("Maximum lock count exceeded"); + setState(nextc); + return true; + } + return false; + } +``` + + + +#### 63.公平锁和非公平锁的区别 + +公平性与否针对获取锁来说的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序,也就是FIFO。 + +#### 64.读写锁 + +#### 读写锁允许同一时刻多个读线程访问,但是写线程和其他写线程均被阻塞。读写锁维护一个读锁一个写锁,读写分离,并发性得到了提升。 + +Java中提供读写锁的实现类是ReentrantReadWriteLock。 + +#### 65.LockSupport工具 + +定义了一组公共静态方法,提供了最基本的线程阻塞和唤醒功能。 + +![image-20200419223303672](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200419223303672.png) + +#### 66.Condition接口 + +提供了类似Object监视器方法,与 Lock配合使用实现等待/通知模式。 + +#### 67.Condition使用 + +代码示例: + +```java +public class XKCondition { + Lock lock = new ReentrantLock(); + Condition cd = lock.newCondition(); + + public void await() throws InterruptedException { + lock.lock(); + try { + cd.await();//相当于Object 方法中的wait() + } finally { + lock.unlock(); + } + } + + public void signal() { + lock.lock(); + try { + cd.signal(); //相当于Object 方法中的notify() + } finally { + lock.unlock(); + } + } + +} + +``` + +#### 68.ArrayBlockingQueue? + +一个由数据支持的有界阻塞队列,此队列FIFO原则对元素进行排序。队列头部在队列中存在的时间最长,队列尾部存在时间最短。 + +#### 69.PriorityBlockingQueue? + +一个支持优先级排序的无界阻塞队列,但它不会阻塞数据生产者,而只会在没有可消费的数据时,阻塞数据的消费者。 + +#### 70.DelayQueue? + +是一个支持延时获取元素的使用优先级队列的实现的无界阻塞队列。队列中的元素必须实现Delayed接口和 Comparable接口,在创建元素时可以指定多久才能从队列中获取当前元素。 + +#### 71.Java并发容器,你知道几个? + +ConcurrentHashMap、CopyOnWriteArrayList 、CopyOnWriteArraySet 、ConcurrentLinkedQueue、 + +ConcurrentLinkedDeque、ConcurrentSkipListMap、ConcurrentSkipListSet、ArrayBlockingQueue、 + +LinkedBlockingQueue、LinkedBlockingDeque、PriorityBlockingQueue、SynchronousQueue、 + +LinkedTransferQueue、DelayQueue + +#### 72.ConcurrentHashMap + +并发安全版HashMap,java7中采用分段锁技术来提高并发效率,默认分16段。Java8放弃了分段锁,采用CAS,同时当哈希冲突时,当链表的长度到8时,会转化成红黑树。(如需了解细节,见jdk中代码) + +#### 73.ConcurrentLinkedQueue + +基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。它采用cas算法来实现。(如需了解细节,见jdk中代码) + +#### 74.什么是阻塞队列? + +阻塞队列是一个支持两个附加操作的队列,这两个附加操作支持阻塞的插入和移除方法。 + +1、支持阻塞的插入方法:当队列满时,队列会阻塞插入元素的线程,直到队列不满。 + +2、支持阻塞的移除方法:当队列空时,获取元素的线程会等待队列变为非空。 + +#### 75.阻塞队列常用的应用场景? + +常用于生产者和消费者场景,生产者是往队列里添加元素的线程,消费者是从队列里取元素的线程。阻塞队列正好是生产者存放、消费者来获取的容器。 + +#### 76.Java里的阻塞的队列 + +```sqlite +ArrayBlockingQueue: 数组结构组成的 |有界阻塞队列 +LinkedBlockingQueue: 链表结构组成的|有界阻塞队列 +PriorityBlockingQueue: 支持优先级排序|无界阻塞队列 +DelayQueue: 优先级队列实现|无界阻塞队列 +SynchronousQueue: 不存储元素| 阻塞队列 +LinkedTransferQueue: 链表结构组成|无界阻塞队列 +LinkedBlockingDeque: 链表结构组成|双向阻塞队列 +``` + +#### 77.Fork/Join + +java7提供的一个用于并行执行任务的框架,把一个大任务分割成若干个小任务,最终汇总每个小任务结果的后得到大任务结果的框架。 + +#### 78.工作窃取算法 + +是指某个线程从其他队列里窃取任务来执行。当大任务被分割成小任务时,有的线程可能提前完成任务,此时闲着不如去帮其他没完成工作线程。此时可以去其他队列窃取任务,为了减少竞争,通常使用双端队列,被窃取的线程从头部拿,窃取的线程从尾部拿任务执行。 + +#### 79.工作窃取算法的有缺点 + +优点:充分利用线程进行并行计算,减少了线程间的竞争。 + +缺点:有些情况下还是存在竞争,比如双端队列中只有一个任务。这样就消耗了更多资源。 + +#### 80.Java中原子操作更新基本类型,Atomic包提供了哪几个类? + +AtomicBoolean:原子更新布尔类型 + +AtomicInteger:原子更新整形 + +AtomicLong:原子更新长整形 + +#### 81.Java中原子操作更新数组,Atomic包提供了哪几个类? + +AtomicIntegerArray: 原子更新整形数据里的元素 + +AtomicLongArray: 原子更新长整形数组里的元素 + +AtomicReferenceArray: 原子更新饮用类型数组里的元素 + +AtomicIntegerArray: 主要提供原子方式更新数组里的整形 + +#### 82.Java中原子操作更新引用类型,Atomic包提供了哪几个类? + +如果原子需要更新多个变量,就需要用引用类型了。 + +AtomicReference : 原子更新引用类型 + +AtomicReferenceFieldUpdater: 原子更新引用类型里的字段。 + +AtomicMarkableReference: 原子更新带有标记位的引用类型。标记位用boolean类型表示,构造方法时AtomicMarkableReference(V initialRef,boolean initialMark) + +#### 83.Java中原子操作更新字段类,Atomic包提供了哪几个类? + +AtomiceIntegerFieldUpdater: 原子更新整形字段的更新器 + +AtomiceLongFieldUpdater: 原子更新长整形字段的更新器 + +AtomiceStampedFieldUpdater: 原子更新带有版本号的引用类型,将整数值 + +#### 84.JDK并发包中提供了哪几个比较常见的处理并发的工具类? + +提供并发控制手段: CountDownLatch、CyclicBarrier、Semaphore + +线程间数据交换: Exchanger + +#### 85.CountDownLatch + +允许一个或多个线程等待其他线程完成操作。 + +CountDownLatch的构造函数接受一个int类型的参数作为计数器,你想等待n个点完成,就传入n。 + +两个重要的方法: + +countDown() : 调用时,n会减1。 + +await() : 调用会阻塞当前线程,直到n变成0。 + +await(long time,TimeUnit unit) : 等待特定时间后,就不会继续阻塞当前线程。 + +tips:计数器必须大于等于0,当为0时,await就不会阻塞当前线程。 + +不提供重新初始化或修改内部计数器的值的功能。 + +#### 86.CyclicBarrier + +可循环使用的屏障。 + +让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。 + +CyclicBarrier默认构造放时CyclicBarrier(int parities) ,其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达屏障,然后当前线程被阻塞。 + +#### 87.CountDownLatch与CyclicBarrier区别 + +CountDownLatch: + +计数器:计数器只能使用一次。 + +等待: 一个线程或多个等待另外n个线程完成之后才能执行。 + +CyclicBarrier: + +计数器:计数器可以重置(通过reset()方法)。 + +等待: n个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。 + +#### 88.Semaphore + +用来控制同时访问资源的线程数量,通过协调各个线程,来保证合理的公共资源的访问。 + +应用场景:流量控制,特别是公共资源有限的应用场景,比如数据链接,限流等。 + +#### 89.Exchanger + +Exchanger是一个用于线程间协作的工具类,它提供一个同步点,在这个同步点上,两个线程可以交换彼此的数据。比如第一个线程执行exchange()方法,它会一直等待第二个线程也执行exchange,当两个线程都到同步点,就可以交换数据了。 + +一般来说为了避免一直等待的情况,可以使用exchange(V x,long timeout,TimeUnit unit),设置最大等待时间。 + +Exchanger可以用于遗传算法。 + +#### 90.为什么使用线程池 + +几乎所有需要异步或者并发执行任务的程序都可以使用线程池。合理使用会给我们带来以下好处。 + +- 降低系统消耗:重复利用已经创建的线程降低线程创建和销毁造成的资源消耗。 +- 提高响应速度: 当任务到达时,任务不需要等到线程创建就可以立即执行。 +- 提供线程可以管理性: 可以通过设置合理分配、调优、监控。 + +#### 91.线程池工作流程 + +1、判断核心线程池里的线程是否都有在执行任务,否->创建一个新工作线程来执行任务。是->走下个流程。 + +2、判断工作队列是否已满,否->新任务存储在这个工作队列里,是->走下个流程。 + +3、判断线程池里的线程是否都在工作状态,否->创建一个新的工作线程来执行任务, + +是->走下个流程。 + +4、按照设置的策略来处理无法执行的任务。 + +#### 92.创建线程池参数有哪些,作用? + +```java + public ThreadPoolExecutor( int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue workQueue, + ThreadFactory threadFactory, + RejectedExecutionHandler handler) +``` + +1.corePoolSize:核心线程池大小,当提交一个任务时,线程池会创建一个线程来执行任务,即使其他空闲的核心线程能够执行新任务也会创建,等待需要执行的任务数大于线程核心大小就不会继续创建。 + +2.maximumPoolSize:线程池最大数,允许创建的最大线程数,如果队列满了,并且已经创建的线程数小于最大线程数,则会创建新的线程执行任务。如果是无界队列,这个参数基本没用。 + +3.keepAliveTime: 线程保持活动时间,线程池工作线程空闲后,保持存活的时间,所以如果任务很多,并且每个任务执行时间较短,可以调大时间,提高线程利用率。 + +4.unit: 线程保持活动时间单位,天(DAYS)、小时(HOURS)、分钟(MINUTES、毫秒MILLISECONDS)、微秒(MICROSECONDS)、纳秒(NANOSECONDS) + +5.workQueue: 任务队列,保存等待执行的任务的阻塞队列。 + +一般来说可以选择如下阻塞队列: + +ArrayBlockingQueue:基于数组的有界阻塞队列。 + +LinkedBlockingQueue:基于链表的阻塞队列。 + +SynchronizedQueue:一个不存储元素的阻塞队列。 + +PriorityBlockingQueue:一个具有优先级的阻塞队列。 + +6.threadFactory:设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。 + +7. handler: 饱和策略也叫拒绝策略。当队列和线程池都满了,即达到饱和状态。所以需要采取策略来处理新的任务。默认策略是AbortPolicy。 + + AbortPolicy:直接抛出异常。 + + CallerRunsPolicy: 调用者所在的线程来运行任务。 + + DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。 + + DiscardPolicy:不处理,直接丢掉。 + + 当然可以根据自己的应用场景,实现RejectedExecutionHandler接口自定义策略。 + +#### 93.向线程池提交任务 + +可以使用execute()和submit() 两种方式提交任务。 + +execute():无返回值,所以无法判断任务是否被执行成功。 + +submit():用于提交需要有返回值的任务。线程池返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()来获取返回值,get()方法会阻塞当前线程知道任务完成。get(long timeout,TimeUnit unit)可以设置超市时间。 + +#### 94.关闭线程池 + +可以通过shutdown()或shutdownNow()来关闭线程池。它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt来中断线程,所以无法响应终端的任务可以能永远无法停止。 + +shutdownNow首先将线程池状态设置成STOP,然后尝试停止所有的正在执行或者暂停的线程,并返回等待执行任务的列表。 + +shutdown只是将线程池的状态设置成shutdown状态,然后中断所有没有正在执行任务的线程。 + +只要调用两者之一,isShutdown就会返回true,当所有任务都已关闭,isTerminaed就会返回true。 + +一般来说调用shutdown方法来关闭线程池,如果任务不一定要执行完,可以直接调用shutdownNow方法。 + +#### 95.线程池如何合理设置 + +配置线程池可以从以下几个方面考虑。 + +- 任务是cpu密集型、IO密集型或者混合型 + +- 任务优先级,高中低。 + +- 任务时间执行长短。 + +- 任务依赖性:是否依赖其他系统资源。 + + cpu密集型可以配置可能小的线程,比如 n + 1个线程。 + + io密集型可以配置较多的线程,如 2n个线程。 + + 混合型可以拆成io密集型任务和cpu密集型任务, + + 如果两个任务执行时间相差大,否->分解后执行吞吐量将高于串行执行吞吐量。 + + 否->没必要分解。 + + 可以通过Runtime.getRuntime().availableProcessors()来获取cpu个数。 + + 建议使用有界队列,增加系统的预警能力和稳定性。 + +#### 96.Executor + +从JDK5开始,把工作单元和执行机制分开。工作单元包括Runnable和Callable,而执行机制由Executor框架提供。 + +#### 97.Executor框架的主要成员 + +ThreadPoolExecutor :可以通过工厂类Executors来创建。 + +可以创建3种类型的ThreadPoolExecutor:SingleThreadExecutor、FixedThreadPool、CachedThreadPool。 + +ScheduledThreadPoolExecutor :可以通过工厂类Executors来创建。 + +可以创建2中类型的ScheduledThreadPoolExecutor:ScheduledThreadPoolExecutor、SingleThreadScheduledExecutor + +Future接口:Future和实现Future接口的FutureTask类来表示异步计算的结果。 + +Runnable和Callable:它们的接口实现类都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行。Runnable不能返回结果,Callable可以返回结果。 + +#### 98.FixedThreadPool + +可重用固定线程数的线程池。 + +查看源码: + +```java +public static ExecutorService newFixedThreadPool(int nThreads) { + return new ThreadPoolExecutor(nThreads, nThreads, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue());} +``` + +corePoolSize 和maxPoolSize都被设置成我们设置的nThreads。 + +当线程池中的线程数大于corePoolSize ,keepAliveTime为多余的空闲线程等待新任务的最长时间,超过这个时间后多余的线程将被终止,如果设为0,表示多余的空闲线程会立即终止。 + +工作流程: + +1.当前线程少于corePoolSize,创建新线程执行任务。 + +2.当前运行线程等于corePoolSize,将任务加入LinkedBlockingQueue。 + +3.线程执行完1中的任务,会循环反复从LinkedBlockingQueue获取任务来执行。 + +LinkedBlockingQueue作为线程池工作队列(默认容量Integer.MAX_VALUE)。因此可能会造成如下赢下。 + +1.当线程数等于corePoolSize时,新任务将在队列中等待,因为线程池中的线程不会超过corePoolSize。 + +2.maxnumPoolSize等于说是一个无效参数。 + +3.keepAliveTime等于说也是一个无效参数。 + +4.运行中的FixedThreadPool(未执行shundown或shundownNow))则不会调用拒绝策略。 + +5.由于任务可以不停的加到队列,当任务越来越多时很容易造成OOM。 + +#### 99.SingleThreadExecutor + +是使用单个worker线程的Executor。 + +查看源码: + +```java +public static ExecutorService newSingleThreadExecutor() { + return new FinalizableDelegatedExecutorService + (new ThreadPoolExecutor(1, 1, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue())); + } +``` + +corePoolSize和maxnumPoolSize被设置为1。其他参数和FixedThreadPool相同。 + +执行流程以及造成的影响同FixedThreadPool. + +#### 100.CachedThreadPool + +根据需要创建新线程的线程池。 + +查看源码: + +```java + public static ExecutorService newCachedThreadPool() { + return new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 60L, TimeUnit.SECONDS, + new SynchronousQueue()); +``` + +corePoolSize设置为0,maxmumPoolSize为Integer.MAX_VALUE。keepAliveTime为60秒。 + +工作流程: + +1.首先执行SynchronousQueue.offer (Runnable task)。如果当前maximumPool 中有空闲线程正在执行S ynchronousQueue.poll(keepAliveTIme,TimeUnit.NANOSECONDS),那么主线程执行offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行,execute方 法执行完成;否则执行下面的步骤2。 + +2. 当初始maximumPool为空或者maximumPool中当前没有空闲线程时,将没有线程执行 SynchronousQueue.poll (keepAliveTime,TimeUnit.NANOSECONDS)。这种情况下,步骤 1将失 败。此时CachedThreadPool会创建一个新线程执行任务,execute()方法执行完成。 + +3.在步骤2中新创建的线程将任务执行完后,会执行SynchronousQueue.poll (keepAliveTime,TimeUnit.NANOSECONDS)。这个poll操作会让空闲线程最多在SynchronousQueue中等待60秒钟。如果60秒钟内主线程提交了一个新任务(主线程执行步骤1),那么这个空闲线程将执行主线程提交的新任务;否则,这个空闲线程将终止。由于空闲60秒的空闲线程会被终止,因此长时间保持空闲的CachedThreadPool不会使用任何资源。 + +一般来说它适合处理时间短、大量的任务。 + +参考: + +- 《Java多线程编程核心技术》 + +- 《Java高并发编程详解》 + +- 《Java 并发编程的艺术》 + + ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) + diff --git "a/Java\351\233\206\345\220\210.md" "b/Java\351\233\206\345\220\210.md" new file mode 100644 index 0000000..c913ba2 --- /dev/null +++ "b/Java\351\233\206\345\220\210.md" @@ -0,0 +1,676 @@ +## java集合 + +![image-20200414114452792](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414114452792.png) + + + +#### 1.**Java集合框架的基础接口有哪些?** + +Collection为集合层级的根接口。一个集合代表一组对象,这些对象即为它的元素。Java平台不提供这个接口任何直接的实现。 + +Set是一个不能包含重复元素的集合。这个接口对数学集合抽象进行建模,被用来代表集合,就如一副牌。 + +List是一个有序集合,可以包含重复元素。你可以通过它的索引来访问任何元素。List更像长度动态变换的数组。 + +Map是一个将key映射到value的对象.一个Map不能包含重复的key:每个key最多只能映射一个value。 + +一些其它的接口有Queue、Dequeue、SortedSet、SortedMap和ListIterator。 + +#### 2.Collection 和 Collections 有什么区别? + +- Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如 List、Set 等。 +- Collections 是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供的排序方法: Collections. sort(list)。 + +#### 3.List、Set、Map是否继承自Collection接口? + +List、Set 是,Map 不是。Map是键值对映射容器,与List和Set有明显的区别,而Set存储的零散的元素且不允许有重复元素(数学中的集合也是如此),List是线性结构的容器,适用于按数值索引访问元素的情形。 + +#### 4.**Collections.sort排序内部原理** + +在Java 6中Arrays.sort()和Collections.sort()使用的是MergeSort,而在Java 7中,内部实现换成了TimSort,其对对象间比较的实现要求更加严格。 + +#### 5.List、Set、Map 之间的区别是什么? + +List、Set、Map 的区别主要体现在两个方面:元素是否有序、是否允许元素重复。 + +三者之间的区别,如下表: + +![image-20200414130713655](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414130713655.png) + +#### 6.HashMap 和 Hashtable 有什么区别? + +(1)HashMap允许key和value为null,而HashTable不允许。 + +(2)HashTable是同步的,而HashMap不是。所以HashMap适合单线程环境,HashTable适合多线程环境。 + +(3)在Java1.4中引入了LinkedHashMap,HashMap的一个子类,假如你想要遍历顺序,你很容易从HashMap转向LinkedHashMap,但是HashTable不是这样的,它的顺序是不可预知的。 + +(4)HashMap提供对key的Set进行遍历,因此它是fail-fast的,但HashTable提供对key的Enumeration进行遍历,它不支持fail-fast。 + +(5)HashTable被认为是个遗留的类,如果你寻求在迭代的时候修改Map,你应该使用CocurrentHashMap。 + +#### 7.如何决定使用 HashMap 还是 TreeMap? + +对于在 Map 中插入、删除、定位一个元素这类操作,HashMap 是最好的选择,因为相对而言 HashMap 的插入会更快,但如果你要对一个 key 集合进行有序的遍历,那 TreeMap 是更好的选择。 + +#### 8.说一下 HashMap 的实现原理? + +HashMap 基于 Hash 算法实现的,我们通过 put(key,value)存储,get(key)来获取。当传入 key 时,HashMap 会根据 key. hashCode() 计算出 hash 值,根据 hash 值将 value 保存在 bucket 里。当计算出的 hash 值相同时,我们称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。当 hash 冲突的个数比较少时,使用链表否则使用红黑树。 + +#### 9.说一下 HashSet 的实现原理? + +HashSet 是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值。 + +#### 10.ArrayList 和 LinkedList 的区别是什么? + +- 数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。 + +- 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。 + +- 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。 + + 综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。 + +#### 11.**为何Map接口不继承Collection接口?** + +尽管Map接口和它的实现也是集合框架的一部分,但Map不是集合,集合也不是Map。因此,Map继承Collection毫无意义,反之亦然。 + +如果Map继承Collection接口,那么元素去哪儿?Map包含key-value对,它提供抽取key或value列表集合的方法,但是它不适合“一组对象”规范。 + +#### 12.**ArrayList和Vector有何异同点?** + +ArrayList和Vector在很多时候都很类似。 + +(1)两者都是基于索引的,内部由一个数组支持。 + +(2)两者维护插入的顺序,我们可以根据插入顺序来获取元素。 + +(3)ArrayList和Vector的迭代器实现都是fail-fast的。 + +(4)ArrayList和Vector两者允许null值,也可以使用索引值对元素进行随机访问。 + +以下是ArrayList和Vector的不同点。 + +(1)Vector是同步的,而ArrayList不是。然而,如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWriteArrayList。 + +(2)ArrayList比Vector快,它因为有同步,不会过载。 + +(3)ArrayList更加通用,因为我们可以使用Collections工具类轻易地获取同步列表和只读列表。 + +#### 13.Array 和 ArrayList 有何区别? + +- Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。 +- Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。 +- Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。 + +#### 14.在 Queue 中 poll()和 remove()有什么区别? + +- 相同点:都是返回第一个元素,并在队列中删除返回的对象。 +- 不同点:如果没有元素 poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 异常。 + +代码示例: + +``` +Queue queue = new LinkedList(); +queue. offer("string"); // add +System. out. println(queue. poll()); +System. out. println(queue. remove()); +System. out. println(queue. size()); +``` + +#### 15.LinkedHashMap有什么特点? + +LinkedHashMap是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。 + +#### 16.HashMap的底层实现原理?(高频问题) + +从结构实现来讲,HashMap是数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下如所示。 + +![image-20200414150006324](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414150006324.png) + +这里需要讲明白两个问题:数据底层具体存储的是什么?这样的存储方式有什么优点呢? + +(1) 从源码可知,HashMap类中有一个非常重要的字段,就是 Node[] table,即哈希桶数组,明显它是一个Node的数组。我们来看Node[JDK1.8]是何物。 + +``` +static class Node implements Map.Entry { + final int hash; //用来定位数组索引位置 + final K key; + V value; + Node next; //链表的下一个node + + Node(int hash, K key, V value, Node next) { … } + public final K getKey(){ … } + public final V getValue() { … } + public final String toString() { … } + public final int hashCode() { … } + public final V setValue(V newValue) { … } + public final boolean equals(Object o) { … } +} +``` + +Node是HashMap的一个内部类,实现了Map.Entry接口,本质是就是一个映射(键值对)。上图中的每个黑色圆点就是一个Node对象。 + +(2) HashMap就是使用哈希表来存储的。哈希表为解决冲突,可以采用开放地址法和链地址法等来解决问题,Java中HashMap采用了链地址法。链地址法,简单来说,就是数组加链表的结合。在每个数组元素上都一个链表结构,当数据被Hash后,得到数组下标,把数据放在对应下标元素的链表上。例如程序执行下面代码: + +``` + map.put("美团","小美"); +``` + +系统将调用"美团"这个key的hashCode()方法得到其hashCode 值(该方法适用于每个Java对象),然后再通过Hash算法的后两步运算(高位运算和取模运算,下文有介绍)来定位该键值对的存储位置,有时两个key会定位到相同的位置,表示发生了Hash碰撞。当然Hash算法计算结果越分散均匀,Hash碰撞的概率就越小,map的存取效率就会越高。 + +如果哈希桶数组很大,即使较差的Hash算法也会比较分散,如果哈希桶数组数组很小,即使好的Hash算法也会出现较多碰撞,所以就需要在空间成本和时间成本之间权衡,其实就是在根据实际情况确定哈希桶数组的大小,并在此基础上设计好的hash算法减少Hash碰撞。那么通过什么方式来控制map使得Hash碰撞的概率又小,哈希桶数组(Node[] table)占用空间又少呢?答案就是好的Hash算法和扩容机制。 + +在理解Hash和扩容流程之前,我们得先了解下HashMap的几个字段。从HashMap的默认构造函数源码可知,构造函数就是对下面几个字段进行初始化,源码如下: + +``` + int threshold; // 所能容纳的key-value对极限 + final float loadFactor; // 负载因子 + int modCount; + int size; +``` + +首先,Node[] table的初始化长度length(默认值是16),Load factor为负载因子(默认值是0.75),threshold是HashMap所能容纳的最大数据量的Node(键值对)个数。threshold = length * Load factor。也就是说,在数组定义好长度之后,负载因子越大,所能容纳的键值对个数越多。 + +结合负载因子的定义公式可知,threshold就是在此Load factor和length(数组长度)对应下允许的最大元素数目,超过这个数目就重新resize(扩容),扩容后的HashMap容量是之前容量的两倍。默认的负载因子0.75是对空间和时间效率的一个平衡选择,建议大家不要修改,除非在时间和空间比较特殊的情况下,如果内存空间很多而又对时间效率要求很高,可以降低负载因子Load factor的值;相反,如果内存空间紧张而对时间效率要求不高,可以增加负载因子loadFactor的值,这个值可以大于1。 + +size这个字段其实很好理解,就是HashMap中实际存在的键值对数量。注意和table的长度length、容纳最大键值对数量threshold的区别。而modCount字段主要用来记录HashMap内部结构发生变化的次数,主要用于迭代的快速失败。强调一点,内部结构发生变化指的是结构发生变化,例如put新键值对,但是某个key对应的value值被覆盖不属于结构变化。 + +在HashMap中,哈希桶数组table的长度length大小必须为2的n次方(一定是合数),这是一种非常规的设计,常规的设计是把桶的大小设计为素数。相对来说素数导致冲突的概率要小于合数,具体证明可以参考[http://blog.csdn.net/liuqiyao_01/article/details/14475159](https://link.zhihu.com/?target=http%3A//blog.csdn.net/liuqiyao_01/article/details/14475159),Hashtable初始化桶大小为11,就是桶大小设计为素数的应用(Hashtable扩容后不能保证还是素数)。HashMap采用这种非常规设计,主要是为了在取模和扩容时做优化,同时为了减少冲突,HashMap定位哈希桶索引位置时,也加入了高位参与运算的过程。 + +这里存在一个问题,即使负载因子和Hash算法设计的再合理,也免不了会出现拉链过长的情况,一旦出现拉链过长,则会严重影响HashMap的性能。于是,在JDK1.8版本中,对数据结构做了进一步的优化,引入了红黑树。而当链表长度太长(默认超过8)时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能,其中会用到红黑树的插入、删除、查找等算法。 + +功能实现-方法 + +HashMap的内部功能实现很多,本文主要从根据key获取哈希桶数组索引位置、put方法的详细执行、扩容过程三个具有代表性的点深入展开讲解。 + +1.确定哈希桶数组索引位置 + +不管增加、删除、查找键值对,定位到哈希桶数组的位置都是很关键的第一步。前面说过HashMap的数据结构是数组和链表的结合,所以我们当然希望这个HashMap里面的元素位置尽量分布均匀些,尽量使得每个位置上的元素数量只有一个,那么当我们用hash算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,不用遍历链表,大大优化了查询的效率。HashMap定位数组索引位置,直接决定了hash方法的离散性能。先看看源码的实现(方法一+方法二): + +``` +方法一: +static final int hash(Object key) { //jdk1.8 & jdk1.7 + int h; + // h = key.hashCode() 为第一步 取hashCode值 + // h ^ (h >>> 16) 为第二步 高位参与运算 + return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); +} +方法二: +static int indexFor(int h, int length) { //jdk1.7的源码,jdk1.8没有这个方法,但是实现原理一样的 + return h & (length-1); //第三步 取模运算 +} +``` + +这里的Hash算法本质上就是三步:**取key的hashCode值、高位运算、取模运算**。 + +对于任意给定的对象,只要它的hashCode()返回值相同,那么程序调用方法一所计算得到的Hash码值总是相同的。我们首先想到的就是把hash值对数组长度取模运算,这样一来,元素的分布相对来说是比较均匀的。但是,模运算的消耗还是比较大的,在HashMap中是这样做的:调用方法二来计算该对象应该保存在table数组的哪个索引处。 + +这个方法非常巧妙,它通过h & (table.length -1)来得到该对象的保存位,而HashMap底层数组的长度总是2的n次方,这是HashMap在速度上的优化。当length总是2的n次方时,h& (length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。 + +在JDK1.8的实现中,优化了高位运算的算法,通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16),主要是从速度、功效、质量来考虑的,这么做可以在数组table的length比较小的时候,也能保证考虑到高低Bit都参与到Hash的计算中,同时不会有太大的开销。 + +下面举例说明下,n为table的长度。 + +![image-20200414151128860](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414151128860.png) + +2.HashMap的put方法 + +HashMap的put方法执行过程可以通过下图来理解,自己有兴趣可以去对比源码更清楚地研究学习。 + +![image-20200414151233037](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414151233037.png) + +①.判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容; + +②.根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向⑥,如果table[i]不为空,转向③; + +③.判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的是hashCode以及equals; + +④.判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向⑤; + +⑤.遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可; + +⑥.插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容。 + +JDK1.8HashMap的put方法源码如下: + +``` +public V put(K key, V value) { + 2 // 对key的hashCode()做hash + 3 return putVal(hash(key), key, value, false, true); + 4 } + 5 + 6 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, + 7 boolean evict) { + 8 Node[] tab; Node p; int n, i; + 9 // 步骤①:tab为空则创建 +10 if ((tab = table) == null || (n = tab.length) == 0) +11 n = (tab = resize()).length; +12 // 步骤②:计算index,并对null做处理 +13 if ((p = tab[i = (n - 1) & hash]) == null) +14 tab[i] = newNode(hash, key, value, null); +15 else { +16 Node e; K k; +17 // 步骤③:节点key存在,直接覆盖value +18 if (p.hash == hash && +19 ((k = p.key) == key || (key != null && key.equals(k)))) +20 e = p; +21 // 步骤④:判断该链为红黑树 +22 else if (p instanceof TreeNode) +23 e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); +24 // 步骤⑤:该链为链表 +25 else { +26 for (int binCount = 0; ; ++binCount) { +27 if ((e = p.next) == null) { +28 p.next = newNode(hash, key,value,null); + //链表长度大于8转换为红黑树进行处理 +29 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st +30 treeifyBin(tab, hash); +31 break; +32 } + // key已经存在直接覆盖value +33 if (e.hash == hash && +34 ((k = e.key) == key || (key != null && key.equals(k)))) break; +36 p = e; +37 } +38 } +39 +40 if (e != null) { // existing mapping for key +41 V oldValue = e.value; +42 if (!onlyIfAbsent || oldValue == null) +43 e.value = value; +44 afterNodeAccess(e); +45 return oldValue; +46 } +47 } + +48 ++modCount; +49 // 步骤⑥:超过最大容量 就扩容 +50 if (++size > threshold) +51 resize(); +52 afterNodeInsertion(evict); +53 return null; +54 } + +``` + +3.扩容机制 + +扩容(resize)就是重新计算容量,向HashMap对象里不停的添加元素,而HashMap对象内部的数组无法装载更多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。当然Java里的数组是无法自动扩容的,方法是使用一个新的数组代替已有的容量小的数组,就像我们用一个小桶装水,如果想装更多的水,就得换大水桶。 + +我们分析下resize的源码,鉴于JDK1.8融入了红黑树,较复杂,为了便于理解我们仍然使用JDK1.7的代码,好理解一些,本质上区别不大,具体区别后文再说。 + +``` + 1 void resize(int newCapacity) { //传入新的容量 + 2 Entry[] oldTable = table; //引用扩容前的Entry数组 + 3 int oldCapacity = oldTable.length; + 4 if (oldCapacity == MAXIMUM_CAPACITY) { //扩容前的数组大小如果已经达到最大(2^30)了 + 5 threshold = Integer.MAX_VALUE; //修改阈值为int的最大值(2^31-1),这样以后就不会扩容了 + 6 return; + 7 } + 8 + 9 Entry[] newTable = new Entry[newCapacity]; //初始化一个新的Entry数组 +10 transfer(newTable); //!!将数据转移到新的Entry数组里 +11 table = newTable; //HashMap的table属性引用新的Entry数组 +12 threshold = (int)(newCapacity * loadFactor);//修改阈值 +13 } +``` + +这里就是使用一个容量更大的数组来代替已有的容量小的数组,transfer()方法将原有Entry数组的元素拷贝到新的Entry数组里。 + +``` + 1 void transfer(Entry[] newTable) { + 2 Entry[] src = table; //src引用了旧的Entry数组 + 3 int newCapacity = newTable.length; + 4 for (int j = 0; j < src.length; j++) { //遍历旧的Entry数组 + 5 Entry e = src[j]; //取得旧Entry数组的每个元素 + 6 if (e != null) { + 7 src[j] = null;//释放旧Entry数组的对象引用(for循环后,旧的Entry数组不再引用任何对象) + 8 do { + 9 Entry next = e.next; +10 int i = indexFor(e.hash, newCapacity); //!!重新计算每个元素在数组中的位置 +11 e.next = newTable[i]; //标记[1] +12 newTable[i] = e; //将元素放在数组上 +13 e = next; //访问下一个Entry链上的元素 +14 } while (e != null); +15 } +16 } +17 } +``` + +newTable[i]的引用赋给了e.next,也就是使用了单链表的头插入方式,同一位置上新元素总会被放在链表的头部位置;这样先放在一个索引上的元素终会被放到Entry链的尾部(如果发生了hash冲突的话),这一点和Jdk1.8有区别,下文详解。在旧数组中同一条Entry链上的元素,通过重新计算索引位置后,有可能被放到了新数组的不同位置上。 + +下面举个例子说明下扩容过程。假设了我们的hash算法就是简单的用key mod 一下表的大小(也就是数组的长度)。其中的哈希桶数组table的size=2, 所以key = 3、7、5,put顺序依次为 5、7、3。在mod 2以后都冲突在table[1]这里了。这里假设负载因子 loadFactor=1,即当键值对的实际大小size 大于 table的实际大小时进行扩容。接下来的三个步骤是哈希桶数组 resize成4,然后所有的Node重新rehash的过程。 + +![image-20200414151607099](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414151607099.png) + +下面我们讲解下JDK1.8做了哪些优化。经过观测可以发现,我们使用的是2次幂的扩展(指长度扩为原来2倍),所以,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置。看下图可以明白这句话的意思,n为table的长度,图(a)表示扩容前的key1和key2两种key确定索引位置的示例,图(b)表示扩容后key1和key2两种key确定索引位置的示例,其中hash1是key1对应的哈希与高位运算结果。 + +![image-20200414151625660](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414151625660.png) + +元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit(红色),因此新的index就会发生这样的变化: + +![img](https://pic2.zhimg.com/80/b2cb057773e3d67976c535d6ef547d51_1440w.png) + +因此,我们在扩充HashMap的时候,不需要像JDK1.7的实现那样重新计算hash,只需要看看原来的hash值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“原索引+oldCap”,可以看看下图为16扩充为32的resize示意图: + +![image-20200414151745341](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414151745341.png) + +这个设计确实非常的巧妙,既省去了重新计算hash值的时间,而且同时,由于新增的1bit是0还是1可以认为是随机的,因此resize的过程,均匀的把之前的冲突的节点分散到新的bucket了。这一块就是JDK1.8新增的优化点。有一点注意区别,JDK1.7中rehash的时候,旧链表迁移新链表的时候,如果在新表的数组索引位置相同,则链表元素会倒置,但是从上图可以看出,JDK1.8不会倒置。有兴趣的同学可以研究下JDK1.8的resize源码,写的很赞,如下: + +``` +1 final Node[] resize() { + 2 Node[] oldTab = table; + 3 int oldCap = (oldTab == null) ? 0 : oldTab.length; + 4 int oldThr = threshold; + 5 int newCap, newThr = 0; + 6 if (oldCap > 0) { + 7 // 超过最大值就不再扩充了,就只好随你碰撞去吧 + 8 if (oldCap >= MAXIMUM_CAPACITY) { + 9 threshold = Integer.MAX_VALUE; +10 return oldTab; +11 } +12 // 没超过最大值,就扩充为原来的2倍 +13 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && +14 oldCap >= DEFAULT_INITIAL_CAPACITY) +15 newThr = oldThr << 1; // double threshold +16 } +17 else if (oldThr > 0) // initial capacity was placed in threshold +18 newCap = oldThr; +19 else { // zero initial threshold signifies using defaults +20 newCap = DEFAULT_INITIAL_CAPACITY; +21 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); +22 } +23 // 计算新的resize上限 +24 if (newThr == 0) { +25 +26 float ft = (float)newCap * loadFactor; +27 newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? +28 (int)ft : Integer.MAX_VALUE); +29 } +30 threshold = newThr; +31 @SuppressWarnings({"rawtypes","unchecked"}) +32 Node[] newTab = (Node[])new Node[newCap]; +33 table = newTab; +34 if (oldTab != null) { +35 // 把每个bucket都移动到新的buckets中 +36 for (int j = 0; j < oldCap; ++j) { +37 Node e; +38 if ((e = oldTab[j]) != null) { +39 oldTab[j] = null; +40 if (e.next == null) +41 newTab[e.hash & (newCap - 1)] = e; +42 else if (e instanceof TreeNode) +43 ((TreeNode)e).split(this, newTab, j, oldCap); +44 else { // 链表优化重hash的代码块 +45 Node loHead = null, loTail = null; +46 Node hiHead = null, hiTail = null; +47 Node next; +48 do { +49 next = e.next; +50 // 原索引 +51 if ((e.hash & oldCap) == 0) { +52 if (loTail == null) +53 loHead = e; +54 else +55 loTail.next = e; +56 loTail = e; +57 } +58 // 原索引+oldCap +59 else { +60 if (hiTail == null) +61 hiHead = e; +62 else +63 hiTail.next = e; +64 hiTail = e; +65 } +66 } while ((e = next) != null); +67 // 原索引放到bucket里 +68 if (loTail != null) { +69 loTail.next = null; +70 newTab[j] = loHead; +71 } +72 // 原索引+oldCap放到bucket里 +73 if (hiTail != null) { +74 hiTail.next = null; +75 newTab[j + oldCap] = hiHead; +76 } +77 } +78 } +79 } +80 } +81 return newTab; +82 } + +``` + +#### 17.HashMap并发安全的问题 + +并发的多线程使用场景中使用HashMap可能造成死循环。代码例子如下(便于理解,仍然使用JDK1.7的环境): + +``` +public class HashMapInfiniteLoop { + + private static HashMap map = new HashMap(2,0.75f); + public static void main(String[] args) { + map.put(5, "C"); + + new Thread("Thread1") { + public void run() { + map.put(7, "B"); + System.out.println(map); + }; + }.start(); + new Thread("Thread2") { + public void run() { + map.put(3, "A); + System.out.println(map); + }; + }.start(); + } +} +``` + +其中,map初始化为一个长度为2的数组,loadFactor=0.75,threshold=2*0.75=1,也就是说当put第二个key的时候,map就需要进行resize。 + +通过设置断点让线程1和线程2同时debug到transfer方法(3.3小节代码块)的首行。注意此时两个线程已经成功添加数据。放开thread1的断点至transfer方法的“Entry next = e.next;” 这一行;然后放开线程2的的断点,让线程2进行resize。结果如下图。 + +![img](https://pic4.zhimg.com/80/fa10635a66de637fe3cbd894882ff0c7_1440w.png) + +注意,Thread1的 e 指向了key(3),而next指向了key(7),其在线程二rehash后,指向了线程二重组后的链表。 + +线程一被调度回来执行,先是执行 newTalbe[i] = e, 然后是e = next,导致了e指向了key(7),而下一次循环的next = e.next导致了next指向了key(3)。 + +![img](https://pic4.zhimg.com/80/d39d7eff6e8e04f98f5b53bebe2d4d7f_1440w.png) + +e.next = newTable[i] 导致 key(3).next 指向了 key(7)。注意:此时的key(7).next 已经指向了key(3), 环形链表就这样出现了。 + +![img](https://pic2.zhimg.com/80/5f3cf5300f041c771a736b40590fd7b1_1440w.png) + +于是,当我们用线程一调用map.get(11)时,悲剧就出现了——Infinite Loop。 + +#### 18.**JDK1.8与JDK1.7的性能对比** + +HashMap中,如果key经过hash算法得出的数组索引位置全部不相同,即Hash算法非常好,那样的话,getKey方法的时间复杂度就是O(1),如果Hash算法技术的结果碰撞非常多,假如Hash算极其差,所有的Hash算法结果得出的索引位置一样,那样所有的键值对都集中到一个桶中,或者在一个链表中,或者在一个红黑树中,时间复杂度分别为O(n)和O(lgn)。 鉴于JDK1.8做了多方面的优化,总体性能优于JDK1.7,下面我们从两个方面用例子证明这一点。 + +Hash较均匀的情况 + +为了便于测试,我们先写一个类Key,如下: + +``` +class Key implements Comparable { + + private final int value; + + Key(int value) { + this.value = value; + } + + @Override + public int compareTo(Key o) { + return Integer.compare(this.value, o.value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) + return false; + Key key = (Key) o; + return value == key.value; + } + + @Override + public int hashCode() { + return value; + } +} +``` + +这个类复写了equals方法,并且提供了相当好的hashCode函数,任何一个值的hashCode都不会相同,因为直接使用value当做hashcode。为了避免频繁的GC,我将不变的Key实例缓存了起来,而不是一遍一遍的创建它们。代码如下: + +``` +public class Keys { + + public static final int MAX_KEY = 10_000_000; + private static final Key[] KEYS_CACHE = new Key[MAX_KEY]; + + static { + for (int i = 0; i < MAX_KEY; ++i) { + KEYS_CACHE[i] = new Key(i); + } + } + + public static Key of(int value) { + return KEYS_CACHE[value]; + } +} +``` + +现在开始我们的试验,测试需要做的仅仅是,创建不同size的HashMap(1、10、100、……10000000),屏蔽了扩容的情况,代码如下: + +``` +static void test(int mapSize) { + + HashMap map = new HashMap(mapSize); + for (int i = 0; i < mapSize; ++i) { + map.put(Keys.of(i), i); + } + + long beginTime = System.nanoTime(); //获取纳秒 + for (int i = 0; i < mapSize; i++) { + map.get(Keys.of(i)); + } + long endTime = System.nanoTime(); + System.out.println(endTime - beginTime); + } + + public static void main(String[] args) { + for(int i=10;i<= 1000 0000;i*= 10){ + test(i); + } + } + +``` + +在测试中会查找不同的值,然后度量花费的时间,为了计算getKey的平均时间,我们遍历所有的get方法,计算总的时间,除以key的数量,计算一个平均值,主要用来比较,绝对值可能会受很多环境因素的影响。结果如下: + +![img](https://pic3.zhimg.com/80/2fd11382221dfa34ab61fbee11adba3a_1440w.png) + +通过观测测试结果可知,JDK1.8的性能要高于JDK1.7 15%以上,在某些size的区域上,甚至高于100%。由于Hash算法较均匀,JDK1.8引入的红黑树效果不明显,下面我们看看Hash不均匀的的情况。 + +Hash极不均匀的情况 + +假设我们又一个非常差的Key,它们所有的实例都返回相同的hashCode值。这是使用HashMap最坏的情况。代码修改如下: + +``` +class Key implements Comparable { + + //… + + @Override + public int hashCode() { + return 1; + } +} +``` + +仍然执行main方法,得出的结果如下表所示: + +![img](https://pic1.zhimg.com/80/608ce9cf1ca4bc9fe08826402a3bc4e8_1440w.png) + +从表中结果中可知,随着size的变大,JDK1.7的花费时间是增长的趋势,而JDK1.8是明显的降低趋势,并且呈现对数增长稳定。当一个链表太长的时候,HashMap会动态的将它替换成一个红黑树,这话的话会将时间复杂度从O(n)降为O(logn)。hash算法均匀和不均匀所花费的时间明显也不相同,这两种情况的相对比较,可以说明一个好的hash算法的重要性。 + +测试环境:处理器为2.2 GHz Intel Core i7,内存为16 GB 1600 MHz DDR3,SSD硬盘,使用默认的JVM参数,运行在64位的OS X 10.10.1上。 + +#### 18.HashMap操作注意事项以及优化? + +(1) 扩容是一个特别耗性能的操作,所以当程序员在使用HashMap的时候,估算map的大小,初始化的时候给一个大致的数值,避免map进行频繁的扩容。 + +(2) 负载因子是可以修改的,也可以大于1,但是建议不要轻易修改,除非情况非常特殊。 + +(3) HashMap是线程不安全的,不要在并发的环境中同时操作HashMap,建议使用ConcurrentHashMap。 + +(4) JDK1.8引入红黑树大程度优化了HashMap的性能。 + +(5) 还没升级JDK1.8的,现在开始升级吧。HashMap的性能提升仅仅是JDK1.8的冰山一角。 + +#### 19.fail-fast 与 fail-safe 之间的区别? + +Fail fast快速地报告任何的failure。无论何时任何一个问题都会引发 fail fast系统fails +在Java Fail fast 迭代器中,迭代objects集合有时会出现并发修改异常,出现这种情况有2个原因 +如果一个线程正在迭代一个集合,而另一个线程同时试图修改这个集合 +在调用remove()方法后,如何我们还试图去修改集合object + +#### 20.ConcurrentHashMap分段锁 + +jdk1.7中: + +ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成。Segment 是一种可重入锁 ReentrantLock,在 ConcurrentHashMap 里扮演锁的角色,HashEntry 则用于存储键值对数据。 + +Segment数组的意义就是将一个大的table分割成多个小的table来进行加锁,也就是上面的提到的锁分离技术,而每一个Segment元素存储的是HashEntry数组+链表,这个和HashMap的数据存储结构一样。 + + + +![img](https://pic2.zhimg.com/80/v2-d8cc7dcb4af87b8146f591aea276400d_720w.jpg) + + + + + +1.8 中: + +放弃了Segment,直接用 Node数组+链表+红黑树 的数据结构来实现,并发控制使用Synchronized + CAS来操作,整个看起来就像是优化过且线程安全的HashMap。 + + + +![img](https://pic3.zhimg.com/80/v2-f79b2da9b987bf4ca653fa099c4bda32_720w.jpg) + + + + + + + +#### 参考: + +https://zhuanlan.zhihu.com/p/21673805 + +https://www.jianshu.com/p/939b8a672070 + +https://www.jianshu.com/p/c45b6d782e91 + +https://blog.csdn.net/Mrs_chens/article/details/92761868 + +https://blog.csdn.net/riemann_/article/details/87217229 + +https://blog.csdn.net/qq_34626097/article/details/83053004 + +https://blog.csdn.net/u010887744/article/details/50575735 + +https://zhuanlan.zhihu.com/p/267545042 + +https://blog.csdn.net/qq_40434646/article/details/81591239 + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) + diff --git a/Jsp.md b/Jsp.md new file mode 100644 index 0000000..b2626fa --- /dev/null +++ b/Jsp.md @@ -0,0 +1,417 @@ +## JSP + +#### 1.浏览器jsp,html之间的关系 + +1.JSP与Java Servlet一样,是在服务器端执行的,通常返回该客户端的就是一个HTML文本,因此客户端只要有浏览器就能浏览 + +2.在大多数Browser/Server结构的Web应用中,浏览器直接通过HTML或者JSP的形式与用户交互,响应用户的请求 + +3.JSP在服务器上执行,并将执行结果输出到客户端浏览器,我们可以说基本上与浏览器无关 + +#### 2.自定义标签要继承哪个类 + +这个类可以继承TagSupport或者BodyTagSupport,两者的差别是前者适用于没有主体的标签,而后者适用于有主体的标签。如果选择继承TagSupport,可以实现doStartTag和doEndTag两个方法实现Tag的功能,如果选择继承BodyTagSupport,可以实现doAfterBody这个方法。 + +#### 3. jsp内置对象和作用? + +有九个内置对象:request、response、out、session、application、pageContext、config、page、exception + +作用如下: + +(1) HttpServletRequest类的Request对象 + +作用:代表请求对象,主要用于接受客户端通过HTTP协议连接传输到服务器端的数据。 + +(2) HttpServletResponse类的Respone对象 + +作用:代表响应对象,主要用于向客户端发送数据 + +(3) JspWriter类的out对象 + +作用:主要用于向客户端输出数据; + +​ Out的基类是JspWriter + +(4) HttpSession类的session对象 + +作用:主要用于来分别保存每个用户信息,与请求关联的会话; + +​ 会话状态维持是Web应用开发者必须面对的问题。 + +(5) ServletContex类的application对象 + +作用:主要用于保存用户信息,代码片段的运行环境; + +​ 它是一个共享的内置对象,即一个容器中的多个用户共享一个application对象,故其保存的信息被所有用户所共享. + +(6) PageContext类的PageContext对象 + +作用:管理网页属性,为JSP页面包装页面的上下文,管理对属于JSP中特殊可见部分中已命名对象的访问,它的创建和初始化都是由容器来完成的。 + +(7) ServletConfig类的Config对象 + +作用:代码片段配置对象,表示Servlet的配置。 + +(8) Object类的Page(相当于this)对象 + +作用:处理JSP网页,是Object类的一个实例,指的是JSP实现类的实例,即它也是JSP本身,只有在JSP页面范围之内才是合法的。 + +(9)Exception + +作用:处理JSP文件执行时发生的错误和异常 + +#### 4.jsp乱码如何解决,几种解决方案 + +一、JSP页面显示乱码 +<%@ page contentType=”text/html; charset=gb2312″%> + +二、表单提交中文时出现乱码 + +request.seCharacterEncoding(̶gb2312″)对请求进行统一编码 + +三、数据库连接出现乱码 +要涉及中文的地方全部是乱码,解决办法:在数据库的数据库URL中加上useUnicode=true&characterEncoding=GBK就OK了。 + +四、通过过滤器完成 + +五、在server.xml中的设置编码格式 + +#### 5.页面间对象传递的方法 + +request,session,application,cookie等 +request.setAttribute(key,value) +session.setAttribute(key,value) +application.setAttribute(key,value) + +#### 6.BS与CS的联系与区别 + +B/S模式是指在TCP/IP的支持下,以HTTP为传输协议,客户端通过Browser访问Web服务器以及与之相连的后台数据库的技术及体系结构。它由浏览器、Web服务器、应用服务器和数据库服务器组成。客户端的浏览器通过URL访问Web服务器,Web服务器请求数据库服务器,并将获得的结果以HTML形式返回客户端浏览器。 + +c/s在系统机构上和B/S相似,不过需要在客户端安装一个客户端软件,由这个软件对服务器的数据进行读写,就像我们常用的qq,就是这种模式。 + +#### 7.描述Jsp页面的运行过程? + +第一步: + +请求进入Web容器,将JSP页面翻译成Servlet代码 + +第二步: + +编译Servlet代码,并将编译过的类文件装入Web容器(JVM)环境 + +第三步: + +Web容器为JSP页面创建一个Servlet类实例,并执行jspInit方法 + +第四步: + +Web容器为该JSP页面调用Servlet实例的_jspService方法;将结果发送给用户 + +#### 8.Jsp工作原理 + +JSP是一种Servlet,但是与HttpServlet的工作方式不太一样。HttpServlet是先由源代码编译为class文件后部署到服务器下,为先编译后部署。而JSP则是先部署后编译。JSP会在客户端第一次请求JSP文件时被编译为HttpJspPage类(接口Servlet的一个子类)。该类会被服务器临时存放在服务器工作目录里面。 + +由于JSP只会在客户端第一次请求的时候被编译 ,因此第一次请求JSP时会感觉比较慢,之后就会感觉快很多。如果把服务器保存的class文件删除,服务器也会重新编译JSP。 + +开发Web程序时经常需要修改JSP。Tomcat能够自动检测到JSP程序的改动。如果检测到JSP源代码发生了改动。Tomcat会在下次客户端请求JSP时重新编译JSP,而不需要重启Tomcat。 + +```java +虽然servlet和jsp本质都是servlet,运行时都是运行.class文件,但是它们的部署方式不一样。 +servlet是先编译后部署,修改完以后,MyEclipse进行编译,然后部署.class文件到servlet容器中。如果web服务器已启动,则之前的.class文件已被servlet容器加载,修改后的.class文件不会被servlet容器执行。 +而jsp是web服务器进行编译,而不是预先编译好,编译后再加载,tomcat会监视jsp文件的改动,改动之后则重新编译、执行,所以jsp改动时不需要重启服务器。 +``` + +#### 9.Jsp包含的部分 + +``` +指令: <%@ %> +java小脚本: <% java代码 %> 语句带封号; +方法声明: <%! %> +表达式: <%= %> 表达式不带分号; +注释: <%-- 注释内容 --%> + java中单行,多行 + html中 +html: +js: +css: +标签: +``` + +#### 10.getAttribute()与getParameter() + +**从获取方向来看:** + +`getParameter()`是获取 POST/GET 传递的参数值; + +`getAttribute()`是获取对象容器中的数据值; + +**从用途来看:** + +`getParameter()`用于客户端重定向时,即点击了链接或提交按扭时传值用,即用于在用表单或url重定向传值时接收数据用。 + +`getAttribute()` 用于服务器端重定向时,即在 sevlet 中使用了 forward 函数。getAttribute 只能收到程序用 setAttribute 传过来的值。 + +另外,可以用 `setAttribute()`,`getAttribute()` 发送接收对象.而 `getParameter()` 显然只能传字符串。 +`setAttribute()` 是应用服务器把这个对象放在该页面所对应的一块内存中去,当你的页面服务器重定向到另一个页面时,应用服务器会把这块内存拷贝另一个页面所对应的内存中。这样`getAttribute()`就能取得你所设下的值,当然这种方法可以传对象。session也一样,只是对象在内存中的生命周期不一样而已。`getParameter()`只是应用服务器在分析你送上来的 request页面的文本时,取得你设在表单或 url 重定向时的值。 + +**总结:** + +`getParameter()`返回的是String,用于读取提交的表单中的值;(获取之后会根据实际需要转换为自己需要的相应类型,比如整型,日期类型啊等等) + +`getAttribute()`返回的是Object,需进行转换,可用`setAttribute()`设置成任意对象,使用很灵活,可随时用 + +#### 11.静态导入与动态导入 + +静态导入: + +```java +<%@include file="validate.jsp" %> +将被导入页面和导入页面,合在一起进行翻译,编译。最后产生一个Servlet,那么两个页面的变量名不能重复。 +``` + +动态导入: + +```java + +动态导入,被导入页面和导入页面分别翻译,编译,产生两个Servlet,所以两个页面的变量名可以重复.都会被执行。 +``` + +#### 12.四种作用域 + +SP中的四种作用域包括page、request、session和application,具体来说: + +- **page**代表与一个页面相关的对象和属性。 +- **request**代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件;需要在页面显示的临时数据可以置于此作用域。 +- **session**代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中。 +- **application**代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。 + +#### 13.会话跟踪技术 + +**1)使用Cookie** + +向客户端发送Cookie + +```java +Cookie c =new Cookie("name","value"); //创建Cookie +c.setMaxAge(60*60*24); //设置最大时效,此处设置的最大时效为一天 +response.addCookie(c); //把Cookie放入到HTTP响应中 +``` + +从客户端读取Cookie + +```java +String name ="name"; +Cookie[]cookies =request.getCookies(); +if(cookies !=null){ + for(int i= 0;i +``` + +**优点:** Cookie被禁时可以使用 + +**缺点:** 所有页面必须是表单提交之后的结果。 + +**4)HttpSession** + +在所有会话跟踪技术中,HttpSession对象是最强大也是功能最多的。当一个用户第一次访问某个网站时会自动创建 HttpSession,每个用户可以访问他自己的HttpSession。可以通过HttpServletRequest对象的getSession方 法获得HttpSession,通过HttpSession的setAttribute方法可以将一个值放在HttpSession中,通过调用 HttpSession对象的getAttribute方法,同时传入属性名就可以获取保存在HttpSession中的对象。与上面三种方式不同的 是,HttpSession放在服务器的内存中,因此不要将过大的对象放在里面,即使目前的Servlet容器可以在内存将满时将HttpSession 中的对象移到其他存储设备中,但是这样势必影响性能。添加到HttpSession中的值可以是任意Java对象,这个对象最好实现了 Serializable接口,这样Servlet容器在必要的时候可以将其序列化到文件中,否则在序列化时就会出现异常。 + +#### 14.<%…%>和<%!…%>的区别 + +<%…%>用于在JSP页面中嵌入Java脚本 + +<%!…%>用于在JSP页面中申明变量或方法,可以在该页面中的<%…%>脚本中调用,声明的变量相当于Servlet中的定义的成员变量。 + +#### 15.描述Jsp页面的指令标记的功能、写法、并示例 + +指令标记影响JSP页面的翻译阶段 + +<%@ page session=”false” %> + +<%@ include file=”incl/copyright.html” %> + +<%@ taglib %> + +#### 16.描述Jsp页面的声明标记的功能、写法、并示例 + +声明标记允许JSP页面开发人员包含类级声明 + +写法: + +<%! JavaClassDeclaration %> + +```text +例: + +<%! public static final String DEFAULT_NAME = “World”; %> + +<%! public String getName(HttpServletRequest request) { + + return request.getParameter(“name”); + + } + +%> + +<%! int counter = 0; %> +``` + +#### 17.描述Jsp页面翻译成Servlet的规则 + +jsp中的注释标记被翻译成Servlet类中的注释 + +jsp中的指令标记被翻译成Servlet类中的import语句等 + +jsp中的声明标记被翻译成Servlet类中的属性 + +jsp中的脚本标记被转移到Servlet类中service方法中的代码 + +jsp中的表达式标记被翻译成Serlvet类中的write()或者print()方法括号中的代码 + +#### 18.page指令的功能,写法、并示例,并描述它的如下属性的功能和用法:import、session、buffer、errorPage、isErrorPage、ContentType、pageEncoding + +import : import 定义了一组servlet类定义必须导入的类和包,值是一个由逗号分隔的完全类名或包的列表。 + +session : session 定义JSP页面是否参与HTTP会话,值可以为true(缺省)或false。 + +buffer : buffer 定义用于输出流(JspWriter对象)的缓冲区大小,值可以为none或Nkb,缺省为8KB或更大。 + +errorPage: 用来指定由另一个jsp页面来处理所有该页面抛出的异常 + +isErrorPage : 定义JSP页面为其它JSP页面errorPage属性的目标,值为true或false(缺省)。 + +ContentType : 定义输出流的MIME类型,缺省为text/html。 + +pageEncoding :定义输出流的字符编码,缺省为ISO-8859-1 + +#### 19.描述MVC各部分的功能? + +1.Model + +封装应用状态 + +响应状态查询 + +暴露应用的功能 + +2.Controller + +验证HTTP请求的数据 + +将用户数据与模型的更新相映射 + +选择用于响应的视图 + +3.View + +产生HTML响应 + +请求模型的更新 + +提供HTML form用于用户请求 + +#### 20.什么是JavaBean? + +用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用java代码 + +创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其 + +他JavaBean、applet程序或者应用来使用这些对象。 + +#### 21.JavaBean的规则? + +使用get和set方法定义属性 + +一个无参构造方法 + +无public实例变量 + +#### 22.什么是jsp标准动作?包含那些?分别都是什么功能?如何使用? + +JSP页面中使用类似于XML的标记表示运行时的动作 + +jsp:userBean + +jsp:setProperty + +jsp:getProperty + +jsp:parameter + +jsp:include + +jsp:forward + +#### 23.用代码示例如下标准动作的使用:useBean、getProperty、setProperty + +```text + + + + + +``` + +#### 24.描述说明页面上的字段和Bean中属性的对应规则 + +id 指javabean的变量名 + +class指javabean类的全路径 + +scope指javabean的应用范围 + +name指所用到的javabean的变量名 + +property指javabean中的属性 + +#### 25.描述useBean动作的处理过程 + +使用id声明变量 + +试图在指定的范围内查找对象 + +如果没找到 + +创建一个类的实例 + +执行useBean标记体初始化对象 + +如果找到 + +将对象转换为类指定的类型 + +#### 参考: + +https://blog.csdn.net/woshizoe2/article/details/78993677 + +https://zhuanlan.zhihu.com/p/252313998 + +https://www.cnblogs.com/itzlg/p/11380691.html diff --git a/Kafka.md b/Kafka.md new file mode 100644 index 0000000..5da8375 --- /dev/null +++ b/Kafka.md @@ -0,0 +1,178 @@ +## Kafka + +#### 1.什么是kafka? + +Apache Kafka是由Apache开发的一种发布订阅消息系统。 + +#### 2.kafka的3个关键功能? + +- 发布和订阅记录流,类似于消息队列或企业消息传递系统。 +- 以容错的持久方式存储记录流。 +- 处理记录流。 + +#### 3.kafka通常用于两大类应用? + +- 建立实时流数据管道,以可靠地在系统或应用程序之间获取数据 +- 构建实时流应用程序,以转换或响应数据流 + +#### 4.kafka特性? + +1. 消息持久化 +2. 高吞吐量 +3. 扩展性 +4. 多客户端支持 +5. Kafka Streams +6. 安全机制 +7. 数据备份 +8. 轻量级 +9. 消息压缩 + +#### 5.kafka的5个核心Api? + +- Producer API + +- Consumer API + +- Streams API + +- Connector API + +- Admin API + +#### 6.什么是Broker(代理)? + +Kafka集群中,一个kafka实例被称为一个代理(Broker)节点。 + +#### 7.什么是Producer(生产者)? + +消息的生产者被称为Producer。 + +Producer将消息发送到集群指定的主题中存储,同时也自定义算法决定将消息记录发送到哪个分区? + +#### 8.什么是Consumer(消费者)? + +消息的消费者,从kafka集群中指定的主题读取消息。 + +#### 9.什么是Topic(主题)? + +主题,kafka通过不同的主题却分不同的业务类型的消息记录。 + +#### 10.什么是Partition(分区)? + +每一个Topic可以有一个或者多个分区(Partition)。 + +#### 11.分区和代理节点的关系? + +一个分区只对应一个Broker,一个Broker可以管理多个分区。 + +#### 12.什么是副本(Replication)? + +每个主题在创建时会要求制定它的副本数(默认1)。 + +#### 13.什么是记录(Record)? + +实际写入到kafka集群并且可以被消费者读取的数据。 + +每条记录包含一个键、值和时间戳。 + +#### 14.kafka适合哪些场景? + +日志收集、消息系统、活动追踪、运营指标、流式处理、时间源等。 + +#### 15.kafka磁盘选用上? + +SSD的性能比普通的磁盘好,这个大家都知道,实际中我们用普通磁盘即可。它使用的方式多是顺序读写操作,一定程度上规避了机械磁盘最大的劣势,即随机读写操作慢,因此SSD的没有太大优势。 + +#### 16.使用RAID的优势? + +- 提供冗余的磁盘存储空间 +- 提供负载均衡 + +#### 17.磁盘容量规划需要考虑到几个因素? + +- 新增消息数 +- 消息留存时间 +- 平均消息大小 +- 备份数 +- 是否启用压缩 + +#### 18.Broker使用单个?多个文件目录路径参数? + +log.dirs 多个 + +log.dir 单个 + +#### 19.一般来说选择哪个参数配置路径?好处? + +log.dirs + +好处: + +提升读写性能,多块物理磁盘同时读写高吞吐。 + +故障转移。一块磁盘挂了转移到另一个上。 + +#### 20.自动创建主题的相关参数是? + +auto.create.topics.enable + +#### 21.解决kafka消息丢失问题? + +- 不要使用 producer.send(msg),而要使用 producer.send(msg, callback)。 +- 设置 acks = all。 +- 设置 retries 为一个较大的值。 +- 设置 unclean.leader.election.enable = false。 +- 设置 replication.factor >= 3。 +- 设置 min.insync.replicas > 1。 +- 确保 replication.factor > min.insync.replicas。 +- 确保消息消费完成再提交。 + +#### 22.如何自定分区策略? + +显式地配置生产者端的参数partitioner.class + +参数为你实现类的 全限定类名,一般来说实现partition方法即可。 + +#### 23.kafka压缩消息可能发生的地方? + +Producer 、Broker。 + +#### 24.kafka消息重复问题? + +做好幂等。 + +数据库方面可以(唯一键和主键)避免重复。 + +在业务上做控制。 + +#### 25.你知道的kafka监控工具? + +- JMXTool 工具 +- Kafka Manager +- Burrow +- JMXTrans + InfluxDB + Grafana +- Confluent Control Center + +#### 26.kafka follower如何与leader同步数据 + +Kafka的复制机制既不是完全的同步复制,也不是单纯的异步复制。完全同步复制要求All Alive Follower都复制完,这条消息才会被认为commit,这种复制方式极大的影响了吞吐率。而异步复制方式下,Follower异步的从Leader复制数据,数据只要被Leader写入log就被认为已经commit,这种情况下,如果leader挂掉,会丢失数据,kafka使用ISR的方式很好的均衡了确保数据不丢失以及吞吐率。Follower可以批量的从Leader复制数据,而且Leader充分利用磁盘顺序读以及send file(zero copy)机制,这样极大的提高复制性能,内部批量写磁盘,大幅减少了Follower与Leader的消息量差。 + +#### 27.什么情况下一个 broker 会从 isr中踢出去 + +leader会维护一个与其基本保持同步的Replica列表,该列表称为ISR(in-sync Replica),每个Partition都会有一个ISR,而且是由leader动态维护 ,如果一个follower比一个leader落后太多,或者超过一定时间未发起数据复制请求,则leader将其重ISR中移除 。 + + + +#### 参考: + +《Kafka并不难学》 + +《kafka入门与实践》 + +极客时间:Kafka核心技术与实战 + +http://kafka.apache.org/ + +https://blog.csdn.net/qq_28900249/article/details/90346599 + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) diff --git a/Linux.md b/Linux.md new file mode 100644 index 0000000..0daf456 --- /dev/null +++ b/Linux.md @@ -0,0 +1,786 @@ +## Linux + +#### 1.什么是Linux? + +是一套免费使用和自由传播的类UNIX操作系统,其内核由林纳斯·本纳第克特·托瓦兹于1991年第一次释出,它主要受到Minix和Unix思想的启发,是一个基于POSIX和Unix的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的Unix工具软件、应用程序和网络协议。它支持32位和64位硬件。 + +#### 2.Linux内核主要负责哪些功能 + +- 系统内存管理 +- 软件程序管理 +- 硬件设备管理 +- 文件系统管理 + +#### 3.交互方式 + +控制台终端、图形化终端 + +#### 4.启动shell + +GNU bash shell能提供对linux 系统的交互式访问。作为普通程序运行,通常在用户登陆终端时启动。登录时系统启动的shell依赖与用户账户的配置。 + +#### 5.bash手册 + +大多数linux发行版自带以查找shell命令及其他GNU工具信息的在线手册。man命令用来访问linux系统上的手册页面。当用man命令查看手册,使用分页的程序来现实的。 + +#### 6.登陆后你在的位置? + +一般登陆后,你的位置位于自己的主目录中。 + +#### 7.绝对文件路径?相对文件路径?快捷方式? + +绝对文件路径:描述了在虚拟目录结构中该目录的确切位置,以虚拟目录跟目录开始,相当于目录全名。 + +以正斜线(/)开始,比如 /usr/local。 + +相对文件路径:允许用户执行一个基于当前位置的目标文件路径。 + +比如:当前在/usr/local + +``` +➜ local ls +Caskroom Frameworks bin go lib sbin var +Cellar Homebrew etc include opt share +➜ local cd go +``` + +快捷方式(在相对路径中使用): + +单点符(.) : 表示当前目录; 双点符(..) : 表示当前目录的父目录。 + +#### 8.迷路,我的当前位置在哪? + +pwd 显示当前目录 + +``` +[root@iz2ze76ybn73dvwmdij06zz local]# pwd +/usr/local +``` + +#### 9.如何切换目录? + +语法: cd destination + +destination : 相对文件路径或绝对文件路径 + +可以跳到存在的任意目录。 + +#### 10.如何查看目录中的文件?区分哪些是文件哪些是目录?递归查? + +ls 命令会用最基本的形式显示当前目录下的文件和目录: + +``` +➜ local ls +Caskroom Frameworks bin go lib sbin var +Cellar Homebrew etc include opt share +``` + +可以看出默认是按照字母序展示的 + +一般来说,ls命令回显示不同的颜色区分不同的文件类型,如果没有安装颜色插件可以用ls -F来区分哪些是目录(目录带/),哪些是文件(文件不带/) + + ls -R 递归展示出目录下以及子目录的文件,目录越多输出越多 + +#### 11.创建文件?创建目录?批量创建? + +创建文件:touch 文件名 + +批量创建文件: touch 文件名 文件名 … + +``` +➜ test touch a +➜ test ls +a +➜ test touch b c +➜ test ls +a b c +``` + +创建目录:mkdir 目录名 + +批量创建目录: mkdir 目录名 目录名 … + +``` +➜ test mkdir aa +➜ test mkdir bb cc +➜ test ls +a aa b bb c cc +➜ test ls -F +a aa/ b bb/ c cc/ +``` + +#### 12.删除文件?强制删除?递归删除? + +语法: rm destination + +-i 询问是否删除,-r 递归删除,-f 强制删除。 + +rm不能删除有文件的目录,需要递归删除。 + +``` +➜ xktest rm jdk +rm: jdk: is a directory +➜ xktest rm -r jdk +➜ xktest ls +``` + +rm -i 询问删除,建议大家平时删除多用 -i,确定一下再删除。 + +``` +➜ xktest touch tomcat +➜ xktest rm -i tomcat +remove tomcat? n +``` + +rm -rf 会直接删除,没有警告信息,使用必须谨慎**。 + +#### 13.制表符自动补全? + +有的时候文件的名字很长,很容易拼出错即使拼写对了也很浪费时间。 + +``` +➜ xktest ls java* +javaxiaokaxiu +``` + + 比如操作javaxiaokaxiu这个文件时,输入到java的时候,然后按制表键(tab)就会补全成javaxiaokaxiu,是不是方便多了。 + +#### 14.复制文件 + +语法: cp source target + +如果target不存在则直接创建,如果存在,默认不会提醒你是否需要覆盖,需要加-i就会询问你是否覆盖,n否y是。 + +``` +➜ xktest cp a c +➜ xktest cp -i a c +overwrite c? (y/n [n]) y +➜ xktest ls +a c +``` + +#### 15.重新命名文件?移动文件? + +语法 : mv soucre target + +重命名: + +``` +➜ xktest ls +➜ xktest touch java +➜ xktest ls +java +➜ xktest mv java java1.8 +➜ xktest ls +java1.8 +``` + +移动文件: + +新建jdk目录把java1.8文件移动到jdk目录下。 + +``` +➜ xktest ls +java1.8 +➜ xktest mkdir jdk +➜ xktest mv java1.8 jdk +➜ xktest ls -R +jdk + +./jdk: +java1.8 +``` + +#### 16.什么是链接文件? + +如过需要在系统上维护同一文件的两份或者多份副本,除了保存多分单独的物理文件副本之外。还可以采用保存一份物理文件副本和多个虚拟副本的方法,这种虚拟的副本就叫做链接。 + +#### 17.查看文件类型?字符编码? + +语法: file destination + +``` +➜ apache file tomcat +tomcat: ASCII text +``` + +可以看出,file命令可以显示文件的类型text以及字符编码ASCII + +#### 18.查看整个文件?按照有文本显示行号?无文本显示行号? + +语法 : cat destination + +-n 显示行号,-b 有文本的显示行号。 (默认是不显示行号的) + +``` +➜ apache cat -n tomcat + 1 text + 2 text + 3 + 4 start + 5 stop + 6 restart + 7 end +➜ apache cat -b tomcat + 1 text + 2 text + + 3 start + 4 stop + 5 restart + 6 end +``` + +#### 19.查看部分文件 + +语法 : tail destination + +默认情况会展示文件的末尾10行。 -n 行数,显示最后n行。 + +``` +➜ apache tail -n 2 tomcat +restart +end +``` + +语法: head destination + +默认情况会展示文件的开头10行。 -n 行数,显示开头n行。 + +``` +➜ apache head -n 2 tomcat +text +text +``` + +#### 20.数据排序?对数字进行排序?对月份排序? + +默认情况下,文件的数据展示是按照原顺序展示的。sort命令可以对文本文件中的数据进行排序。sort默认会把数据当成字符处理。 + +语法: sort destination + +sort -n 所以排序数字时需要用-n,它的含义是说当前排序是的数字。 + +sort -M 比如月份Jan、Feb、Mar,如果希望它按照月份排序,加入-M就会按照月份的大小来排序。 + +#### 21.查找匹配数据?反向搜? + +语法: grep [options] pattern [file] + +该命令会查找匹配执行模式的字符串的行,并输出。 + +``` +➜ apache grep start tomcat +start +restart +``` + +-v 反向搜 + +``` +➜ apache grep -v start tomcat +text +text + +stop +end +``` + +-n 显示行号 + +-c 显示匹配的行数 + +#### 22.压缩工具有哪些? + +![image-20200421122324314](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200421122324314.png) + +#### 23.如何压缩文件?如何解压文件? + +比如以.gz的格式举例。 + +压缩语法: gzip destination + +``` +➜ apache gzip tomcat +➜ apache ls +tomcat.gz +``` + +解压语法: gunzip destination + +``` +➜ apache gunzip tomcat.gz +➜ apache ls +tomcat +``` + +#### 24.Linux广泛使用的归档数据方法? + +虽然zip命令能压缩和解压单个文件,但是更多的时候广泛使用tar命令来做归档。 + +语法: tar function [options] obj1 obj2 + +![image-20200421122932671](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200421122932671.png) + +``` +➜ apache tar -cvf service.tar service1 service2 // 创建规定文件service.tar +a service1 +a service2 +➜ apache tar -tf service.tar //查看文件中的目录内容 +service1 +service2 +➜ apache tar zxvf service.tar //解压 +x service1 +x service2 +``` + +#### 25.如何查看命令历史记录? + + history 命令可以展示你用的命令的历史记录。 + +``` + 4463 touch service1 service2 + 4464 ls + 4465 tar -cvf service.tar service1 service2 + 4466 tar -tf service.tar + 4467 tar zxvf service + 4468 tar zxvf service.t + 4469 tar zxvf service.tar + 4470 ls + 4471 tar -zxvf service.tar + 4472 ls +``` + +#### 26.查看已有别名?建立属于自己的别名? + +alias -p 查看当前可用别名 + +``` +[root@iz2ze76ybn73dvwmdij06zz ~]# alias -p +alias cp='cp -i' +alias egrep='egrep —color=auto' +alias fgrep='fgrep —color=auto' +alias grep='grep —color=auto' +alias l.='ls -d .* —color=auto' +alias ll='ls -l —color=auto' +``` + +alias li = 'ls -li' 创建别名 + +#### 27.什么是环境变量? + +bash shell用一个叫作环境变量(environment variable)的特性来存储有关shell会话和工作环境的信息。这项特性允许你在内存中存储数据,以便程序或shell中运行的脚本能够轻松访问到它们。这也是存储持久数据的一种简便方法。 + +在bash shell中,环境变量分为两类: + + 全局变量:对于 shell会话和所有生成的子shell都是可见的。 局部变量: 只对创建他们的shell可见。 + +#### 28.储存用户的文件是?包括哪些信息? + +/etc/passwd存储来一些用户有关的信息。 + +``` +[root@iz2ze76ybn73dvwmdij06zz ~]# cat /etc/passwd +root:x:0:0:root:/root:/bin/bash +bin:x:1:1:bin:/bin:/sbin/nologin +``` + +文件信息包括如下内容。 + +- 登录用户名 +- 用户密码 +- 用户账户的UID(数字形式) +- 用户账户的组ID(GID)(数字形式) +- 用户账户的文本描述(称为备注字段) +- 用户HOME目录的位置 +- 用户的默认shell + +#### 29.账户默认信息?添加账户?删除用户? + +``` +[root@iz2ze76ybn73dvwmdij06zz ~]# useradd -D//查看系统默认创建用户信息 +GROUP=100 +HOME=/home +INACTIVE=-1 +EXPIRE= +SHELL=/bin/bash +SKEL=/etc/skel +CREATE_MAIL_SPOOL=yes +[root@iz2ze76ybn73dvwmdij06zz ~]# useradd xiaoka//添加用户 + +[root@iz2ze76ybn73dvwmdij06zz /]# userdel xiaoka//删除用户 +``` + +#### 30.查看组信息?如何创建组?删除组? + +``` +[root@iz2ze76ybn73dvwmdij06zz ~]# cat /etc/group +root:x:0: +bin:x:1: +daemon:x:2: +sys:x:3: +adm:x:4: +tty:x:5: +disk:x:6: +[root@iz2ze76ybn73dvwmdij06zz ~]# groupadd java //创建组 +[root@iz2ze76ybn73dvwmdij06zz ~]# groupdel java //创建组 +``` + +#### 31.文件描述符?每个描述符的含义? + +``` +[root@iz2ze76ybn73dvwmdij06zz xiaoka]# ls -l +总用量 0 +-rw-r—r— 1 root root 0 4月 21 13:17 a +-rw-r—r— 1 root root 0 4月 21 13:17 b +-rw-r—r— 1 root root 0 4月 21 13:17 c +-rw-r—r— 1 root root 0 4月 21 13:17 d +-rw-r—r— 1 root root 0 4月 21 13:17 e +``` + +1、文件类型: + +- -代表文件 +- d代表目录 +- l代表链接 +- c代表字符型设备 +- b代表块设备 +- n代表网络设备 + +2、访问权限符号: + +- r代表对象是可读的 +- w代表对象是可写的 +- x代表对象是可执行的 + +若没有某种权限,在该权限位会出现单破折线。 + +3、这3组权限分别对应对象的3个安全级别: + +- 对象的属主 +- 对象的属组 +- 系统其他用户 + +#### 31.修改权限? + +chmod options mode file + +比如给文件附加可以执行权限: + +``` +[root@xiaoka ~]# chmod +x filename +``` + +#### 32.如何执行可以执行文件? + +``` +[root@xiaoka ~]# sh sleep.sh +hello,xiaoka +[root@xiaoka ~]# ./sleep.sh +hello,xiaoka +``` + +#### 33.列出已经安装的包?安装软件?更新软件?卸载? + +列出已经安装的包: yum list installed + +安装软件: yum install package_name + +更新软件: yum update package_name + +卸载软件:yum remove package_name //只删除软件包保留数据文件和配置文件 + +如果不希望保留数据文件和配置文件 + +可以执行:yum erase package_name + +#### 34.源码安装通常的路子? + +``` + tar -zxvf xx.gz //解包 + cd xx + ./configure + make + make install +``` + +#### 35.vim编辑器几种操作模式?基本操作? + +操作模式: + +- 普通模式 +- 插入模式 + + 基础操作: + +- h:左移一个字符。 +- j:下移一行(文本中的下一行)。 +- k:上移一行(文本中的上一行)。 +- l:右移一个字符。 + +vim提供了一些能够提高移动速度的命令: + +- PageDown(或Ctrl+F):下翻一屏 +- PageUp(或Ctrl+B):上翻一屏。 +- G:移到缓冲区的最后一行。 +- num G:移动到缓冲区中的第num行。 +- gg:移到缓冲区的第一行。 + +退出vim: + +- q:如果未修改缓冲区数据,退出。 +- q!:取消所有对缓冲区数据的修改并退出。 +- w filename:将文件保存到另一个文件中。 +- wq:将缓冲区数据保存到文件中并退出。 + +#### 36.查看设备还有多少磁盘空间? + +df 可以查看所有已挂在磁盘的使用情况。 + +-m 用兆字节,G代替g字节 + +``` +[root@iz2ze76ybn73dvwmdij06zz ~]# df +文件系统 1K-块 已用 可用 已用% 挂载点 +devtmpfs 1931568 0 1931568 0% /dev +tmpfs 1940960 0 1940960 0% /dev/shm +tmpfs 1940960 720 1940240 1% /run +tmpfs 1940960 0 1940960 0% /sys/fs/cgroup +/dev/vda1 41152812 9068544 30180560 24% / +tmpfs 388192 0 388192 0% /run/user/0 +``` + +###### 快速判断某个特定目录是否有超大文件? + +默认情况,du会显示当前目录的所有文件、目录、子目录的磁盘使用情况。 + +``` +[root@iz2ze76ybn73dvwmdij06zz src]# du +4 ./debug +4 ./kernels +12 +``` + +#### 37.默认进程信息显示? + +ps它能输出运行在系统上的所有程序的许多信息。 + +默认情况下ps值显示运行在当前控制台下的当前用户的进程。 + +``` +[root@iz2ze76ybn73dvwmdij06zz ~]# ps + PID TTY TIME CMD +10102 pts/0 00:00:00 bash +10131 pts/0 00:00:00 ps +``` + +#### 38.实时监测进程 + +与ps相比,top可以实时监控进程信息。 + +![image-20200421114633852](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200421114633852.png) + +平均负载有3个值:最近1分钟的、最近5分钟的和最近15分钟的平均负载。值越大说明系统 的负载越高。由于进程短期的突发性活动,出现最近1分钟的高负载值也很常见,但如果近15分 钟内的平均负载都很高,就说明系统可能有问题。 + +#### 39.如何中断一个进程? + +在一个终端中, Ctrl + c + +通过这个命令许多(不是全部)命令行程序都可以被中断。 + +#### 40.如何把一个进程放到后台运行? + +``` +[root@iz2ze76ybn73dvwmdij06zz ~]# ./sleep.sh & +``` + +此时,进程并不能被Ctrl + c 中断。 + +#### 41.如何停止一个进程? + +kill命令被用来给程序发送信号。如果没有指定信号,默认发送TERM(终止)信号。 + +语法 : kill [-signal] PID … + +![image-20200421141556974](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200421141556974.png) + +\#### + +#### 42.验证网络可链接命令是什么?什么原理? + + ping。这个 ping 命令发送一个特殊的网络数据包(叫做 IMCP ECHO REQUEST)到一台指定的主机。大多数接收这个包的网络设备将会回复它,来允许网络连接验证。 + +![image-20200421142307602](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200421142307602.png) + +一旦启动,ping会持续在特定时间(默认1秒)发送数据包。 + +#### 43.查看某端口是否被占用? + +netstat -ntulp|grep 8080 + +``` +[root@iz2ze76ybn73dvwmdij06zz ~]# netstat -ntulp|grep 8080 +tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 4517/java +``` + +参数说明: + +- -t (tcp) 仅显示tcp相关选项 +- -u (udp)仅显示udp相关选项 +- -n 拒绝显示别名,能显示数字的全部转化为数字 +- -l 仅列出在Listen(监听)的服务状态 +- -p 显示建立相关链接的程序名 + +#### 44.如何查找匹配的文件?基于文件属性? + + find 程序能基于各种各样的属性,搜索一个给 定目录(以及它的子目录),来查找文件。 + +find 命令的最简单使用是,搜索一个或多个目录。 + +普通查找,按照name查找: + +``` +[root@iz2ze76ybn73dvwmdij06zz ~]# find -name xiaoka +./xiaoka +``` + +文件类型查找: + +比如,输出我们的家目录文件数量 + +``` +[root@iz2ze76ybn73dvwmdij06zz ~]# find ~|wc -l +17130 +``` + +根据文件类型查: + +``` +[root@iz2ze76ybn73dvwmdij06zz ~]# find ~ -type d | wc -l +7340 +``` + +find支持的类型: b 块设备文件、 c 字符设备文件、d 目录、f 普通文件、l 符号链接 + +#### 45.如何查看当前主机名?如何修改?如何重启后生效? + +``` +[root@iz2ze76ybn73dvwmdij06zz ~]# hostname//查看当前主机名 +iz2ze76ybn73dvwmdij06zz +[root@iz2ze76ybn73dvwmdij06zz ~]# hostname xiaoka//修改当前主机名 +[root@iz2ze76ybn73dvwmdij06zz ~]# hostname +xiaoka +``` + +大家知道一般来讲命令重启就会失效,目前基本上用的centos7的比较多,两种方式可以支持重启生效。 + +一、命令 + +``` +[root@iz2ze76ybn73dvwmdij06zz ~]# hostnamectl set-hostname xiaoka +[root@iz2ze76ybn73dvwmdij06zz ~]# hostname +xiaoka +[root@xiaoka ~]# +``` + + 二、修改配置文件:/etc/hostname + +``` +[root@xiaoka ~]# vim /etc/hostname +``` + +#### 46.如何写一条规则,拒绝某个ip访问本机8080端口? + +``` +iptables -I INPUT -s ip -p tcp —dport 8080 -j REJECT +``` + +#### 47.哪个文件包含了主机名和ip的映射关系? + +/etc/hosts + +#### 48.如何用sed只打印第5行?删除第一行?替换字符串? + + 只打印第5行: + +``` +➜ apache sed -n "5p" tomcat +stop +``` + +删除第一行: + +``` +[root@xiaoka ~]# cat story +Long ago a lion and a bear saw a kid. +They sprang upon it at the same time. +The lion said to the bear, “I caught this kid first, and so this is mine.” +[root@xiaoka ~]# cat story +They sprang upon it at the same time. +The lion said to the bear, “I caught this kid first, and so this is mine.” +``` + +替换字符串: + +``` +➜ apache cat story +Long ago a lion and a bear saw a kid. +They sprang upon it at the same time. +The lion said to the bear, “I caught this kid first, and so this is mine.” +➜ apache sed 's#this#that#g' story +Long ago a lion and a bear saw a kid. +They sprang upon it at the same time. +The lion said to the bear, “I caught that kid first, and so that is mine.” +``` + +#### 49.打印文件第一行到第三行? + +​ 文件tomcat中内容: + +``` +➜ apache cat tomcat +text21 +text22 +text23 +start +stop +restart +end +➜ apache head -3 tomcat +text21 +text22 +text23 +➜ apache sed -n '1,3p' tomcat +text21 +text22 +text23 +➜ apache awk 'NR>=1&&NR<=3' tomcat +text21 +text22 +text23 +``` + +#### 50.如何用awk查看第2行倒数第3个字段? + +``` +➜ apache awk 'NR==3{print $(NF-2)}' story +this +➜ apache cat story +Long ago a lion and a bear saw a kid. +They sprang upon it at the same time. +The lion said to the bear, “I caught this kid first, and so this is mine.” +``` + +参考: + +- 《鸟哥Linux私房菜》 + +- 《快乐的命令行》 + +- 《Linux命令行与shell脚本编程大全(第3版)》 + +- 《Linux从入门到精通》 + +- 百度百科 + + ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) diff --git a/Maven.md b/Maven.md new file mode 100644 index 0000000..55544c9 --- /dev/null +++ b/Maven.md @@ -0,0 +1,318 @@ +## Maven + +#### 1.Maven 是什么? + +Maven 主要服务于基于 Java 平台的项目构建、依赖管理和项目信息管理。 + +Maven 的主要功能主要分为 5 点: + +- 依赖管理系统 +- 多模块构建 +- 一致的项目结构 +- 一致的构建模型和插件机制 + +#### 2.什么选用 Maven 进行构建? + +- 首先,Maven 是一个优秀的项目构建工具。使用 maven,可以很方便的对项目进行分模块构建,这样在开发和测试打包部署时,效率会提高很多。 +- 其次,Maven 可以进行依赖的管理。使用 Maven ,可以将不同系统的依赖进行统一管理,并且可以进行依赖之间的传递和继承。 + +#### 3. Maven 规约是什么? + +- `/src/main/java/` :Java 源码。 +- `/src/main/resource` :Java 配置文件,资源文件。 +- `/src/test/java/` :Java 测试代码。 +- `/src/test/resource` :Java 测试配置文件,资源文件。 +- `/target` :文件编译过程中生成的 `.class` 文件、jar、war 等等。 +- `pom.xml` :配置文件 + +Maven 要负责项目的自动化构建,以编译为例,Maven 要想自动进行编译,那么它必须知道 Java 的源文件保存在哪里,这样约定之后,不用我们手动指定位置,Maven 能知道位置,从而帮我们完成自动编译。 + +遵循 **“约定>>> 配置 >>> 编码”**。即能进行配置的不要去编码指定,能事先约定规则的不要去进行配置。这样既减轻了劳动力,也能防止出错。 + +#### 4.Maven 常用命令 + +- `mvn archetype:create` :创建 Maven 项目。 +- `mvn compile` :编译源代码。 +- `mvn deploy` :发布项目。 +- `mvn test-compile` :编译测试源代码。 +- `mvn test` :运行应用程序中的单元测试。 +- `mvn site` :生成项目相关信息的网站。 +- `mvn clean` :清除项目目录中的生成结果。 +- `mvn package` :根据项目生成的 jar/war 等。 +- `mvn install` :在本地 Repository 中安装 jar 。 +- `mvn eclipse:eclipse` :生成 Eclipse 项目文件。 +- `mvn jetty:run` 启动 Jetty 服务。 +- `mvn tomcat:run` :启动 Tomcat 服务。 +- `mvn clean package -Dmaven.test.skip=true` :清除以前的包后重新打包,跳过测试类。 + +用到最多的命令 + +- `mvn eclipse:clean` :清除 Project 中以前的编译的东西,重新再来。 +- `mvn eclipse:eclipse` :开始编译 Maven 的 Project 。 +- `mvn clean package` :清除以前的包后重新打包。 + +#### 5.Maven 有哪些优点和缺点 + +##### 1)优点 + +- 简化了项目依赖管理。 + + 当年,多少人被 SSH 整合搞死搞活,很多时候,是因为依赖不完整,或者版本不正确。自从 Maven 出来后,终于可以无痛了~ 当然,也有一部分功劳是 Spring Boot ,这是后话。 + +- 易于上手,对于新手可能一个 `mvn clean package` 命令就可能满足我们的工作。 + +- 便于与持续集成工具 (Jenkins) 整合。 + +- 便于项目升级,无论是项目本身升级还是项目使用的依赖升级。 + +- 有助于多模块项目的开发,一个模块开发好后,发布到仓库,依赖该模块时可以直接从仓库更新,而不用自己去编译。 + +- Maven 有很多插件,便于功能扩展,比如生产站点,自动发布版本等。 + +##### 2)缺点 + +- Maven 是一个庞大的构建系统,学习难度大。 + + 这里的学习,更多指的完整学习。如果基本使用,并不会存在该问题。 + +- Maven 采用约定优于配置的策略 (convention over configuration),虽然上手容易,但是一旦出了问题,难于调试。 + + 这个确实,略微痛苦。 + +- 当依赖很多时,m2eclipse 老是搞得 Eclipse 很卡。 + + 使用 IDEA ,而不是 Eclipse ,完美解决。 + +- 中国的网络环境差,很多 repository 无法访问,比如 Google Code、 JBoss 仓库无法访问等。 + + 这个也好解决,在 `` 中增加阿里巴巴的 Maven 私服,具体可以参见 [《提高 Maven 速度 —— Maven 仓库修改成国内阿里巴巴地址》](https://my.oschina.net/af8991/blog/833513) 文章。 + +#### 6.什么是Maven的坐标 + +Maven的坐标通过groupId,artifactId,version唯一标志一个构件。groupId通常为公司或组织名字,artifactId通常为项目名称,versionId为版本号。 + +#### 7.通过坐标如何定位地址 + +加上groupId为org.codehaus.mojo,artifactId为myproject,versionId为v1.0.0,则对应地址为:仓库目录(.m2)/org/codehaus/mojo/myproject/v1.0.0 + +#### 8.Maven的依赖范围有哪些(在scope中指定) + +compile:默认范围,如果未指定任何范围,则使用该范围。编译依赖项在所有(编译,测试,运行)类路径中都可用。此外,这些依赖关系会传播到依赖的项目 + +provided:这很像compile,但表示您希望JDK或容器在运行时提供它。它只在编译和测试类路径上可用,不可传递。 + +runtime:此范围表示编译不需要依赖项,但需要执行依赖项。它在运行时和测试类路径中,但不在编译类路径中。(servlet-api) + +test:表示应用程序的正常使用不需要依赖项,并且仅在测试编译和执行阶段可用。它不是传递的。(jdbc) + +system:系统依赖范围。该依赖与三种classpath的关系和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植。 + +#### 9.Maven生命周期 + + 有三套什么周期,分别为clean,default,site + +   clean: + +    此生命周期旨在给工程做清理工作,它主要包含以下阶段: + +    pre-clean - 执行项目清理前所需要的工作。 + +    clean - 清理上一次build项目生成的文件。 + +    post-clean - 执行完成项目清理所需的工作。 + +   default: + +    validate - 验证项目是否正确且所有必要的信息都可用。 + +    initialize - 初始化构建工作,如:设置参数,创建目录等。 + +    generate-sources - 为包含在编译范围内的代码生成源代码. + +    process-sources - 处理源代码, 如过滤值. + +    generate-resources - + +    process-resources - 复制并处理资源文件,至目标目录,准备打包。 + +    compile - 编译项目中的源代码. + +    process-classes - 为编译生成的文件做后期工作, 例如做Java类的字节码增强. + +    generate-test-sources - 为编译内容生成测试源代码. + +    process-test-sources - 处理测试源代码。 + +    generate-test-resources - + +    process-test-resources - 复制并处理资源文件,至目标测试目录。 + +    test-compile - 将需测试源代码编译到路径。一般来说,是编译/src/test/java目录下的java文件至目标输出的测试classpath目录中。 + +    process-test-classes - + +    test - 使用合适的单元测试框架运行测试。这些测试代码不会被打包或部署。 + +    prepare-package - + +    package - 接受编译好的代码,打包成可发布的格式,如 JAR 。 + +    pre-integration-test - + +    integration-test - 按需求将发布包部署到运行环境。 + +    post-integration-test - + +    verify - + +    install -将包安装到本地仓库,给其他本地引用提供依赖。 + +    deploy -完成集成和发布工作,将最终包复制到远程仓库以便分享给其他开发人员。 + +   site: + +    pre-site - 执行一些生成项目站点前的准备工作。 + +    site - 生成项目站点的文档。 + +    post-site - 执行需完成站点生成的工作,如站点部署的准备工作。 + +    site-deploy - 向制定的web服务器部署站点生成文件。 + +#### 10.Maven命令 + + mvn archetype:generate 创建Maven项目 + +   mvn compile 编译源代码 + +   mvn deploy 发布项目 + +   mvn test-compile 编译测试源代码 + +   mvn test 运行应用程序中的单元测试 + +   mvn site 生成项目相关信息的网站 + +   mvn clean 清除项目目录中的生成结果 + +   mvn package 根据项目生成的jar + +   mvn install 在本地Repository中安装jar + +   mvn eclipse:eclipse 生成eclipse项目文件 + +   mvn[jetty](https://baike.baidu.com/item/jetty):run 启动jetty服务 + +   mvn[tomcat](https://baike.baidu.com/item/tomcat):run 启动tomcat服务 + +   mvn clean package -Dmaven.test.skip=true:清除以前的包后重新打包,跳过测试 + +#### 11.依赖的解析机制 + +当依赖的范围是 system 的时候,Maven 直接从本地文件系统中解析构件。 + +根据依赖坐标计算仓库路径,尝试直接从本地仓库寻找构件,如果发现对应的构件,就解析成功。 + +如果在本地仓库不存在相应的构件,就遍历所有的远程仓库,发现后,下载并解析使用。 + +如果依赖的版本是 RELEASE 或 LATEST,就基于更新策略读取所有远程仓库的元数据文件(groupId/artifactId/maven-metadata.xml),将其与本地仓库的对应元合并后,计算出 RELEASE 或者 LATEST 真实的值,然后基于该值检查本地仓库,或者从远程仓库下载。 + +如果依赖的版本是 SNAPSHOT,就基于更新策略读取所有远程仓库的元数据文件,将它与本地仓库对应的元数据合并,得到最新快照版本的值,然后根据该值检查本地仓库,或从远程仓库下载。 + +如果最后解析得到的构件版本包含有时间戳,先将该文件下载下来,再将文件名中时间戳信息删除,剩下 SNAPSHOT 并使用(以非时间戳的形式使用)。 + +#### 12.创建Maven的普通Java项目 + + mvn archetype:create -DgroupId=packageName -DartifactId=projectName + +#### 13.创建 Maven 的 Web 项目 + + mvn archetype:create -DgroupId=packageName -DartifactId=webappName -DarchetypeArtifactId=maven-archetype-webapp + +#### 14.反向生成 maven 项目的骨架 + + mvn artifacttype:generate + +#### 15.编译源代码 + + mvn compile + +#### 16.编译测试代码 + + mvn test-compile + +#### 17.运行测试 + + mvn test + +#### 18.产生 site + + mvn site + +#### 19.打包 + + mvn package + +#### 20.在本地 Repository 中安装 jar + + mvn install(例:installing D:\xxx\xx.jar to D:\xx\xxxx) + +#### 21.清除产生的项目 + + mvn clean + +#### 22.生成 Eclipse 项目/idea项目 + +eclipse项目 + + mvn eclipse:eclipse + + idea 项目 + + mvn idea:idea + +#### 23.组合使用 goal 命令,如只打包不测试 + + mvn -Dtest package + +#### 24.编译测试的内容 + + mvn test-compile + +#### 25.只打 jar 包 + + mvn jar:jar + +#### 26.只测试而不编译,也不测试编译 + + mvn test -skipping compile -skipping test-compile + +#### 27.清除 eclipse 的一些系统设置 + + mvn eclipse:clean + +#### 28.查找当前项目已被解析的依赖 + + mvn dependency:list + +#### 29.上传到私服 + + mvn deploy + +#### 30.强制检查更新,由于快照版本的更新策略(一天更新几次、隔断时间更新一次)存在,如果想强制更新就会用到此命令 + + mvn clean install-U + +#### 31.源码打包 + + mvn source:jar + + 或 + + mvn source:jar-no-fork + +#### 参考 + +https://blog.csdn.net/a303549861/article/details/93752178 + +https://www.cnblogs.com/lin0/p/14153982.html diff --git a/MongoDB.md b/MongoDB.md new file mode 100644 index 0000000..24c76a6 --- /dev/null +++ b/MongoDB.md @@ -0,0 +1,609 @@ +## MongoDB + +#### 1.什么是MongoDB + +``` +MongoDB是一个文档数据库,提供好的性能,领先的非关系型数据库。采用BSON存储文档数据。 +BSON()是一种类json的一种二进制形式的存储格式,简称Binary JSON. +相对于json多了date类型和二进制数组。 +``` + +#### 2.MongoDB的优势有哪些 + +- 面向文档的存储:以 JSON 格式的文档保存数据。 +- 任何属性都可以建立索引。 +- 复制以及高可扩展性。 +- 自动分片。 +- 丰富的查询功能。 +- 快速的即时更新。 + +#### 3.什么是数据库 + +``` +数据库可以看成是一个电子化的文件柜,用户可以对文件中的数据运行新增、检索、更新、删除等操作。数据库是一个 +所有集合的容器,在文件系统中每一个数据库都有一个相关的物理文件。 +``` + +#### 4.什么是集合(表) + +``` +集合就是一组 MongoDB 文档。它相当于关系型数据库(RDBMS)中的表这种概念。集合位于单独的一个数据库中。 +一个集合内的多个文档可以有多个不同的字段。一般来说,集合中的文档都有着相同或相关的目的。 +``` + +#### 5 什么是文档(记录) + +``` +  文档由一组key value组成。文档是动态模式,这意味着同一集合里的文档不需要有相同的字段和结构。在关系型 +数据库中table中的每一条记录相当于MongoDB中的一个文档 +``` + +#### 6 MongoDB和关系型数据库术语对比图 + +![img](https://img2018.cnblogs.com/blog/1521877/201904/1521877-20190429170250020-1693717595.png) + +#### 7.什么是非关系型数据库 + +``` + 非关系型数据库的显著特点是不使用SQL作为查询语言,数据存储不需要特定的表格模式。 +``` + +#### 8.为什么用MOngoDB? + +- 架构简单 +- 没有复杂的连接 +- 深度查询能力,MongoDB支持动态查询。 +- 容易调试 +- 容易扩展 +- 不需要转化/映射应用对象到数据库对象 +- 使用内部内存作为存储工作区,以便更快的存取数据。 + +#### 9.MongoDB中的命名空间是什么意思? + +``` +MongoDB内部有预分配空间的机制,每个预分配的文件都用0进行填充。 + +数据文件每新分配一次,它的大小都是上一个数据文件大小的2倍,每个数据文件最大2G。 + +MongoDB每个集合和每个索引都对应一个命名空间,这些命名空间的元数据集中在16M的*.ns文件中,平均每个命名占用约 628 字节,也即整个数据库的命名空间的上限约为24000。 + +如果每个集合有一个索引(比如默认的_id索引),那么最多可以创建12000个集合。如果索引数更多,则可创建的集合数就更少了。同时,如果集合数太多,一些操作也会变慢。 + +要建立更多的集合的话,MongoDB 也是支持的,只需要在启动时加上“--nssize”参数,这样对应数据库的命名空间文件就可以变得更大以便保存更多的命名。这个命名空间文件(.ns文件)最大可以为 2G。 + +每个命名空间对应的盘区不一定是连续的。与数据文件增长相同,每个命名空间对应的盘区大小都是随分配次数不断增长的。目的是为了平衡命名空间浪费的空间与保持一个命名空间数据的连续性。 + +需要注意的一个命名空间$freelist,这个命名空间用于记录不再使用的盘区(被删除的Collection或索引)。每当命名空间需要分配新盘区时,会先查看$freelist是否有大小合适的盘区可以使用,如果有就回收空闲的磁盘空间。 +``` + +#### 10.在哪些场景使用MongoDB + +- 大数据 +- 内容管理系统 +- 移动端Apps +- 数据管理 + +#### 11.monogodb 中的分片什么意思 + +``` +分片是将数据水平切分到不同的物理节点。当应用数据越来越大的时候,数据量也会越来越大。当数据量增长 +时,单台机器有可能无法存储数据或可接受的读取写入吞吐量。利用分片技术可以添加更多的机器来应对数据量增加 +以及读写操作的要求。 +``` + +#### 12.为什么要在MongoDB中使用分析器 + +``` +mongodb中包括了一个可以显示数据库中每个操作性能特点的数据库分析器.通过这个分析器你可以找到比预期慢 +的查询(或写操作);利用这一信息,比如,可以确定是否需要添加索引。 +``` + +#### 13.MongoDB支持主键外键关系吗 + +``` +默认MongoDB不支持主键和外键关系。 用Mongodb本身的API需要硬编码才能实现外键关联,不够直观且难度较大 +``` + +#### 14.MongoDB支持哪些数据类型 + + +- String +- Integer +- Double +- Boolean +- Object +- Object ID +- Arrays +- Min/Max Keys +- Datetime +- Code +- Regular Expression等 + +#### 15.为什么要在MongoDB中用"Code"数据类型 + +``` +"Code"类型用于在文档中存储 JavaScript 代码。 +``` + +#### 16.32位系统上有什么细微差别? + +journaling会激活额外的内存映射文件。这将进一步抑制32位版本上的数据库大小。因此,现在journaling在32位系统上默认是禁用的。 + +#### 17.为什么在MongoDB中使用"Object ID"数据类型 + +``` +"ObjectID"数据类型用于存储文档id +``` + +#### 18."ObjectID"有哪些部分组成 + +``` +一共有四部分组成:时间戳、客户端ID、客户进程ID、三个字节的增量计数器 + +_id是一个 12 字节长的十六进制数,它保证了每一个文档的唯一性。在插入文档时,需要提供_id。如果你不提供,那么 MongoDB 就会为每一文档提供一个唯一的 id。_id的头 4 个字节代表的是当前的时间戳,接着的后 3 个字节表示的是机器 id 号,接着的 2 个字节表示 MongoDB 服务器进程 id,最后的 3 个字节代表递增值。 +``` + +#### 19.在MongoDb中什么是索引 + +``` +索引用于高效的执行查询,没有索引的MongoDB将扫描整个集合中的所有文档,这种扫描效率很低,需要处理大量的数据. +索引是一种特殊的数据结构,将一小块数据集合保存为容易遍历的形式.索引能够存储某种特殊字段或字段集的值,并按照索引指定的方式将字段值进行排序. +``` + +#### 20.如何添加索引 + +``` +使用db.collection.createIndex()在集合中创建一个索引 +``` + +#### 21.如何查询集合中的文档 + +``` +db.collectionName.find({key:value}) +``` + +#### 22.用什么方法可以格式化输出结果 + +``` +db.collectionName.find().pretty() +``` + +#### 23.如何使用"AND"或"OR"条件循环查询集合中的文档 + +``` +db.mycol.find( + { + $or: [ + {key1: value1}, {key2:value2} + ] + } +).pretty() +``` + +#### 24.你怎么比较MongoDB、CouchDB及CouchBase? + +MongoDB和CouchDB都是面向文档的数据库。MongoDB和CouchDB都是开源NoSQL数据库的最典型代表。 除了都以文档形式存储外它们没有其他的共同点。MongoDB和CouchDB在数据模型实现、接口、对象存储以及复制方法等方面有很多不同。 + +细节可以参见下面的链接: + +MongDB vs CouchDB + +CouchDB vs CouchBase + +#### 25.名字空间(namespace)是什么? + +MongoDB存储BSON对象在丛集(collection)中。数据库名字和丛集名字以句点连结起来叫做名字空间(namespace)。 + +#### 26.如果用户移除对象的属性,该属性是否从存储层中删除? + +是的,用户移除属性然后对象会重新保存(re-save())。 + +#### 27.什么是聚合 + +聚合操作能够处理数据记录并返回计算结果。聚合操作能将多个文档中的值组合起来,对成组数据执行各种操作,返回单一的结果。它相当于 SQL 中的 count(*) 组合 group by。对于 MongoDB 中的聚合操作,应该使用`aggregate()`方法。 + +``` +db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION) +``` + +#### 28.在MongoDB中什么是副本集(避免单点故障) + +``` +在MongoDB中副本集由一组MongoDB实例组成,包括一个主节点多个次节点,MongoDB客户端的所有数据都 +写入主节点(Primary),副节点从主节点同步写入数据,以保持所有复制集内存储相同的数据,提高数据可用性。 +``` + +#### 29.什么是NoSQL数据库?NoSQL和RDBMS有什么区别?在哪些情况下使用和不使用NoSQL数据库? + +``` +NoSQL是非关系型数据库,NoSQL = Not Only SQL。 关系型数据库采用的结构化的数据,NoSQL采用的是键值对的方式存储数据。 + +在处理非结构化/半结构化的大数据时;在水平方向上进行扩展时;随时应对动态增加的数据项时可以优先考虑使用NoSQL数据库。 + +在考虑数据库的成熟度;支持;分析和商业智能;管理及专业性等问题时,应优先考虑关系型数据库。 +``` + +#### 30.MongoDB支持存储过程吗?如果支持的话,怎么用? + +``` +MongoDB支持存储过程,它是javascript写的,保存在db.system.js表中。 +``` + +#### 31.如何理解MongoDB中的GridFS机制,MongoDB为何使用GridFS来存储文件? + +GridFS是一种将大型文件存储在MongoDB中的文件规范。使用GridFS可以将大文件分隔成多个小文档存放,这样我们能够有效的保存大文档,而且解决了BSON对象有限制的问题。 + +#### 32.如何执行事务/加锁? + +MongoDB没有使用传统的锁或者复杂的带回滚的事务,因为它设计的宗旨是轻量,快速以及可预计的高性能。可以把它类比成MySQL MylSAM的自动提交模式。通过精简对事务的支持,性能得到了提升,特别是在一个可能会穿过多个服务器的系统里。 + +#### 33.启用备份故障恢复需要多久? + +从备份数据库声明主数据库宕机到选出一个备份数据库作为新的主数据库将花费10到30秒时间。这期间在主数据库上的操作将会失败--包括写入和强一致性读取(strong consistent read)操作。然而,你还能在第二数据库上执行最终一致性查询(eventually consistent query)(在slaveOk模式下),即使在这段时间里。 + +#### 34.我应该启动一个集群分片(sharded)还是一个非集群分片的 MongoDB 环境? + +为开发便捷起见,我们建议以非集群分片(unsharded)方式开始一个 MongoDB 环境,除非一台服务器不足以存放你的初始数据集。从非集群分片升级到集群分片(sharding)是无缝的,所以在你的数据集还不是很大的时候没必要考虑集群分片(sharding)。 + +#### 35.分片(sharding)和复制(replication)是怎样工作的? + +每一个分片(shard)是一个分区数据的逻辑集合。分片可能由单一服务器或者集群组成,我们推荐为每一个分片(shard)使用集群。 + +#### 36.数据在什么时候才会扩展到多个分片(shard)里? + +MongoDB 分片是基于区域(range)的。所以一个集合(collection)中的所有的对象都被存放到一个块(chunk)中。只有当存在多余一个块的时候,才会有多个分片获取数据的选项。现在,每个默认块的大小是 64Mb,所以你需要至少 64 Mb 空间才可以实施一个迁移。 + +#### 37.我可以把moveChunk目录里的旧文件删除吗? + +没问题,这些文件是在分片(shard)进行均衡操作(balancing)的时候产生的临时文件。一旦这些操作已经完成,相关的临时文件也应该被删除掉。但目前清理工作是需要手动的,所以请小心地考虑再释放这些文件的空间。 + +#### 38.分片(sharding)和复制(replication)是怎样工作的? + +每一个分片(shard)是一个分区数据的逻辑集合.分片可能由单一服务器或者集群组成,我们推荐为每一个分片(shard)使用集群。 + +#### 39.如果块移动操作(movechunk)失败了,我需要手动清除部分转移的文档吗? + + 不需要,移动操作是一致(consistent)并且是确定性的(deterministic);一次失败后,移动操作会不断重试;当完成后,数据只会出现在新的分片里(shard)。 + +#### 40.mongodb是否支持事务 + +MongoDB 4.0的新特性——事务(Transactions):MongoDB 是不支持事务的,因此开发者在需要用到事务的时候,不得不借用其他工具,在业务代码层面去弥补数据库的不足。 + +事务和会话(Sessions)关联,一个会话同一时刻只能开启一个事务操作,当一个会话断开,这个会话中的事务也会结束。 + +#### 41.哪些语言支持MongoDB? + +- C +- C++ +- C# +- Java +- Node.js +- Perl +- Php 等 + +#### 42.如何使用"AND"或"OR"条件循环查询集合中的文档 + +在`find()`方法中,如果传入多个键,并用逗号(`,`)分隔它们,那么 MongoDB 会把它看成是**AND**条件。 + +```text +>db.mycol.find({key1:value1, key2:value2}).pretty() +``` + +若基于**OR**条件来查询文档,可以使用关键字**$or**。 + +```text +>db.mycol.find( + { + $or: [ + {key1: value1}, {key2:value2} + ] + } +).pretty() +``` + +#### 43.如何删除文档 + +MongoDB 利用 `remove()` 方法 清除集合中的文档。它有 2 个可选参数: + +- deletion criteria:(可选)删除文档的标准。 +- justOne:(可选)如果设为 true 或 1,则只删除一个文档。 + +```text +>db.collectionName.remove({key:value}) +``` + +#### 44.在MongoDB中如何排序 + +MongoDB 中的文档排序是通过`sort()`方法来实现的。`sort()`方法可以通过一些参数来指定要进行排序的字段,并使用 1 和 -1 来指定排序方式,其中 1 表示升序,而 -1 表示降序。 + +```text +>db.connectionName.find({key:value}).sort({columnName:1}) +``` + +#### 45.举例说明您将从Redis和MongoDB一起使用中受益的情况? + +Redis和MongoDB可以一起使用,效果很好。Craiglist是一家以运行MongoDB和Redis(以及MySQL和Sphinx)而闻名的公司。请参阅Jeremy +Zawodny的[演示文稿](http://www.slideshare.net/jzawodn/living-with-sql-and-nosql-at-craigslist-a-pragmatic-approach)。 + +MongoDB对于以各种方式索引的持久性,面向文档的数据很有趣。对于易失性数据或对延迟敏感的半永久性数据,Redis更有趣。 + +以下是在MongoDB之上具体使用Redis的一些示例。 + +- 2.2版之前的MongoDB还没有到期机制。上限集合不能真正用于实现真正的TTL。Redis具有基于TTL的过期机制,可以方便地存储易失性数据。例如,用户会话通常存储在Redis中,而用户数据将存储在MongoDB中并建立索引。请注意,MongoDB 2.2在集合级别引入了一种低精度的过期机制(例如,用于清除数据)。 +- Redis提供了一种方便的集合数据类型及其关联的操作(联合,交集,多个集合的差等)。在此功能之上实现基本的多面搜索或标记引擎非常容易,这是对MongoDB更传统的索引功能的有趣补充。 +- Redis支持有效地阻止列表上的弹出操作。这可用于实现临时分布式排队系统。它比MongoDB可尾游标IMO更具灵活性,因为后端应用程序可以在超时的情况下侦听多个队列,原子地将项目转移到另一个队列,等等…如果应用程序需要排队,则将队列存储在Redis中是有意义的,并将持久性功能数据保留在MongoDB中。 +- Redis还提供了发布/订阅机制。在分布式应用程序中,事件传播系统可能会有用。对于持久性数据保留在MongoDB中而言,这也是Redis的绝佳用例。 + +由于使用MongoDB设计数据模型要比使用Redis容易得多(Redis更底层),因此可以从MongoDB的主要持久性数据灵活性和Redis提供的额外功能(低延迟)中受益。 +,项目到期,队列,发布/订阅,原子块等)。这确实是一个很好的组合。 + +请注意,您永远不要在同一台机器上运行Redis和MongoDB服务器。MongoDB内存被设计为可以换出,Redis不是。如果MongoDB触发某些交换活动,则Redis的性能将是灾难性的。它们应该在不同的节点上隔离。 + +#### 46.MongoDB + Azure + Android:com.mongodb.WriteConcernException err:“非主用户”代码:“ 10058” + +**背景** : + +嗨,我正在Azure上运行MongoDB副本集,并已从Android应用程序中远程连接到它。我已使读取在所有实例上都能很好地工作(已更新:因为允许它们在主节点和辅助节点上读取)。但是,对数据库的写入仍然会出现间歇性错误,并出现以下错误,因为写入必须仅在主节点上进行。 + +另外,如果您可以提供更多具体资源来解决此问题,那么这也将非常有帮助。我已经阅读了大多数文档,并搜索了很多此错误。 + +**问题** : + +如何防止此错误并允许100%的时间写入? + +``` +E/AndroidRuntime(): com.mongodb.WriteConcernException: { + "serverUsed" : "/:27017" , "err" : "not master" , + "code" : 10058 , "n" : 0 , "lastOp" : { "$ts" : 0 , "$inc" : 0} , + "connectionId" : 1918 , "ok" : 1.0} +``` + +**堆栈跟踪** : + +``` +E/AndroidRuntime(13731): FATAL EXCEPTION: Thread-7629 +E/AndroidRuntime(13731): Process: com.myapplication.examplemongodb, PID: 13731 +E/AndroidRuntime(13731): com.mongodb.WriteConcernException: { "serverUsed" : "/:27017" , "err" : "not master" , "code" : 10058 , "n" : 0 , "lastOp" : { "$ts" : 0 , "$inc" : 0} , "connectionId" : 1918 , "ok" : 1.0} +E/AndroidRuntime(13731): at com.mongodb.CommandResult.getException(CommandResult.java:77) +E/AndroidRuntime(13731): at com.mongodb.CommandResult.throwOnError(CommandResult.java:110) +E/AndroidRuntime(13731): at com.mongodb.DBTCPConnector._checkWriteError(DBTCPConnector.java:102) +E/AndroidRuntime(13731): at com.mongodb.DBTCPConnector.say(DBTCPConnector.java:142) +E/AndroidRuntime(13731): at com.mongodb.DBTCPConnector.say(DBTCPConnector.java:115) +E/AndroidRuntime(13731): at com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:248) +E/AndroidRuntime(13731): at com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:204) +E/AndroidRuntime(13731): at com.mongodb.DBCollection.insert(DBCollection.java:76) +E/AndroidRuntime(13731): at com.mongodb.DBCollection.insert(DBCollection.java:60) +E/AndroidRuntime(13731): at com.mongodb.DBCollection.insert(DBCollection.java:105) +E/AndroidRuntime(13731): at com.myapplication.examplemongodb.ActivityMain$1.run(ActivityMain.java:83) +E/AndroidRuntime(13731): at java.lang.Thread.run(Thread.java:841) +``` + +**注意事项** : + +- 我正在使用[mongo-java-driver v2.11.3](http://central.maven.org/maven2/org/mongodb/mongo-java-driver/)。 + +- 我使用了 + + mongo-azure库 + + 来帮助创建具有两个工作角色的MongoDB副本集。 + + - (如果您还有其他资源,那么我很乐意阅读。我已经阅读了GitHub自述文件[this](http://docs.mongodb.org/ecosystem/tutorial/deploy-mongodb-worker-roles-in-azure/),[this](http://docs.mongodb.org/ecosystem/tutorial/configure-worker-roles-in-azure/)和其他一些与MongoDB / Azure不相关的内容。但是,这些资源不是更新,也不详细。) + +**可能的解决方案** : + +- 我认为这与设置副本集有关。 +- 我不确定这种情况是否会发生,因为我只有两个实例副本集(一个主要副本和一个次要副本),并且他们正在为谁想成为主要副本而进行争夺(阅读:投票)。也许需要仲裁员?但是,我目前不知道该怎么做。 + +**更新** : + +- 感谢@David Makogon的帮助,我非常确定问题在于如何建立与Azure的连接以及如何访问辅助角色。因此,这是我关于系统配置方式的最新注释: + - 两个工作角色(MongoDB.WindowsAzure.MongoDBRole),我通过`TCP Input Endpoint`Android应用程序通过端口27017 直接连接到这些角色。正如@David所说,我目前无法控制连接到哪个实例。 + - 我不做任何事情的一个Web角色(MongoDB.WindowsAzure.Manager)`HTTP Input Endpoint`在端口80上有一个。默认情况下,这是我上面提到的mongo-azure库的默认设置。我不确定是否应该对此做任何事情。 + +#### 47.使用Spring Security + Spring数据+ MongoDB进行身份验证 + +我想将Spring安全性与MongoDB结合使用(使用Spring数据),并从我自己的数据库中检索用户以获取Spring安全性。但是,由于我的用户服务类型似乎不受支持,所以我不能这样做。 + +这是我的UserService类: + +``` +public class UserService { + private ApplicationContext applicationContext; + private MongoOperations mongoOperations; + + public UserService() { + applicationContext = new AnnotationConfigApplicationContext(MongoConfig.class); + mongoOperations = (MongoOperations) applicationContext.getBean("mongoTemplate"); + } + + public User find(String username) { + return mongoOperations.findOne(Query.query(Criteria.where("username").is(username)), User.class); + } +} +``` + +和我的SecurityConfig类: + +``` +@Configuration +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + @Autowired + UserService userService; + + @Autowired + public void configAuthBuilder(AuthenticationManagerBuilder builder) throws Exception { + builder.userDetailsService(userService); //THIS DOES NOT WORK + builder.inMemoryAuthentication().withUser("username").password("password").roles("USER"); + } + +} +``` + +我评论的那句话说: + +``` +The inferred type UserService is not a valid substitute for the bounded parameter products; +} + +@Document(collection = "album") +public class Album extends Product { + @DBRef(lazy = true) + public List songs; +} + +@Document(collection = "single") +public class Single extends Product { + @DBRef(lazy = true) + public List songs; +} + +@Document(collection = "song") +public class Song { + @Id + public String id; + + public String title; +} + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, + property = "productType", + include = JsonTypeInfo.As.EXTERNAL_PROPERTY) +@JsonSubTypes(value = { + @JsonSubTypes.Type(value = Single.class), + @JsonSubTypes.Type(value = Album.class) +}) +public abstract class Product { + @Id + public String id; +} +``` + +**生成的JSON** + +``` +{ + "id": "someId1", + "products": [ + { + "id": "someId2", + "songs": [ + { + "id": "someId3", + "title": "Some title", + "target": { + "id": "someId3", + "title": "Some title" + } + } + ] + } + ] +} +``` + +#### 50.表示MongoDB中具有属性的多对多关系的最佳模型 + +代表具有属性的多对多关系的最“ mongo”方式是什么? + +因此,例如: + +##### 介绍 + +------ + +MYSQL表 + +``` +people` => `firstName, lastName, ... +Movies` => `name, length .. +peopleMovies` => `movieId, personId, language, role +``` + +##### 解决方案1 + +------ + +将人们嵌入电影中…? + +在MongoDB中,我知道这样`denormalize and embed`做很好,但是我不想让`embed`人们看电影,从逻辑上讲这没有任何意义。因为人们不一定只属于电影。 + +##### 解决方案2 + +------ + +``` +People`并且`Movies`将两个单独的集合。 `People`=>嵌入`[{movieId: 12, personId: 1, language: "English", role: "Main"} ...] +Movies` =>嵌入 `[{movieId: 12, personId: 1, language: "English", role: "Main"} ...] +``` + +该解决方案的问题在于,当我们要`role`为特定对象更新人员时,`movie`我们需要运行两个更新查询以确保两个集合中的数据同步。 + +##### 解决方案3 + +------ + +我们还可以做一些与关系有关的事情,最后得到三个集合 + +``` +People`=> `firstName, lastName, ...` `Movies`=> `name, length ..` +`Castings`=>`movieId, personId, language, role +``` + +问题在于,由于MongoDB中缺少join语句,因此需要`3 queries`从人那里去->电影,反之亦然。 + +这是我的问题,还有什么其他方式可以对此类事物进行建模`MongoDB`以及更多`NoSQL`方式。就提供的解决方案而言,在mongo的性能和约定方面哪一种是最好的。 + +#### 参考 + +https://www.cnblogs.com/angle6-liu/p/10791875.html + +https://www.jb51.net/article/179908.htm + +https://zhuanlan.zhihu.com/p/37437657 + +https://blog.csdn.net/weixin_45669794/article/details/102991806 + +http://www.zzvips.com/article/69641.html + +https://funyan.cn/p/6173.html + +http://www.mianshigee.com/question/132761aes/ + +http://www.mianshigee.com/question/65730fcs/ + +http://www.mianshigee.com/question/67979kov/ + +http://www.mianshigee.com/question/159947bya/ \ No newline at end of file diff --git "a/MySql-\344\270\212\345\215\267.md" "b/MySql-\344\270\212\345\215\267.md" new file mode 100644 index 0000000..3b60733 --- /dev/null +++ "b/MySql-\344\270\212\345\215\267.md" @@ -0,0 +1,582 @@ +## Mysql(上) + +#### 1.什么是数据库? + +数据库是“按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。 + +#### 2.如何查看某个操作的语法? + + 比如看建表的语法: + +``` +mysql> ? create table +Name: 'CREATE TABLE' +Description: +Syntax: +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name + (create_definition,…) + [table_options] + [partition_options] + +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name + [(create_definition,…)] + [table_options] + [partition_options] + [IGNORE | REPLACE] + [AS] query_expression + +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name +``` + +#### 3.MySql的存储引擎有哪些? + +MyISAM、 InnoDB、BDB、MEMORY、MERGE、EXAMPLE、NDB Cluster、 ARCHIVE、CSV、BLACKHOLE、FEDERATED。 + +Tips:InnoDB和BDB提供事务安全表,其他存储引擎都是非事务安全表。 + +#### 4.常用的2种存储引擎? + +1.Myisam是Mysql的默认存储引擎,当create创建新表时,未指定新表的存储引擎时,默认使用Myisam。 + +每个MyISAM 在磁盘上存储成三个文件。文件名都和表名相同,扩展名分别是 .frm (存储表定义) 、.MYD (MYData,存储数据)、.MYI (MYIndex,存储索引)。 + +数据文件和索引文件可以放置在不同的目录,平均分布io,获得更快的速度。 + +2.InnoDB 存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是对比 Myisam 的存储引擎,InnoDB 写的处理效率差一些并且会占用更多的磁盘空间以保留数据和索引。 + +#### 6.可以针对表设置引擎吗?如何设置? + +可以, ENGINE=xxx 设置引擎。 + +代码示例: + +``` +create table person( + id int primary key auto_increment, + username varchar(32) +) ENGINE=InnoDB +``` + +#### 6.选择合适的存储引擎? + +选择标准: 根据应用特点选择合适的存储引擎,对于复杂的应用系统可以根据实际情况选择 多种存储引擎进行组合. 下面是常用存储引擎的适用环境: + +1. MyISAM: 默认的 MySQL 插件式存储引擎, 它是在 Web、 数据仓储和其他应用环境下最常使用的存储引擎之一。 +2. InnoDB:用于事务处理应用程序,具有众多特性,包括 ACID 事务支持。 +3. Memory: 将 所有数据保存在RAM 中, 在 需要快速查找引用和其他类似数据的环境下,可 提供极快的访问。 +4. Merge:允许 MySQL DBA 或开发人员将一系列等同的 MyISAM 表以逻辑方式组合在一起,并作为 1 个对象引用它们。对于诸如数据仓储等 VLDB 环境十分适合。 + +#### 7.选择合适的数据类型 + +前提: 使用适合存储引擎。 + +选择原则: 根据选定的存储引擎,确定如何选择合适的数据类型下面的选择方法按存储引擎分类 : + +1. MyISAM 数据存储引擎和数据列 + + MyISAM数据表,最好使用固定长度的数据列代替可变长度的数据列。 + +2. MEMORY存储引擎和数据列 + + MEMORY数据表目前都使用固定长度的数据行存储,因此无论使用CHAR或VARCHAR列都没有关系。两者都是作为CHAR类型处理的。 + +3. InnoDB 存储引擎和数据列 + + 建议使用 VARCHAR类型 + + 对于InnoDB数据表,内部的行存储格式没有区分固定长度和可变长度列(所有数据行 都使用指向数据列值的头指针) ,因此在本质上,使用固定长度的CHAR列不一定比使 用可变长度VARCHAR列简单。 因而, 主要的性能因素是数据行使用的存储总量。 由于 CHAR 平均占用的空间多于VARCHAR,因此使用VARCHAR来最小化需要处理的数据行的存储总 量和磁盘I/O是比较好的。 + +#### 8.char & varchar + +保存和检索的方式不同。它们的最大长度和是否尾部空格被保留等方面也不同。在存储或检索过程中不进行大小写转换。 + +#### 9.Mysql字符集 + +mysql服务器可以支持多种字符集 (可以用show character set命令查看所有mysql支持 的字符集) ,在同一台服务器、同一个数据库、甚至同一个表的不同字段都可以指定使用不 同的字符集。 + +mysql的字符集包括字符集(CHARACTER)和校对规则(COLLATION)两个概念。 + +#### 10.如何选择字符集? + +建议在能够完全满足应用的前提下,尽量使用小的字符集。因为更小的字符集意味着能够节省空间、 减少网络传输字节数,同时由于存储空间的较小间接的提高了系统的性能。 + +有很多字符集可以保存汉字,比如 utf8、gb2312、gbk、latin1 等等,但是常用的是 gb2312 和 gbk。因为 gb2312 字库比 gbk 字库小,有些偏僻字(例如:洺)不能保存,因此 在选择字符集的时候一定要权衡这些偏僻字在应用出现的几率以及造成的影响, 不能做出肯 定答复的话最好选用 gbk。 + +#### 11.什么是索引? + +在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。 + +#### 12.索引设计原则? + +1. 搜索的索引列,不 一定是所要选择的列。最适合索引的列是出现在WHERE子句中的列,或连接子句中指定的列,而不是出现在SELECT 关键字后的选择列表中的列。 + +2. 使用惟一索引。考虑某列中值的分布。 对于惟一值的列,索引的效果最好,而具有多个 重复值的列,其索引效果最差。 + +3. 使用短索引。如果对串列进行索引,应该指定一个前缀长度,只要有可能就应该这做样。 例如,如果有一个 CHAR(200) 列,如果在前 10 个或 20 个字符内,多数值是惟一的, 那么就不要对整个列进行索引。 + +4. 利用最左前缀。在创建 一个 n 列的索引时,实际是创建了 MySQL 可利用的 n 个索引。 多列索引可起几个索引的作用,因为可利用索引中最左边的列集来匹配行。 这样的列集 称为最左前缀。 (这与索引一个列的前缀不同,索引一个列的前缀是利用该的n前个字 符作为索引值 ) + +5. 不要过度索引。每个额外的索引都要占用额外的磁盘空间,并降低写操作的性能,这一点我们前面已经介绍 过。在修改表的内容时,索引必须进行更新,有时可能需要重构, 因此, 索引越多,所花的时间越长。 + + 如果有一个索引很少利用或从不使用,那么会不必要地减缓表的修改速度。 此外,MySQL 在生成一个执行计划时,要考虑各个索引,这也要费时间。 + + 创建多余的索引给查询优化带来了更多的工作。索引太多,也可能会使 MySQL选择不到所要使用的 最好索引。 只保持所需的索引有利于查询优化。 如果想给已索引的表增加索引, 应 该考虑所要增加的索引是否是现有多列索引的最左索引。 + +6. 考虑在列上进行的比较类型。 索引可用于“ <”、“ < = ”、“ = ”、“ > =”、“ > ”和 BETWEEN 运算。在模式具有一个直接量前缀时,索引也用于 LIKE 运算。如果只将某个列用于其他类型的运算时(如 STRCMP( )) ,对其进行索引没有价值。 + +#### 13.MySql有哪些索引? + +- ###### 数据结构角度 + +1. BTREE +2. HASH +3. FULLTEXT +4. R-Tree + +- 物理存储角度 + +1、聚集索引(clustered index) + +2、非聚集索引(non-clustered index) + +- 从逻辑角度 + +1. 普通索引:仅加速查询 +2. 唯一索引:加速查询 + 列值唯一(可以有null) +3. 主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个 +4. 组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并 +5. 全文索引:对文本的内容进行分词,进行搜索 + +#### 14.Hash索引和B+树索引的底层实现原理: + +hash索引底层就是hash表,进行查找时,调用一次hash函数就可以获取到相应的键值,之后进行回表查询获得实际数据.B+树底层实现是多路平衡查找树.对于每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据. + +那么可以看出他们有以下的不同: + +- hash索引进行等值查询更快(一般情况下),但是却无法进行范围查询. + +因为在hash索引中经过hash函数建立索引之后,索引的顺序与原顺序无法保持一致,不能支持范围查询.而B+树的的所有节点皆遵循(左节点小于父节点,右节点大于父节点,多叉树也类似),天然支持范围. + +- hash索引不支持使用索引进行排序,原理同上. +- hash索引不支持模糊查询以及多列索引的最左前缀匹配.原理也是因为hash函数的不可预测.**AAAA**和**AAAAB**的索引没有相关性. +- hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询. +- hash索引虽然在等值查询上较快,但是不稳定.性能不可预测,当某个键值存在大量重复的时候,发生hash碰撞,此时效率可能极差.而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节点,且树的高度较低. + +因此,在大多数情况下,直接选择B+树索引可以获得稳定且较好的查询速度.而不需要使用hash索引. + +#### **15. 非聚簇索引一定会回表查询吗?** + +不一定,这涉及到查询语句所要求的字段是否全部命中了索引,如果全部命中了索引,那么就不必再进行回表查询. + +举个简单的例子,假设我们在员工表的年龄上建立了索引,那么当进行`select age from employee where age < 20`的查询时,在索引的叶子节点上,已经包含了age信息,不会再次进行回表查询. + +#### 16.如何查询最后一行记录? + +``` +select * from table_name order by id desc limit 1; +``` + +#### 17.MySQL自增id不连续问题? + +- 唯一键冲突 +- 事务回滚 +- 批量申请自增id的策略 + +#### 18.sql注入问题? + +原因:用户传入的参数中注入符合sql的语法,从而破坏原有sql结构语意,达到攻击效果。 + +#### 19.什么是3NF(范式)? + +- 1NF 指的是数据库表中的任何属性都具有原子性的,不可再分解 +- 2NF 是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性 +- 3NF是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余 + +#### 20. NULL和空串判断? + +NULL值是没有值,,它不是空串。如果指定''(两个单引号,其间没有字符),这在NOT NULL列中是允许的。空串是一个有效的值,它不是无值。 + +判断NULL需要用 IS NULL 或者 IS NOT NULL。 + +#### 21.什么是事务? + +可以用来维护数据库的完整性,它保证成批的MySQL操作要么完全执行,要么完全不执行。 + +#### 22.事务4个特性? + +事务是必须满足4个条件(ACID): + +- **原子性 Atomicity:**一个事务中的所有操作,要么全部完成,要么全部不完成,最小的执行单位。 +- **一致性 Consistency:**事务执行前后,都处于一致性状态。 +- **隔离性 Isolation:**数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。 +- **持久性 Durability:**事务执行完成后,对数据的修改就是永久的,即便系统故障也不会丢失。 + +#### 23.事务隔离级别分别是? + +- READ_UNCOMMITTED 这是事务最低的隔离级别,它充许另外一个事务可以看到这个事务未提交的数据。解决第一类丢失更新的问题,但是会出现脏读、不可重复读、第二类丢失更新的问题,幻读 。 +- READ_COMMITTED 保证一个事务修改的数据提交后才能被另外一个事务读取,即另外一个事务不能读取该事务未提交的数据。解决第一类丢失更新和脏读的问题,但会出现不可重复读、第二类丢失更新的问题,幻读问题 +- REPEATABLE_READ 保证一个事务相同条件下前后两次获取的数据是一致的 (注意是 一个事务,可以理解为事务间的数据互不影响)解决第一类丢失更新,脏读、不可重复读、第二类丢失更新的问题,但会出幻读。 +- SERIALIZABLE 事务串行执行,解决了脏读、不可重复读、幻读。但效率很差,所以实际中一般不用。 + +#### 24.InnoDB默认事务隔离级别?如何查看当前隔离级别 + +可重复读(REPEATABLE-READ) + +查看: + +``` +mysql> select @@global.tx_isolation; ++———————————+ +| @@global.tx_isolation | ++———————————+ +| REPEATABLE-READ | ++———————————+ +1 row in set, 1 warning (0.01 sec) +``` + +#### 25.什么是锁? + +数据库的锁是为了支持对共享资源进行并发访问,提供数据的完整性和一致性,这样才能保证在高并发的情况下,访问数据库的时候,数据不会出现问题。 + +#### 26.死锁? + +是指两个或两个以上进程执行过程中,因竞争共享资源造成的相互等待现象。 + +#### 27.如何处理死锁? + +- 设置超时时间。超时后自动释放。 +- 发起死锁检测,主动回滚其中一条事务,让其他事务继续执行。 + +#### 28.如何创建用户?授权? + +创建用户: + +``` +CREATE USER 'username'@'host' IDENTIFIED BY 'password'; +``` + +授权: + +``` +GRANT privileges ON databasename.tablename TO 'username'@'host'; +``` + +- username:用户名 +- host:可以登陆的主机地址。本地用户用localhost表示,任意远程主机用通配符%。 +- password:登陆密码,密码可以为空表示不需要密码登陆服务器 +- databasename: 数据库名称。 +- tablename:表名称,*代表所有表。 + +#### 29.如何查看表结构? + +​ desc table_name; + +``` +mysql> desc zipkin_spans; ++———————+———————+———+——+————+———+ +| Field | Type | Null | Key | Default | Extra | ++———————+———————+———+——+————+———+ +| trace_id_high | bigint(20) | NO | PRI | 0 | | +| trace_id | bigint(20) | NO | PRI | NULL | | +| id | bigint(20) | NO | PRI | NULL | | +| name | varchar(255) | NO | MUL | NULL | | +| parent_id | bigint(20) | YES | | NULL | | +| debug | bit(1) | YES | | NULL | | +| start_ts | bigint(20) | YES | MUL | NULL | | +| duration | bigint(20) | YES | | NULL | | ++———————+———————+———+——+————+———+ +8 rows in set (0.01 sec) +``` + +#### 30.Mysql删除表的几种方式?区别? + +1.delete : 仅删除表数据,支持条件过滤,支持回滚。记录日志。因此比较慢。 + +``` +delete from table_name; +``` + +2.truncate: 仅删除所有数据,不支持条件过滤,不支持回滚。不记录日志,效率高于delete。 + +``` +truncate table table_name; +``` + +3.drop:删除表数据同时删除表结构。将表所占的空间都释放掉。删除效率最高。 + +``` +drop table table_name; +``` + +#### 31.like走索引吗? + +Xxx% 走索引, %xxx不走索引。 + +#### 32.什么是回表? + +在普通索引查到主键索引后,再去主键索引定位记录。等于说非主键索引需要多走一个索引树。 + +#### 33.如何避免回表? + +索引覆盖被查询的字段。 + +#### 34.索引覆盖是什么? + +如果一个索引包含(或覆盖)所有需要查询的字段的值,称为‘覆盖索引’。 + +#### 35.视图的优缺点? + +**优点** + +> 简单化,数据所见即所得 +> +> 安全性,用户只能查询或修改他们所能见到得到的数据 +> +> 逻辑独立性,可以屏蔽真实表结构变化带来的影响 + +**缺点** + +> 性能相对较差,简单的查询也会变得稍显复杂 +> +> 修改不方便,特变是复杂的聚合视图基本无法修改 + +#### 36.主键和唯一索引区别? + +本质区别,主键是一种约束,唯一索引是一种索引。 + +主键不能有空值(非空+唯一),唯一索引可以为空。 + +主键可以是其他表的外键,唯一索引不可以。 + +一个表只能有一个主键,唯一索引 可以多个。 + +都可以建立联合主键或联合唯一索引。 + +主键-》聚簇索引,唯一索引->非聚簇索引。 + +#### 37.如何随机获取一条记录? + +``` +SELECT * FROM table_name ORDER BY rand() LIMIT 1; +``` + +#### 38.Mysql中的数值类型? + +![image-20200422123555254](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200422123555254.png) + +#### 39.查看当前表有哪些索引? + +``` +show index from table_name; +``` + +#### 40.索引不生效的情况? + +- 使用不等于查询 +- NULL值 +- 列参与了数学运算或者函数 +- 在字符串like时左边是通配符.比如 %xxx +- 当mysql分析全表扫描比使用索引快的时候不使用索引. +- 当使用联合索引,前面一个条件为范围查询,后面的即使符合最左前缀原则,也无法使用索引. + +#### 41.MVVC? + +MVCC 全称是多版本并发控制系统,InnoDB 的 MVCC 是通过在每行记录后面保存两个隐藏的列来实现,这两个列一个保存了行的创建时间,一个保存行的过期时间(删除时间)。当然存储的并不是真实的时间而是系统版本号(system version number)。每开始一个新的事务,系统版本号都会自动新增,事务开始时刻的系统版本号会作为事务的版本号,用来查询到每行记录的版本号进行比较。 + +#### 42.sql语句的执行流程? + +客户端连接数据库,验证身份。 + +获取当前用户权限。 + +当你查询时,会先去缓存看看,如果有返回。 + +如果没有,分析器对sql做词法分析。 + +优化器对sql进行“它认为比较好的优化”。 + +执行器负责具体执行sql语句。 + +最后把数据返回给客户端。 + +#### 43.如何获取select 语句执行计划? + +explain sql; + +#### 44.explain列有哪些?含义? + +![image-20200501154131370](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200501154131370.png) + +一、 id + +SQL查询中的序列号。 + +id列数字越大越先执行,如果说数字一样大,那么就从上往下依次执行。 + +二、select_type + + ![image-20200501154212022](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200501154212022.png) + +三、table + +显示这一行的数据是关于哪张表的。不一定是实际存在的表名。 可以为如下的值: + +- : 引用id为M和N UNION后的结果。 +- : 引用id为N的结果派生出的表。派生表可以是一个结果集,例如派生自FROM中子查询的结果。 +- : 引用id为N的子查询结果物化得到的表。即生成一个临时表保存子查询的结果。 + +四、type + +这是**最重要的字段之一**,显示查询使用了何种类型。从最好到最差的连接类型依次为: + +> system,const,eq_ref,ref,fulltext,ref_or_null,index_merge,unique_subquery,index_subquery,range,index,ALL + +1、system + +表中只有一行数据或者是空表,这是const类型的一个特例。且只能用于myisam和memory表。如果是Innodb引擎表,type列在这个情况通常都是all或者index + +2、const + +最多只有一行记录匹配。当联合主键或唯一索引的所有字段跟常量值比较时,join类型为const。其他数据库也叫做唯一索引扫描 + +3、eq_ref + +多表join时,对于来自前面表的每一行,在当前表中只能找到一行。这可能是除了system和const之外最好的类型。当主键或唯一非NULL索引的所有字段都被用作join联接时会使用此类型。 + +eq_ref可用于使用'='操作符作比较的索引列。比较的值可以是常量,也可以是使用在此表之前读取的表的列的表达式。 + +> 相对于下面的ref区别就是它使用的唯一索引,即主键或唯一索引,而ref使用的是非唯一索引或者普通索引。 eq_ref只能找到一行,而ref能找到多行。 + +4、ref + +对于来自前面表的每一行,在此表的索引中可以匹配到多行。若联接只用到索引的最左前缀或索引不是主键或唯一索引时,使用ref类型(也就是说,此联接能够匹配多行记录)。 + +ref可用于使用'='或'<=>'操作符作比较的索引列。 + +5、 fulltext + +使用全文索引的时候是这个类型。要注意,**全文索引的优先级很高**,若全文索引和普通索引同时存在时,mysql不管代价,优先选择使用全文索引 + +6、ref_or_null + +跟ref类型类似,只是增加了null值的比较。实际用的不多。 + +7、index_merge + +表示查询使用了两个以上的索引,最后取交集或者并集,常见and ,or的条件使用了不同的索引,官方排序这个在ref_or_null之后,但是实际上由于要读取多个索引,性能可能大部分时间都不如range + +8、unique_subquery + +用于where中的in形式子查询,子查询返回不重复值唯一值,可以完全替换子查询,效率更高。 该类型替换了下面形式的IN子查询的ref: `value IN (SELECT primary_key FROM single_table WHERE some_expr)` + +9、index_subquery + +该联接类型类似于unique_subquery。适用于非唯一索引,可以返回重复值。 + +10、range + +索引范围查询,常见于使用 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN()或者like等运算符的查询中。 + +11、index + +索引全表扫描,把索引从头到尾扫一遍。这里包含两种情况: 一种是查询使用了覆盖索引,那么它只需要扫描索引就可以获得数据,这个效率要比全表扫描要快,因为索引通常比数据表小,而且还能避免二次查询。在extra中显示Using index,反之,如果在索引上进行全表扫描,没有Using index的提示。 + +12、all + +全表扫描,性能最差。 + +五、possible_keys + +查询可能使用到的索引都会在这里列出来。 + +六、Key + +key列显示MySQL实际使用的键(索引) + +要想强制MySQL使用或忽视possible_keys列中的索引,可以使用FORCE INDEX、USE INDEX或者IGNORE INDEX。 + +select_type为index_merge时,这里可能出现两个以上的索引,其他的select_type这里只会出现一个。 + +七、key_len + +表示索引中使用的字节数。 + +key_len只计算where条件用到的索引长度,而排序和分组就算用到了索引,也不会计算到key_len中。 + +不损失精确性的情况下,长度越短越好 。 + +八、ref + +表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值。 + +九、rows + +rows 也是一个重要的字段。 这是mysql估算的需要扫描的行数(不是精确值)。 + +十、Extra + +该列包含MySQL解决查询的详细信息,有以下几种情况: + +- Using where:列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤。 +- Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询。 +- Using filesort:MySQL中无法利用索引完成的排序操作称为“文件排序”。 +- Using join buffer:改值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。 +- Impossible where:这个值强调了where语句会导致没有符合条件的行。 +- Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行。 + +链接:https://www.jianshu.com/p/8fab76bbf448 + +#### 45.MySql最多创建多少列索引? + +16 + +#### 46.为什么最好建立一个主键? + +主键是数据库确保数据行在整张表唯一性的保障,即使业务上本张表没有主键,也建议添加一个自增长的ID列作为主键.设定了主键之后,在后续的删改查的时候可能更加快速以及确保操作数据范围安全. + +#### 47.**字段为什么要求建议为not null?** + +MySQL官网这样介绍: + +> NULL columns require additional space in the rowto record whether their values are NULL. For MyISAM tables, each NULL columntakes one bit extra, rounded up to the nearest byte. + +null值会占用更多的字节,且会在程序中造成很多与预期不符的情况. + +#### 48.**varchar(10)和int(10)代表什么含义** + +varchar的10代表了申请的空间长度,也是可以存储的数据的最大长度,而int的10只是代表了展示的长度,不足10位以0填充.也就是说,int(1)和int(10)所能存储的数字大小以及占用的空间都是相同的,只是在展示时按照长度展示。 + +#### 49.视图是什么?对比普通表优势? + +视图(View)是一种虚拟存在的表,对于使用视图的用户来说基本上是透明的。视图并 不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时 动态生成的。 + + 视图相对于普通的表的优势主要包括以下几项。 + +- 简单:使用视图的用户完全不需要关心后面对应的表的结构、关联条件和筛选条件, + + 对用户来说已经是过滤好的复合条件的结果集。 + +- 安全:使用视图的用户只能访问他们被允许查询的结果集,对表的权限管理并不能 + + 限制到某个行某个列,但是通过视图就可以简单的实现。 + +- 数据独立:一旦视图的结构确定了,可以屏蔽表结构变化对用户的影响,源表增加 + + 列对视图没有影响;源表修改列名,则可以通过修改视图来解决,不会造成对访问 者的影响。 + +#### 50.count(*)在不同引擎的实现方式? + +MyISAM :把一个表的总行数存在了磁盘上,执行 count(*) 的时候会直接返回这个数,效率很高。 + +InnoDB : 比较麻烦,它执行 count(*) 的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数。 + +参考: + +- 《深入浅出MySQL》 +- 《高性能MySql》 +- 《MySQL技术内幕(第5版)》 +- 《MySQL必知必会》 +- 极客时间:MySQL实战45讲 +- 百度百科 + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file diff --git "a/MySql-\344\270\213\345\215\267.md" "b/MySql-\344\270\213\345\215\267.md" new file mode 100644 index 0000000..b566e7c --- /dev/null +++ "b/MySql-\344\270\213\345\215\267.md" @@ -0,0 +1,584 @@ +## Mysql(下) + +#### 1.能说下myisam 和 innodb的区别吗? + +myisam引擎是5.1版本之前的默认引擎,支持全文检索、压缩、空间函数等,但是不支持事务和行级锁,所以一般用于有大量查询少量插入的场景来使用,而且myisam不支持外键,并且索引和数据是分开存储的。 + +innodb是基于聚簇索引建立的,和myisam相反它支持事务、外键,并且通过MVCC来支持高并发,索引和数据存储在一起。 + + +#### 2.说下mysql的索引有哪些吧,聚簇和非聚簇索引又是什么? + +索引按照数据结构来说主要包含 B + 树和 Hash 索引。 + +假设我们有张表,结构如下: + +``` +create table user( +id int(11) not null, +age int(11) not null, +primary key(id), key(age) +); +``` + +B + 树是左小右大的顺序存储结构,节点只包含 id 索引列,而叶子节点包含索引列和数据,这种数据和索引在一起存储的索引方式叫做聚簇索引,一张表只能有一个聚簇索引。假设没有定义主键,InnoDB 会选择一个唯一的非空索引代替,如果没有的话则会隐式定义一个主键作为聚簇索引。 + +![img](https://img-blog.csdnimg.cn/20201007140904245.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ1MjcwNjY3,size_16,color_FFFFFF,t_70#pic_center) + + +这是主键聚簇索引存储的结构,那么非聚簇索引的结构是什么样子呢?非聚簇索引 (二级索引) 保存的是主键 id 值,这一点和 myisam 保存的是数据地址是不同的。 + +![img](https://img-blog.csdnimg.cn/20201007141110124.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ1MjcwNjY3,size_1,color_FFFFFF,t_70#pic_center) + + +最终,我们一张图看看 InnoDB 和 Myisam 聚簇和非聚簇索引的区别 + +![img](https://img-blog.csdnimg.cn/20201007141238754.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ1MjcwNjY3,size_1,color_FFFFFF,t_70#pic_center) + + + +#### 3.那你知道什么是覆盖索引和回表吗? + +覆盖索引指的是在一次查询中,如果一个索引包含或者说覆盖所有需要查询的字段的值,我们就称之为覆盖索引,而不再需要回表查询。 + +而要确定一个查询是否是覆盖索引,我们只需要 explain sql 语句看 Extra 的结果是否是 “Using index” 即可。 + +以上面的 user 表来举例,我们再增加一个 name 字段,然后做一些查询试试。 + +``` +explain select * from user where age=1; // 查询的 name 无法从索引数据获取 +explain select id,age from user where age=1; // 可以直接从索引获取 +``` + +#### 4、锁的类型有哪些呢 + +mysql 锁分为共享锁和排他锁,也叫做读锁和写锁。 + +读锁是共享的,可以通过 lock in share mode 实现,这时候只能读不能写。 + +写锁是排他的,它会阻塞其他的写锁和读锁。从颗粒度来区分,可以分为表锁和行锁两种。 + +表锁会锁定整张表并且阻塞其他用户对该表的所有读写操作,比如 alter 修改表结构的时候会锁表。 + +行锁又可以分为乐观锁和悲观锁,悲观锁可以通过 for update 实现,乐观锁则通过版本号实现。 + +#### 5、你能说下事务的基本特性和隔离级别吗? + +事务基本特性 ACID 分别是: + +原子性指的是一个事务中的操作要么全部成功,要么全部失败。 + +一致性指的是数据库总是从一个一致性的状态转换到另外一个一致性的状态。比如 A 转账给 B100 块钱,假设中间 sql 执行过程中系统崩溃 A 也不会损失 100 块,因为事务没有提交,修改也就不会保存到数据库。 + +隔离性指的是一个事务的修改在最终提交前,对其他事务是不可见的。 + +持久性指的是一旦事务提交,所做的修改就会永久保存到数据库中。 + +而隔离性有 4 个隔离级别,分别是: + +read uncommit 读未提交,可能会读到其他事务未提交的数据,也叫做脏读。 + +用户本来应该读取到 id=1 的用户 age 应该是 10,结果读取到了其他事务还没有提交的事务,结果读取结果 age=20,这就是脏读。 + +![img](https://img-blog.csdnimg.cn/20201007141450216.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ1MjcwNjY3,size_1,color_FFFFFF,t_70#pic_center) + + +read commit 读已提交,两次读取结果不一致,叫做不可重复读。 + +不可重复读解决了脏读的问题,他只会读取已经提交的事务。 + +用户开启事务读取 id=1 用户,查询到 age=10,再次读取发现结果 = 20,在同一个事务里同一个查询读取到不同的结果叫做不可重复读。 + +![img](https://img-blog.csdnimg.cn/20201007141612150.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ1MjcwNjY3,size_16,color_FFFFFF,t_70#pic_center) + + +repeatable read 可重复复读,这是 mysql 的默认级别,就是每次读取结果都一样,但是有可能产生幻读。 + +serializable 串行,一般是不会使用的,他会给每一行读取的数据加锁,会导致大量超时和锁竞争的问题。 + +#### 6、那 ACID 靠什么保证的呢? + +A 原子性由 undo log 日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的 sql + +C 一致性一般由代码层面来保证 + +I 隔离性由 MVCC 来保证 + +D 持久性由内存 + redo log 来保证,mysql 修改数据同时在内存和 redo log 记录这次操作,事务提交的时候通过 redo log 刷盘,宕机的时候可以从 redo log 恢复 + +#### 7、那你说说什么是幻读,什么是 MVCC? + +要说幻读,首先要了解 MVCC,MVCC 叫做多版本并发控制,实际上就是保存了数据在某个时间节点的快照。 + +我们每行数实际上隐藏了两列,创建时间版本号,过期 (删除) 时间版本号,每开始一个新的事务,版本号都会自动递增。 + +还是拿上面的 user 表举例子,假设我们插入两条数据,他们实际上应该长这样。 + +a-draft-type=“table” data-size=“normal” data-row-style=“normal”> +id name create_version delete_version +这时候假设小明去执行查询,此时 current_version=3 + +select * from user where id<=3; + +同时,小红在这时候开启事务去修改 id=1 的记录,current_version=4 + +update user set name=‘张三三’ where id=1; + +执行成功后的结果是这样的 + +a-draft-type=“table” data-size=“normal” data-row-style=“normal”> +id name create_version delete_version +如果这时候还有小黑在删除 id=2 的数据,current_version=5,执行后结果是这样的。 + +a-draft-type=“table” data-size=“normal” data-row-style=“normal”> +id name create_version delete_version +由于 MVCC 的原理是查找创建版本小于或等于当前事务版本,删除版本为空或者大于当前事务版本,小明的真实的查询应该是这样 + +select * from user where id<=3 and create_version<=3 and (delete_version>3 or delete_version is null); + +所以小明最后查询到的 id=1 的名字还是’张三’,并且 id=2 的记录也能查询到。这样做是为了保证事务读取的数据是在事务开始前就已经存在的,要么是事务自己插入或者修改的。 + +明白 MVCC 原理,我们来说什么是幻读就简单多了。举一个常见的场景,用户注册时,我们先查询用户名是否存在,不存在就插入,假定用户名是唯一索引。 + +小明开启事务 current_version=6 查询名字为’王五’的记录,发现不存在。 +小红开启事务 current_version=7 插入一条数据,结果是这样: +a-draft-type=“table” data-size=“normal” data-row-style=“normal”> +id Name create_version delete_version +小明执行插入名字’王五’的记录,发现唯一索引冲突,无法插入,这就是幻读。 + +#### 8、 那你知道什么是间隙锁吗? + +间隙锁是可重复读级别下才会有的锁,结合 MVCC 和间隙锁可以解决幻读的问题。我们还是以 user 举例,假设现在 user 表有几条记录 + +e data-draft-node=“block” data-draft-type=“table” data-size=“normal” data-row-style=“normal”> +当我们执行: + +begin; + +select * from user where age=20 for update; + + begin; + + insert into user(age) values(10); #成功 + + insert into user(age) values(11); #失败 + + insert into user(age) values(20); #失败 + +insert into user(age) values(21); #失败 + + insert into user(age) values(30); #失败 + + + +只有 10 可以插入成功,那么因为表的间隙 mysql 自动帮我们生成了区间 (左开右闭) + +(negative infinity,10],(10,20],(20,30],(30,positive infinity) + +由于 20 存在记录,所以 (10,20],(20,30] 区间都被锁定了无法插入、删除。 + +如果查询 21 呢?就会根据 21 定位到 (20,30) 的区间(都是开区间)。 + +需要注意的是唯一索引是不会有间隙索引的。 + +#### 9、你们数据量级多大?分库分表怎么做的? + +首先分库分表分为垂直和水平两个方式,一般来说我们拆分的顺序是先垂直后水平。 + +**垂直分库** + +基于现在微服务拆分来说,都是已经做到了垂直分库了。 + +![img](https://img-blog.csdnimg.cn/20201007142110579.png#pic_center) + + +**垂直分表** + +如果表字段比较多,将不常用的、数据较大的等等做拆分。 + +![img](https://img-blog.csdnimg.cn/20201007142229828.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ1MjcwNjY3,size_1,color_FFFFFF,t_70#pic_center) +**水平分表** + +首先根据业务场景来决定使用什么字段作为分表字段 (sharding_key),比如我们现在日订单 1000 万,我们大部分的场景来源于 C 端,我们可以用 user_id 作为 sharding_key,数据查询支持到最近 3 个月的订单,超过 3 个月的做归档处理,那么 3 个月的数据量就是 9 亿,可以分 1024 张表,那么每张表的数据大概就在 100 万左右。 + +比如用户 id 为 100,那我们都经过 hash(100),然后对 1024 取模,就可以落到对应的表上了。 + +#### 10、那分表后的 ID 怎么保证唯一性的呢? + +因为我们主键默认都是自增的,那么分表之后的主键在不同表就肯定会有冲突了。有几个办法考虑: + +1. 设定步长,比如 1-1024 张表我们分别设定 1-1024 的基础步长,这样主键落到不同的表就不会冲突了。 +2. 分布式 ID,自己实现一套分布式 ID 生成算法或者使用开源的比如雪花算法这种。 +3. 分表后不使用主键作为查询依据,而是每张表单独新增一个字段作为唯一主键使用,比如订单表订单号是唯一的,不管最终落在哪张表都基于订单号作为查询依据,更新也一样。 + +#### 11、 分表后非 sharding_key 的查询怎么处理呢? + +1. 可以做一个 mapping 表,比如这时候商家要查询订单列表怎么办呢?不带 user_id 查询的话你总不能扫全表吧?所以我们可以做一个映射关系表,保存商家和用户的关系,查询的时候先通过商家查询到用户列表,再通过 user_id 去查询。 +2. 打宽表,一般而言,商户端对数据实时性要求并不是很高,比如查询订单列表,可以把订单表同步到离线(实时)数仓,再基于数仓去做成一张宽表,再基于其他如 es 提供查询服务。 +3. 数据量不是很大的话,比如后台的一些查询之类的,也可以通过多线程扫表,然后再聚合结果的方式来做。或者异步的形式也是可以的。 + +``` +List>> taskList = Lists.newArrayList(); for (int shardingIndex = 0; shardingIndex < 1024; shardingIndex++) { taskList.add(() -> (userMapper.getProcessingAccountList(shardingIndex))); } List list = null; try { list = taskExecutor.executeTask(taskList); } catch (Exception e) { //do something } public class TaskExecutor { public List executeTask(Collection> tasks) throws Exception { List result = Lists.newArrayList(); List> futures = ExecutorUtil.invokeAll(tasks); for (Future future : futures) { result.add(future.get()); } return result; } } +``` + +#### 12、说说 mysql 主从同步怎么做的吧? + +首先先了解 mysql 主从同步的原理 + +1. master 提交完事务后,写入 binlog + +2. slave 连接到 master,获取 binlog + +3. master 创建 dump 线程,推送 binglog 到 slave + +4. slave 启动一个 IO 线程读取同步过来的 master 的 binlog,记录到 relay log 中继日志中 + +5. slave 再开启一个 sql 线程读取 relay log 事件并在 slave 执行,完成同步 + +6. slave 记录自己的 binglog + + ![img](https://img-blog.csdnimg.cn/20201007142848225.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ1MjcwNjY3,size_1,color_FFFFFF,t_70#pic_center) + + 由于 mysql 默认的复制方式是异步的,主库把日志发送给从库后不关心从库是否已经处理,这样会产生一个问题就是假设主库挂了,从库处理失败了,这时候从库升为主库后,日志就丢失了。由此产生两个概念。 + +**全同步复制** + +主库写入 binlog 后强制同步日志到从库,所有的从库都执行完成后才返回给客户端,但是很显然这个方式的话性能会受到严重影响。 + +**半同步复制** + +和全同步不同的是,半同步复制的逻辑是这样,从库写入日志成功后返回 ACK 确认给主库,主库收到至少一个从库的确认就认为写操作完成。 + +#### 13、那主从的延迟怎么解决呢? + +这个问题貌似真的是个无解的问题,只能是说自己来判断了,需要走主库的强制走主库查询。 + +#### 14.Mysql中有哪几种锁? + +1.表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。 + +2.行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。 + +3.页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。 + +#### 15.主键和候选键有什么区别? + +表格的每一行都由主键唯一标识,一个表只有一个主键。 + +主键也是候选键。按照惯例,候选键可以被指定为主键,并且可以用于任何外键引用。 + +#### 16.myisamchk是用来做什么的? + +它用来压缩MyISAM表,这减少了磁盘或内存使用。 + +#### 17.MyISAM Static和MyISAM Dynamic有什么区别? + +在MyISAM Static上的所有字段有固定宽度。动态MyISAM表将具有像TEXT,BLOB等字段,以适应不同长度的数据类型。 + +MyISAM Static在受损情况下更容易恢复。 + +#### 18.如果一个表有一列定义为TIMESTAMP,将发生什么? + +每当行被更改时,时间戳字段将获取当前时间戳。 + +#### 19.列设置为AUTO INCREMENT时,如果在表中达到最大值,会发生什么情况? + +它会停止递增,任何进一步的插入都将产生错误,因为密钥已被使用。 + +#### 20.怎样才能找出最后一次插入时分配了哪个自动增量? + +LAST_INSERT_ID将返回由Auto_increment分配的最后一个值,并且不需要指定表名称。 + +#### 21.你怎么看到为表格定义的所有索引? + +索引是通过以下方式为表格定义的: + +SHOW INDEX FROM +; + +#### 22.LIKE声明中的%和_是什么意思? + +%对应于0个或更多字符,_只是LIKE语句中的一个字符。 + +#### 23.如何在Unix和Mysql时间戳之间进行转换? + +UNIX_TIMESTAMP是从Mysql时间戳转换为Unix时间戳的命令 +FROM_UNIXTIME是从Unix时间戳转换为Mysql时间戳的命令 + +#### 24.列对比运算符是什么? + +在SELECT语句的列比较中使用=,<>,<=,<,> =,>,<<,>>,<=>,AND,OR或LIKE运算符。 + +#### 25.BLOB和TEXT有什么区别? + +BLOB是一个二进制对象,可以容纳可变数量的数据。TEXT是一个不区分大小写的BLOB。 + +BLOB和TEXT类型之间的唯一区别在于对BLOB值进行排序和比较时区分大小写,对TEXT值不区分大小写。 + +#### 26.mysql_fetch_array和mysql_fetch_object的区别是什么? + +以下是mysql_fetch_array和mysql_fetch_object的区别: + +mysql_fetch_array() – 将结果行作为关联数组或来自数据库的常规数组返回。 + +mysql_fetch_object – 从数据库返回结果行作为对象。 + +#### 27.MyISAM表格将在哪里存储,并且还提供其存储格式? + +每个MyISAM表格以三种格式存储在磁盘上: + +·“.frm”文件存储表定义 + +·数据文件具有“.MYD”(MYData)扩展名 + +索引文件具有“.MYI”(MYIndex)扩展名 + +#### 28.Mysql如何优化DISTINCT? + +DISTINCT在所有列上转换为GROUP BY,并与ORDER BY子句结合使用。 + +1 + +SELECT DISTINCT t1.a FROM t1,t2 where t1.a=t2.a; + +#### 29.如何显示前50行? + +在Mysql中,使用以下代码查询显示前50行: + +SELECT*FROM + +LIMIT 0,50; + +#### 30.可以使用多少列创建索引? + +任何标准表最多可以创建16个索引列。 + +#### 31.NOW()和CURRENT_DATE()有什么区别? + +NOW()命令用于显示当前年份,月份,日期,小时,分钟和秒。 + +CURRENT_DATE()仅显示当前年份,月份和日期。 + +#### 32.什么是非标准字符串类型? + +1. TINYTEXT +2. TEXT +3. MEDIUMTEXT +4. LONGTEXT + +#### 33.什么是通用SQL函数? + +1. CONCAT(A, B) – 连接两个字符串值以创建单个字符串输出。通常用于将两个或多个字段合并为一个字段。 +2. FORMAT(X, D)- 格式化数字X到D有效数字。 +3. CURRDATE(), CURRTIME()- 返回当前日期或时间。 +4. NOW() – 将当前日期和时间作为一个值返回。 +5. MONTH(),DAY(),YEAR(),WEEK(),WEEKDAY() – 从日期值中提取给定数据。 +6. HOUR(),MINUTE(),SECOND() – 从时间值中提取给定数据。 +7. DATEDIFF(A,B) – 确定两个日期之间的差异,通常用于计算年龄 +8. SUBTIMES(A,B) – 确定两次之间的差异。 +9. FROMDAYS(INT) – 将整数天数转换为日期值。 + +#### 34.mysql里记录货币用什么字段类型好 + +NUMERIC和DECIMAL类型被Mysql实现为同样的类型,这在SQL92标准允许。他们被用于保存值,该值的准确精度是极其重要的值,例如与金钱有关的数据。当声明一个类是这些类型之一时,精度和规模的能被(并且通常是)指定。 + +例如: + +salary DECIMAL(9,2) + +在这个例子中,9(precision)代表将被用于存储值的总的小数位数,而2(scale)代表将被用于存储小数点后的位数。 + +因此,在这种情况下,能被存储在salary列中的值的范围是从-9999999.99到9999999.99。 + +#### 35.mysql有关权限的表都有哪几个? + +Mysql服务器通过权限表来控制用户对数据库的访问,权限表存放在mysql数据库里,由mysql_install_db脚本初始化。这些权限表分别user,db,table_priv,columns_priv和host。 + +#### 36.列的字符串类型可以是什么? + +字符串类型是: + +1. SET +2. BLOB +3. ENUM +4. CHAR +5. TEXT + +#### 37.MySQL数据库作发布系统的存储,一天五万条以上的增量,预计运维三年,怎么优化? + +a. 设计良好的数据库结构,允许部分数据冗余,尽量避免join查询,提高效率。 +b. 选择合适的表字段数据类型和存储引擎,适当的添加索引。 +c. mysql库主从读写分离。 +d. 找规律分表,减少单表中的数据量提高查询速度。 +e。添加缓存机制,比如memcached,apc等。 +f. 不经常改动的页面,生成静态页面。 +g. 书写高效率的SQL。比如 SELECT * FROM TABEL 改为 SELECT field_1, field_2, field_3 FROM TABLE. + +#### 38.锁的优化策略 + +1. 读写分离 + +2. 分段加锁 + +3. 减少锁持有的时间 + +4. 多个线程尽量以相同的顺序去获取资源 + +不能将锁的粒度过于细化,不然可能会出现线程的加锁和释放次数过多,反而效率不如一次加一把大锁。 + +#### 39.索引的底层实现原理和优化 + +B+树,经过优化的B+树 + +主要是在所有的叶子结点中增加了指向下一个叶子节点的指针,因此InnoDB建议为大部分表使用默认自增的主键作为主索引。 + +#### 40.什么情况下设置了索引但无法使用 + +​ 1.以“%”开头的LIKE语句,模糊匹配 + +2. OR语句前后没有同时使用索引 + +3. 数据类型出现隐式转化(如varchar不加单引号的话可能会自动转换为int型) + +#### 41.实践中如何优化MySQL + +最好是按照以下顺序优化: + +1.SQL语句及索引的优化 + +2.数据库表结构的优化 + +3.系统配置的优化 + +4.硬件的优化 + +详细可以查看 [阿里P8架构师谈:MySQL慢查询优化、索引优化、以及表等优化总结](https://link.zhihu.com/?target=http%3A//youzhixueyuan.com/mysql-slow-query-optimization-index-optimization.html) + +#### 42.优化数据库的方法 + +1. 选取最适用的字段属性,尽可能减少定义字段宽度,尽量把字段设置NOTNULL,例如’省份’、’性别’最好适用ENUM +2. 使用连接(JOIN)来代替子查询 +3. 适用联合(UNION)来代替手动创建的临时表 +4. 事务处理 +5. 锁定表、优化事务处理 +6. 适用外键,优化锁定表 +7. 建立索引 +8. 优化查询语句 + +#### 43.简单描述mysql中,索引,主键,唯一索引,联合索引的区别,对数据库的性能有什么影响(从读写两方面) + +索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。 + +普通索引(由关键字KEY或INDEX定义的索引)的唯一任务是加快对数据的访问速度。 + +普通索引允许被索引的数据列包含重复的值。如果能确定某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该用关键字UNIQUE把它定义为一个唯一索引。也就是说,唯一索引可以保证数据记录的唯一性。 + +主键,是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯一标识一条记录,使用关键字 PRIMARY KEY 来创建。 + +索引可以覆盖多个数据列,如像INDEX(columnA, columnB)索引,这就是联合索引。 + +索引可以极大的提高数据的查询速度,但是会降低插入、删除、更新表的速度,因为在执行这些写操作时,还要操作索引文件。 + +#### 44.SQL注入漏洞产生的原因?如何防止? + +SQL注入产生的原因:程序开发过程中不注意规范书写sql语句和对特殊字符进行过滤,导致客户端可以通过全局变量POST和GET提交一些sql语句正常执行。 + +防止SQL注入的方式: +开启配置文件中的magic_quotes_gpc 和 magic_quotes_runtime设置 + +执行sql语句时使用addslashes进行sql语句转换 + +Sql语句书写尽量不要省略双引号和单引号。 + +过滤掉sql语句中的一些关键词:update、insert、delete、select、 * 。 + +提高数据库表和字段的命名技巧,对一些重要的字段根据程序的特点命名,取不易被猜到的。 + +#### 45.为表中得字段选择合适得数据类型 + +字段类型优先级: 整形>date,time>enum,char>varchar>blob,text +优先考虑数字类型,其次是日期或者二进制类型,最后是字符串类型,同级别得数据类型,应该优先选择占用空间小的数据类型 + +**存储时期** + +Datatime:以 YYYY-MM-DD HH:MM:SS 格式存储时期时间,精确到秒,占用8个字节得存储空间,datatime类型与时区无关 +Timestamp:以时间戳格式存储,占用4个字节,范围小1970-1-1到2038-1-19,显示依赖于所指定得时区,默认在第一个列行的数据修改时可以自动得修改timestamp列得值 +Date:(生日)占用得字节数比使用字符串.[http://datatime.int](https://link.zhihu.com/?target=http%3A//datatime.int)储存要少,使用date只需要3个字节,存储日期月份,还可以利用日期时间函数进行日期间得计算 +Time:存储时间部分得数据 +注意:不要使用字符串类型来存储日期时间数据(通常比字符串占用得储存空间小,在进行查找过滤可以利用日期得函数) +使用int存储日期时间不如使用timestamp类型 + +#### 46.对于关系型数据库而言,索引是相当重要的概念,请回答有关索引的几个问题: + +1.索引的目的是什么? +快速访问数据表中的特定信息,提高检索速度 +创建唯一性索引,保证数据库表中每一行数据的唯一性。 +加速表和表之间的连接 + +使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间 + +2.索引对数据库系统的负面影响是什么? +负面影响: +创建索引和维护索引需要耗费时间,这个时间随着数据量的增加而增加;索引需要占用物理空间,不光是表需要占用数据空间,每个索引也需要占用物理空间;当对表进行增、删、改、的时候索引也要动态维护,这样就降低了数据的维护速度。 + +3.为数据表建立索引的原则有哪些? +在最频繁使用的、用以缩小查询范围的字段上建立索引。 + +在频繁使用的、需要排序的字段上建立索引 + +4.什么情况下不宜建立索引? +对于查询中很少涉及的列或者重复值比较多的列,不宜建立索引。 + +对于一些特殊的数据类型,不宜建立索引,比如文本字段(text)等 + +#### 47.解释MySQL外连接、内连接与自连接的区别 + +先说什么是交叉连接: 交叉连接又叫笛卡尔积,它是指不使用任何条件,直接将一个表的所有记录和另一个表中的所有记录一一匹配。 + +内连接 则是只有条件的交叉连接,根据某个条件筛选出符合条件的记录,不符合条件的记录不会出现在结果集中,即内连接只连接匹配的行。 +外连接 其结果集中不仅包含符合连接条件的行,而且还会包括左表、右表或两个表中 +的所有数据行,这三种情况依次称之为左外连接,右外连接,和全外连接。 + +左外连接,也称左连接,左表为主表,左表中的所有记录都会出现在结果集中,对于那些在右表中并没有匹配的记录,仍然要显示,右边对应的那些字段值以NULL来填充。右外连接,也称右连接,右表为主表,右表中的所有记录都会出现在结果集中。左连接和右连接可以互换,MySQL目前还不支持全外连接。 + +#### 48.Myql中的事务回滚机制概述 + +事务是用户定义的一个数据库操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位,事务回滚是指将该事务已经完成的对数据库的更新操作撤销。 + +要同时修改数据库中两个不同表时,如果它们不是一个事务的话,当第一个表修改完,可能第二个表修改过程中出现了异常而没能修改,此时就只有第二个表依旧是未修改之前的状态,而第一个表已经被修改完毕。而当你把它们设定为一个事务的时候,当第一个表修改完,第二表修改出现异常而没能修改,第一个表和第二个表都要回到未修改的状态,这就是所谓的事务回滚 + +#### 49.SQL语言包括哪几部分?每部分都有哪些操作关键字? + +SQL语言包括数据定义(DDL)、数据操纵(DML),数据控制(DCL)和数据查询(DQL)四个部分。 + +数据定义:Create Table,Alter Table,Drop Table, Craete/Drop Index等 + +数据操纵:Select ,insert,update,delete, + +数据控制:grant,revoke + +数据查询:select + +#### 50.完整性约束包括哪些? + +数据完整性(Data Integrity)是指数据的精确(Accuracy)和可靠性(Reliability)。 + +**分为以下四类:** + +1) 实体完整性:规定表的每一行在表中是惟一的实体。 + +2) 域完整性:是指表中的列必须满足某种特定的数据类型约束,其中约束又包括取值范围、精度等规定。 + +3) 参照完整性:是指两个表的主关键字和外关键字的数据应一致,保证了表之间的数据的一致性,防止了数据丢失或无意义的数据在数据库中扩散。 + +4) 用户定义的完整性:不同的关系数据库系统根据其应用环境的不同,往往还需要一些特殊的约束条件。用户定义的完整性即是针对某个特定关系数据库的约束条件,它反映某一具体应用必须满足的语义要求。 + +与表有关的约束:包括列约束(NOT NULL(非空约束))和表约束(PRIMARY KEY、foreign key、check、UNIQUE) 。 + +#### 参考 + +https://zhuanlan.zhihu.com/p/59838091 + +https://blog.csdn.net/m0_45270667/article/details/108950184 + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file diff --git a/Mybatis.md b/Mybatis.md new file mode 100644 index 0000000..6b35aaf --- /dev/null +++ b/Mybatis.md @@ -0,0 +1,255 @@ +## Mybatis + +#### 1.什么是Mybatis? + +MyBatis 是一款优秀的支持自定义 SQL 查询、存储过程和高级映射的持久层框架,消除了 几乎所有的 JDBC 代码和参数的手动设置以及结果集的检索 。 MyBatis 可以使用 XML 或注解进 行配置和映射, MyBatis 通过将参数映射到配置的 SQL 形成最终执行的 SQL 语句 ,最后将执行 SQL 的结果映射成 Java对象返回。 + +#### 2.Hibernate优点? + +Hibernate建立在POJO和数据库表模型的直接映射关系上。通过xml或注解即可和数据库表做映射。通过pojo直接可以操作数据库的数据。它提供的是全表的映射模型。 + +- 消除代码映射规则,被分离到xml或注解里配置。 +- 无需在管理数据库连接,配置在xml中即可。 +- 一个会话中,不要操作多个对象,只要操作Session对象即可。 +- 关闭资源只需关闭Session即可。 + +#### 3.Hibernate缺点? + +- 全表映射带来的不便,比如更新需要发送所有的字段。 +- 无法根据不同的条件组装不同的sql。 +- 对多表关联和复杂的sql查询支持较差,需要自己写sql,返回后,需要自己将数据组成pojo。 +- 不能有效支持存储过程。 +- 虽然有hql,但是性能较差,大型互联网需要优化sql,而hibernate做不到。 + +#### 4.Mybatis优点? + +1. 小巧,学习成本低,会写sql上手就很快了。 +2. 比jdbc,基本上配置好了,大部分的工作量就专注在sql的部分。 +3. 方便维护管理,sql不需要在Java代码中找,sql代码可以分离出来,重用。 +4. 接近jdbc,灵活,支持动态sql。 +5. 支持对象与数据库orm字段关系映射。 + +#### 5.Mybatis缺点? + +- 由于工作量在sql上,需要 sql的熟练度高。 +- 移植性差。sql语法依赖数据库。不同数据库的切换会因语法差异,会报错。 + +#### 6.什么时候用Mybatis? + +如果你需要一个灵活的、可以动态生成映射关系的框架。目前来说,因为适合,互联网项目用Mybatis的比例还是很高滴。 + +#### 7.Mybatis的核心组件有哪些?分别是? + +SqlSessionFactoryBuilder(构造器) :它会根据配置信息或者代码来生成SqlSessionFactory。 + +SqlSessionFactory(工厂接口):依靠工厂来生成SqlSession。 + +SqlSession(会话):是一个既可以发送 sql去执行返回结果,也可以获取Mapper接口。 + +SQL Mapper:它是新设计的组件,是由一个Java接口和XML文件(或注解)构成的。需要给出对象的SQl和映射规则。它负责发送SQL去执行,并返回结果。 + +#### 8.#{}和${}的区别是什么? + +${}是字符串替换,#{}是预编译处理。一般用#{}防止 sql注入问题。 + +#### 9.Mybatis中9个动态标签是? + +- if +- c h o o s e (when 、 oterwise) +- trim (where、 set) +- foreach +- bind + +#### 10.xml映射文件中,有哪些标签? + +select|insert|updae|delete|resultMap|parameterMap|sql|include|selectKey 加上9个动态标签,其中为sql片段标签,通过标签引入sql片段,为不支持自增的主键生成策略标签。 + +#### 11.Mybatis支持注解吗?优点?缺点? + +支持。 + +优点:对于需求简单sql逻辑简单的系统,效率较高。 + +缺点: 当sql变化需要重新编译代码,sql复杂时,写起来更不方便,不好维护。 + +#### 12.Mybatis动态sql? + +Mybatis 动态 sql 可以让我们在 Xml 映射文件内,以标签的形式编写动态 sql,完成逻辑 +判断和动态拼接 sql 的功能 + +#### 13.**Mybatis 是如何进行分页的?分页插件的原理是什么?** + +1)Mybatis 使用 RowBounds 对象进行分页,也可以直接编写 sql 实现分页,也可以使用 +Mybatis 的分页插件。 +2)分页插件的原理:实现 Mybatis 提供的接口,实现自定义插件,在插件的拦截方法内拦 +截待执行的 sql,然后重写 sql。 +举例:select from student,拦截 sql 后重写为:select t. from (select from student)t +limit 0,10 + +#### 14.如何获取自增主键? + +注解: + +```java +@Options(useGeneratedKeys =true, keyProperty =”id”) + +int insert( ); +``` + +Xml: + +```xml + + +sql + + +``` + +#### 15.为什么Mapper接口没有实现类,却能被正常调用? + +这是因为MyBatis在Mapper接口上使用了动态代理。 + +#### 16.用注解好还是xml好? + +简单的增删改查可以注解。 + +复杂的sql还是用xml,官方也比较推荐xml方式。 + +xml的方式更便于统一维护管理代码。 + +#### 17.如果不想手动指定别名,如何用驼峰的形式自动映射? + +mapUnderscoreToCamelCase=true + +#### 18.当实体属性名和表中字段不一致,怎么办? + + 一、别名: 比如 order_num + +select order_num orderNum + +二、通过做映射 + +​ + +三、如果是驼峰注解用17问的方式。 + +#### 19.嵌套查询用什么标签? + +association 标签的嵌套查询常用的属性如下 。 + +select:另一个映射查询的 id, MyBatis会额外执行这个查询获取嵌套对象的结果。 + +column:列名(或别名),将主查询中列的结果作为嵌套查询的 参数,配置 方式如 column={propl=coll , prop2=col2}, propl 和 prop2 将作为嵌套查询的参数。 + +fetch Type:数据加载方式,可选值为 lazy 和 eager,分别为延迟加载和积极加载, 这个配置会覆盖全局的 lazyLoadingEnabled 配置 。 + +#### 20.like模糊查询怎么写? + +(1)'%${value}%' 不推荐 + +(2)CONCAT('%',#{value},'%') 推荐 + + (3) like '%'|| #{value} || '%' + +(4)使用bind + + + + LIKE #{pattern} + +#### 21.Mybatis支持枚举吗? + +支持。有转换器EnumTypeHandler和EnumOrdinalTypeHandler + +```xml + + + +``` + +#### 22.SqlSessionFactoryBuilder生命周期? + +利用xml或者Java代码获得资源构建SqlSessionFactoryBuilder,它可以创建多个SessionFactory,一旦构建成功SessionFactory,就可以回收它了。 + +#### 23.一级缓存的结构?如何开启一级缓存?如何不使用一级缓存? + +Map 。默认情况下,一级缓存是开启的。< select >标签内加属性flushCache=true。 + +#### 24.二级缓存如何配置? + +```xml + +〈 !一其他自己直一 〉 + + +``` + +这个参数是二级缓存的全局开关,默认值是 true,初始状态 为启用状态 。 如果把这个参数设置为 false,即使有后面 的 二级缓 存配置,也不会生效 。 + +#### 25.**简述 Mybatis 的插件运行原理,以及如何编写一个插件?** + +1)Mybatis 仅可以编写针对 ParameterHandler、ResultSetHandler、StatementHandler、 +Executor 这 4 种接口的插件,Mybatis 通过动态代理,为需要拦截的接口生成代理对象以实 +现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是 +InvocationHandler 的 invoke()方法,当然,只会拦截那些你指定需要拦截的方法。 +2)实现 Mybatis 的 Interceptor 接口并复写 intercept()方法,然后在给插件编写注解,指定 +要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。 + +#### 26.二级缓存的回收策略有哪些? + +eviction (收回策略) + +LRU(最近最少使用的) : 移除最长时间不被使用的对象,这是默认值 。 + + IFO(先进先出〉 : 按对象进入缓存的顺序来移除它们 。 + + SOFT(软引用) : 移除基于垃圾回收器状态和软引用规则的对象 。 + +WEAK (弱引用) : 更积极地移除基于垃圾收集器状态和弱引用规则的对象 。 + +#### 27.Mybatis的Xml文件中id可以重复吗? + +同一namespace下,id不可重复。不同namespace下,可以重复。 + +#### 28. 和Mybatis搭配java框架中比较好用的缓存框架?有哪些特点? + +EhCache是一个纯牌的 Java进程内的缓存框架,具有快速、精干等特点。 + +具体来说, EhCache主要的特性如下 。 + +- 快速。 +- 简单。 +- 多种缓存策略 。 +- 缓存数据有内存和磁盘两级,无须担心容量问题 。 +- 缓存数据会在虚拟机重启 的过程中写入磁盘。 +- 可 以通过 RMI、可插入 API 等方式进行分布式缓存。 +- .具有缓存和缓存管理器的侦 昕接口。 +- 支持多缓存管理器实例 以及一个实例的多个缓存区域。 + +#### 29.说一下resultMap和resultType + +resultmap是手动提交,人为提交,resulttype是自动提交 +MyBatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap,resultType是直接表示返回类型的,而resultMap则是对外部ResultMap的引用,但是resultType跟resultMap不能同时存在。 +在MyBatis进行查询映射时,其实查询出来的每一个属性都是放在一个对应的Map里面的,其中键是属性名,值则是其对应的值。 +1.当提供的返回类型属性是resultType时,MyBatis会将Map里面的键值对取出赋给resultType所指定的对象对应的属性。所以其实MyBatis的每一个查询映射的返回类型都是ResultMap,只是当提供的返回类型属性是resultType的时候,MyBatis对自动的给把对应的值赋给resultType所指定对象的属性。 +2.当提供的返回类型是resultMap时,因为Map不能很好表示领域模型,就需要自己再进一步的把它转化为对应的对象,这常常在复杂查询中很有作用。 + +#### 30.Mybatis动态sql有什么用?执行原理?有哪些动态sql? + +有九种动态sql标签:trim,where,set,foreach,if,choose,when,bind,otherwise +Mybatis的动态sql可以在xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值,完成逻辑判断并动态拼接sql的功能! + +#### 参考 + +《 MyBatis从入门到精通》 + +《深入浅出 MyBatis技术原理与实战》 + +《MyBatis技术》 + +https://zhuanlan.zhihu.com/p/60257737 + + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) + diff --git a/Netty.md b/Netty.md new file mode 100644 index 0000000..0140557 --- /dev/null +++ b/Netty.md @@ -0,0 +1,587 @@ +## Netty + +#### 1.Netty的特点? + +- 一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持 +- 使用更高效的socket底层,对epoll空轮询引起的cpu占用飙升在内部进行了处理,避免了直接使用NIO的陷阱,简化了NIO的处理方式。 +- 采用多种decoder/encoder 支持,对TCP粘包/分包进行自动化处理 +- 可使用接受/处理线程池,提高连接效率,对重连、心跳检测的简单支持 +- 可配置IO线程数、TCP参数, TCP接收和发送缓冲区使用直接内存代替堆内存,通过内存池的方式循环利用ByteBuf +- 通过引用计数器及时申请释放不再引用的对象,降低了GC频率 +- 使用单线程串行化的方式,高效的Reactor线程模型 +- 大量使用了volitale、使用了CAS和原子类、线程安全类的使用、读写锁的使用 + +#### 2.Netty的线程模型? + +- Netty通过Reactor模型基于多路复用器接收并处理用户请求,内部实现了两个线程池,boss线程池和work线程池,其中boss线程池的线程负责处理请求的accept事件,当接收到accept事件的请求时,把对应的socket封装到一个NioSocketChannel中,并交给work线程池,其中work线程池负责请求的read和write事件,由对应的Handler处理。 +- 单线程模型:所有I/O操作都由一个线程完成,即多路复用、事件分发和处理都是在一个Reactor线程上完成的。既要接收客户端的连接请求,向服务端发起连接,又要发送/读取请求或应答/响应消息。一个NIO 线程同时处理成百上千的链路,性能上无法支撑,速度慢,若线程进入死循环,整个程序不可用,对于高负载、大并发的应用场景不合适。 +- 多线程模型:有一个NIO 线程(Acceptor) 只负责监听服务端,接收客户端的TCP 连接请求;NIO 线程池负责网络IO 的操作,即消息的读取、解码、编码和发送;1 个NIO 线程可以同时处理N 条链路,但是1 个链路只对应1 个NIO 线程,这是为了防止发生并发操作问题。但在并发百万客户端连接或需要安全认证时,一个Acceptor 线程可能会存在性能不足问题。 +- 主从多线程模型:Acceptor 线程用于绑定监听端口,接收客户端连接,将SocketChannel 从主线程池的Reactor 线程的多路复用器上移除,重新注册到Sub 线程池的线程上,用于处理I/O 的读写等操作,从而保证mainReactor只负责接入认证、握手等操作; + +#### 3.TCP 粘包/拆包的原因及解决方法? + +- TCP是以流的方式来处理数据,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送。 +- TCP粘包/分包的原因: + - 应用程序写入的字节大小大于套接字发送缓冲区的大小,会发生拆包现象,而应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包现象; + - 进行MSS大小的TCP分段,当TCP报文长度-TCP头部长度>MSS的时候将发生拆包 + - 以太网帧的payload(净荷)大于MTU(1500字节)进行ip分片。 +- 解决方法 + - 消息定长:FixedLengthFrameDecoder类 + - 包尾增加特殊字符分割:行分隔符类:LineBasedFrameDecoder或自定义分隔符类 :DelimiterBasedFrameDecoder + - 将消息分为消息头和消息体:LengthFieldBasedFrameDecoder类。分为有头部的拆包与粘包、长度字段在前且有头部的拆包与粘包、多扩展头部的拆包与粘包。 + +#### 4.了解哪几种序列化协议? + +- 序列化(编码)是将对象序列化为二进制形式(字节数组),主要用于网络传输、数据持久化等;而反序列化(解码)则是将从网络、磁盘等读取的字节数组还原成原始对象,主要用于网络传输对象的解码,以便完成远程调用。 +- 影响序列化性能的关键因素:序列化后的码流大小(网络带宽的占用)、序列化的性能(CPU资源占用);是否支持跨语言(异构系统的对接和开发语言切换)。 +- Java默认提供的序列化:无法跨语言、序列化后的码流太大、序列化的性能差 +- XML,优点:人机可读性好,可指定元素或特性的名称。缺点:序列化数据只包含数据本身以及类的结构,不包括类型标识和程序集信息;只能序列化公共属性和字段;不能序列化方法;文件庞大,文件格式复杂,传输占带宽。适用场景:当做配置文件存储数据,实时数据转换。 +- JSON,是一种轻量级的数据交换格式,优点:兼容性高、数据格式比较简单,易于读写、序列化后数据较小,可扩展性好,兼容性好、与XML相比,其协议比较简单,解析速度比较快。缺点:数据的描述性比XML差、不适合性能要求为ms级别的情况、额外空间开销比较大。适用场景(可替代XML):跨防火墙访问、可调式性要求高、基于Web browser的Ajax请求、传输数据量相对小,实时性要求相对低(例如秒级别)的服务。 +- Fastjson,采用一种“假定有序快速匹配”的算法。优点:接口简单易用、目前java语言中最快的json库。缺点:过于注重快,而偏离了“标准”及功能性、代码质量不高,文档不全。适用场景:协议交互、Web输出、Android客户端 +- Thrift,不仅是序列化协议,还是一个RPC框架。优点:序列化后的体积小, 速度快、支持多种语言和丰富的数据类型、对于数据字段的增删具有较强的兼容性、支持二进制压缩编码。缺点:使用者较少、跨防火墙访问时,不安全、不具有可读性,调试代码时相对困难、不能与其他传输层协议共同使用(例如HTTP)、无法支持向持久层直接读写数据,即不适合做数据持久化序列化协议。适用场景:分布式系统的RPC解决方案 +- Avro,Hadoop的一个子项目,解决了JSON的冗长和没有IDL的问题。优点:支持丰富的数据类型、简单的动态语言结合功能、具有自我描述属性、提高了数据解析速度、快速可压缩的二进制数据形式、可以实现远程过程调用RPC、支持跨编程语言实现。缺点:对于习惯于静态类型语言的用户不直观。适用场景:在Hadoop中做Hive、Pig和MapReduce的持久化数据格式。 +- Protobuf,将数据结构以.proto文件进行描述,通过代码生成工具可以生成对应数据结构的POJO对象和Protobuf相关的方法和属性。优点:序列化后码流小,性能高、结构化数据存储格式(XML JSON等)、通过标识字段的顺序,可以实现协议的前向兼容、结构化的文档更容易管理和维护。缺点:需要依赖于工具生成代码、支持的语言相对较少,官方只支持Java 、C++ 、python。适用场景:对性能要求高的RPC调用、具有良好的跨防火墙的访问属性、适合应用层对象的持久化 +- 其它 + - protostuff 基于protobuf协议,但不需要配置proto文件,直接导包即可 + - Jboss marshaling 可以直接序列化java类, 无须实java.io.Serializable接口 + - Message pack 一个高效的二进制序列化格式 + - Hessian 采用二进制协议的轻量级remoting onhttp工具 + - kryo 基于protobuf协议,只支持java语言,需要注册(Registration),然后序列化(Output),反序列化(Input) + +#### 5.如何选择序列化协议? + +- 具体场景 + - 对于公司间的系统调用,如果性能要求在100ms以上的服务,基于XML的SOAP协议是一个值得考虑的方案。 + - 基于Web browser的Ajax,以及Mobile app与服务端之间的通讯,JSON协议是首选。对于性能要求不太高,或者以动态类型语言为主,或者传输数据载荷很小的的运用场景,JSON也是非常不错的选择。 + - 对于调试环境比较恶劣的场景,采用JSON或XML能够极大的提高调试效率,降低系统开发成本。 + - 当对性能和简洁性有极高要求的场景,Protobuf,Thrift,Avro之间具有一定的竞争关系。 + - 对于T级别的数据的持久化应用场景,Protobuf和Avro是首要选择。如果持久化后的数据存储在hadoop子项目里,Avro会是更好的选择。 + - 对于持久层非Hadoop项目,以静态类型语言为主的应用场景,Protobuf会更符合静态类型语言工程师的开发习惯。由于Avro的设计理念偏向于动态类型语言,对于动态语言为主的应用场景,Avro是更好的选择。 + - 如果需要提供一个完整的RPC解决方案,Thrift是一个好的选择。 + - 如果序列化之后需要支持不同的传输层协议,或者需要跨防火墙访问的高性能场景,Protobuf可以优先考虑。 +- protobuf的数据类型有多种:bool、double、float、int32、int64、string、bytes、enum、message。protobuf的限定符:required: 必须赋值,不能为空、optional:字段可以赋值,也可以不赋值、repeated: 该字段可以重复任意次数(包括0次)、枚举;只能用指定的常量集中的一个值作为其值; +- protobuf的基本规则:每个消息中必须至少留有一个required类型的字段、包含0个或多个optional类型的字段;repeated表示的字段可以包含0个或多个数据;[1,15]之内的标识号在编码的时候会占用一个字节(常用),[16,2047]之内的标识号则占用2个字节,标识号一定不能重复、使用消息类型,也可以将消息嵌套任意多层,可用嵌套消息类型来代替组。 +- protobuf的消息升级原则:不要更改任何已有的字段的数值标识;不能移除已经存在的required字段,optional和repeated类型的字段可以被移除,但要保留标号不能被重用。新添加的字段必须是optional或repeated。因为旧版本程序无法读取或写入新增的required限定符的字段。 +- 编译器为每一个消息类型生成了一个.java文件,以及一个特殊的Builder类(该类是用来创建消息类接口的)。如:UserProto.User.Builder builder = UserProto.User.newBuilder();builder.build(); +- Netty中的使用:ProtobufVarint32FrameDecoder 是用于处理半包消息的解码类;ProtobufDecoder(UserProto.User.getDefaultInstance())这是创建的UserProto.java文件中的解码类;ProtobufVarint32LengthFieldPrepender 对protobuf协议的消息头上加上一个长度为32的整形字段,用于标志这个消息的长度的类;ProtobufEncoder 是编码类 +- 将StringBuilder转换为ByteBuf类型:copiedBuffer()方法 + +#### 6.Netty的零拷贝实现? + +- Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。堆内存多了一次内存拷贝,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。ByteBuffer由ChannelConfig分配,而ChannelConfig创建ByteBufAllocator默认使用Direct Buffer +- CompositeByteBuf 类可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。addComponents方法将 header 与 body 合并为一个逻辑上的 ByteBuf, 这两个 ByteBuf 在CompositeByteBuf 内部都是单独存在的, CompositeByteBuf 只是逻辑上是一个整体 +- 通过 FileRegion 包装的FileChannel.tranferTo方法 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel,避免了传统通过循环write方式导致的内存拷贝问题。 +- 通过 wrap方法, 我们可以将 byte[] 数组、ByteBuf、ByteBuffer等包装成一个 Netty ByteBuf 对象, 进而避免了拷贝操作。 +- Selector BUG:若Selector的轮询结果为空,也没有wakeup或新消息处理,则发生空轮询,CPU使用率100%, +- Netty的解决办法:对Selector的select操作周期进行统计,每完成一次空的select操作进行一次计数,若在某个周期内连续发生N次空轮询,则触发了epoll死循环bug。重建Selector,判断是否是其他线程发起的重建请求,若不是则将原SocketChannel从旧的Selector上去除注册,重新注册到新的Selector上,并将原来的Selector关闭。 + +#### 7.Netty的高性能表现在哪些方面? + +- **心跳**,对服务端:会定时清除闲置会话inactive(netty5),对客户端:用来检测会话是否断开,是否重来,检测网络延迟,其中idleStateHandler类 用来检测会话状态 +- **串行无锁化设计**,即消息的处理尽可能在同一个线程内完成,期间不进行线程切换,这样就避免了多线程竞争和同步锁。表面上看,串行化设计似乎CPU利用率不高,并发程度不够。但是,通过调整NIO线程池的线程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列-多个工作线程模型性能更优。 +- **可靠性**,链路有效性检测:链路空闲检测机制,读/写空闲超时机制;内存保护机制:通过内存池重用ByteBuf;ByteBuf的解码保护;优雅停机:不再接收新消息、退出前的预处理操作、资源的释放操作。 +- **Netty安全性**:支持的安全协议:SSL V2和V3,TLS,SSL单向认证、双向认证和第三方CA认证。 +- **高效并发编程的体现**:volatile的大量、正确使用;CAS和原子类的广泛使用;线程安全容器的使用;通过读写锁提升并发性能。IO通信性能三原则:传输(AIO)、协议(Http)、线程(主从多线程) +- **流量整型**的作用(变压器):防止由于上下游网元性能不均衡导致下游网元被压垮,业务流中断;防止由于通信模块接受消息过快,后端业务线程处理不及时导致撑死问题。 +- **TCP参数配置**:SO_RCVBUF和SO_SNDBUF:通常建议值为128K或者256K;SO_TCPNODELAY:NAGLE算法通过将缓冲区内的小封包自动相连,组成较大的封包,阻止大量小封包的发送阻塞网络,从而提高网络应用效率。但是对于时延敏感的应用场景需要关闭该优化算法; + +#### 8.客户端关闭的时候会抛出异常,死循环 + +解决方案 + + + int read = channel.read(buffer); + if(read > 0){ + byte[] data = buffer.array(); + String msg = new String(data).trim(); + System.out.println(“服务端收到信息:” + msg); + //回写数据 + ByteBuffer outBuffer = ByteBuffer.wrap("好的".getBytes()); + channel.write(outBuffer);// 将消息回送给客户端 + }else{ + System.out.println("客户端关闭"); + key.cancel(); + } + + +#### 9、selector.select();阻塞,那为什么说nio是非阻塞的IO? + +``` +selector.select() +selector.select(1000);不阻塞 会继续往下执行程序 +selector.wakeup();也可以唤醒selector 继续往下执行程序 +selector.selectNow();也可以立马返回程序 死循环 +``` + +#### 10、SelectionKey.OP_WRITE是代表什么意思 + +OP_WRITE表示底层缓冲区是否有空间,是则响应返还true +netty版本大致版本分为 netty3.x 和 netty4.x、netty5.x + +netty可以运用在那些领域? + +#### 11.分布式进程通信 + +例如: hadoop、dubbo、akka等具有分布式功能的框架,底层RPC通信都是基于netty实现的,这些框架使用的版本通常都还在用netty3.x + +#### 12、游戏服务器开发 + +最新的游戏服务器有部分公司可能已经开始采用netty4.x 或 netty5.x + +#### 13、netty服务端hello world案例 + +``` +SimpleChannelHandler 处理消息接收和写 +{ +messageReceived接收消息 + +channelConnected新连接,通常用来检测IP是否是黑名单 + +channelDisconnected链接关闭,可以再用户断线的时候清楚用户的缓存数据等 +} +``` + +#### 14、netty客户端hello world案例 + +channelDisconnected与channelClosed的区别? + +channelDisconnected只有在连接建立后断开才会调用 +channelClosed无论连接是否成功都会调用关闭资源 + +#### 15.一个NIO是不是只能有一个selector? + +不是,一个系统可以有多个selector + +#### 16.selector是不是只能注册一个ServerSocketChannel? + +不是,可以注册多个 +一个thread + 队列 == 一个单线程线程池 =====> 线程安全的,任务是线性串行执行的 +线程安全,不会产生阻塞效应 ,使用对象组 +线程不安全,会产生阻塞效应, 使用对象池 + +#### 17.心跳其实就是一个普通的请求,特点数据简单,业务也简单 + +心跳对于服务端来说,定时清除闲置会话inactive(netty5) channelclose(netty3) +心跳对客户端来说,用来检测会话是否断开,是否重连! 用来检测网络延时! + +#### 18.Netty IdleStateHandler出现问题-我是否以错误的方式对其进行了测试? + +我有一个玩具Netty服务器,并且尝试在客户端的通道未发生任何事件时向其发送心跳消息。我正在通过telnet到服务器,编写消息然后不发送任何内容来对此进行测试,但是我听不到任何声音! + +安慰: + +``` +>>telnet localhost 6969 +Trying 127.0.0.1... +Connected to localhost. +Escape character is '^]'. +>>foo +Did you say 'foo'? +``` + +MyPipelineFactory.java + +``` +public class MyPipelineFactory implements ChannelPipelineFactory { + private final Timer timer; + private static final ChannelHandler stringDecoder = new StringDecoder(); + private static final ChannelHandler stringEncoder = new StringEncoder(); + private final ChannelHandler idleStateHandler; + + public MyPipelineFactory(Timer t) { + this.timer = t; + this.idleStateHandler = new IdleStateHandler(timer, 5, 5, 5); + } + + public ChannelPipeline getPipeline() { + // create default pipeline from static method + ChannelPipeline pipeline = Channels.pipeline(); + pipeline.addLast("idleStateHandler", this.idleStateHandler); // heartbeat + pipeline.addLast("framer", new DelimiterBasedFrameDecoder(1024, Delimiters.lineDelimiter())); + //pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024,0,1)); // get header from message + pipeline.addLast("stringDecoder", stringDecoder); + pipeline.addLast("stringEncoder", stringEncoder); + pipeline.addLast("ServerHandler", new ServerHandler()); // goes at the end + + return pipeline; + } +} +``` + +HeartbeatHandler.java + +``` +public class HeartbeatHandler extends IdleStateAwareChannelHandler { + + @Override + public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e) { + if (e.getState() == IdleState.READER_IDLE) { + System.out.println("Reader idle, closing channel"); + //e.getChannel().close(); + e.getChannel().write("heartbeat-reader_idle"); + } + else if (e.getState() == IdleState.WRITER_IDLE) { + System.out.println("Writer idle, sending heartbeat"); + e.getChannel().write("heartbeat-writer_idle"); + } + else if (e.getState() == IdleState.ALL_IDLE) { + System.out.println("All idle, sending heartbeat"); + e.getChannel().write("heartbeat-all_idle"); + } + } +} +``` + +------ + +固定: + +我忘记了HeartbeatHandler,它需要IdleStateHandler(这部分对我来说并不明显)。这样可行。 + +``` +public class MyPipelineFactory implements ChannelPipelineFactory { + private final Timer timer; + private static final ChannelHandler stringDecoder = new StringDecoder(); + private static final ChannelHandler stringEncoder = new StringEncoder(); + private final ChannelHandler idleStateHandler; + private final ChannelHandler heartbeatHandler; + + public MyPipelineFactory(Timer t) { + this.timer = t; + this.idleStateHandler = new IdleStateHandler(timer, 5, 5, 5); + this.heartbeatHandler = new HeartbeatHandler(); + } + + public ChannelPipeline getPipeline() { + // create default pipeline from static method + ChannelPipeline pipeline = Channels.pipeline(); + pipeline.addLast("idleStateHandler", this.idleStateHandler); + pipeline.addLast("heartbeatHandler", this.heartbeatHandler); // heartbeat + pipeline.addLast("framer", new DelimiterBasedFrameDecoder(1024, Delimiters.lineDelimiter())); + //pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024,0,1)); // get header from message + pipeline.addLast("stringDecoder", stringDecoder); + pipeline.addLast("stringEncoder", stringEncoder); + pipeline.addLast("ServerHandler", new ServerHandler()); // goes at the end + + return pipeline; + } +} +``` + +#### 19.Netty如何使用线程池? + +您能解释一下Netty如何使用线程池工作吗?我是否正确理解,线程池有两种:老板线程和工人线程。老板用于执行I / +O,而worker用于调用用户回调(messageReceived)来处理数据? + + + +这是来自NioServerSocketChannelFactory文档 + +> 一个ServerSocketChannelFactory,它创建一个基于NIO的服务器端ServerSocketChannel。它利用NIO引入的非阻塞I +> / O模式来有效地服务许多并发连接。 +> +> 线程如何工作 +> NioServerSocketChannelFactory中有两种类型的线程:一个是老板线程,另一个是工作线程。 +> +> 老板线程 +> +> 每个绑定的ServerSocketChannel都有自己的老板线程。例如,如果您打开了两个服务器端口(例如80和443),则将有两个老板线程。Boss线程接受传入的连接,直到未绑定端口。一旦成功接受了连接,老板线程就将接受的Channel传递给NioServerSocketChannelFactory管理的工作线程之一。 +> +> 工作线程 +> 一个NioServerSocketChannelFactory可以具有一个或多个工作线程。工作线程以非阻塞模式对一个或多个通道执行非阻塞读写。 + +在Nio模型中,bossThread照顾所有有界套接字(监听套接字),workerThread照顾Accepted- +socket(包括IO和调用messageMethod等接收事件的方法)。 + +#### 20.Netty IllegalReferenceCountException + +尽管我的业务逻辑没有问题,但事实证明我没有使用Netty +`ByteBuf`。更新要使用的测试代码后`ByteBuf`,我遇到了[IllegalReferenceCountException](http://netty.io/wiki/reference-counted-objects.html)的无尽循环。我承认对Netty还是陌生的,但这并不能证明在手动分配和释放资源的日子里回来。创建GC就是为了避免这种混乱。迪斯科,有人吗?那贝尔底呢? + +``` +public class StringDecoder extends AbstractDecoder { + private static final IntPredicate NEWLINE_DELIMITER = b -> b == '\n' || b == '\r'; + + @Override + public Flux decode(Publisher publisher, ResolvableType elementType, MimeType mimeType, Map hints) { + return Flux.from(publisher) + .scan(Tuples., Optional>of(Flux.empty(), Optional.empty()), + (acc, buffer) -> { + List results = new ArrayList<>(); + int startIdx = 0, endIdx = 0, limit = buffer.readableByteCount(); + Optional incomplete = acc.getT2(); + + while (startIdx < limit && endIdx != -1) { + endIdx = buffer.indexOf(NEWLINE_DELIMITER, startIdx); + int length = (endIdx == -1 ? limit : endIdx) - startIdx; + DataBuffer slice = buffer.slice(startIdx, length); + DataBuffer tmp = incomplete.map(b -> b.write(slice)) + .orElse(buffer.factory().allocateBuffer(length).write(slice)); + tmp = DataBufferUtils.retain(tmp); + + if (endIdx != -1) { + startIdx = endIdx + 1; + results.add(tmp); + incomplete = Optional.empty(); + } else { + incomplete = Optional.of(tmp); + } + } + releaseBuffer(buffer); + + return Tuples.of(Flux.fromIterable(results), incomplete); + }) + .flatMap(t -> { + t.getT2().ifPresent(this::releaseBuffer); + return t.getT1(); + }) + .map(buffer -> { + // charset resolution should in general use supplied mimeType + String s = UTF_8.decode(buffer.asByteBuffer()).toString(); + releaseBuffer(buffer); + + return s; + }) + .log(); + } + + private void releaseBuffer(DataBuffer buffer) { + boolean release = DataBufferUtils.release(buffer); + if (release) { + System.out.println("Buffer was released."); + } + } +} + +public class StringDecoderTest { + private StringDecoder stringDecoder = new StringDecoder(); + DataBufferFactory dataBufferFactory = new NettyDataBufferFactory(UnpooledByteBufAllocator.DEFAULT); + + @Test + public void testDecode() { + Flux pub = Flux.just("abc\n", "abc", "def\n", "abc", "def\nxyz\n", "abc", "def", "xyz\n") + .map(s -> dataBufferFactory.wrap(s.getBytes(UTF_8))); + + StepVerifier.create(stringDecoder.decode(pub, null, null, null)) + .expectNext("abc", "abcdef", "abcdef", "xyz", "abcdefxyz") + .verifyComplete(); + } +} +``` + +我不断得到: + +``` +[ERROR] (main) onError(io.netty.util.IllegalReferenceCountException: refCnt: 0) +[ERROR] (main) - io.netty.util.IllegalReferenceCountException: refCnt: 0 +io.netty.util.IllegalReferenceCountException: refCnt: 0 + at io.netty.buffer.AbstractByteBuf.ensureAccessible(AbstractByteBuf.java:1415) + at io.netty.buffer.UnpooledHeapByteBuf.nioBuffer(UnpooledHeapByteBuf.java:314) + at io.netty.buffer.AbstractUnpooledSlicedByteBuf.nioBuffer(AbstractUnpooledSlicedByteBuf.java:434) + at io.netty.buffer.CompositeByteBuf.nioBuffers(CompositeByteBuf.java:1496) + at io.netty.buffer.CompositeByteBuf.nioBuffer(CompositeByteBuf.java:1468) + at io.netty.buffer.AbstractByteBuf.nioBuffer(AbstractByteBuf.java:1205) + at org.springframework.core.io.buffer.NettyDataBuffer.asByteBuffer(NettyDataBuffer.java:234) + at org.abhijitsarkar.java.StringDecoder.lambda$decode$4(StringDecoder.java:61) +``` + +工作代码: + +``` +public class StringDecoder extends AbstractDecoder { + private static final IntPredicate NEWLINE_DELIMITER = b -> b == '\n' || b == '\r'; + + @Override + public Flux decode(Publisher publisher, ResolvableType elementType, MimeType mimeType, Map hints) { + DataBuffer incomplete = new NettyDataBufferFactory(UnpooledByteBufAllocator.DEFAULT).allocateBuffer(0); + + return Flux.from(publisher) + .scan(Tuples., DataBuffer>of(Flux.empty(), retain(incomplete)), + (acc, buffer) -> { + List results = new ArrayList<>(); + int startIdx = 0, endIdx = 0, limit = buffer.readableByteCount(); + + while (startIdx < limit && endIdx != -1) { + endIdx = buffer.indexOf(NEWLINE_DELIMITER, startIdx); + int length = (endIdx == -1 ? limit : endIdx) - startIdx; + + DataBuffer slice = buffer.slice(startIdx, length); + byte[] slice1 = new byte[length]; + slice.read(slice1, 0, slice1.length); + + if (endIdx != -1) { + byte[] slice2 = new byte[incomplete.readableByteCount()]; + incomplete.read(slice2, 0, slice2.length); + // call retain to match release during decoding to string later + results.add(retain( + incomplete.factory().allocateBuffer() + .write(slice2) + .write(slice1) + )); + startIdx = endIdx + 1; + } else { + incomplete.write(slice1); + } + } + + return Tuples.of(Flux.fromIterable(results), incomplete); + }) + .flatMap(Tuple2::getT1) + .map(buffer -> { + // charset resolution should in general use supplied mimeType + String s = UTF_8.decode(buffer.asByteBuffer()).toString(); + + return s; + }) + .doOnTerminate(() -> release(incomplete)) + .log(); + } +} +``` + +该代码可能更[简洁](https://jira.spring.io/browse/SPR-16351)一些,但是适用于Spring bug +[SPR-16351](https://jira.spring.io/browse/SPR-16351)。 + +#### 21.Java Netty负载测试问题 + +我编写了使用文本协议接受连接和轰炸消息(〜100字节)的服务器,并且我的实现能够与3rt客户端发送约400K / +sec的回送消息。我为此任务选择了Netty,即SUSE 11 RealTime,JRockit +RTS。但是,当我开始基于Netty开发自己的客户端时,吞吐量却急剧下降(从400K msg / sec降低到1.3K msg / +sec)。客户端的代码非常简单。能否请您提供建议或示例,说明如何编写更有效的客户。实际上,我实际上更关心延迟,但是从吞吐量测试开始,我认为在环回中以1.5Kmsg +/ sec的速度正常是不正常的。PS客户端的目的只是接收来自服务器的消息,很少发送心跳信号。 + + + +``` +Client.java + +public class Client { + +private static ClientBootstrap bootstrap; +private static Channel connector; +public static boolean start() +{ + ChannelFactory factory = + new NioClientSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + ExecutionHandler executionHandler = new ExecutionHandler( new OrderedMemoryAwareThreadPoolExecutor(16, 1048576, 1048576)); + + bootstrap = new ClientBootstrap(factory); + + bootstrap.setPipelineFactory( new ClientPipelineFactory() ); + + bootstrap.setOption("tcpNoDelay", true); + bootstrap.setOption("keepAlive", true); + bootstrap.setOption("receiveBufferSize", 1048576); + ChannelFuture future = bootstrap + .connect(new InetSocketAddress("localhost", 9013)); + if (!future.awaitUninterruptibly().isSuccess()) { + System.out.println("--- CLIENT - Failed to connect to server at " + + "localhost:9013."); + bootstrap.releaseExternalResources(); + return false; + } + + connector = future.getChannel(); + + return connector.isConnected(); +} +public static void main( String[] args ) +{ + boolean started = start(); + if ( started ) + System.out.println( "Client connected to the server" ); +} + +} + +ClientPipelineFactory.java + +public class ClientPipelineFactory implements ChannelPipelineFactory{ + +private final ExecutionHandler executionHandler; +public ClientPipelineFactory( ExecutionHandler executionHandle ) +{ + this.executionHandler = executionHandle; +} +@Override +public ChannelPipeline getPipeline() throws Exception { + ChannelPipeline pipeline = pipeline(); + pipeline.addLast("framer", new DelimiterBasedFrameDecoder( + 1024, Delimiters.lineDelimiter())); + pipeline.addLast( "executor", executionHandler); + pipeline.addLast("handler", new MessageHandler() ); + + return pipeline; +} + +} + +MessageHandler.java +public class MessageHandler extends SimpleChannelHandler{ + +long max_msg = 10000; +long cur_msg = 0; +long startTime = System.nanoTime(); +@Override +public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { + cur_msg++; + + if ( cur_msg == max_msg ) + { + System.out.println( "Throughput (msg/sec) : " + max_msg* NANOS_IN_SEC/( System.nanoTime() - startTime ) ); + cur_msg = 0; + startTime = System.nanoTime(); + } +} + +@Override +public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { + e.getCause().printStackTrace(); + e.getChannel().close(); +} + +} +``` + +更新。在服务器端,存在一个定期线程,该线程写入已接受的客户端通道。而且该频道很快就无法写入。更新N2。在管道中添加了OrderedMemoryAwareExecutor,但是吞吐量仍然很低(大约4k +msg / sec) + +固定。我将执行程序放在整个管道堆栈的前面,结果成功了! + + + +如果服务器正在发送固定大小(〜100字节)的消息,则可以将ReceiveBufferSizePredictor设置为客户端引导程序,这将优化读取 + +``` +bootstrap.setOption("receiveBufferSizePredictorFactory", + new AdaptiveReceiveBufferSizePredictorFactory(MIN_PACKET_SIZE, INITIAL_PACKET_SIZE, MAX_PACKET_SIZE)); +``` + +根据您发布的代码段:客户端的nio工作线程正在做管道中的所有事情,因此它将忙于解码和执行消息处理程序。您必须添加一个执行处理程序。 + +您已经说过,通道从服务器端变得不可写,因此您可能必须在服务器引导程序中调整水印大小。您可以定期监视写缓冲区大小(写队列大小),并确保由于消息无法写到网络而使通道变得不可写。可以通过以下类似的util类来完成。 + +``` +package org.jboss.netty.channel.socket.nio; + +import org.jboss.netty.channel.Channel; + +public final class NioChannelUtil { + public static long getWriteTaskQueueCount(Channel channel) { + NioSocketChannel nioChannel = (NioSocketChannel) channel; + return nioChannel.writeBufferSize.get(); + } +} +``` + +#### 22. + +#### 参考 + +https://www.cnblogs.com/xuxinstyle/p/9872915.html + +https://blog.csdn.net/qq_38772518/article/details/105834573 + +http://www.mianshigee.com/ \ No newline at end of file diff --git a/Nginx.md b/Nginx.md new file mode 100644 index 0000000..32de863 --- /dev/null +++ b/Nginx.md @@ -0,0 +1,226 @@ +## Nginx + +#### 1.什么是Nginx? + +Nginx是一个高性能的HTTP和反向代理服务器。同时也是一个 IMAP/POP3/SMTP 代理服务器。 官方网站:http://nginx.org。 + +#### 2.Nginx主要特征? + +处理静态文件,索引文件以及自动索引;打开文件描述符缓冲. 无缓存的反向代理加速,简单的负载均衡和容错. FastCGI,简单的负载均衡和容错.模块化的结构。包括 gzipping, byte ranges, chunked responses,以及 SSI-filter 等filter。如果由 FastCGI 或其它代理服务器处理单页中存在的多个 SSI,则这项处理可以并行 运行,而不需要相互等待。 + +支持 SSL 和 TLSSNI. + +Nginx 它支持内核 Poll 模型,能经受高负载的考验,有报告表明能支持高达 50,000 个并发连接数。 + +Nginx 具有很高的稳定性。 例如当前 apache 一旦上到 200 个以上进程,web 响应速度就明显非常缓慢了。而 Nginx 采取了分阶段资源分配技术,使得它的 CPU 与内存占用率非常低。nginx 官方表示保持 10,000 个没有活动的连接,它只占 2.5M 内存,所以类似 DOS 这样的攻击对 nginx 来说基本上是毫无用处的。 + +Nginx 支持热部署。它的启动特别容易, 并且几乎可以做到 7*24 不间断运行,即使运 行数个月也不需要重新启动。对软件版本进行进行热升级。 + +Nginx 采用 master-slave 模型,能够充分利用 SMP 的优势,且能够减少工作进程在磁 盘 I/O 的阻塞延迟。当采用 select()/poll()调用时,还可以限制每个进程的连接数。 + +Nginx 代码质量非常高,代码很规范,手法成熟, 模块扩展也很容易。特别值得一提的是强大的 Upstream 与 Filter 链。 + +Nginx 采用了一些 os 提供的最新特性如对 sendfile (Linux2.2+),accept-filter (FreeBSD4.1+),TCP_DEFER_ACCEPT (Linux 2.4+)的支持,从而大大提高了性能。 + +免费开源,可以做高并发负载均衡。 + +#### 3.Nginx 常用命令? + +启动 nginx 。 +停止 nginx -s stop 或 nginx -s quit 。 +重载配置 ./sbin/nginx -s reload(平滑重启) 或 service nginx reload 。 +重载指定配置文件 .nginx -c /usr/local/nginx/conf/nginx.conf 。 +查看 nginx 版本 nginx -v 。 +检查配置文件是否正确 nginx -t 。 +显示帮助信息 nginx -h 。 + +#### 4.工作模式及连接数上限? + +```nginx +events { + +use epoll; #epoll 是多路复用 IO(I/O Multiplexing)中的一种方 式,但是仅用于 linux2.6 以上内核,可以大大提高 nginx 的性能 + +worker_connections 1024;#单个后台 worker process 进程的最大并发链接数 + +# multi_accept on; +} +``` + +#### 5.Nginx负载均衡几种算法? + +5种。 + +1.轮询模式(默认) +每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。 +2.权重模式 +指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况 +3.IP_hash模式 (IP散列) +每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。 +4.url_hash模式 +5.fair模式 +按后端服务器的响应时间来分配请求,响应时间短的优先分配。 + +#### 6.nginx有几种进程模型? + +分为master-worker模式和单进程模式。在master-worker模式下,有一个master进程和至少一个的worker进程,单进程模式顾名思义只有一个进程。 + +#### 7.如何定义错误提示页面? + +\# 定义错误提示页面 + +```nginx +error_page 500 502 503 504 /50x.html; + +location = /50x.html { root /root; + +} +``` + +#### 8.如何精准匹配路径? + +location =开头表示精准匹配 + +```nginx +location = /get { +#规则 A } +``` + +#### 9.路径匹配优先级? + +多个 location 配置的情况下匹配顺序为 +首先匹配 =,其次匹配^~, 其次是按文件中顺序的正则匹配,最后是交给 / 通用匹配。当 有匹配成功时候,停止匹配,按当前匹配规则处理请求。 + +#### 10.如何把请求转发给后端应用服务器? + +```nginx +location = / { + +proxy_pass http://tomcat:8080/index + +} +``` + +#### 11.如何根据文件类型设置过期时间? + +```nginx +location ~* \.(js|css|jpg|jpeg|gif|png|swf)$ { +if (-f $request_filename) { +expires 1h; +break; +} +} +``` + +#### 12.禁止访问某个目录? + +```nginx +location ^~/path/ { + deny all; +} + + +``` + +#### 13.Nginx负载均衡实现过程? + +首先在 http 模块中配置使用 upstream 模块定义后台的 webserver 的池子,名为 proxy-web,在池子中我们可以添加多台后台 webserver,其中状态 检查、调度算法都是在池子中配置;然后在 serverr 模块中定义虚拟主机,但是这个虚拟主 机不指定自己的 web 目录站点,它将使用 location 匹配 url 然后转发到上面定义好的 web 池子中,最后根据调度策略再转发到后台 web server 上 。 + +#### 14.负载均衡配置? + +```nginx +Upstream proxy_nginx { + +server 192.168.0.254 weight=1max_fails=2 fail_timeout=10s ; +server 192.168.0.253 weight=2 max_fails=2fail_timeout=10s; +server192.168.0.252 backup; server192.168.0.251 down; + +} + +server{ + listen 80; + server_name xiaoka.com; + +location / { + + proxy_pass http:// proxy_nginx; + proxy_set_header Host + proxy_set_header X-Real-IP + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + +} +``` + +#### 15.设置超时时间? + +```nginx +http { + ………. + keepalive_timeout 60; ###设置客户端连接保持会话的超时时间,超过这个时间,服务器会关闭该连接。 tcp_nodelay on; + \####打开 tcp_nodelay,在包含了 keepalive 参数才有效 + client_header_timeout 15; ####设置客户端请求头读取超时时间,如果超过这个时间,客户端还没有发送任何数据, Nginx 将返回“Request time out(408)”错误 + client_body_timeout 15; + +\####设置客户端请求主体读取超时时间,如果超过这个时间,客户端还没有发送任何数据, Nginx 将返回“Request time out(408)”错误 + send_timeout 15; ####指定响应客户端的超时时间。这个超过仅限于两个连接活动之间的时间,如果超过这 个时间,客户端没有任何活动,Nginx 将会关闭连接。 + +…… } +``` + +#### 16.开启压缩功能好处?坏处? + +好处:压缩是可以节省带宽,提高传输效率 + +坏处:但是由于是在服务器上进行压缩,会消耗服务器起源 + +#### 17.Nginx配置文件nginx.conf有哪些属性模块? + +``` +worker_processes 1; # worker进程的数量 +events { # 事件区块开始 + worker_connections 1024; # 每个worker进程支持的最大连接数 +} # 事件区块结束 +http { # HTTP区块开始 + include mime.types; # Nginx支持的媒体类型库文件 + default_type application/octet-stream; # 默认的媒体类型 + sendfile on; # 开启高效传输模式 + keepalive_timeout 65; # 连接超时 + server { # 第一个Server区块开始,表示一个独立的虚拟主机站点 + listen 80; # 提供服务的端口,默认80 + server_name localhost; # 提供服务的域名主机名 + location / { # 第一个location区块开始 + root html; # 站点的根目录,相当于Nginx的安装目录 + index index.html index.htm; # 默认的首页文件,多个用空格分开 + } # 第一个location区块结果 + error_page 500502503504 /50x.html; # 出现对应的http状态码时,使用50x.html回应客户 + location = /50x.html { # location区块开始,访问50x.html + root html; # 指定对应的站点目录为html + } + } + ...... + +``` + +#### 18.Nginx静态资源? + + 静态资源访问,就是存放在nginx的html页面,我们也可以编写 + +#### 19.如何用Nginx解决前端跨域问题? + +- 使用Nginx转发请求。把跨域的接口写成调本域的接口,然后将这些接口转发到真正的请求地址。 + +#### 20.Nginx虚拟主机怎么配置? + +- 1、基于域名的虚拟主机,通过域名来区分虚拟主机——应用:外部网站 +- 2、基于端口的虚拟主机,通过端口来区分虚拟主机——应用:公司内部网站,外部网站的管理后台 +- 3、基于ip的虚拟主机。 + +#### 参考: + +- 《Nginx从入门到精通》 + +- 《Nginx高性能Web服务器详解》 + +- 《深入理解nginx》 + + ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) diff --git a/RabbitMQ.md b/RabbitMQ.md new file mode 100644 index 0000000..3ae64f6 --- /dev/null +++ b/RabbitMQ.md @@ -0,0 +1,215 @@ +## RabbitMQ + + + +#### 1、什么是 RabbitMQ?为什么使用 RabbitMQ? + +RabbitMQ 是一款开源的,Erlang 编写的,基于 AMQP 协议的,消息中间件; + +可以用它来:解耦、异步、削峰。 + +#### 2、RabbitMQ 有什么优缺点? + +优点:解耦、异步、削峰; + +缺点:降低了系统的稳定性:本来系统运行好好的,现在你非要加入个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性会降低; + +增加了系统的复杂性:加入了消息队列,要多考虑很多方面的问题,比如:一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等。因此,需要考虑的东西更多,复杂性增大。 + +#### 3.rabbitmq 的使用场景 + +(1)服务间异步通信 + +(2)顺序消费 + +(3)定时任务 + +(4)请求削峰 + +#### 4.RabbitMQ基本概念 + + Broker: 简单来说就是消息队列服务器实体 +Exchange: 消息交换机,它指定消息按什么规则,路由到哪个队列 +Queue: 消息队列载体,每个消息都会被投入到一个或多个队列 +Binding: 绑定,它的作用就是把exchange和queue按照路由规则绑定起来 +Routing Key: 路由关键字,exchange根据这个关键字进行消息投递 +VHost: vhost 可以理解为虚拟 broker ,即 mini-RabbitMQ server。其内部均含有独立的 queue、exchange 和 binding 等,但最最重要的是,其拥有独立的权限系统,可以做到 vhost 范围的用户控制。当然,从 RabbitMQ 的全局角度,vhost 可以作为不同权限隔离的手段(一个典型的例子就是不同的应用可以跑在不同的 vhost 中)。 +Producer: 消息生产者,就是投递消息的程序 +Consumer: 消息消费者,就是接受消息的程序 +Channel: 消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务 +由Exchange、Queue、RoutingKey三个才能决定一个从Exchange到Queue的唯一的线路。 + +#### 5.RabbitMQ 中的 broker 是指什么?cluster 又是指什么? + +`broker` 是指一个或多个 `erlang node` 的逻辑分组,且 `node` 上运行着 `RabbitMQ` 应用程序。 +`cluster` 是在 `broker` 的基础之上,增加了 `node` 之间共享元数据的约束。 + +#### 6、RabbitMQ 概念里的 channel、exchange 和 queue 是逻辑概念,还是对应着进程实体?分别起什么作用? + +**Queue** 具有自己的 **erlang** 进程;**exchange** 内部实现为保存 binding 关系的查找表;channel 是实际进行路由工作的实体,即负责按照 routing_key 将 message 投递给 queue 。由 AMQP 协议描述可知,channel 是真实 TCP 连接之上的虚拟连接,所有 AMQP 命令都是通过 channel 发送的,且每一个 channel 有唯一的 ID。一个 channel 只能被单独一个操作系统线程使用,故投递到特定 channel 上的 message 是有顺序的。但一个操作系统线程上允许使用多个 channel 。 + +#### 7 vhost 是什么?起什么作用? + +`vhost` 可以理解为虚拟 `broker` ,即 `mini-RabbitMQ server`。其内部均含有独立的 `queue`、`exchange` 和 `binding` 等,但最最重要的是,其拥有独立的权限系统,可以做到 `vhost` 范围的用户控制。当然,从 `RabbitMQ` 的全局角度,`vhost` 可以作为不同权限隔离的手段(一个典型的例子就是不同的应用可以跑在不同的 `vhost` 中)。 + +#### 8. 消息基于什么传输? + +由于TCP连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ使用信道的方式来传输数据。信道是建立在真实的TCP连接内的虚拟连接,且每条TCP连接上的信道数量没有限制。 + +#### 9. 消息如何分发? + +若该队列至少有一个消费者订阅,消息将以循环(round-robin)的方式发送给消费者。每条消息只会分发给一个订阅的消费者(前提是消费者能够正常处理消息并进行确认)。 + +#### 10. 消息怎么路由? + +从概念上来说,消息路由必须有三部分:**交换器**、**路由**、**绑定**。生产者把消息发布到交换器上;绑定决定了消息如何从路由器路由到特定的队列;消息最终到达队列,并被消费者接收。 + +消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。 +通过队列路由键,可以把队列绑定到交换器上。 +消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则)。如果能够匹配到队列,则消息会投递到相应队列中;如果不能匹配到任何队列,消息将进入 “黑洞”。 + +常用的交换器主要分为一下三种: + +- direct:如果路由键完全匹配,消息就被投递到相应的队列 +- fanout:如果交换器收到消息,将会广播到所有绑定的队列上 +- topic:可以使来自不同源头的消息能够到达同一个队列。使用topic交换器时,可以使用通配符。 + 比如:“*” 匹配特定位置的任意文本, “.” 把路由键分为了几部分,“#” 匹配所有规则等。 + 特别注意:发往topic交换器的消息不能随意的设置选择键(routing_key),必须是由"."隔开的一系列的标识符组成。 + +#### 11. 什么是元数据?元数据分为哪些类型?包括哪些内容?与 cluster 相关的元数据有哪些?元数据是如何保存的?元数据在 cluster 中是如何分布的? + +在非 `cluster` 模式下,元数据主要分为 `Queue` 元数据(queue 名字和属性等)、`Exchange`元数据(exchange 名字、类型和属性等)、`Binding` 元数据(存放路由关系的查找表)、`Vhost`元数据(vhost 范围内针对前三者的名字空间约束和安全属性设置)。 +在 `cluster` 模式下,还包括 cluster 中 node 位置信息和 node 关系信息。元数据按照 erlang node 的类型确定是仅保存于 RAM 中,还是同时保存在 RAM 和 disk 上。元数据在 cluster 中是全 node 分布的。 + +#### 12. 在单node 系统和多 node 构成的 cluster 系统中声明 queue、exchange ,以及进行 binding 会有什么不同? + +答:当你在单 node 上声明 queue 时,只要该 node 上相关元数据进行了变更,你就会得到 Queue.Declare-ok 回应;而在 cluster 上声明 queue ,则要求 cluster 上的全部 node 都要进行元数据成功更新,才会得到 Queue.Declare-ok 回应。另外,若 node 类型为 RAM node 则变更的数据仅保存在内存中,若类型为 disk node 则还要变更保存在磁盘上的数据。 + +死信队列&死信交换器:DLX 全称(Dead-Letter-Exchange),称之为死信交换器,当消息变成一个死信之后,如果这个消息所在的队列存在x-dead-letter-exchange参数,那么它会被发送到x-dead-letter-exchange对应值的交换器上,这个交换器就称之为死信交换器,与这个死信交换器绑定的队列就是死信队列。 + +#### 13. 如何确保消息正确地发送至RabbitMQ? + +**RabbitMQ**使用发送方确认模式,确保消息正确地发送到**RabbitMQ**。 +发送方确认模式:将信道设置成`confirm`模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的ID。一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一ID)。如果`RabbitMQ`发生内部错误从而导致消息丢失,会发送一条`nack`(not acknowledged,未确认)消息。发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。 + +#### 14. 如何确保消息接收方消费了消息? + +接收方消息确认机制:消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,`RabbitMQ`才能安全地把消息从队列中删除。这里并没有用到超时机制,`RabbitMQ`仅通过`Consumer`的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,`RabbitMQ`给了`Consumer`足够长的时间来处理消息。 + +下面罗列几种特殊情况: + +- 如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ会认为消息没有被分发,然后重新分发给下一个订阅的消费者。(可能存在消息重复消费的隐患,需要根据bizId去重) +- 如果消费者接收到消息却没有确认消息,连接也未断开,则RabbitMQ认为该消费者繁忙,将不会给该消费者分发更多的消息。 + +#### 15、如何保证 RabbitMQ 不被重复消费? + +先说为什么会重复消费:正常情况下,消费者在消费消息的时候,消费完毕后,会发送一个确认消息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除; + +但是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将消息分发给其他的消费者。 + +针对以上问题,一个解决思路是:保证消息的唯一性,就算是多次传输,不要让消息的多次消费带来影响;保证消息等幂性; + +比如:在写入消息队列的数据做唯一标示,消费消息时,根据唯一标识判断是否消费过; + +#### 16、如何保证 RabbitMQ 消息的可靠传输? + +消息不可靠的情况可能是消息丢失,劫持等原因; + +丢失又分为:生产者丢失消息、消息列表丢失消息、消费者丢失消息; + +生产者丢失消息:从生产者弄丢数据这个角度来看,RabbitMQ 提供 transaction 和 confirm 模式来确保生产者不丢消息; + +transaction 机制就是说:发送消息前,开启事务(channel.txSelect()), 然后发送消息,如果发送过程中出现什么异常,事务就会回滚(channel.txRollback()), 如果发送成功则提交事务(channel.txCommit())。然而,这种方式有个缺点:吞吐量下降; + +confirm 模式用的居多:一旦 channel 进入 confirm 模式,所有在该信道上发布的消息都将会被指派一个唯一的 ID(从 1 开始),一旦消息被投递到所有匹配的队列之后; + +rabbitMQ 就会发送一个 ACK 给生产者(包含消息的唯一 ID),这就使得生产者知道消息已经正确到达目的队列了; + +如果 rabbitMQ 没能处理该消息,则会发送一个 Nack 消息给你,你可以进行重试操作。 + +消息队列丢数据:消息持久化。 + +处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。 + +这个持久化配置可以和 confirm 机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个 Ack 信号。 + +这样,如果消息持久化磁盘之前,rabbitMQ 阵亡了,那么生产者收不到 Ack 信号,生产者会自动重发。 + +那么如何持久化呢? + +这里顺便说一下吧,其实也很容易,就下面两步 + +1. 将 queue 的持久化标识 durable 设置为 true, 则代表是一个持久的队列 +2. 发送消息的时候将 deliveryMode=2 + +这样设置以后,即使 rabbitMQ 挂了,重启后也能恢复数据 + +消费者丢失消息:消费者丢数据一般是因为采用了自动确认消息模式,改为手动确认消息即可! + +消费者在收到消息之后,处理消息之前,会自动回复 RabbitMQ 已收到消息; + +如果这时处理消息失败,就会丢失该消息; + +解决方案:处理消息成功后,手动回复确认消息。 + +#### 17、如何保证 RabbitMQ 消息的顺序性? + +单线程消费保证消息的顺序性;对消息进行编号,消费者处理消息是根据编号处理消息; + +#### 18. 死信队列和延迟队列的使用? + +**死信消息:** + +消息被拒绝(Basic.Reject或Basic.Nack)并且设置 requeue 参数的值为 false +消息过期了 +队列达到最大的长度 + +**过期消息:** + +在 rabbitmq 中存在2种方可设置消息的过期时间,第一种通过对队列进行设置,这种设置后,该队列中所有的消息都存在相同的过期时间,第二种通过对消息本身进行设置,那么每条消息的过期时间都不一样。如果同时使用这2种方法,那么以过期时间小的那个数值为准。当消息达到过期时间还没有被消费,那么那个消息就成为了一个 死信 消息。 + +**队列设置:**在队列申明的时候使用 x-message-ttl 参数,单位为 毫秒 + +**单个消息设置:**是设置消息属性的 expiration 参数的值,单位为 毫秒 + +**延时队列:**在rabbitmq中不存在延时队列,但是我们可以通过设置消息的过期时间和死信队列来模拟出延时队列。消费者监听死信交换器绑定的队列,而不要监听消息发送的队列。 + +------ + +有了以上的基础知识,我们完成以下需求: + +需求:用户在系统中创建一个订单,如果超过时间用户没有进行支付,那么自动取消订单。 + +分析: + +1、上面这个情况,我们就适合使用延时队列来实现,那么延时队列如何创建 +2、延时队列可以由 过期消息+死信队列 来时间 +3、过期消息通过队列中设置 x-message-ttl 参数实现 +4、死信队列通过在队列申明时,给队列设置 x-dead-letter-exchange 参数,然后另外申明一个队列绑定x-dead-letter-exchange对应的交换器。 + +#### 19. 使用了消息队列会有什么缺点? + +1.系统可用性降低:你想啊,本来其他系统只要运行好好的,那你的系统就是正常的。现在你非要加个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性降低 + +2.系统复杂性增加:要多考虑很多方面的问题,比如一致性问题、如何保证消息不被重复消费,如何保证保证消息可靠传输。因此,需要考虑的东西更多,系统复杂性增大。 + +#### 20. 多个消费者监听一个队列时,消息如何分发? + +- 轮询: 默认的策略,消费者轮流,平均地接收消息 +- 公平分发: 根据消费者的能力来分发消息,给空闲的消费者发送更多消息 + +当消费者有x条消息没有响应ACK时,不再给这个消费者发送消息 + +```java +channel.basicQos(int x) +``` + + + +#### 参考 + +https://www.cnblogs.com/woadmin/p/10537174.html + +https://blog.csdn.net/thinkwon/article/details/104588612/ + +https://www.cnblogs.com/coder-programming/p/12465314.html \ No newline at end of file diff --git "a/Redis-\344\270\212\345\215\267.md" "b/Redis-\344\270\212\345\215\267.md" new file mode 100644 index 0000000..148dbb9 --- /dev/null +++ "b/Redis-\344\270\212\345\215\267.md" @@ -0,0 +1,433 @@ +## Redis + +#### 1.Redis是什么? + +Redis是一个开放源代码(BSD许可)的内存中数据结构存储,可用作数据库,缓存和消息代理,是一个基于键值对的NoSQl数据库。 + +#### 2.Redis特性? + +- 速度快 +- 基于键值对的数据结构服务器 +- 丰富的功能、丰富的数据结构 +- 简单稳定 +- 客户端语言多 +- 持久化 +- 主从复制 +- 高可以 & 分布式 + +#### 3.Redis合适的应用场景? + +- 缓存 +- 排行榜 +- 计数器 +- 分布式会话 +- 分布式锁 +- 社交网络 +- 最新列表 +- 消息系统 + +#### 4.除了Redis你还知道哪些NoSQL数据库? + +MongoDB、MemcacheDB、Cassandra、CouchDB、Hypertable、Leveldb。 + +#### 5.Redis和Memcache区别? + +支持的存储类型不同,memcached只支持简单的k/v结构。redis支持更多类型的存储结构类型(详见问题6)。 + +memcached数据不可恢复,redis则可以把数据持久化到磁盘上。 + +新版本的redis直接自己构建了VM 机制 ,一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。 + +redis当物理内存用完时,可以将很久没用到的value交换到磁盘。 + +#### 6.Redis的有几种数据类型? + +基础:字符串(String)、哈希(hash)、列表(list)、集合(set)、有序集合(zset)。 + +还有HyperLogLog、流、地理坐标等。 + +#### 7.Redis有哪些高级功能? + +消息队列、自动过期删除、事务、数据持久化、分布式锁、附近的人、慢查询分析、Sentinel 和集群等多项功能。 + +#### 8.安装过Redis吗,简单说下步骤? + + 1.下载Redis指定版本源码安装包压缩到当前目录。 + +1. 解压缩Redis源码安装包。 + +2. 建立一个redis目录软链接,指向解压包。 + +3. 进入redis目录 + +4. 编译 + +5. 安装 + + 对于使用docker的童靴来说就比较容易了。 + + docker pull redis + +#### 9.redis几个比较主要的可执行文件?分别是? + +![image-20200425221430652](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200425221430652.png) + +#### 10.启动Redis的几种方式? + +1.默认配置 : + +./redis-server + +2.运行启动: redis-server 加上要修改配置名和值(可以是多对),没有配置的将使用默认配置。 + +例如: redis-server ———port 7359 + +3.指定配置文件启动: + +./redis-server /opt/redis/redis.conf + +#### 11.Redis配置需要自己写?如何配置? + +redis目录下有一个redis.conf的模板配置。所以只需要复制模板配置然后修改即可。 + +一般来说大部分生产环境都会用指定配置文件的方式启动redis。 + +#### 12.Redis客户端命令执行的方式? + +1.交互方式: + +``` +redis-cli -h 127.0.0.1 -p 6379 +``` + +连接到redis后,后面执行的命令就可以通过交互方式实现了。 + +2.命令行方式: + +``` +redis-cli -h 127.0.0.1 -p 6379 get value +``` + +#### 13.如何停止redis服务? + +Kill -9 pid (粗暴,请不要使用,数据不仅不会持久化,还会造成缓存区等资源不能被优雅关闭) + +可以用redis 的shutdown 命令,可以选择是否在关闭前持久化数据。 + +``` +redis-cli shutdown nosave|save +``` + +#### 14.如何查看当前键是否存在? + +exists key + +#### 15.如何删除数据? + +del key + +#### 16.redis为什么快?单线程? + +- redis使用了单线程架构和I/O多路复用模型模型。 +- 纯内存访问。 +- 由于是单线程避免了线程上下文切换带来的资源消耗。 + +#### 17.字符串最大不能超过多少? + +512MB + +#### 18.redis默认分多少个数据库? + +16 + +#### 19.redis持久化的几种方式? + +RDB、AOF、混合持久化。 + +#### 20.RDB持久化? + +RDB(Redis DataBase)持久化是把当前进程数据生成快照保存到硬盘的过程。 + +Tips:是以二进制的方式写入磁盘。 + +#### 21.RDB的持久化是如何触发的? + +手动触发: + +save: 阻塞当前Redis服务器,直到RDB过程完成为止,如果数据比较大的话,会造成长时间的阻塞, + +线上不建议。 + +bgsave:redis进程执行 fork操作创作子进程,持久化由子进程负责,完成后自动结束,阻塞只发生在 + +fork阶段,一半时间很短。 + +自动触发: + +save xsecends n: + +表示在x秒内,至少有n个键发生变化,就会触发RDB持久化。也就是说满足了条件就会触发持久化。 + +flushall : + +主从同步触发 + +#### 22.RDB的优点? + +- rdb是一个紧凑的二进制文件,代表Redis在某个时间点上的数据快照。 +- 适合于备份,全量复制的场景,对于灾难恢复非常有用。 +- Redis加载RDB恢复数据的速度远快于AOF方式。 + +#### 23.RDB的缺点? + +- RDB没法做到实时的持久化。中途意外终止,会丢失一段时间内的数据。 +- RDB需要fork()创建子进程,属于重量级操作,可能导致Redis卡顿若干秒。 + +#### 24.如何禁用持久化? + +一般来说生成环境不会用到,了解一下也有好处的。 + +``` +config set save "" +``` + +#### 25.AOF持久化? + +AOF(append only file)为了解决rdb不能实时持久化的问题,aof来搞定。以独立的日志方式记录把每次命令记录到aof文件中。 + +#### 26.如何查询AOF是否开启? + +``` +config get appendonly +``` + +#### 27.如何开启AOF? + +命令行方式: 实时生效,但重启后失效。 + +``` +config set appendonly +``` + +配置文件:需要重启生效,重启后依然生效。 + +``` +appendonly yes +``` + +#### 28.AOF工作流程? + +1.所有写入命令追加到aof_buf缓冲区。 + +2.AOF缓冲区根据对应的策略向硬盘做同步操作。 + +3.随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。 + +4.当redis服务器重启时,可以加载AOF文件进行数据恢复。 + +#### 29.为什么AOF要先把命令追加到缓存区(aof_buf)中? + +Redis使用单线程响应命令,如果每次写入文件命令都直接追加到硬盘,性能就会取决于硬盘的负载。如果使用缓冲区,redis提供多种缓冲区策略,在性能和安全性方面做出平衡。 + +#### 30.AOF持久化如何触发的? + +自动触发:满足设置的策略和满足重写触发。 + +策略:(在配置文件中配置) + +![image-20200427101221526](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200427101221526.png + +手动触发:(执行命令) + +``` +bgrewriteaof +``` + +#### 31.AOF优点? + +- AOF提供了3种保存策略:每秒保存、跟系统策略、每次操作保存。实时性比较高,一般来说会选择每秒保存,因此意外发生时顶多失去一秒的数据。 +- 文件追加写形式,所以文件很少有损坏问题,如最后意外发生少写数据,可通过redis-check-aof工具修复。 +- AOF由于是文本形式,直接采用协议格式,避免二次处理开销,另外对于修改也比较灵活。 + +#### 32.AOF缺点? + +- AOF文件要比RDB文件大。 +- AOF冷备没RDB迅速。 +- 由于执行频率比较高,所以负载高时,性能没有RDB好。 + +#### 33.混合持久化?优缺点? + +一般来说我们的线上都会采取混合持久化。redis4.0以后添加了新的混合持久化方式。 + +优点: + +- 在快速加载的同时,避免了丢失过更多的数据。 + +缺点: + +- 由于混合了两种格式,所以可读性差。 +- 兼容性,需要4.0以后才支持。 + +#### 34.Redis的Java客户端官方推荐?实际选择? + +官方推荐的有3种:Jedis、Redisson和lettuce。 + +一般来说用的比较多的有:Jedis|Redisson。 + +Jedis:更轻量、简介、不支持读写分离需要我们来实现,文档比较少。API提供了比较全面的Redis命令的支持。 + +Redisson:基于Netty实现,性能高,支持异步请求。提供了很多分布式相关操作服务。高级功能能比较多,文档也比较丰富,但实用上复杂度也相对高。和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。 + +#### 35.Redis事务? + +事务提供了一种将多个命令请求打包,一次性、按顺序的执行多个命令的机制。并且在事务执行期间,服务器不会中断事务而改去执行其他客户端命令请求,它会 + +#### 36.Redis事务开始到结束的几个阶段? + +- 开启事务 +- 命令入队 +- 执行事务/放弃事务 + +#### 37.Redis中key的过期操作? + +​ 设置key的生存时间为n秒 + +``` +expire key nseconds +``` + +​ 设置key的生存时间为nmilliseconds + +``` +pxpire key milliseconds +``` + +​ 设置过期时间为timestamp所指定的秒数时间戳 + +``` +expireat key timespamp +``` + + 设置过期时间为timestamp毫秒级时间戳 + +``` +pexpireat key millisecondsTimestamp +``` + +#### 38.Redis过期键删除策略? + +定时删除:在设置的过期时间同时,创建一个定时器在键的过期时间来临时,立即执行队键的操作删除。 + +惰性删除:放任过期键不管,但每次从键空间中获取键时,都检查取得的键是否过期,如果过期就删除,如果没有就返回该键。 + +定期删除:每隔一段时间执行一次删除过期键操作,并通过先吃删除操作执行的时长和频率来减少删除操作对cpu时间的影响。 + +#### 39.Pipeline是什么?为什么要它? + +命令批处理技术,对命令进行组装,然后一次性执行多个命令。 + +可以有效的节省RTT(Round Trip Time 往返时间)。 + +经过测试验证: + +- pipeline执行速度一般比逐条执行快。 +- 客户端和服务的网络延越大,pipeline效果越明显。 + +#### 40.如何获取当前最大内存?如何动态设置? + +获取最大内存: + +``` +config get maxmemory +``` + +设置最大内存: + +命令设置: + +``` +config set maxmemory 1GB +``` + +#### 41.Redis内存溢出控制? + +当Redis所用内存达到maxmemory上限时,会出发相应的溢出策略。 + +#### 42.Redis内存溢出策略? + +1.noeviction(默认策略):拒绝所有写入操作并返回客户端错误信息(error) OOM command not allowed when used memory,只响应读操作。 + +1. volatile-lru:根据LRU算法删除设置了超时属性(expire)的键,直到腾出足够空间为止。如果没有可删除的键对象,回退到noeviction策略。 +2. allkeys-lru:根据LRU算法删除键,不管数据有没有设置超时属性, 直到腾出足够空间为止。 +3. allkeys-random:随机删除所有键,直到腾出足够空间为止。 +4. volatile-random:随机删除过期键,直到腾出足够空间为止。 +5. volatile-tth根据键值对象的ttl属性,删除最近将要过期数据。如果没有,回退到noeviction策略。 + +#### 43.Redis高可用方案? + +Redis Sentinel(哨兵)能自动完成故障发现和转移。 + +#### 44.Redis集群方案? + +Twemproxy、Redis Cluster、Codis。 + +#### 45.Redis Cluster槽范围? + +0~16383 + +#### 46.Redis锁实现思路? + +setnx (set if not exists),如果创建成功则表示获取到锁。 + +setnx lock true 创建锁 + +del lock 释放锁 + +如果中途崩溃,无法释放锁? + +此时需要考虑到超时时间的问题。比如 :expire lock 300 + +由于命令是非原子的,所以还是会死锁,如何解决? + +Redis 支持 set 并设置超时时间的功能。 + +比如: set lock true ex 30 nx + +#### 47.什么是布隆过滤器? + +是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。 + +Tips:当判断一定存在时,可能会误判,当判断不存在时,就一定不存在。 + +#### 48.什么是缓存穿透?处理问题? + +缓存穿透:缓存层不命中,存储层不命中。 + +处理方式1:缓存空对象,不过此时会占用更多内存空间,所以根据大家业务特性去设置超时时间来控制内存占用的问题。 + +处理方式2:布隆过滤器。 + +#### 49.什么是缓存预热? + +就是系统上线后,提前将相关数据加载到缓存系统,避免用户先查库,然后在缓存。 + +#### 50.什么是缓存雪崩?处理问题? + +缓存雪崩:由于缓存层承载着大量请求,有效的保护了存储层,但如果存储层由于某些原因不能提供服务,存储层调用暴增,造成存储层宕机。 + +处理: + +- 保证缓存层服务高可用性。 +- 对缓存系统做实时监控,报警等。 +- 依赖隔离组件为后端限流并降级。 +- 做好持久化,以便数据的快速恢复。 + +参考: + +- 《Redis深度历险:核心原理和应用实践》 +- 《Redis开发与运维》 +- 《Redis设计与实现》 +- https://redis.io/ +- 百度百科 + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file diff --git "a/Redis-\344\270\213\345\215\267.md" "b/Redis-\344\270\213\345\215\267.md" new file mode 100644 index 0000000..5947af1 --- /dev/null +++ "b/Redis-\344\270\213\345\215\267.md" @@ -0,0 +1,359 @@ +## Redis + +#### 1.查看配置语法 + +``` +CONFIG GET CONFIG_SETTING_NAME +``` + +#### 2.获取所有配置项 + +``` +127.0.0.1:6379> config get * + 1) "dbfilename" + 2) "dump.rdb" + 3) "requirepass" + 4) "" + 5) "masterauth" + 6) "" + 7) "cluster-announce-ip" + 8) "" + 9) "unixsocket" + 10) "" + 11) "logfile" + 12) "" +... +``` + +#### 3.设置字符串 + +``` +127.0.0.1:6379> set xk www.javaxks.com +OK +``` + +#### 4.获取字符串 + +``` +127.0.0.1:6379> get xk +"www.javaxks.com" +``` + +#### 5.获取随机key + +``` +127.0.0.1:6379> randomkey +"xk" +``` + +#### 6.获取key存储的类型 + +``` +127.0.0.1:6379> randomkey +"xk" +``` + +#### 7.判断key是否存在 + +``` +127.0.0.1:6379> randomkey +"xk" +``` + +#### 8.修改key的名称 + +``` +127.0.0.1:6379> randomkey +"xk" +``` + +#### 9.返回key存储的字符串的长度 + +``` +127.0.0.1:6379> randomkey +"xk" +``` + +#### 10.同时设置多个kv对 + +``` +127.0.0.1:6379> mset date 5.21 mood haha +OK +``` + +#### 11.获取多个key的值 + +``` +127.0.0.1:6379> mget date mood +1) "5.21" +2) "haha" +``` + +#### 12.设置key 10秒后过期 + +``` +127.0.0.1:6379> expire date 10 +(integer) 1 +``` + +#### 13.查看当前还剩几秒过期 + +``` +127.0.0.1:6379> ttl date +(integer) 4 +``` + +#### 14.过期后获取key + +``` +127.0.0.1:6379> get date +(nil) +``` + +#### 15.列表最多可以存多少元素 + +2的32次方 - 1 + +#### 16.从左向列表中添加元素 + +``` +127.0.0.1:6379> lpush fruit apple +(integer) 1 +127.0.0.1:6379> lpush fruit strawberry +(integer) 2 +127.0.0.1:6379> lpush fruit pear +(integer) 3 +127.0.0.1:6379> lpush fruit orange +(integer) 4 +``` + +#### 17.获取列表中所有的元素 + +``` +127.0.0.1:6379> lrange fruit 0 -1 +1) "orange" +2) "pear" +3) "strawberry" +4) "apple" +``` + +#### 18.从左弹出元素 + +``` +127.0.0.1:6379> lpop fruit +"orange" +127.0.0.1:6379> lrange fruit 0 -1 +1) "pear" +2) "strawberry" +3) "apple" +``` + +#### 19.获取指定位置的元素 + +``` +127.0.0.1:6379> lindex fruit 2 +"apple" +``` + +#### 20.获取列表长度 + +``` +127.0.0.1:6379> llen fruit +(integer) 3 +``` + +#### 21.集合 + +set是string类型的无序集合,集合中的元素都是唯一的。 + +#### 22.向集合中添加元素 + +``` +127.0.0.1:6379> sadd books java +(integer) 1 +127.0.0.1:6379> sadd books python +(integer) 1 +127.0.0.1:6379> sadd books php +(integer) 1 +127.0.0.1:6379> sadd books go +(integer) 1 +127.0.0.1:6379> sadd books ruby +(integer) 1 +``` + +#### 23.获取集合中的元素 + +``` +127.0.0.1:6379> smembers books +1) "go" +2) "python" +3) "java" +4) "php" +5) "ruby" +``` + +#### 24.删除集合中的指定元素 + +``` +127.0.0.1:6379> srem books ruby +(integer) 1 +127.0.0.1:6379> smembers books +1) "php" +2) "java" +3) "python" +4) "go" +``` + +#### 25.zset类型 + +有序的集合 + +元素为string类型 + +元素唯一,不重复。每个元素有自己的权重。 + +#### 26.向有序集合中添加元素 + +``` +127.0.0.1:6379> zadd animal 1 monkey +(integer) 1 +127.0.0.1:6379> zadd animal 2 chicken +(integer) 1 +127.0.0.1:6379> zadd animal 3 mouse +(integer) 1 +127.0.0.1:6379> zadd animal 4 horse +(integer) 1 +``` + +#### 27.获取有序集合元素个数 + +``` +127.0.0.1:6379> zcard animal +(integer) 4 +``` + +#### 28.获取所有元素 + +``` +127.0.0.1:6379> zrange animal 0 -1 +1) "monkey" +2) "chicken" +3) "mouse" +4) "horse" +``` + +#### 29.获取所有元素并带上分值 + +``` +127.0.0.1:6379> zrange animal 0 -1 withscores +1) "monkey" +2) "1" +3) "chicken" +4) "2" +5) "mouse" +6) "3" +7) "horse" +8) "4" +``` + +#### 30.HyperLogLog + +Redis 在 2.8.9 版本添加了 HyperLogLog 结构。 + +Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。 + +在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。 + +但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。 + +#### 31.向HyperLogLog中添加元素 + +``` +127.0.0.1:6379> pfadd color blue +(integer) 1 +127.0.0.1:6379> pfadd color yellow +(integer) 1 +127.0.0.1:6379> pfadd color pink +(integer) 1 +127.0.0.1:6379> pfadd color white +(integer) 1 +127.0.0.1:6379> pfadd color black +(integer) 1 +``` + +#### 32.获取HyperLogLog元素个数 + +``` +127.0.0.1:6379> pfcount color +(integer) 5 +``` + +#### 33.发布订阅 + +种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。 + +Redis 客户端可以订阅任意数量的频道。 + +#### 34.创建订阅频道 + +``` +127.0.0.1:6379> subscribe everydayNews +Reading messages... (press Ctrl-C to quit) +1) "subscribe" +2) "everydayNews" +3) (integer) 1 +1) "message" +``` + +#### 35.在对应频道发布消息 + +``` +127.0.0.1:6379> publish everydayNews "morning" +(integer) 1 +``` + +订阅者收到: + +``` +Reading messages... (press Ctrl-C to quit) +1) "subscribe" +2) "everydayNews" +3) (integer) 1 +1) "message" +2) "everydayNews" +3) "morning" +``` + +#### 36.非后台执行备份 + +``` +127.0.0.1:6379> save +OK +``` + +#### 37.后台执行备份 + +``` +127.0.0.1:6379> BGSAVE +Background saving started +``` + +#### 38.查看是否需要密码验证 + +``` +127.0.0.1:6379> config get requirepass +1) "requirepass" +2) "" +``` + + + +#### 参考: + +https://redis.io/ + +http://redisdoc.com/ + +https://www.runoob.com/redis/ + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file diff --git a/Servlet.md b/Servlet.md new file mode 100644 index 0000000..87eeb69 --- /dev/null +++ b/Servlet.md @@ -0,0 +1,247 @@ +## Servlet + +#### 1.Servlet生命周期 + +servlet的生命周期是初始化(init)、服务(service)、销毁(destroy) + +初始化(init):默认第一次请求前,只初始化一次。修改web.xml,允许服务器启动时初始化。 +服务(service):方法被调用时进行服务,在项目启动期间可以进行多次服务(请求一次执行一次) +销毁(destory):当服务器关闭时进行销毁。只销毁一次 +Servlet接口中声明3个方法,tomcat在不同的时候将调用不同的方法。 +init 初始化方法,2种情况被调用 +情况1:默认,第一次请求前 +情况2:在web项目核心配置文件web.xml中,配置初始化,将在服务器启动时初始化。 +每次请求时,调用服务 +服务器关闭时,调用销毁。 + +#### 2.什么是jsp?jsp和Servlet有什么区别? + +Servlet是服务器端的程序 +JSP是服务器页面程序 +JSP本质上就是一个Servlet,在访问jsp时,在服务器端会将jsp先转换成servlet,再将生产的servlet的结果响应给浏览器。 +jsp是html页面中内嵌Java代码,侧重页面显示;Servlet是中书写Java代码,侧重逻辑控制; + +![img](https://img-blog.csdnimg.cn/20200910144535754.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI1ODU0NTk5,size_16,color_FFFFFF,t_70#pic_center) + +#### 3.Servlet 接口中有哪些方法? + +1. init(ServletConfig):初始化方法,默认第一次请求前执行,完成 servlet 初始化工作 + +2. service(ServletRequest,ServletResponse):执行方法,一次请求执行一次。 + +3. destroy():销毁方法,Servlet 对象应该从服务中被移除的时候,容器会调用该方法进行销毁操作 + +4. getServletConfig():获得 ServletConfig 配置对象,包括初始化参数等。 + +5. getServletInfo():获得 Servlet 描述,一般没有用。 + + ![img](https://img-blog.csdnimg.cn/20200910144604142.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI1ODU0NTk5,size_16,color_FFFFFF,t_70#pic_center) + + + +#### 4.Servlet 3.0 中的异步处理指的是什么? + +异步处理允许 Servlet 重新发起一条新线程去调用 耗时业务方法,这样就可以避免等待 + +![img](https://img-blog.csdnimg.cn/2020091014463133.png#pic_center) + +#### 5.Servlet 中如何获取用户提交的查询参数或表单数据? + +1. request.getParameterValues(“参数”); // 获得指定参数名的一组参数值 (String[]) +2. request.getParameter(“参数”); // 获得指定参数名的一个参数值(String) , UserServlet?username=jack , 通过 username 获得值 jack + +``` +public class TestRequestParam { + private HttpServletRequest request; + + public void testDemo01(){ + //请求数据:index.html?username=jack&hobby=抽烟&hobby=喝酒&hobby=烫头 + + // 获得username的值,一个值 + String username = request.getParameter("username"); + + // 获得hobby的值,一组值 + String[] hobbyArr = request.getParameterValues("hobby"); + + // 所有值 , map.key 参数名称,map.value 参数的值 + Map map = request.getParameterMap(); + + } +} +``` + +#### 6.区别请求的转发与重定向? + + 可以从以下三个方面进行比较 + +1.地址栏: + +转发: 显示的是请求的URL + +重定向: 显示的不是请求的URL, 而是重定向指向的新的URL + +2.浏览器发了几次请求? + +转发: 1次请求 + +重定向: 2次请求 + +1. 是否可以进行Request的数据共享? + +转发: 两个资源之间是同一个request对象, 可以共享request中的数据 + +重定向: 两个资源之间不是同一个request对象, 不可以共享 + +**经典现实案例:** + +![Servlet相关技术常见面试题](https://img-blog.csdnimg.cn/img_convert/48ff7f8346d74c66da6806855a4b4cb7.png) + +#### 7. 比较一下Servlet与Filter + +从四个方面来区分: + +**概念** + +  servlet是一种运行在服务器端的Java应用程序,独立于平台和协议,可以动态的生成web页面,它工作于客户端请求和服务器的中间层 + +  filter是一个可以复用的代码片段,可以用来转换请求,响应以及头信息,filter不能产生请求和响应,他只能在请求到达servlet之前对请求进行修改,或者在请求返回客户端之前对响应进行处理 + +**生命周期** + +  servlet是在系统启动或者请求到达servlet时,通过init()方法进行初始化,一旦被装入了web服务器,一般不会从Web服务器删除,直到服务器关闭才会调用  destroy()方法进行销毁。每次请求,Request都会被初始化,响应请求后,请求被销毁。但是servlet不会随着请求的销毁而销毁 + +  如果某个Servlet配置了 1 ,该Servlet也是在Tomcat(Servlet容器)启动时初始化。 +  如果Servlet没有配置1 ,该Servlet不会在Tomcat启动时初始化,而是在请求到来时初始化。 + +  filter + +    是在系统启动的时候通过init()初始化的,每次请求都只会调用dofiter方法进行处理,服务器停止的时候调用destroy()进行销毁 + +**注意**:服务器关闭时,servlet和filter依次销毁 + +**职责** + +  **servlet** + +    可以动态创建基于客户请求的页面;可以读取客户端发来的隐藏数据和显示数据;可以和其他的服务器资源进行通讯;通过状态代码和响应头向客户端返回数据。 + +  **filter** + +    主要是对请求到达servlet之前对请求和请求头信息进行前处理,和对数据返回客户端之前进行后处理 + +**区别** + +  servlet的流程比较短,url来了之后就对其进行处理,处理完就返回数据或者转向另一个页面 + +  filter的流程比较长,在一个filter处理之后还可以转向另一个filter进行处理,然后再交给servlet,但是servlet处理之后不能向下传递了。 + +  filter可用来进行字符编码的过滤,检测用户是否登陆的过滤,禁止页面缓存等 + +#### 8.我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串? + +``` + Public String translate (String str) { +    String tempStr = ""; + +    try { +      tempStr = new String(str.getBytes("ISO-8859-1"), "GBK"); + +      tempStr = tempStr.trim(); + +    } + +    catch (Exception e) { +      System.err.println(e.getMessage()); + +    } + +    return tempStr; + +  } +``` + +#### 9.Servlet执行时一般实现哪几个方法? + +public void init(ServletConfig config) + +public ServletConfig getServletConfig() + +public String getServletInfo() + +public void service(ServletRequest request,ServletResponse response) + +public void destroy() + + +#### 10.描述Cookie和Session的作用,区别和各自的应用范围,Session工作原理。 + +1)cookie 是一种发送到客户浏览器的文本串句柄,并保存在客户机硬盘上,可以用来在某个WEB站点会话间持久的保持数据。 + +2)session其实指的就是访问者从到达某个特定主页到离开为止的那段时间。 Session其实是利用Cookie进行信息处理的,当用户首先进行了请求后,服务端就在用户浏览器上创建了一个Cookie,当这个Session结束时,其实就是意味着这个Cookie就过期了。 + +注:为这个用户创建的Cookie的名称是aspsessionid。这个Cookie的唯一目的就是为每一个用户提供不同的身份认证。 + +3)cookie和session的共同之处在于:cookie和session都是用来跟踪浏览器用户身份的会话方式。 + +4)cookie 和session的区别是:cookie数据保存在客户端,session数据保存在服务器端。 + +5)session工作原理:session技术中所有的数据都保存在服务器上,客户端每次请求服务器的时候会发送当前会话的sessionid,服务器根据当前sessionid判断相应的用户数据标志,以确定用户是否登录或具有某种权限。 + +#### 11.Applet和Servlet有什么区别? + +Applet是运行在客户端主机的浏览器上的客户端Java程序。而Servlet是运行在web服务器上的服务端的组件。applet可以使用用户界面类,而Servlet没有用户界面,相反,Servlet是等待客户端的HTTP请求,然后为请求产生响应。 + +#### 12.GenericServlet和HttpServlet有什么区别? + +GenericServlet是一个通用的协议无关的Servlet,它实现了Servlet和ServletConfig接口。继承自GenericServlet的Servlet应该要覆盖service()方法。最后,为了开发一个能用在网页上服务于使用HTTP协议请求的Servlet,你的Servlet必须要继承自HttpServlet。这里有Servlet的例子。 + +#### 13.什么是服务端包含(Server Side Include)? + +服务端包含(SSI)是一种简单的解释型服务端脚本语言,大多数时候仅用在Web上,用servlet标签嵌入进来。SSI最常用的场景把一个或多个文件包含到Web服务器的一个Web页面中。当浏览器访问Web页面的时候,Web服务器会用对应的servlet产生的文本来替换Web页面中的servlet标签。 + +#### 14.什么是Servlet链(Servlet Chaining)? + +Servlet链是把一个Servlet的输出发送给另一个Servlet的方法。第二个Servlet的输出可以发送给第三个Servlet,依次类推。链条上最后一个Servlet负责把响应发送给客户端。 + +#### 15.如何知道是哪一个客户端的机器正在请求你的Servlet + +ServletRequest类可以找出客户端机器的IP地址或者是主机名。getRemoteAddr()方法获取客户端主机的IP地址,getRemoteHost()可以获取主机名。看下这里的例子。 + +#### 16.什么是cookie?session和cookie有什么区别? + +cookie是Web服务器发送给浏览器的一块信息。浏览器会在本地文件中给每一个Web服务器存储cookie。以后浏览器在给特定的Web服务器发请求的时候,同时会发送所有为该服务器存储的cookie。下面列出了session和cookie的区别: + +• 无论客户端浏览器做怎么样的设置,session都应该能正常工作。客户端可以选择禁用cookie,但是,session仍然是能够工作的,因为客户端无法禁用服务端的session。 + +• 在存储的数据量方面session和cookies也是不一样的。session能够存储任意的Java对象,cookie只能存储String类型的对象。 + +#### 17.浏览器和Servlet通信使用的是什么协议? + +浏览器和Servlet通信使用的是HTTP协议。 + +#### 18.什么是URL编码和URL解码? + +URL编码是负责把URL里面的空格和其他的特殊字符替换成对应的十六进制表示,反之就是解码。 + +JSP + +#### 19.什么是Scriptlets? + +JSP技术中,scriptlet是嵌入在JSP页面中的一段Java代码。scriptlet是位于标签内部的所有的东西,在标签与标签之间,用户可以添加任意有效的scriplet。 + +#### 20.声明(Decalaration)在哪里? + +声明跟Java中的变量声明很相似,它用来声明随后要被表达式或者scriptlet使用的变量。添加的声明必须要用开始和结束标签包起来。 + +#### + +#### 参考: + +https://blog.csdn.net/qq_25854599/article/details/108513815 + +https://blog.csdn.net/msjhw_com/article/details/113240556 + +https://www.cnblogs.com/htyj/p/8619198.html + +https://blog.csdn.net/Jeff_Seid/article/details/80761076 + diff --git a/Spring.md b/Spring.md new file mode 100644 index 0000000..f89a218 --- /dev/null +++ b/Spring.md @@ -0,0 +1,344 @@ +## Spring + +#### 1.Spring框架? + +Spring框架是由于软件开发的复杂性而创建的,Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事。从简单性、可测性和松耦合性角度而言,绝大部分Java应用都可以用Spring。 + +#### 2.Spring的整体架构? + +![image-20200425151510055](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200425151510055.png) + + + +大约分为20个模块。 + +#### 3.Spring可以做什么? + +![image-20200425152654798](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200425152654798.png) + +#### 4.Spring的优点?缺点? + +优点: + +- Spring属于低侵入设计。 +- IOC将对象之间的依赖关系交给Spring,降低组件之间的耦合,实现各个层之间的解耦,让我们更专注于业务逻辑。 +- 提供面向切面编程。 +- 对各种主流插件提供很好的集成支持。 +- 对事务支持的很好,只要配置即可,无须手动控制。 + +缺点: + +- 依赖反射,影响性能。 + +#### 5.你能说几个Spring5的新特性吗? + +- spring5整个框架基于java8 +- 支持http/2 +- Spring Web MVC支持最新API +- Spring WebFlux 响应式编程 +- 支持Kotlin函数式编程 + +#### 6.IOC? + +负责创建对象、管理对象(通过依赖注入)、整合对象、配置对象以及管理这些对象的生命周期。 + +#### 7.什么是依赖注入? + +依赖注入是Spring实现IoC的一种重要手段,将对象间的依赖关系的控制权从开发人员手里转移到容器。 + +#### 8.IOC注入哪几种方式? + +1.构造器注入 + +2.setter注入 + +3.接口注入(我们几乎不用) + +#### 9.IOC优点?缺点? + +优点: + +- 组件之间的解耦,提高程序可维护性、灵活性。 + +缺点: + +- 创建对象步骤复杂,有一定学习成本。 +- 利用反射创建对象,效率上有损。(对于代码的灵活性和可维护性来看,Spring对于我们的开发带来了很大的便利,这点损耗不算什么哦) + +#### 10.bean的生命周期? + +1.Spring 对bean进行实例化。 + +2.Spring将值和bean的引用注入到 bean对应的属性中。 + +3.如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBeanName()方法。 + +4.如果bean实现了BeanFactoryAware接口, Spring将调用setBeanFactory()方法,将 bean所在的应用引用传入进来。 + +5.如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用引用传入进来。 + +6.如果bean实现了BeanPostProcessor 接口,Spring将调用他们的post-ProcessBeforeInitalization()方法。 + +7.如果bean实现了InitializingBean接口,Spring将调用他们的after-PropertiesSet()方法,类似地,如果bean使用init-method声明了初始化方法,该方法也会被调用。 + +8.如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessAfterInitialization()方法。 + +9.此时, bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用被销毁。 + +10.如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。 + +#### 11.Spring有几种配置方式? + +- 基于xml +- 基于注解 +- 基于Java + +#### 12.Spring中的bean有几种scope? + +- singleton: 单例,每一个bean只创建一个对象实例。 +- prototype,原型,每次对该bean请求调用都会生成各自的实例。 +- request,请求,针对每次HTTP请求都会生成一个新的bean。表示在一次 HTTP 请求内有效。 +- session,在一个http session中,一个bean定义对应一个bean实例。 +- global session:在一个全局http session中,一个bean定义对应一个bean实例。 + +#### 13.什么是AOP(面向切面编程)? + +在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。 + +#### 14.切面有几种类型的通知?分别是? + +前置通知(Before): 目标方法被调用之前调用通知功能。 + +后置通知(After): 目标方法完成之后调用通。 + +返回通知(After-returning): 目标方法成功执行之后调用通知。 + +异常通知(After-throwing): 目标方法抛出异常后调用通知。 + +环绕通知(Around): 在被通知的方法调用之前和调用之后执行自定义的行为。 + +#### 15.什么是连接点 (Join point)? + +连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。 + +#### 16.什么是切点(Pointcut)? + +切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。有些AOP框架允许我们创建动态的切点,可以根据运行时的决策(比如方法的参数值)来决定是否应用通知。 + +#### 17.什么是切面(Aspect)? + +切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 + +#### 18.织入(Weaving)? + +织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。 + +#### 19.引入(Introduction)? + +􏶸􏰫􏶹􏵡􏰸􏱲􏰇􏲘􏱴􏰉􏵇􏶺􏶻􏶼􏰽􏱸􏵼􏶽􏶾􏱎􏶸􏰫􏶹􏵡􏰸􏱲􏰇􏲘􏱴􏰉􏵇􏶺􏶻􏶼􏰽􏱸􏵼􏶽􏶾􏱎引入允许我们向现有的类添加新方法或属性。 + +#### 20.在目标对象的生命周期里有多个点可以进行织入? + +- 编译期:切面在目标类编译时被织入。AspectJ的织入编译器就是以这种方式织入切面的。 +- 类加载期:切面在目标类加载到JVM时被织入。它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ 5的加载时织入(load-time weaving,LTW)就支持以这种方式织入切面。 +- 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。Spring AOP就是以这种方式织入切面的。 + +#### 21.AOP动态代理策略? + +- 如果目标对象实现了接口,默认采用JDK 动态代理。可以强制转为CgLib实现AOP。 +- 如果没有实现接口,采用CgLib进行动态代理。 + +#### 22.什么是MVC框架? + +MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。 + +MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。 + +#### 23.什么是SpringMVC? + +SpringMVC是Spring框架的一个模块。是一个基于MVC的框架。 + +#### 24.SpringMVC的核心? + +DispatcherServlet + +#### 25.SpringMVC的几个组件? + +DispatcherServlet : 前端控制器,也叫中央控制器。相关组件都是它来调度。 + +HandlerMapping : 处理器映射器,根据URL路径映射到不同的Handler。 + +HandlerAdapter : 处理器适配器,按照HandlerAdapter的规则去执行Handler。 + +Handler : 处理器,由我们自己根据业务开发。 + +ViewResolver : 视图解析器,把逻辑视图解析成具体的视图。 + +View : 一个接口,它的实现支持不同的视图类型(freeMaker,JSP等) + +#### 26.SpringMVC工作流程? + +1.用户请求旅程的第一站是DispatcherServlet。 + +2.收到请求后,DispatcherServlet调用HandlerMapping,获取对应的Handler。 + +3.如果有拦截器一并返回。 + +4.拿到Handler后,找到HandlerAdapter,通过它来访问Handler,并执行处理器。 + +5.执行Handler的逻辑。 + +6.Handler会返回一个ModelAndView对象给DispatcherServlet。 + +7.将获得到的ModelAndView对象返回给DispatcherServlet。 + +8.请求ViewResolver解析视图,根据逻辑视图名解析成真正的View。 + +9.返回View给DispatcherServlet。 + +10.DispatcherServlet对View进行渲染视图。 + +11.DispatcherServlet响应用户。 + +#### 27.SpringMVC的优点? + +1.具有Spring的特性。 + +2.可以支持多种视图(jsp,freemaker)等。 + +3.配置方便。 + +4.非侵入。 + +5.分层更清晰,利于团队开发的代码维护,以及可读性好。 + +Tips:Jsp目前很少有人用了。 + +#### 28.单例bean是线程安全的吗? + +不是。具体线程问题需要开发人员来处理。 + +#### 29.Spring从哪两个角度实现自动装配? + +组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。 + +自动装配(autowiring):Spring自动满足bean之间的依赖。 + +#### 30.自动装配有几种方式?分别是? + +no - 默认设置,表示没有自动装配。 + +byName : 根据名称装配。 + +byType : 根据类型装配。 + +constructor : 把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应入参中。 + +autodetect :先尝试constructor装配,失败再尝试byType方式。 + +default:由上级标签的default-autowire属性确定。 + +#### 31.说几个声明Bean 的注解? + +- @Component +- @Service +- @Repository +- @Controller + +#### 32.注入Java集合的标签? + +- 允许有相同的值。 +- 不允许有相同的值。 +- 键和值都只能为String类型。 +- < map > 键和值可以是任意类型。 + +#### 33.**Spring支持的ORM**? + +1. Hibernate +2. iBatis +3. JPA (Java Persistence API) +4. TopLink +5. JDO (Java Data Objects) +6. OJB + +#### 34.@Repository注解? + + Dao 层实现类注解,扫描注册 bean。 + +#### 35.@Value注解? + +讲常量、配置中的变量值、等注入到变量中。 + +#### 36.@Controller注解? + +定义控制器类。 + +#### 37.声明一个切面注解是哪个? + +@Aspect + +#### 38.映射web请求的注解是? + +@RequestMapping + +#### 39.@ResponseBody注解? + +作用是将返回对象通过适当的转换器转成置顶格式,写进response的body区。通常用来返回json、xml等。 + +#### 40.@ResponseBody + @Controller =? + +@RestController + +#### 41.接收路径参数用哪个注解? + +@PathVariable + +#### 42.@Cacheable注解? + +用来标记缓存查询。 + +#### 43.清空缓存是哪个注解? + +@CacheEvict + +#### 44.@Component注解? + +泛指组件,不好归类时,可以用它。 + +#### 45.**BeanFactory 和 ApplicationContext**区别? + +![image-20200428210402245](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200428210402245.png) + +#### 46.@Qualifier注解? + +当创建多个相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配,在这种情况下,你可以使用 @Qualifier 注释和 @Autowired 注释通过指定哪一个真正的 bean 将会被装配来消除混乱。 + +#### 47.事务的注解是? + +@Transaction + +#### 48.Spring事务实现方式有? + +声明式:声明式事务也有两种实现方式。 + +- xml 配置文件的方式。 +- 注解方式(在类上添加 @Transaction 注解)。 + +编码式:提供编码的形式管理和维护事务。 + +#### 49.什么是事务传播? + +事务在嵌套方法调用中如何传递,具体如何传播,取决于事务传播行为。 + +#### 50.Spring事务传播行为有哪些? + +![image-20200428223544334](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200428223544334.png) + +参考: + +- 《Spring in action 4》 +- 《SPRING技术内幕》 +- 《Spring源码深度解析》 +- 《Spring5企业级开发实战》 +- https://spring.io +- 百度百科 \ No newline at end of file diff --git a/SpringBoot.md b/SpringBoot.md new file mode 100644 index 0000000..9c927e5 --- /dev/null +++ b/SpringBoot.md @@ -0,0 +1,285 @@ +## SpringBoot + +#### 1.什么是SpringBoot? + +通过Spring Boot,可以轻松地创建独立的,基于生产级别的Spring的应用程序,您可以“运行”它们。大多数Spring Boot应用程序需要最少的Spring配置。 + +#### 2.SpringBoot的特征? + +- 创建独立的Spring应用程序 +- 直接嵌入Tomcat,Jetty或Undertow(无需部署WAR文件) +- 提供固化的“starter”依赖项,以简化构建配置 +- 尽可能自动配置Spring和3rd Party库 +- 提供可用于生产的功能,例如指标,运行状况检查和外部化配置 +- 完全没有代码生成,也不需要XML配置 + +#### 3.如何快速构建一个SpringBoot项目? + +- 通过Web界面使用。http://start.spring.io +- 通过Spring Tool Suite使用。 +- 通过IntelliJ IDEA使用。 +- 使用Spring Boot CLI使用。 + +#### 4.SpringBoot启动类注解?它是由哪些注解组成? + +@SpringBootApplication + +- @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。 +- @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项。 +- @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) +- @ComponentScan:Spring组件扫描 + +#### 5.什么是yaml? + +YAML(/ˈjæməl/,尾音类似camel骆驼)是一个可读性高,用来表达数据序列化的格式。YAML参考了其他多种语言,包括:C语言、Python、Perl。更具有结构性。 + +#### 6.SpringBoot支持配置文件的格式? + +1.properties + +```properties +java.xiaokaxiu.name = xiaoka +``` + +2.yml + +```yaml +java: + xiaokaxiu: + name: xiaoka +``` + +#### 7.SpringBoot启动方式? + +1. main方法 + +2. 命令行 java -jar 的方式 + +3. mvn/gradle + +#### 8.SpringBoot需要独立的容器运行? + +不需要,内置了 Tomcat/Jetty。 + +#### 9.SpringBoot配置途径? + +1. 命令行参数 +2. java:comp/env里的JNDI属性 +3. JVM系统属性 +4. 操作系统环境变量 +5. 随机生成的带random.*前缀的属性(在设置其他属性时,可以引用它们,比如${random. long}) +6. 应用程序以外的application.properties或者appliaction.yml文件 +7. 打包在应用程序内的application.properties或者appliaction.yml文件 +8. 通过@PropertySource标注的属性源 +9. 默认属性 + +tips:这个列表按照优先级排序,也就是说,任何在高优先级属性源里设置的属性都会覆盖低优先级的相同属性。 + +#### 10.application.properties和application.yml文件可放位置?优先级? + +1. 外置,在相对于应用程序运行目录的/config子目录里。 + +2. 外置,在应用程序运行的目录里。 + +3. 内置,在config包内。 + +4. 内置,在Classpath根目录。 + + 这个列表按照优先级排序,优先级高的会覆盖优先级低的。 + + 当然我们可以自己指定文件的位置来加载配置文件。 + +```powershell +java -jar xiaoka.jar ———spring.config.location=/home/application.yml +``` + +#### 11.SpringBoot自动配置原理? + +@EnableAutoConfiguration (开启自动配置) +该注解引入了AutoConfigurationImportSelector,该类中的方法会扫描所有存在META-INF/spring.factories的jar包。 + +#### 12.SpringBoot热部署方式? + +- spring-boot-devtools + +- Spring Loaded + +- Jrebel + +- 模版热部署 + + +#### 13.**bootstrap.yml** 和**application.yml**? + +bootstrap.yml 优先于application.yml + +#### 14.SpringBoot如何修改端口号? + +yml中: + +```yaml +server : + port : 8888 + +``` + +properties: + +```properties +server.port = 8888 +``` + +命令1: + +```powershell +java -jar xiaoka.jar ——— server.port=8888 +``` + +命令2: + +```powershell +java - Dserver.port=8888 -jar xiaoka.jar +``` + +#### 15.开启SpringBoot特性的几种方式? + +1. 继承spring-boot-starter-parent项目 +2. 导入spring-boot-dependencies项目依赖 + +#### 16.SpringBoot如何兼容Spring项目? + +在启动类加: + +@ImportResource(locations = {"classpath:spring.xml"}) + +#### 17.SpringBoot配置监控? + +```xml + + org.springframework.boot + spring-boot-starter-actuator + +``` + +#### 18.获得Bean装配报告信息访问哪个端点? + +/beans 端点 + +#### 19.关闭应用程序访问哪个端点? + +/shutdown + +该端点默认是关闭的,如果开启,需要如下设置。 + +```yaml + endpoints: + shutdown: + enabled: true +``` + +或者properties格式也是可以的。 + +#### 20.查看发布应用信息访问哪个端点? + +/info + +#### 21.针对请求访问的几个组合注解? + +@PatchMapping + +@PostMapping + +@GetMapping + +@PutMapping + +@DeleteMapping + +#### 22.SpringBoot 中的starter? + +可以理解成对依赖的一种合成,starter会把一个或一套功能相关依赖都包含进来,避免了自己去依赖费事,还有各种包的冲突问题。大大的提升了开发效率。 + +并且相关配置会有一个默认值,如果我们自己去配置,就会覆盖默认值。 + +#### 23.SpringBoot集成Mybatis? + +mybatis-spring-boot-starter + +#### 24.什么是SpringProfiles? + +一般来说我们从开发到生产,经过开发(dev)、测试(test)、上线(prod)。不同的时刻我们会用不同的配置。Spring Profiles 允许用户根据配置文件(dev,test,prod 等)来注册 bean。它们可以让我们自己选择什么时候用什么配置。 + +#### 25.不同的环境的配置文件? + +可以是 application-{profile}.properties/yml ,但默认是启动主配置文件application.properties,一般来说我们的不同环境配置如下。 + +- `application.properties`:主配置文件 +- `application-dev.properties`:开发环境配置文件 +- `application-test.properties`:测试环境配置文件 +- `application.prop-properties`:生产环境配置文件 + +#### 26.如何激活某个环境的配置? + +比如我们激活开发环境。 + +yml: + +```yaml +spring: + profiles: + active: dev +``` + +properties: + +```properties +spring.profiles.active=dev +``` + +命令行: + +```powershell +java -jar xiaoka-v1.0.jar ———spring.profiles.active=dev +``` + +#### 27.编写测试用例的注解? + +@SpringBootTest + +#### 28.SpringBoot异常处理相关注解? + + @ControllerAdvice + +@ExceptionHandler + +#### 29.SpringBoot 1.x 和 2.x区别?······· + +1. SpringBoot 2基于Spring5和JDK8,Spring 1x用的是低版本。 +2. 配置变更,参数名等。 +3. SpringBoot2相关的插件最低版本很多都比原来高 +4. 2.x配置中的中文可以直接读取,不用转码 +5. Actuator的变化 +6. CacheManager 的变化 + +#### 30.SpringBoot读取配置相关注解有? + +- @PropertySource +- @Value +- @Environment +- @ConfigurationProperties + +参考: + +- 《SpringBoot实战(第4版)》 + +- 《Spring Boot编程思想》 + +- 《深入浅出Spring Boot 2.x》 + +- https://spring.io/projects/spring-boot + +- 百度百科 + + ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) + +## \ No newline at end of file diff --git a/SpringCloud.md b/SpringCloud.md new file mode 100644 index 0000000..1c79996 --- /dev/null +++ b/SpringCloud.md @@ -0,0 +1,235 @@ +## SpringCloud + +#### 1.什么是SpringCloud? + +Spring Cloud为开发人员提供了工具,以快速构建分布式系统中的一些常见模式(例如,配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁,领导选举,分布式会话,群集状态)。它们可以在任何分布式环境中正常工作,包括开发人员自己的笔记本电脑,裸机数据中心以及Cloud Foundry等托管平台。 + +#### 2.什么是微服务? + +所谓的微服务是SOA架构下的最终产物,该架构的设计目标是为了肢解业务,使得服务能够独立运行。微服务设计原则: + +1、各司其职 。 + +2、服务高可用和可扩展性。 + +#### 3.SpringCloud有哪些特征? + +Spring Cloud专注于为典型的用例和可扩展性机制(包括其他用例)提供良好的开箱即用体验。 + +- 分布式/版本化配置 +- 服务注册和发现 +- 路由 +- 服务到服务的调用 +- 负载均衡 +- 断路器 +- 全局锁 +- 领导选举和集群状态 +- 分布式消息传递 + +#### 4.SpringCloud核心组件? + +Eureka : 注册中心 + +Ribbon :客服端负载均衡 + +Hystrix : 服务容错处理 + +Feign: 声明式REST客户端 + +Zuul : 服务网关 + +Config : 分布式配置 + +#### 5.SpringCloud基于什么协议? + +HTTP + +#### 6.SpringCloud和Dubbo区别? + +![image-20200429230443620](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200429230443620.png) + +#### 7.Eureka是什么? + +云端服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。 + +#### 8.服务治理的基础角色? + +服务注册中心:提供服务注册与发现的能力。 + +服务提供者:提供服务的应用,会把自己提供的服务注册到注册中心。 + +服务消费者:服务的消费者,从注册中心获取服务列表。 + +#### 9.什么是服务续约? + +在注册完服务以后,服务提供者会维护一个心跳来向注册中心证明自己还活着,以此防止被“剔除服务”。 + +#### 10.什么是服务下线? + +当服务实例进行正常关闭时,会发送一个REST请求(我要下线了)给注册中心,收到请求后,将该服务状态设置下线(DOWN),并把这事件传播出去。 + +#### 11.什么是失效剔除? + +当服务非正常下线时,可能服务注册中心没有收到下线请求,注册中心会创建一个定时任务(默认60s)将没有在固定时间(默认90s)内续约的服务剔除掉。 + +#### 12.什么是自我保护机制? + +在运行期间,注册中心会统计心跳失败比例在15分钟之内是否低于85%,如果低于的情况,注册中心会将当前注册实例信息保护起来,不再删除这些实例信息,当网络恢复后,退出自我保护机制。 + +自我保护机制让服务集群更稳定、健壮。 + +#### 13.Ribbon是什么? + +提供云端负载均衡,有多种负载均衡策略可供选择,可配合服务发现和断路器使用。 + +#### 14.Ribbon负载均衡的注解是? + +@LoadBalanced + +#### 15.Ribbon负载均衡策略有哪些? + +RandomRule : 随机。 + +RoundRobinRule : 轮询。 + +RetryRule : 重试。 + +WeightedResponseTimeRule : 权重。 + +ClientConfigEnabledRoundRobinRule : 一般不用,通过继承该策略,默认的choose就实现了线性轮询机制。可以基于它来做扩展。 + +BestAvailableRule : 通过便利负载均衡器中维护的所有服务实例,会过滤到故障的,并选择并发请求最小的一个。 + +PredicateBasedRule : 先过滤清单,再轮询。 + +AvailabilityFilteringRule :继承了父类的先过滤清单,再轮询。调整了算法。 + +ZoneAvoidanceRule : 该类也是PredicateBasedRule的子类,它可以组合过滤条件。以ZoneAvoidancePredicate为主过滤条件,以AvailabilityPredicate为次过滤条件。 + +#### 16.什么是服务熔断? + +服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。 + +#### 17.什么是服务降级? + +服务降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。 + +#### 18.什么是Hystrix? + +熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。 + +#### 19.断路器Hystrix的有哪些功能? + +- 通过第三方客户端访问依赖服务出现高延迟或者失败时,为系统提供保护和控制 。 +- 在复杂的分布式系统中防止级联失败(服务雪崩效应) 。 +- 快速失败 (Failfast) 同时能快速恢复。 +- 提供失败回滚 (Fallback) 和优雅的服务降级机制。 +- 提供近实时的监控、 报警和运维控制手段。 + +#### 20.Hystrix将远程调用封装到? + +HystrixCommand 或者 HystrixObservableCommand对象中。 + +#### 21.启动熔断降级服务的注解? + +@EnableHystrix + +#### 22.什么是Feign? + +Feign是一种声明式、模板化的HTTP客户端。 + +#### 23.Feign优点? + +1.feign采用的是基于接口的注解。 2.feign整合了ribbon,具有负载均衡的能力。 3.整合了Hystrix,具有熔断的能力。 + +#### 24.什么是Config? + +配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git以及Subversion。 + +#### 25.Config组件中的两个角色? + +Config Server : 配置中心服务端。 + +Config Client : 配置中心客户端。 + +#### 26.什么是Zuul? + +Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。 + +#### 27.使用Zuul的优点? + +- 方便监控。可以在微服务网管手机监控数据并将其推送到外部系统进行分析。 +- 方便认证。可在网关进行统一认证,然后在讲请求转发到后端服务。 +- 隐藏架构实现细节,提供统一的入口给客户端请求,减少了客户端和每个微服务的交互次数。 +- 可以统一处理切面任务,避免每个微服务自己开发,提升效率。 +- 高可用高伸缩性的服务,避免单点失效。 + +#### 28.Zuul的核心是? + +过滤器。 + +#### 29.Zuul有几种过滤器类型?分别是? + +4种。 + +pre : 可以在请求被路由之前调用。 + +适用于身份认证的场景,认证通过后再继续执行下面的流程。 + +route : 在路由请求时被调用。 + +适用于灰度发布场景,在将要路由的时候可以做一些自定义的逻辑。 + +post :在 route 和 error 过滤器之后被调用。 + +这种过滤器将请求路由到达具体的服务之后执行。适用于需要添加响应头,记录响应日志等应用场景。 + +error : 处理请求时发生错误时被调用。 + +在执行过程中发送错误时会进入 error 过滤器,可以用来统一记录错误信息。 + +#### 30.什么是Sleuth? + +日志收集工具包,封装了Dapper和log-based追踪以及Zipkin和HTrace操作,为SpringCloud应用实现了一种分布式追踪解决方案。 + +#### 31.Sleuth帮助我们做了哪些工作? + +- 可以方便的了解到每个采样的请求耗时,分析出哪些服务调用比较耗时。 +- 对于程序未捕捉的异常,可以在集成Zipkin服务页面上看到。 +- 识别调用比较频繁的服务,从而进行优化。 + +#### 32.什么是Bus? + +事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署。 + +#### 33.eureka比zookeeper的优势在? + +A:高可用 C:一致性,P:分区容错性 + +Zookeeper保证了CP,Eureka保证了AP。 + +Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像Zookeeper那样使整个微服务瘫痪。 + +#### 34.什么是Stream? + +数据流操作开发包,封装了与Redis,Rabbit、Kafka等发送接收消息。 + +#### 35.更多知识? + +SpringCloud这个体系东西还是挺大的,小编会不断的丰富内容以及优化。欢迎大家关注我的公众号获得最新动态《Java小咖秀》。 + +参考: + +- 《Spring Cloud微服务实战》 + +- 《Spring Cloud微服务全栈技术与案例解析》 + +- 《Spring Cloud微服务架构开发实战》 + +- ​ https://spring.io/projects/spring-cloud + +- ​ https://www.springcloud.cc/ + +- ​ 百度百科 + + ![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file diff --git a/Tomcat.md b/Tomcat.md new file mode 100644 index 0000000..465d994 --- /dev/null +++ b/Tomcat.md @@ -0,0 +1,440 @@ +## Tomcat + +#### 1.Tomcat的缺省端口是多少,怎么修改? + +1)找到Tomcat目录下的conf文件夹 + +2)进入conf文件夹里面找到server.xml文件 + +3)打开server.xml文件 + +4)在server.xml文件里面找到下列信息 + + +port="8080"改成你想要的端口 + +#### 2.tomcat 有哪几种Connector 运行模式(优化)? + +bio:传统的Java I/O操作,同步且阻塞IO。 +maxThreads="150"//Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数。默认值200。可以根据机器的时期性能和内存大小调整,一般可以在400-500。最大可以在800左右。 +minSpareThreads="25"---Tomcat初始化时创建的线程数。默认值4。如果当前没有空闲线程,且没有超过maxThreads,一次性创建的空闲线程数量。Tomcat初始化时创建的线程数量也由此值设置。 +maxSpareThreads="75"--一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程。默认值50。一旦创建的线程超过此数值,Tomcat会关闭不再需要的线程。线程数可以大致上用 “同时在线人数*每秒用户操作次数*系统平均操作时间” 来计算。 +acceptCount="100"----指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理。默认值10。如果当前可用线程数为0,则将请求放入处理队列中。这个值限定了请求队列的大小,超过这个数值的请求将不予处理。 +connectionTimeout="20000" --网络连接超时,默认值20000,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。 + +nio:JDK1.4开始支持,同步阻塞或同步非阻塞IO。 +指定使用NIO模型来接受HTTP请求 +protocol="org.apache.coyote.http11.Http11NioProtocol" 指定使用NIO模型来接受HTTP请求。默认是BlockingIO,配置为protocol="HTTP/1.1" +acceptorThreadCount="2" 使用NIO模型时接收线程的数目 + +aio(nio.2):JDK7开始支持,异步非阻塞IO。 + +apr:Tomcat将以JNI的形式调用Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作,从而大大地 提高Tomcat对静态文件的处理性能。 + + + + + + + + + + +其他配置 + +maxHttpHeaderSize="8192" http请求头信息的最大程度,超过此长度的部分不予处理。一般8K。 +URIEncoding="UTF-8" 指定Tomcat容器的URL编码格式。 +disableUploadTimeout="true" 上传时是否使用超时机制 +enableLookups="false"--是否反查域名,默认值为true。为了提高处理能力,应设置为false +compression="on" 打开压缩功能 +compressionMinSize="10240" 启用压缩的输出内容大小,默认为2KB +noCompressionUserAgents="gozilla, traviata" 对于以下的浏览器,不启用压缩 +compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" 哪些资源类型需要压缩 + +#### 3.Tomcat有几种部署方式? + +1)直接把Web项目放在webapps下,Tomcat会自动将其部署 + +2)在server.xml文件上配置节点,设置相关的属性即可 + +3)通过Catalina来进行配置:进入到conf\Catalina\localhost文件下,创建一个xml文件,该文件的名字就是站点的名字。 + +编写XML的方式来进行设置。 + +#### 4.tomcat容器是如何创建servlet类实例?用到了什么原理? + +当容器启动时,会读取在webapps目录下所有的web应用中的web.xml文件,然后对xml文件进行解析, + +并读取servlet注册信息。然后,将每个应用中注册的servlet类都进行加载,并通过反射的方式实例化。 + +(有时候也是在第一次请求时实例化)在servlet注册时加上如果为正数,则在一开始就实例化, + +如果不写或为负数,则第一次请求实例化。 + +#### 5.tomcat 如何优化? + +1、优化连接配置.这里以tomcat7的参数配置为例,需要修改conf/server.xml文件,修改连接数,关闭客户端dns查询。 + +参数解释: + +URIEncoding=”UTF-8″ :使得tomcat可以解析含有中文名的文件的url,真方便,不像apache里还有搞个mod_encoding,还要手工编译 + +maxSpareThreads : 如果空闲状态的线程数多于设置的数目,则将这些线程中止,减少这个池中的线程总数。 + +minSpareThreads : 最小备用线程数,tomcat启动时的初始化的线程数。 + +enableLookups : 这个功效和Apache中的HostnameLookups一样,设为关闭。 + +connectionTimeout : connectionTimeout为网络连接超时时间毫秒数。 + +maxThreads : maxThreads Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数,即最大并发数。 + +acceptCount : acceptCount是当线程数达到maxThreads后,后续请求会被放入一个等待队列,这个acceptCount是这个队列的大小,如果这个队列也满了,就直接refuse connection + +maxProcessors与minProcessors : 在 Java中线程是程序运行时的路径,是在一个程序中与其它控制线程无关的、能够独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员写出CPU最 大利用率的高效程序,使空闲时间保持最低,从而接受更多的请求。 + +通常Windows是1000个左右,Linux是2000个左右。 + +useURIValidationHack: + +我们来看一下tomcat中的一段源码: + +【security】 + +``` +if (connector.getUseURIValidationHack()) { + +String uri = validate(request.getRequestURI()); + +if (uri == null) { + +res.setStatus(400); + +res.setMessage(“Invalid URI”); + +throw new IOException(“Invalid URI”); + +} else { + +req.requestURI().setString(uri); + +// Redoing the URI decoding + +req.decodedURI().duplicate(req.requestURI()); + +req.getURLDecoder().convert(req.decodedURI(), true); +``` + +可以看到如果把useURIValidationHack设成”false”,可以减少它对一些url的不必要的检查从而减省开销。 + +enableLookups=”false” : 为了消除DNS查询对性能的影响我们可以关闭DNS查询,方式是修改server.xml文件中的enableLookups参数值。 + +disableUploadTimeout :类似于Apache中的keeyalive一样 + +给Tomcat配置gzip压缩(HTTP压缩)功能 + +compression=”on” compressionMinSize=”2048″ + +compressableMimeType=”text/html,text/xml,text/JavaScript,text/css,text/plain” + +HTTP 压缩可以大大提高浏览网站的速度,它的原理是,在客户端请求网页后,从服务器端将网页文件压缩,再下载到客户端,由客户端的浏览器负责解压缩并浏览。相对于普通的浏览过程HTML,CSS,javascript , Text ,它可以节省40%左右的流量。更为重要的是,它可以对动态生成的,包括CGI、PHP , JSP , ASP , Servlet,SHTML等输出的网页也能进行压缩,压缩效率惊人。 + +1)compression=”on” 打开压缩功能 + +2)compressionMinSize=”2048″ 启用压缩的输出内容大小,这里面默认为2KB + +3)noCompressionUserAgents=”gozilla, traviata” 对于以下的浏览器,不启用压缩 + +4)compressableMimeType=”text/html,text/xml” 压缩类型 + +最后不要忘了把8443端口的地方也加上同样的配置,因为如果我们走https协议的话,我们将会用到8443端口这个段的配置,对吧? + +``` + + + +``` + + + +#### 6.内存调优 + +内存方式的设置是在catalina.sh中,调整一下JAVA_OPTS变量即可,因为后面的启动参数会把JAVA_OPTS作为JVM的启动参数来处理。 +具体设置如下: +JAVA_OPTS="$JAVA_OPTS -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4" +其各项参数如下: +-Xmx3550m:设置JVM最大可用内存为3550M。 +-Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。 +-Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。 +-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。 +-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5 +-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6 +-XX:MaxPermSize=16m:设置持久代大小为16m。 +-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。 + + + +#### 7.垃圾回收策略调优 + +垃圾回收的设置也是在catalina.sh中,调整JAVA_OPTS变量。 +具体设置如下: +JAVA_OPTS="$JAVA_OPTS -Xmx3550m -Xms3550m -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100" +具体的垃圾回收策略及相应策略的各项参数如下: +串行收集器(JDK1.5以前主要的回收方式) +-XX:+UseSerialGC:设置串行收集器 +并行收集器(吞吐量优先) +示例: +java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 + +-XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。 +-XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。 +-XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集 +-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。 +-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。 +并发收集器(响应时间优先) +示例:java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC +-XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。 +-XX:+UseParNewGC: 设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。 +-XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。 + +#### 8.添加JMS远程监控 + +对于部署在局域网内其它机器上的Tomcat,可以打开JMX监控端口,局域网其它机器就可以通过这个端口查看一些常用的参数(但一些比较复杂的功能不支持),同样是在JVM启动参数中配置即可,配置如下: +-Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false +-Djava.rmi.server.hostname=192.168.71.38 设置JVM的JMS监控监听的IP地址,主要是为了防止错误的监听成127.0.0.1这个内网地址 +-Dcom.sun.management.jmxremote.port=1090 设置JVM的JMS监控的端口 +-Dcom.sun.management.jmxremote.ssl=false 设置JVM的JMS监控不实用SSL +-Dcom.sun.management.jmxremote.authenticate=false 设置JVM的JMS监控不需要认证 + +#### 9.专业点的分析工具有 + +IBM ISA,JProfiler、probe 等,具体监控及分析方式去网上搜索即可 + +#### 10.关于Tomcat的session数目 + +这个可以直接从Tomcat的web管理界面去查看即可 ; 或者借助于第三方工具Lambda Probe来查看,它相对于Tomcat自带的管理稍微多了点功能,但也不多 ; + +#### 11.监视Tomcat的内存使用情况 + +使用JDK自带的jconsole可以比较明了的看到内存的使用情况,线程的状态,当前加载的类的总量等; JDK自带的jvisualvm可以下载插件(如GC等),可以查看更丰富的信息。如果是分析本地的Tomcat的话,还可以进行内存抽样等,检查每个类的使用情况 + +#### 12.打印类的加载情况及对象的回收情况 + +这个可以通过配置JVM的启动参数,打印这些信息(到屏幕(默认也会到catalina.log中)或者文件),具体参数如下: + +``` +-XX:+PrintGC:输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs] [Full GC 121376K->10414K(130112K), 0.0650971 secs] +-XX:+PrintGCDetails:输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs] [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs] +-XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可与上面两个混合使用,输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs] +-XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用。输出形式:Application time: 0.5291524 seconds +-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。可与上面混合使用。输出形式:Total time for which application threads were stopped: 0.0468229 seconds +-XX:PrintHeapAtGC: 打印GC前后的详细堆栈信息 +-Xloggc:filename:与上面几个配合使用,把相关日志信息记录到文件以便分析 +-verbose:class 监视加载的类的情况 +-verbose:gc 在虚拟机发生内存回收时在输出设备显示信息 +-verbose:jni 输出native方法调用的相关情况,一般用于诊断jni调用错误信息 +``` + +#### 13.Tomcat一个请求的完整过程 + +Ng:(nginx) + +upstream yy_001{ + server 10.99.99.99:8080; + server 10.99.99.100:8080; + + hash $**; + + healthcheck_enabled; + healthcheck_delay 3000; + healthcheck_timeout 1000; + healthcheck_failcount 2; + healthcheck_send 'GET /healthcheck.html HTTP/1.0' 'Host: wo.com' 'Connection: close'; + } + + server { + include base.conf; + server_name wo.de.tian; + ... + location /yy/ { + proxy_pass http://yy_001; + } + +首先 dns 解析 wo.de.tian机器,一般是ng服务器ip地址 +然后 ng根据server的配置,寻找路径为 yy/的机器列表,ip和端口 +最后 选择其中一台机器进行访问—->下面为详细过程 + +1) 请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Connector获得 +2) Connector把该请求交给它所在的Service的Engine来处理,并等待来自Engine的回应 +3) Engine获得请求localhost/yy/index.jsp,匹配它所拥有的所有虚拟主机Host +4) Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机) +5) localhost Host获得请求/yy/index.jsp,匹配它所拥有的所有Context +6) Host匹配到路径为/yy的Context(如果匹配不到就把该请求交给路径名为”“的Context去处理) +7) path=”/yy”的Context获得请求/index.jsp,在它的mapping table中寻找对应的servlet +8) Context匹配到URL PATTERN为*.jsp的servlet,对应于JspServlet类 +9) 构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法 +10)Context把执行完了之后的HttpServletResponse对象返回给Host +11)Host把HttpServletResponse对象返回给Engine +12)Engine把HttpServletResponse对象返回给Connector +13)Connector把HttpServletResponse对象返回给客户browser + + +#### 14.Tomcat工作模式? + +笔者回答:Tomcat是一个JSP/Servlet容器。其作为Servlet容器,有三种工作模式:独立的Servlet容器、进程内的Servlet容器和进程外的Servlet容器。 + +进入Tomcat的请求可以根据Tomcat的工作模式分为如下两类: + +Tomcat作为应用程序服务器:请求来自于前端的web服务器,这可能是Apache, IIS, Nginx等; + +Tomcat作为独立服务器:请求来自于web浏览器; + +#### 15.tomcat结构目录有哪些? + +①bin:启动和关闭tomcat的bat文件。 + +②conf:配置文件。 + +③server.xml该文件用于配置server相关的信息,比如tomcat启动的端口号,配置主机(Host)。 + +④web.xml文件配置与web应用(web应用相当于一个web站点) + +⑤tomcat-user.xml配置用户名密码和相关权限。 + +⑥lib:该目录放置运行tomcat运行需要的jar包。 + +⑦logs:存放日志,当我们需要查看日志的时候,可以查询信息。 + +⑧webapps:放置我们的web应用。 + +⑨work工作目录:该目录用于存放jsp被访问后生成对应的server文件和.class文件。 + +#### 16.如何配置Tomcat虚拟目录? + +1、在server.xml中的节点下添加如下代码。path表示的是访问时输入的web项目名,docBase表示的是站点目录的绝对路径。 + +2、进入到confCatalinalocalhost文件下,创建一个xml文件,该文件的名字就是站点的名字。 + +#### 17.Tomcat体系结构是怎样的? + +浏览器 -> tomcat server-> service ->connector -> engine(引擎) -> host(主机) -> web应用。 + +#### 18.Web请求在Tomcat请求中的请求流程是怎么样的? + +①浏览器输入URL地址; + +②查询本机hosts文件寻找IP; + +③查询DNS服务器寻找IP; + +④向该IP发送Http请求; + +⑤Tomcat容器解析主机名; + +⑥Tomcat容器解析Web应用; + +⑦Tomcat容器解析资源名称; + +⑧Tomcat容器获取资源; + +⑨Tomcat响应浏览器。 + +#### 19.如何在tomcat集群中实现Session共享 + +Apache集群实现Tomcat的Session共享配置其实很简单,在Tomcat自带的文档中有详细的说明( /docs/cluster-howto.html ),只不过是英语的,所以联合下面根据说下怎么配置吧: + +1、既然是集群肯定要多准备几个Tomcat来模拟,比如分别为Tomcat01、Tomcat02、Tomcat03。 + +如果各Tomcat程序放在不同的机器上,那么就不会有端口的冲突。如果是放在同一台机器上的话,那就简单改几个端口,防止端口占用造成的冲突。打开conf文件夹中的server.xml文件,需要修改的端口有: + + 1、 + + 2、 + + 3、 + +以上port需要修改,至于修改成什么样子,看你自己了,只要不出现端口冲突就可以了,要保证各个Tomcat实例间没有端口冲突。 + +#### 20.tomcat有哪些Connector? + +Tomcat的Web服务器连接器支持两种协议:AJP和HTTP,它们均定义了以二进制格式在Web服务器和Tomcat之间进行数据传输,并提供相应的控制命令。 + +AJP(Apache JServ Protocol)协议:目前正在使用的AJP协议的版本是通过JK和JK2连接器提供支持的AJP13,它基于二进制的格式在Web服务器和Tomcat之间传输数据,而此前的版本AJP10和AJP11则使用文本格式传输数据。 + +HTTP协议:诚如其名称所表示,其是使用HTTP或HTTPS协议在Web服务器和Tomcat之间建立通信,此时,Tomcat就是一个完全功能的HTTP服务器,它需要监听在某端口上以接收来自于商前服务器的请求。 + +#### 21.tomcat的Valve的作用是什么? + +Valve类似于过滤器,它可以工作于Engine和Host/Context之间、Host和Context之间以及Context和Web应用程序的某资源之间。一个容器内可以建立多个Valve,而且Valve定义的次序也决定了它们生效的次序。 + +#### 22.Webserver和 Application Server的区别是什么? + +最大区别,WebServer 一般仅仅指Web(如servlet,jsp)的应用服务器,ApplicationServer不仅可以是Web,还可以是Ejb等其它的应用服务器。 + +web server可以是application server的一部分,也可以是单独存在。 + +#### 23.Tomcat的缺省端口是多少,怎么修改? + +1)找到Tomcat目录下的conf文件夹; + +2)进入conf文件夹里面找到server.xml文件; + +3)打开server.xml文件; + +4)在server.xml文件里面找到下列信息; + +port=“8080”改成你想要的端口 + +#### 24.Tomcat 有几种部署方式? + +1)直接把Web项目放在webapps下,Tomcat会自动将其部署 + +2)在server.xml文件上配置节点,设置相关的属性即可 + +3)通过Catalina来进行配置:进入到conf\Catalina\localhost文件下,创建一个xml文件,该文件的名字就是站点的名字。编写XML的方式来进行设置。 + +#### 25.tomcat容器是如何创建servlet类实例?用到了什么原理? + +当容器启动时,会读取在webapps 目录下所有的web应用中的web.xml 文件,然后对xml文件进行解析,并读取servlet注册信息。然后,将每个应用中注册的servlet类都进行加载,并通过反射的方式实例化。(有时候也是在第一次请求时实例化)在servlet注册时加上如果为正数,则在一开始就实例化,如果不写或为负数,则第一次请求实例化。 + +#### 参考 + +https://blog.csdn.net/qq_25934401/article/details/81536958 + +http://blog.itpub.net/69902581/viewspace-2673221/ + + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) + diff --git "a/Web\345\256\211\345\205\250.md" "b/Web\345\256\211\345\205\250.md" new file mode 100644 index 0000000..fe8ce08 --- /dev/null +++ "b/Web\345\256\211\345\205\250.md" @@ -0,0 +1,602 @@ +## Web安全 + +#### 1.CIA三元组知道吗? + +- 机密性(Confidentiality) +- 完整性(Integrity) +- 可用性(Availability) + +#### 2. XSS攻击是如何产生的? + +黑客在你的浏览器中插入一段恶意 JavaScript 脚本,窃取你的隐私信息、冒充你的身份进行操作。这就是 XSS 攻击(Cross-Site Scripting,跨站脚本攻击) + +因为浏览器无法区分脚本是被恶意注入的还是正常的内容,它都会执行,况且 HTML 非常灵活,可以在任何时候对它进行修改。 + +#### 3.知道XSS有哪几种类型吗? + +- 反射型 XSS (也叫非持久型) +- 基于 DOM 的 XSS +- 存储型 XSS (也叫持久型 XSS) + +#### 4.反射型 XSS实现原理 + +顾名思义,恶意 JavaScript 脚本属于用户发送给网站请求中的一部分,随后网站又将这部分返回给用户,恶意脚本在页面中被执行。一般发生在前后端一体的应用中,服务端逻辑会改变最终的网页代码。 + +#### 5.DOM型实现原理 + +目前更流行前后端分离的项目,反射型 XSS 无用武之地。 + +但这种攻击不需要经过服务器,我们知道,网页本身的 JavaScript 也是可以改变 HTML 的,黑客正是利用这一点来实现插入恶意脚本。 + +#### 6.存储型实现原理 + +又叫持久型 XSS,顾名思义,黑客将恶意 JavaScript 脚本长期保存在服务端数据库中,用户一旦访问相关页面数据,恶意脚本就会被执行。常见于搜索、微博、社区贴吧评论等。 + +#### 7.说下3者的区别 + +反射型的 XSS 的恶意脚本存在 URL 里,存储型 XSS 的恶意代码存在数据库里。 + +而基于DOM型的XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,其他两种 XSS 都属于服务端的安全漏洞。 + + + +**反射型** + +![image-20210622131957034](C:\Users\qq\AppData\Roaming\Typora\typora-user-images\image-20210622131957034.png) + +**基于DOM型** + +[![img](https://s2.51cto.com/oss/202006/24/8ca9d116817109c5db78883ae65ee7c6.jpg)](https://s2.51cto.com/oss/202006/24/8ca9d116817109c5db78883ae65ee7c6.jpg) + +**存储型** + +[![img](https://s3.51cto.com/oss/202006/24/63a8d3a9d932ba7084930621a2a28983.jpg)](https://s3.51cto.com/oss/202006/24/63a8d3a9d932ba7084930621a2a28983.jpg) + + + +#### 8.黑客可以通过XSS攻击做哪些事儿? + +- 盗取用户 Cookie +- 未授权操作 +- 修改 DOM +- 刷浮窗广告 +- 发动 XSS 蠕虫攻击 +- 劫持用户行为,进一步渗透内网 + +#### 9.XSS攻击如何进行防护? + +- 一切用户输入皆不可信,在输出时进行验证 +- 将 HTML 元素内容、属性以及 URL 请求参数、CSS 值进行编码 +- 当编码影响业务时,使用白名单规则进行检测和过滤 +- 使用 W3C 提出的 CSP (Content Security Policy,内容安全策略),定义域名白名单 +- 设置 Cookie 的 HttpOnly 属性 + +#### 10.知道哪些XSS攻击案例简单说一下 + +- 2005年,年仅19岁的 Samy Kamkar 发起了对 MySpace.com 的 XSS Worm 攻击。 + + Samy Kamkar 的蠕虫在短短几小时内就感染了100万用户——它在每个用户的自我简介后边加了一句话:“but most of all, Samy is my hero.”(Samy是我的偶像)。这是 Web 安全史上第一个重量级的 XSS Worm,具有里程碑意义。 + +- 2007年12月,百度空间收到蠕虫攻击,用户之间开始转发垃圾短消息。 +- QQ 邮箱 m.exmail.qq.com 域名被发现反射型 XSS 漏洞 +- 2011年新浪微博曾被黑客 XSS 攻击,黑客诱导用户点击一个带有诱惑性的链接,便会自动发送一条带有同样诱惑性链接微博。攻击范围层层扩大,也是一种蠕虫攻击。 + +#### 11.什么是CSRF攻击? + +CSRF 英文全称是 Cross-site request forgery,又称为“跨站请求伪造”。 + +顾名思义,CSRF 攻击就是黑客引诱用户打开黑客的网站,利用用户的登陆状态发起跨站请求。 + +降维解释:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。 + +利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证, 达到冒充用户对被攻击的网站执行某项操作的目的。 + +#### 12.CSRF攻击一般怎么实现? + +- 最容易实现的是 Get 请求,一般进入黑客网站后,可以通过设置 img的 src 属性来自动发起请求 +- 在黑客的网站中,构造隐藏表单来自动发起 Post 请求 +- 通过引诱链接诱惑用户点击触发请求,利用 a 标签的 href。 + +#### 13.CSRF攻击和XSS攻击有什么区别? + +CSRF 攻击不需要将恶意代码注入用户的页面,仅仅是利用服务器的漏洞和用户的登录状态来实施攻击。 + +CSRF 攻击成本也比 XSS 低,用户每天都要访问大量网页,无法确认每一个网页的合法性, + +从用户角度来说,无法彻底防止 CSRF 攻击。 + +#### 14.那应该如何防范CSRF攻击? + +- 针对实际情况,设置关键 Cookie 的 SameSite 属性为 Strict 或 Lax +- 服务端验证请求来源站点(Referer、Origin) +- 使用 CSRF Token,服务端随机生成返回给浏览器的 Token,每一次请求都会携带不同的 CSRF Token +- 加入二次验证(独立的支付密码) + +#### 15.字符集处理过程中可能出现的安全问题有哪些? + +1. 上下层使用的字符集不一致,导致数据的意义出现问题 +2. 处理多字节字符集时出现问题 +3. 对于非法数据的处理上理解不一致 +4. 某些字符集的天生缺陷 +5. 数据库里的校对规则(前后使用字符集不一致就出现问题) + +#### 16.如何测试网站是否存在跨站点脚本漏洞? + +关键字:Web到目前为止,对于跨站点脚本攻击具有很大的威胁这一点大家并无异议。如果您很精通 XSS 并且只想看看有什么好的测试方法可供借鉴,那么请直接跳到本文的测试部分。如果您对此一无所知,请按顺序认真阅读!如果某个怀有恶意的人(攻击者)可以强迫某个不知情的用户(受害者)运行攻击者选择的客户端脚本,那么便会发生跨站点脚本攻击。“跨站点脚本”这个词应该属于用词不当的情况,因为它不仅与脚本有关,而且它甚至不一定是跨站点的。所以,它就是一个在发现这种攻击时起的一个名字,并且一直沿用至今。从现在开始,我们将使用它常见的缩写名称“XSS”。 + + XSS 攻击的过程涉及以下三方: + + ? 攻击者 + + ? 受害者 + + ? 存在漏洞的网站(攻击者可以使用它对受害者采取行动) + + + +​ 在这三方之中,只有受害者会实际运行攻击者的代码。网站仅仅是发起攻击的一个载体,一般不会受到影响。可以用多种方式发起 XSS 攻击。例如,攻击者可通过电子邮件、IM 或其他途径向受害者发送一个经过经心构造的恶意 URL。当受害者在 Web 浏览器中打开该 URL 的时侯,网站会显示一个页面并在受害者的计算机上执行脚本。 + +###### XSS 漏洞是什么样的呢? + +​ 作为一名 Web 开发人员或测试人员,您肯定知道 Web 应用程序的技术基础是由 HTTP 和 HTML 组成的。HTTP 协议是 HTML 的传输机制,可使用代码设计 Web 页面布局和生成页面。 + + + +​ 如果 Web 应用程序接受用户通过 HTTP 请求(如 GET 或 POST)提交的输入信息,然后使用输出 HTML 代码在某些地方显示这些信息,便可能存在 XSS 漏洞。下面是一个最简单的例子: + +###### 1.Web 请求如下所示: + + GET http://www.somesite.com/page.asp?pageid=10&lang=en&title=Section%20Title + +###### 2. 在发出请求后,服务器返回的 HTML 内容包括:Section Title + + 可以看到,传递给“title”查询字符串参数的用户输入可能被保存在一个字符串变量中并且由 Web 应用程序插入到标记中。通过提供输入内容,攻击者可以控制 HTML。 + +###### 3.现在,如果站点没有在服务器端对用户输入加以过滤(因为总是可以绕过客户端控件),那么恶意用户便可以使用许多手段对此漏洞加以滥用: + + + + 攻击者可以通过摆脱标记来注入代码: + + http://www.somesite.com/page.asp?pageid=10&lang=en&title=Section%20Title + + + +< SCRIPT>alert(‘XSS%20attack’)< /SCRIPT> + + 这个请求的 HTML 输出将为:Section Title + +< SCRIPT>alert(‘XSS attack’)< /SCRIPT> + + + + 即便是这个最简单的例子,攻击者也可以利用此连接完成数不清的事情。让我们看看会有哪些潜在的威胁,然后讨论一些更高级的测试方法。 + + + +###### XSS 攻击的威胁有多么严重? + +​ 由于能够在生成的 Web 页面中注入代码,能想到的威胁有多么严重,就可以有多么严重的威胁。攻击者可以使用 XSS 漏洞窃取 Cookie,劫持帐户,执行 ActiveX,执行 Flash 内容,强迫您下载软件,或者是对硬盘和数据采取操作。 + + + +​ 只要您点击了某些 URL,这一切便有可能发生。每天之中,在阅读来自留言板或新闻组的受信任的电子邮件的时侯,您会多少次地单击其中的 URL? + + + +​ 网络钓鱼攻击通常利用 XSS 漏洞来装扮成合法站点。可以看到很多这样的情况,比如您的银行给你发来了一封电子邮件,向您告知对您的帐户进行了一些修改并诱使您点击某些超链接。如果仔细观察这些 URL,它们实际上可能利用了银行网站中存在的漏洞,它们的形式类似于 http://mybank.com/somepage?redirect= < SCRIPT>alert(‘XSS’)< /SCRIPT> ,这里利用了“redirect”参数来执行攻击。 + + + +​ 如果您足够狡猾的话,可以将管理员定为攻击目标,您可以发送一封具有如下主题的邮件:“求救!这个网站地址总是出现错误!”在管理员打开该 URL 后,便可以执行许多恶意操作,例如窃取他(或她)的凭证。 + +​ 好了,现在我们已经理解了它的危害性 — 危害用户,危害管理员,给公司带来坏的公共形象。现在,让我们看看本文的重点 — 测试您的网站是否存在这些问题。 + +###### 测试 XSS 漏洞 + +​ 多年以来,我一直是一名全职的安全顾问,已经做过无数次的这种测试了。我将好的测试计划归结为两个字:彻底。对于你我来说,查找这些漏洞与能够有机会在 Bugtraq 或 Vulnwatch 上吹嘘一番没有任何关系;它只与如何出色完成负责的工作有关。如果这意味着对应用程序中所有的单个查询字符串参数、cookie 值 以及 POST 数据值进行检查,那么这只能表明我们的工作还不算太艰巨。 + + + +​ 显然,一次完整的安全性检查所涉及的内容通常远远超出寻找 XSS 漏洞那样简单;它需要建立整体的威胁模型,测试溢出漏洞、信息泄漏、错误处理、SQL 注入、身份验证和授权错误。好在执行这样彻底的工作时,各个领域之间都存在重叠。比如,在测试 XSS 漏洞时,经常会同时找出错误处理或信息泄漏问题。 + + + +​ 我假设您属于某个负责对 Web 应用程序进行开发和测试的小组。在这个幸运的位置上,您可以混合使用黑盒和白盒方法。每种方法都有它自己的优点,结合使用时甚至能相互提供支持。 + + + +​ \1. 按顺序准备您的工具包 + +​ 测试工作也可以是自动化的,但是我们在这里只讨论手动操作。手动测试的必备工具包括: + + + + ? Paros proxy (http://www.parosproxy.org),用于截获 HTTP 通信数据 + + + + ? Fiddler (http://www.fiddlertool.com/fiddler),用于截获 HTTP 通信数据 + + + + ? Burp proxy (http://www.portswigger.net/proxy/) + + + + ? TamperIE (http://www.bayden.com/dl/TamperIESetup.exe),用于修改 GET 和 POST + + + + + +​ 我们以上至少列出了三种 Web 代理软件。也可以寻找其他不同的类似产品,因为每种产品都有它自己的独到之处。下面,您需要在 Web 浏览器发出 HTTP 请求之前截获这些请求,并修改它们以注入 XSS 测试代码。上面所有这些工具都可以完成这项任务,某些工具还会显示返回的 HTML 源代码(如果您选择了截获服务器响应)。 + + + +​ 截获客户端发出的 GET 和 POST 请求非常重要。这样可以绕过所有的客户端 javascript 输入验证代码。我在这里要提醒所有 Web 开发人员 — 客户端安全控制是靠不住的。应该总是在服务器端执行有效性验证。 + + + +​ \2. 确定站点及其功能 — 与开发人员和 PM 交流 + +​ 绘制一些简单的数据流图表,对站点上的页面及其功能进行描述。此时,可以安排一些与开发人员和项目经理的会议来建立威胁模型。在会议上尽可能对应用程序进行深入探讨。站点公开了 Web 服务吗?是否有身份验证表单?有留言板吗?有用户设置页面吗?确保列出了所有这些页面。 + + + +​ \3. 找出并列出所有由用户提供输入的点 + +​ 对站点地图进行进一步细化。我通常会为此创建一个电子表格。对于每个页面,列出所有查询字符串参数、cookie 值、自定义 HTTP 标头、POST 数据值和以其他形式传递的用户输入。不要忘记搜索 Web 服务和类似的 SOAP 请求,并找出所有允许用户输入的字段。 + + + +​ 分别列出每个输入参数,因为下面需要独立测试每个参数。这可能是最重要的一个步骤!如果阅读下面的电子表格,您会看到我已经在示例站点中找出了一大堆这样的东西。如 forwardURL 和 lang 这样的查询字符串。如 name、password、msgBody、msgTitle 和这样的 POST 数据,甚至某些 Cookie 值。所有这些都是我们感兴趣的重要测试内容。 + + + +​ \4. 认真思考并列出测试用例 + +​ 使用已经得到的电子表格并列出各种用来测试 XSS 漏洞的方法。我们稍候将讨论各种方法,但是现在先让我们看看我的电子表格的屏幕截图,请注意,我列出了页面上允许的每个值以及每个值的所有测试类型。这种记录测试的方法仅是我自己的习惯,您可以使用自己的方法。我喜欢记录所有东西,以便我能知道已经做了哪些工作和哪些工作没有做。 + + + +​ \5. 开始测试并注意输出结果 + +​ 在查找漏洞的过程中,最重要的部分并不是您是否找到了漏洞。而是您是否真正知道究竟发生了哪些事情。对于 XSS,只需检查 HTML 输出并看看您输入的内容在什么地方。它在一个 HREF 标记中吗?是否在 IFRAME 标记中?它在 CLSID 标记中吗?在 IMG SRC 中吗?某些 Flash 内容的 PARAM NAME 是怎样的? + + + +​ 我会检查所有这些情况,如果您对所输入内容的目的十分了解,可以调整您的测试来找出问题。这意味着您可能需要添加一个额外的封闭括号“>”来让某个标记变得完整,或者添加一个双引号来关闭标记内的一个元素。或者,您可能需要使用 URL 或 HTML 来编码您的字符,例如将双引号变为 %22 或 。 + +​ 嗯,并不那么容易,这个站点看来防范比较严密。现在该怎么办呢? + +​ 那么,也许您的简单测试用例 < SCRIPT>alert(‘hi’)< /SCRIPT> 并不能产生期望中的警告对话框。仔细想想这个问题并在可能的情况下与开发人员进行交流。也许他们对输入中的尖括号、单引号或圆括号进行了过滤。也许他们会过滤“script”这个词。重新研究为何输入会产生这样的输出,并理解每个值(查询字符串、cookie、POST 数据)的作用。“pageId=10”这样的查询字符串值可能对输出没有影响,因此不值得花费时间测试它。有时,最好试着注入单个字符(例如尖括号、双引号标记或者圆括号),看看应用程序是否过滤这些字符。然后,便可以知道您面对的过滤级别究竟如何。接着,可以调整测试方法,对这些字符进行编码并重试,或者寻找其他注入办法。 + + + +###### 改变测试用例 + + + +​ 我恐怕无法充分对此进行说明:研究输入的值会输出什么样的 HTML 页面非常重要。如果它们不能产生输出,那么不要在它们上面浪费时间。如果可以,请进行研究,因为您需要根据输出对测试进行相应的修改。我使用了各种变化形式来找出能接受和显示脚本代码的参数。因为这涉及太多内容,因此在这里无法一一进行讨论,但是请务必注意以下几种情况: + + + +​ 有许多变化形式可以尝试。关键在于了解程序究竟使用何种方式处理输入和显示输出页面。如同这些例子所展示的,常见的变化形式经常是在脚本代码前面加上 “>””,以尝试封闭网站可能在输出中生成的标记。还可以对代码进行 URL 编码,尝试绕过服务器端的输入过滤功能。此外,因为尖括号“<>”通常会在输入时被过滤和从输出中删除,所以还必须尝试不需要尖括号的 XSS,例如 ”&{alert(XSS)};” + + + +###### 持久和动态 + +​ 找出一个成功的 XSS 颇费周折,因为在开始时 XSS 攻击可能并不是那么显而易见的。随便举一个例子,如果向网站添加一条新留言并在“msgTitle”值中注入代码,在提交数据后,您可能不会立即看到脚本代码被执行。但是,当您访问留言板的时侯,将会在 HTML 页面中使用“msgTitle”值并将其作为脚本代码执行。这种情况被称作持久性 XSS 攻击,如果包含脚本代码的值将被保存到客户端或者后端系统并在稍候的时间被执行,便会发生此种攻击。 + + + +​ 而与此相对的是动态 XSS 攻击,这种攻击会立即执行并只发生一次。比如,如果某个链接或 GET 请求在某个用来控制页面输出的查询字符串中包含了脚本代码,那么在点击链接后会立即显示输出。 + +#### 17.在网站测试中应该如何进行安全性测试? + +安全性测试(security testing)是有关验证应用程序的安全服务和识别潜在安全性缺陷的过程。 + + + +  注意:安全性测试并不最终证明应用程序是安全的,而是用于验证所设立策略的有效性,这些对策是基于威胁分析阶段所做的假设而选择的。 + + + +  以下是我读<<软件评测试教程>>中的Web安全性测试章节内容,并进行修改的笔记,前面看了好多朋友写的,不过不是很全,希望对大家有所帮助,建议大家还是买本<<软件评测试教程>>此书绝对物超所值^_^ + + + +WEB安全性测试 + +  一个完整的WEB安全性测试可以从部署与基础结构、输入验证、身份验证、授权、配置管理、敏感数据、会话管理、加密。参数操作、异常管理、审核和日志记录等几个方面入手。 + +\1. 安全体系测试 + +1) 部署与基础结构 + +  网络是否提供了安全的通信 + +  部署拓扑结构是否包括内部的防火墙 + +  部署拓扑结构中是否包括远程应用程序服务器 + +  基础结构安全性需求的限制是什么 + +  目标环境支持怎样的信任级别 + +2) 输入验证 + +l 如何验证输入 + +A. 是否清楚入口点 + +B. 是否清楚信任边界 + +C. 是否验证Web页输入 + +D. 是否对传递到组件或Web服务的参数进行验证 + +E. 是否验证从数据库中检索的数据 + +F. 是否将方法集中起来 + +G. 是否依赖客户端的验证 + +H. 应用程序是否易受SQL注入攻击 + +I. 应用程序是否易受XSS攻击 + +l 如何处理输入 + +3) 身份验证 + +  是否区分公共访问和受限访问 + +  是否明确服务帐户要求 + +  如何验证调用者身份 + +  如何验证数据库的身份 + +  是否强制试用帐户管理措施 + +4) 授权 + +  如何向最终用户授权 + +  如何在数据库中授权应用程序 + +  如何将访问限定于系统级资源 + +5) 配置管理 + +  是否支持远程管理 + +  是否保证配置存储的安全 + +  是否隔离管理员特权 + +6) 敏感数据 + +  是否存储机密信息 + +  如何存储敏感数据 + +  是否在网络中传递敏感数据 + +  是否记录敏感数据 + +7) 会话管理 + +  如何交换会话标识符 + +  是否限制会话生存期 + +  如何确保会话存储状态的安全 + +8) 加密 + +  为何使用特定的算法 + +  如何确保加密密钥的安全性 + +9) 参数操作 + +  是否验证所有的输入参数 + +  是否在参数过程中传递敏感数据 + +  是否为了安全问题而使用HTTP头数据 + +10) 异常管理 + +  是否使用结构化的异常处理 + +  是否向客户端公开了太多的信息 + +11) 审核和日志记录 + +  是否明确了要审核的活动 + +  是否考虑如何流动原始调用这身份 + +\2. 应用及传输安全 + +  WEB应用系统的安全性从使用角度可以分为应用级的安全与传输级的安全,安全性测试也可以从这两方面入手。 + +  应用级的安全测试的主要目的是查找Web系统自身程序设计中存在的安全隐患,主要测试区域如下。 + +  注册与登陆:现在的Web应用系统基本采用先注册,后登录的方式。 + +A. 必须测试有效和无效的用户名和密码 + +B. 要注意是否存在大小写敏感, + +C. 可以尝试多少次的限制 + +D. 是否可以不登录而直接浏览某个页面等。 + +  在线超时:Web应用系统是否有超时的限制,也就是说,用户登陆一定时间内(例如15分钟)没有点击任何页面,是否需要重新登陆才能正常使用。 + +  操作留痕:为了保证Web应用系统的安全性,日志文件是至关重要的。需要测试相关信息是否写进入了日志文件,是否可追踪。 + +  备份与恢复:为了防范系统的意外崩溃造成的数据丢失,备份与恢复手段是一个Web系统的必备功能。备份与恢复根据Web系统对安全性的要求可以采用多种手段,如数据库增量备份、数据库完全备份、系统完全备份等。出于更高的安全性要求,某些实时系统经常会采用双机热备或多级热备。除了对于这些备份与恢复方式进行验证测试以外,还要评估这种备份与恢复方式是否满足Web系统的安全性需求。 + +  传输级的安全测试是考虑到Web系统的传输的特殊性,重点测试数据经客户端传送到服务器端可能存在的安全漏洞,以及服务器防范非法访问的能力。一般测试项目包括以下几个方面。 + +  HTTPS和SSL测试:默认的情况下,安全HTTP(Soure HTTP)通过安全套接字SSL(Source Socket Layer)协议在端口443上使用普通的HTTP。HTTPS使用的公共密钥的加密长度决定的HTTPS的安全级别,但从某种意义上来说,安全性的保证是以损失性能为代价的。除了还要测试加密是否正确,检查信息的完整性和确认HTTPS的安全级别外,还要注意在此安全级别下,其性能是否达到要求。 + +  服务器端的脚本漏洞检查:存在于服务器端的脚本常常构成安全漏洞,这些漏洞又往往被黑客利用。所以,还要测试没有经过授权,就不能在服务器端放置和编辑脚本的问题。 + +  防火墙测试:防火墙是一种主要用于防护非法访问的路由器,在Web系统中是很常用的一种安全系统。防火墙测试是一个很大很专业的课题。这里所涉及的只是对防火墙功能、设置进行测试,以判断本Web系统的安全需求。 + + + +另推荐安全性测试工具: + +  Watchfire AppScan:商业网页漏洞扫描器(此工具好像被IBM收购了,所以推荐在第一位) + +  AppScan按照应用程序开发生命周期进行安全测试,早在开发阶段就进行单元测试和安全保证。Appscan能够扫描多种常见漏洞,例如跨网站脚本、HTTP应答切开、参数篡改、隐藏值篡改、后门/调试选项和缓冲区溢出等等。 + +  Acunetix Web Vulnerability Scanner:商业漏洞扫描器(目前用的比较多,不过这东东N占内存) + +Acunetix WVS自动检查您的网页程序漏洞,例如SQL注入、跨网站脚本和验证页面弱密码破解。Acunetix WVS有着非常友好的用户界面,还可以生成个性化的网站安全评估报告。 + +#### 18.Web应用攻击中目录遍历攻击的方法及如何预防? + +对于一个安全的Web服务器来说,对Web内容进行恰当的访问控制是极为关键的。目录遍历是Http所存在的一个安全漏洞,它使得攻击者能够访问受限制的目录,并在Web服务器的根目录以外执行命令。 + +  Web服务器主要提供两个级别的安全机制: + + + +  访问控制列表——就是我们常说的ACL   根目录访问 + +  访问控制列表是用于授权过程的,它是一个Web服务器的管理员用来说明什么用户或用户组能够在服务器上访问、修改和执行某些文件的列表,同时也包含了其他的一些访问权限内容。 + + + +  根目录是服务器文件系统中一个特定目录,它往往是一个限制,用户无法访问位于这个目录之上的任何内容。 + + + +  例如:在Windows的IIS其默认的根目录是C:\Inetpub\wwwroot,那么用户一旦通过了ACL的检查,就可以访问C:\Inetpub\wwwroot\news目录以及其他位于这个根目录以下的所有目录和文件,但无法访问C:\Windows目录。 + + + +  根目录的存在能够防止用户访问服务器上的一些关键性文件,譬如在Windows平台上的cmd.exe或是Linux/Unix平台上的口令文件。 + + + +  这个漏洞可能存在于Web服务器软件本身,也可能存在于Web应用程序的代码之中。 + + + +  要执行一个目录遍历攻击,攻击者所需要的只是一个web浏览器,并且有一些关于系统的一些缺省文件和目录所存在的位置的知识即可。 + + + +  如果你的站点存在这个漏洞,攻击者可以用它来做些什么? + + + +  利用这个漏洞,攻击者能够走出服务器的根目录,从而访问到文件系统的其他部分,譬如攻击者就能够看到一些受限制的文件,或者更危险的,攻击者能够执行一些造成整个系统崩溃的指令。 + + + +  依赖于web站点的访问是如何设置的,攻击者能够仿冒成站点的其他用户来执行操作,而这就依赖系统对Web站点的用户是如何授权的。 + + + +  利用Web应用代码进行目录遍历攻击的实例 + + + +  在包含动态页面的Web应用中,输入往往是通过GET或是POST的请求方法从浏览器获得,以下是一个GET的Http URL请求示例: + + + +  http://test.webarticles.com/show.asp?view=oldarchive.html + + + +  利用这个URL,浏览器向服务器发送了对动态页面show.asp的请求,并且伴有值为oldarchive.html的view参数,当请求在Web服务器端执行时,show.asp会从服务器的文件系统中取得oldarchive.html文件,并将其返回给客户端的浏览器,那么攻击者就可以假定show.asp能够从文件系统中获取文件并编制如下的URL: + + + +  http://test.webarticles.com/show.asp?view=../../../../../Windows/system.ini + + + +  那么,这就能够从文件系统中获取system.ini文件并返回给用户,../的含义这里就不用多说了,相信大家都会明白。攻击者不得不去猜测需要往上多少层才能找到Windows目录,但可想而知,这其实并不困难,经过若干次的尝试后总会找到的。 + + + + 利用Web服务器进行目录遍历攻击的实例: + + + +  除了Web应用的代码以外,Web服务器本身也有可能无法抵御目录遍历攻击。这有可能存在于Web服务器软件或是一些存放在服务器上的示例脚本中。 + + + +  在最近的Web服务器软件中,这个问题已经得到了解决,但是在网上的很多Web服务器仍然使用着老版本的IIS和Apache,而它们则可能仍然无法抵御这类攻击。即使你使用了已经解决了这个漏洞的版本的Web服务器软件,你仍然可能会有一些对黑客来说是很明显的存有敏感缺省脚本的目录。 + + + +  例如,如下的一个URL请求,它使用了IIS的脚本目录来移动目录并执行指令:http://server.com/scripts/..%5c../Windows/System32/cmd.exe?/c dir c:\ + + + +  这个请求会返回C:\目录下所有文件的列表,它使通过调用cmd.exe然后再用dir c:\来实现的,%5c是web服务器的转换符,用来代表一些常见字符,这里表示的是“\” + + + +  新版本的Web服务器软件会检查这些转换符并限制它们通过,但对于一些老版本的服务器软件仍然存在这个问题。 + + + +  如何判断是否存在目录遍历漏洞? + + + +  最好的方式就是使用Web漏洞扫描器,Web漏洞扫描器能够遍历你Web站点的所有目录以判断是否存在目录遍历漏洞,如果有它会报告该漏洞并给出解决的方法,除了目录遍历漏洞以外,Web应用扫描还能检查SQL注入、跨站点脚本攻击以及其他的漏洞。 + +#### 19.介绍一下DDoS攻击新中的反弹技术 + +反弹技术就是利用反弹服务器实现攻击的技术。 +所谓反弹服务器(Reflector)是指当收到一个请求数据报后就会产生一个回应数据报的主机.例如,所有的Web服务器,DNS服务器和路由服务器都是反弹服务器.攻击者可以利用这些回应的数据报对目标机器发动DDoS攻击。 +反弹技术原理 +反弹服务器攻击过程和传统的DDoS攻击过程相似,如前面所述的4个步骤中,只是第4步改为:攻击者锁定大量的可以作为反弹服务器的服务器群,攻击命令发出后,代理守护进程向已锁定的反弹服务器群发送大量的欺骗请求数据包,其原地址为受害服务器或目标服务器. +反弹技术实现DDoS攻击与传统DDoS攻击的区别: +1.反弹技术实现DDoS攻击比传统DDoS攻击更加难以抵御.实际上它的攻击网络结构和传统的相比多了第四层——被锁定的反弹服务器层.反弹服务器的数量可以远比驻有守护进程的代理服务器多,故反弹技术可以使攻击时的洪水流量变弱,最终才在目标机汇合为大量的洪水,其攻击规模也比传统DDoS攻击大得多。 +2.目标机更难追查到攻击来源.目标机接收到的攻击数据报的源IP是真实的,反弹服务器追查到的数据报源IP是假的.又由于反弹服务器上收发数据报的流量较小(远小于代理服务器发送的数量),所以,服务器根据网络流量来自动检测是否为DDoS攻击源的这种机制将不起作用。 + +#### 20.请介绍一下盗号木马和网页木马的原理和机制 + +**盗号木马**是指隐秘在电脑中的一种恶意程序,跟灰鸽子不同,这是以盗号为目的并且能够伺机盗取各种需要密码的账户(游戏,应用程序等)的木马病毒。 + +- 在传统的远程控制木马基础上发展出的以窃取敏感信息为目标的专用木马。 +- QQ盗号木马:数十款,流行网游:均发现相应的盗号木马 +- 免杀机制:继承可执行程序加壳/变形等技术方法 + +**网页木马**就是表面上伪装成普通的网页文件或是将恶意的代码直接插入到正常的网页文件中,当有人访问时,网页木马就会利用对方系统或者浏览器的漏洞自动将配置好的木马的服务端下载到访问者的电脑上来自动执行。 + +网页木马实际上是一个HTML网页,与其它网页不同的是该网页是黑客精心制作的,用户一旦访问了该网页就会中木马。为什么说是黑客精心制作的呢?因为嵌入在这个网页中的脚本恰如其分地利用了IE浏览器的漏洞,让IE在后台自动下载黑客放置在网络上的木马并运行(安装)这个木马,也就是说,这个网页能下载木马到本地并运行(安装)下载到本地电脑上的木马,整个过程都在后台运行,用户一旦打开这个网页,下载过程和运行(安装)过程就自动开始。 + +- 网页木马本质上并非木马,而是Web方式的渗透攻击代码 +- 网页木马一般以JavaScript, VBScript等脚本语言实现 +- 免杀机制:1)通过大小写变换、十六进制编码、unicode编码、base64编码、escape编码等方法对网页木马进行编码混淆 2)通过通用(screnc等)或定制的加密工具(xxtea等)对网页木马进行加密 3)修改网页木马文件掩码、混淆文件结构、分割至多个文件等 + +#### 参考: + +https://developer.51cto.com/art/202006/619575.htm + +http://www.100mian.com/category/webanquan/ + + + + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file diff --git a/Zookeeper.md b/Zookeeper.md new file mode 100644 index 0000000..7c35eae --- /dev/null +++ b/Zookeeper.md @@ -0,0 +1,448 @@ +## Zookeeper + +#### 1.ZooKeeper 是什么? + +ZooKeeper 是一个开源的分布式协调服务。它是一个为分布式应用提供一致性服务的软件,分布式应用程序可以基于 Zookeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。 + +ZooKeeper 的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。 + +Zookeeper 保证了如下分布式一致性特性: + +(1)顺序一致性 + +(2)原子性 + +(3)单一视图 + +(4)可靠性 + +(5)实时性(最终一致性) + +客户端的读请求可以被集群中的任意一台机器处理,如果读请求在节点上注册了监听器,这个监听器也是由所连接的 zookeeper 机器来处理。对于写请求,这些请求会同时发给其他 zookeeper 机器并且达成一致后,请求才会返回成功。因此,随着 zookeeper 的集群机器增多,读请求的吞吐会提高但是写请求的吞吐会下降。 + +有序性是 zookeeper 中非常重要的一个特性,所有的更新都是全局有序的,每个更新都有一个唯一的时间戳,这个时间戳称为 zxid(Zookeeper Transaction Id)。而读请求只会相对于更新有序,也就是读请求的返回结果中会带有这个zookeeper 最新的 zxid。 + + +#### 2.ZooKeeper 提供了什么? + +- 文件系统 +- 通知机制 + +#### 3.Zookeeper 文件系统 + +Zookeeper 提供一个多层级的节点命名空间(节点称为 znode)。与文件系统不同的是,这些节点都可以设置关联的数据,而文件系统中只有文件节点可以存放数据而目录节点不行。 + +Zookeeper 为了保证高吞吐和低延迟,在内存中维护了这个树状的目录结构,这种特性使得 Zookeeper 不能用于存放大量的数据,每个节点的存放数据上限为1M。 + + +#### 4.Zookeeper 怎么保证主从节点的状态同步? + + Zookeeper 的核心是原子广播机制,这个机制保证了各个 server 之间的同步。实现这个机制的协议叫做 Zab 协议。Zab 协议有两种模式,它们分别是恢复模式和广播模式。 + +##### 恢复模式 + +当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数 server 完成了和 leader 的状态同步以后,恢复模式就结束了。状态同步保证了 leader 和 server 具有相同的系统状态。 + +##### 广播模式 + +一旦 leader 已经和多数的 follower 进行了状态同步后,它就可以开始广播消息了,即进入广播状态。这时候当一个 server 加入 ZooKeeper 服务中,它会在恢复模式下启动,发现 leader,并和 leader 进行状态同步。待到同步结束,它也参与消息广播。ZooKeeper 服务一直维持在 Broadcast 状态,直到 leader 崩溃了或者 leader 失去了大部分的 followers 支持。 + + +#### 5.四种类型的数据节点 Znode + +(1)PERSISTENT-持久节点 + 除非手动删除,否则节点一直存在于 Zookeeper 上 + +(2)EPHEMERAL-临时节点 + 临时节点的生命周期与客户端会话绑定,一旦客户端会话失效(客户端与zookeeper 连接断开不一定会话失效),那么这个客户端创建的所有临时节点都会被移除。 + +(3)PERSISTENT_SEQUENTIAL-持久顺序节点 + 基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。 + +(4)EPHEMERAL_SEQUENTIAL-临时顺序节点 + 基本特性同临时节点,增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。 + +#### 6.Zookeeper Watcher 机制 – 数据变更通知 + +Zookeeper 允许客户端向服务端的某个 Znode 注册一个 Watcher 监听,当服务端的一些指定事件触发了这个 Watcher,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能,然后客户端根据 Watcher 通知状态和事件类型做出业务上的改变。 + +工作机制: + +(1)客户端注册 watcher + +(2)服务端处理 watcher + +(3)客户端回调 watcher + +#### 7.Watcher 特性总结 + +##### 一次性 + +无论是服务端还是客户端,一旦一个 Watcher 被 触 发 ,Zookeeper 都会将其从相应的存储中移除。这样的设计有效的减轻了服务端的压力,不然对于更新非常频繁的节点,服务端会不断的向客户端发送事件通知,无论对于网络还是服务端的压力都非常大。 + +##### 客户端串行执行 + +客户端 Watcher 回调的过程是一个串行同步的过程。 + +##### 轻量 + +3.1、Watcher 通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。 +3.2、客户端向服务端注册 Watcher 的时候,并不会把客户端真实的 Watcher 对象实体传递到服务端,仅仅是在客户端请求中使用 boolean 类型属性进行了标记。 + +watcher event 异步发送 watcher 的通知事件从 server 发送到 client 是异步的,这就存在一个问题,不同的客户端和服务器之间通过 socket 进行通信,由于网络延迟或其他因素导致客户端在不通的时刻监听到事件,由于 Zookeeper 本身提供了 ordering guarantee,即客户端监听事件后,才会感知它所监视 znode发生了变化。所以我们使用 Zookeeper 不能期望能够监控到节点每次的变化。Zookeeper 只能保证最终的一致性,而无法保证强一致性。 + +注册 watcher getData、exists、getChildren + +触发 watcher create、delete、setData + +当一个客户端连接到一个新的服务器上时,watch 将会被以任意会话事件触发。当与一个服务器失去连接的时候,是无法接收到 watch 的。而当 client 重新连接时,如果需要的话,所有先前注册过的 watch,都会被重新注册。通常这是完全透明的。只有在一个特殊情况下,watch 可能会丢失:对于一个未创建的 znode的 exist watch,如果在客户端断开连接期间被创建了,并且随后在客户端连接上之前又删除了,这种情况下,这个 watch 事件可能会被丢失。 + +#### 8.客户端注册 Watcher 实现 + +(1)调用 getData()/getChildren()/exist()三个 API,传入 Watcher 对象 + +(2)标记请求 request,封装 Watcher 到 WatchRegistration + +(3)封装成 Packet 对象,发服务端发送 request + +(4)收到服务端响应后,将 Watcher 注册到 ZKWatcherManager 中进行管理 + +(5)请求返回,完成注册。 + +#### 9.服务端处理 Watcher 实现 + +##### 1.服务端接收 Watcher 并存储 + +接收到客户端请求,处理请求判断是否需要注册 Watcher,需要的话将数据节点的节点路径和 ServerCnxn(ServerCnxn 代表一个客户端和服务端的连接,实现了 Watcher 的 process 接口,此时可以看成一个 Watcher 对象)存储在WatcherManager 的 WatchTable 和 watch2Paths 中去。 + +##### 2.Watcher 触发 + +以服务端接收到 setData() 事务请求触发 NodeDataChanged 事件为例: + +2.1 封装 WatchedEvent +将通知状态(SyncConnected)、事件类型(NodeDataChanged)以及节点路径封装成一个 WatchedEvent 对象 + +2.2 查询 Watcher +从 WatchTable 中根据节点路径查找 Watcher + +2.3 没找到;说明没有客户端在该数据节点上注册过 Watcher + +2.4 找到;提取并从 WatchTable 和 Watch2Paths 中删除对应 Watcher(从这里可以看出 Watcher 在服务端是一次性的,触发一次就失效了) + +##### 3.调用 process 方法来触发 Watcher + +这里 process 主要就是通过 ServerCnxn 对应的 TCP 连接发送 Watcher 事件通知。 + +#### 10.客户端回调 Watcher + +- 客户端 SendThread 线程接收事件通知,交由 EventThread 线程回调 Watcher。 +- 客户端的 Watcher 机制同样是一次性的,一旦被触发后,该 Watcher 就失效了。 + +#### 11.ACL 权限控制机制 + +UGO(User/Group/Others) + +目前在 Linux/Unix 文件系统中使用,也是使用最广泛的权限控制方式。是一种粗粒度的文件系统权限控制模式。 + +ACL(Access Control List)访问控制列表 + +包括三个方面: + +权限模式(Scheme) +(1)IP:从 IP 地址粒度进行权限控制 +(2)Digest:最常用,用类似于 username:password 的权限标识来进行权限配置,便于区分不同应用来进行权限控制 +(3)World:最开放的权限控制方式,是一种特殊的 digest 模式,只有一个权限标识“world:anyone” +(4)Super:超级用户 + +授权对象 +授权对象指的是权限赋予的用户或一个指定实体,例如 IP 地址或是机器灯。 + +权限 Permission +(1)CREATE:数据节点创建权限,允许授权对象在该 Znode 下创建子节点 +(2)DELETE:子节点删除权限,允许授权对象删除该数据节点的子节点 +(3)READ:数据节点的读取权限,允许授权对象访问该数据节点并读取其数据内容或子节点列表等 +(4)WRITE:数据节点更新权限,允许授权对象对该数据节点进行更新操作 +(5)ADMIN:数据节点管理权限,允许授权对象对该数据节点进行 ACL 相关设置操作 + +#### 12.Chroot 特性 + +3.2.0 版本后,添加了 Chroot 特性,该特性允许每个客户端为自己设置一个命名空间。如果一个客户端设置了 Chroot,那么该客户端对服务器的任何操作,都将会被限制在其自己的命名空间下。 + +通过设置 Chroot,能够将一个客户端应用于 Zookeeper 服务端的一颗子树相对应,在那些多个应用公用一个 Zookeeper 进群的场景下,对实现不同应用间的相互隔离非常有帮助。 + +#### 13.会话管理 + +分桶策略:将类似的会话放在同一区块中进行管理,以便于 Zookeeper 对会话进行不同区块的隔离处理以及同一区块的统一处理。 + +分配原则:每个会话的“下次超时时间点”(ExpirationTime) + +计算公式: + +ExpirationTime_ = currentTime + sessionTimeout + +ExpirationTime = (ExpirationTime_ / ExpirationInrerval + 1) * + +ExpirationInterval , ExpirationInterval 是指 Zookeeper 会话超时检查时间间隔,默认 tickTime + +#### 14.服务器角色 + +Leader + +(1)事务请求的唯一调度和处理者,保证集群事务处理的顺序性 + +(2)集群内部各服务的调度者 + +Follower + +(1)处理客户端的非事务请求,转发事务请求给 Leader 服务器 + +(2)参与事务请求 Proposal 的投票 + +(3)参与 Leader 选举投票 + +Observer + +(1)3.0 版本以后引入的一个服务器角色,在不影响集群事务处理能力的基础上提升集群的非事务处理能力 + +(2)处理客户端的非事务请求,转发事务请求给 Leader 服务器 + +(3)不参与任何形式的投票 + + +#### 15.Zookeeper 下 Server 工作状态 + +服务器具有四种状态,分别是 LOOKING、FOLLOWING、LEADING、OBSERVING。 + +(1)LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。 + +(2)FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。 + +(3)LEADING:领导者状态。表明当前服务器角色是 Leader。 + +(4)OBSERVING:观察者状态。表明当前服务器角色是 Observer。 + +#### 16.Leader 选举 + +Leader 选举是保证分布式数据一致性的关键所在。当 Zookeeper 集群中的一台服务器出现以下两种情况之一时,需要进入 Leader 选举。 + + + +  (1) 服务器初始化启动。 + +  (2) 服务器运行期间无法和 Leader 保持连接。 + +  下面就两种情况进行分析讲解。 + +  1. 服务器启动时期的 Leader 选举 + +  若进行 Leader 选举,则至少需要两台机器,这里选取 3 台机器组成的服务器集群为例。在集群初始化阶段,当有一台服务器 Server1 启动时,其单独无法进行和完成 Leader 选举,当第二台服务器 Server2 启动时,此时两台机器可以相互通信,每台机器都试图找到 Leader,于是进入 Leader 选举过程。选举过程如下 + +  (1) 每个 Server 发出一个投票。由于是初始情况,Server1 和 Server2 都会将自己作为 Leader 服务器来进行投票,每次投票会包含所推举的服务器的 myid 和 ZXID,使用 (myid, ZXID) 来表示,此时 Server1 的投票为(1, 0),Server2 的投票为(2, 0),然后各自将这个投票发给集群中其他机器。 + +  (2) 接受来自各个服务器的投票。集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自 LOOKING 状态的服务器。 + +  (3) 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行 PK,PK 规则如下 + +    · 优先检查 ZXID。ZXID 比较大的服务器优先作为 Leader。 + +    · 如果 ZXID 相同,那么就比较 myid。myid 较大的服务器作为 Leader 服务器。 + +  对于 Server1 而言,它的投票是 (1, 0),接收 Server2 的投票为 (2, 0),首先会比较两者的 ZXID,均为 0,再比较 myid,此时 Server2 的 myid 最大,于是更新自己的投票为 (2, 0),然后重新投票,对于 Server2 而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。 + +  (4) 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于 Server1、Server2 而言,都统计出集群中已经有两台机器接受了 (2, 0) 的投票信息,此时便认为已经选出了 Leader。 + +  (5) 改变服务器状态。一旦确定了 Leader,每个服务器就会更新自己的状态,如果是 Follower,那么就变更为 FOLLOWING,如果是 Leader,就变更为 LEADING。 + +  2. 服务器运行时期的 Leader 选举 + +  在 Zookeeper 运行期间,Leader 与非 Leader 服务器各司其职,即便当有非 Leader 服务器宕机或新加入,此时也不会影响 Leader,但是一旦 Leader 服务器挂了,那么整个集群将暂停对外服务,进入新一轮 Leader 选举,其过程和启动时期的 Leader 选举过程基本一致。假设正在运行的有 Server1、Server2、Server3 三台服务器,当前 Leader 是 Server2,若某一时刻 Leader 挂了,此时便开始 Leader 选举。选举过程如下 + +  (1) 变更状态。Leader 挂后,余下的非 Observer 服务器都会讲自己的服务器状态变更为 LOOKING,然后开始进入 Leader 选举过程。 + +  (2) 每个 Server 会发出一个投票。在运行期间,每个服务器上的 ZXID 可能不同,此时假定 Server1 的 ZXID 为 123,Server3 的 ZXID 为 122;在第一轮投票中,Server1 和 Server3 都会投自己,产生投票 (1, 123),(3, 122),然后各自将投票发送给集群中所有机器。 + +  (3) 接收来自各个服务器的投票。与启动时过程相同。 + +  (4) 处理投票。与启动时过程相同,此时,Server1 将会成为 Leader。 + +  (5) 统计投票。与启动时过程相同。 + +  (6) 改变服务器的状态。与启动时过程相同。 + +  2.2 Leader 选举算法分析 + +  在 3.4.0 后的 Zookeeper 的版本只保留了 TCP 版本的 FastLeaderElection 选举算法。当一台机器进入 Leader 选举时,当前集群可能会处于以下两种状态 + +    · 集群中已经存在 Leader。 + +    · 集群中不存在 Leader。 + +  对于集群中已经存在 Leader 而言,此种情况一般都是某台机器启动得较晚,在其启动之前,集群已经在正常工作,对这种情况,该机器试图去选举 Leader 时,会被告知当前服务器的 Leader 信息,对于该机器而言,仅仅需要和 Leader 机器建立起连接,并进行状态同步即可。而在集群中不存在 Leader 情况下则会相对复杂,其步骤如下 + +  (1) 第一次投票。无论哪种导致进行 Leader 选举,集群的所有机器都处于试图选举出一个 Leader 的状态,即 LOOKING 状态,LOOKING 机器会向所有其他机器发送消息,该消息称为投票。投票中包含了 SID(服务器的唯一标识)和 ZXID(事务 ID),(SID, ZXID) 形式来标识一次投票信息。假定 Zookeeper 由 5 台机器组成,SID 分别为 1、2、3、4、5,ZXID 分别为 9、9、9、8、8,并且此时 SID 为 2 的机器是 Leader 机器,某一时刻,1、2 所在机器出现故障,因此集群开始进行 Leader 选举。在第一次投票时,每台机器都会将自己作为投票对象,于是 SID 为 3、4、5 的机器投票情况分别为 (3, 9),(4, 8), (5, 8)。 + +  (2) 变更投票。每台机器发出投票后,也会收到其他机器的投票,每台机器会根据一定规则来处理收到的其他机器的投票,并以此来决定是否需要变更自己的投票,这个规则也是整个 Leader 选举算法的核心所在,其中术语描述如下 + +    · vote_sid:接收到的投票中所推举 Leader 服务器的 SID。 + +    · vote_zxid:接收到的投票中所推举 Leader 服务器的 ZXID。 + +    · self_sid:当前服务器自己的 SID。 + +    · self_zxid:当前服务器自己的 ZXID。 + +  每次对收到的投票的处理,都是对 (vote_sid, vote_zxid) 和(self_sid, self_zxid)对比的过程。 + +    规则一:如果 vote_zxid 大于 self_zxid,就认可当前收到的投票,并再次将该投票发送出去。 + +    规则二:如果 vote_zxid 小于 self_zxid,那么坚持自己的投票,不做任何变更。 + +    规则三:如果 vote_zxid 等于 self_zxid,那么就对比两者的 SID,如果 vote_sid 大于 self_sid,那么就认可当前收到的投票,并再次将该投票发送出去。 + +    规则四:如果 vote_zxid 等于 self_zxid,并且 vote_sid 小于 self_sid,那么坚持自己的投票,不做任何变更。 + +  结合上面规则,给出下面的集群变更过程。 + +  (3) 确定 Leader。经过第二轮投票后,集群中的每台机器都会再次接收到其他机器的投票,然后开始统计投票,如果一台机器收到了超过半数的相同投票,那么这个投票对应的 SID 机器即为 Leader。此时 Server3 将成为 Leader。 + +  由上面规则可知,通常那台服务器上的数据越新(ZXID 会越大),其成为 Leader 的可能性越大,也就越能够保证数据的恢复。如果 ZXID 相同,则 SID 越大机会越大。 + +  2.3 Leader 选举实现细节 + +  1. 服务器状态 + +  服务器具有四种状态,分别是 LOOKING、FOLLOWING、LEADING、OBSERVING。 + +  LOOKING:寻找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。 + +  FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。 + +  LEADING:领导者状态。表明当前服务器角色是 Leader。 + +  OBSERVING:观察者状态。表明当前服务器角色是 Observer。 + +  2. 投票数据结构 + +  每个投票中包含了两个最基本的信息,所推举服务器的 SID 和 ZXID,投票(Vote)在 Zookeeper 中包含字段如下 + +  id:被推举的 Leader 的 SID。 + +  zxid:被推举的 Leader 事务 ID。 + +  electionEpoch:逻辑时钟,用来判断多个投票是否在同一轮选举周期中,该值在服务端是一个自增序列,每次进入新一轮的投票后,都会对该值进行加 1 操作。 + +  peerEpoch:被推举的 Leader 的 epoch。 + +  state:当前服务器的状态。 + +  3. QuorumCnxManager:网络 I/O + +  每台服务器在启动的过程中,会启动一个 QuorumPeerManager,负责各台服务器之间的底层 Leader 选举过程中的网络通信。 + +  (1) 消息队列。QuorumCnxManager 内部维护了一系列的队列,用来保存接收到的、待发送的消息以及消息的发送器,除接收队列以外,其他队列都按照 SID 分组形成队列集合,如一个集群中除了自身还有 3 台机器,那么就会为这 3 台机器分别创建一个发送队列,互不干扰。 + +    · recvQueue:消息接收队列,用于存放那些从其他服务器接收到的消息。 + +    · queueSendMap:消息发送队列,用于保存那些待发送的消息,按照 SID 进行分组。 + +    · senderWorkerMap:发送器集合,每个 SenderWorker 消息发送器,都对应一台远程 Zookeeper 服务器,负责消息的发送,也按照 SID 进行分组。 + +    · lastMessageSent:最近发送过的消息,为每个 SID 保留最近发送过的一个消息。 + +  (2) 建立连接。为了能够相互投票,Zookeeper 集群中的所有机器都需要两两建立起网络连接。QuorumCnxManager 在启动时会创建一个 ServerSocket 来监听 Leader 选举的通信端口 (默认为 3888)。开启监听后,Zookeeper 能够不断地接收到来自其他服务器的创建连接请求,在接收到其他服务器的 TCP 连接请求时,会进行处理。为了避免两台机器之间重复地创建 TCP 连接,Zookeeper 只允许 SID 大的服务器主动和其他机器建立连接,否则断开连接。在接收到创建连接请求后,服务器通过对比自己和远程服务器的 SID 值来判断是否接收连接请求,如果当前服务器发现自己的 SID 更大,那么会断开当前连接,然后自己主动和远程服务器建立连接。一旦连接建立,就会根据远程服务器的 SID 来创建相应的消息发送器 SendWorker 和消息接收器 RecvWorker,并启动。 + +  (3) 消息接收与发送。消息接收:由消息接收器 RecvWorker 负责,由于 Zookeeper 为每个远程服务器都分配一个单独的 RecvWorker,因此,每个 RecvWorker 只需要不断地从这个 TCP 连接中读取消息,并将其保存到 recvQueue 队列中。消息发送:由于 Zookeeper 为每个远程服务器都分配一个单独的 SendWorker,因此,每个 SendWorker 只需要不断地从对应的消息发送队列中获取出一个消息发送即可,同时将这个消息放入 lastMessageSent 中。在 SendWorker 中,一旦 Zookeeper 发现针对当前服务器的消息发送队列为空,那么此时需要从 lastMessageSent 中取出一个最近发送过的消息来进行再次发送,这是为了解决接收方在消息接收前或者接收到消息后服务器挂了,导致消息尚未被正确处理。同时,Zookeeper 能够保证接收方在处理消息时,会对重复消息进行正确的处理。 + +  4. FastLeaderElection:选举算法核心 + +  · 外部投票:特指其他服务器发来的投票。 + +  · 内部投票:服务器自身当前的投票。 + +  · 选举轮次:Zookeeper 服务器 Leader 选举的轮次,即 logicalclock。 + +  · PK:对内部投票和外部投票进行对比来确定是否需要变更内部投票。 + +  (1) 选票管理 + +  · sendqueue:选票发送队列,用于保存待发送的选票。 + +  · recvqueue:选票接收队列,用于保存接收到的外部投票。 + +  · WorkerReceiver:选票接收器。其会不断地从 QuorumCnxManager 中获取其他服务器发来的选举消息,并将其转换成一个选票,然后保存到 recvqueue 中,在选票接收过程中,如果发现该外部选票的选举轮次小于当前服务器的,那么忽略该外部投票,同时立即发送自己的内部投票。 + +  · WorkerSender:选票发送器,不断地从 sendqueue 中获取待发送的选票,并将其传递到底层 QuorumCnxManager 中。 + +  (2) 算法核心 + +![img](https://images2018.cnblogs.com/blog/632316/201808/632316-20180803082744195-266416769.png) + +  上图展示了 FastLeaderElection 模块是如何与底层网络 I/O 进行交互的。Leader 选举的基本流程如下 + +  1. 自增选举轮次。Zookeeper 规定所有有效的投票都必须在同一轮次中,在开始新一轮投票时,会首先对 logicalclock 进行自增操作。 + +  2. 初始化选票。在开始进行新一轮投票之前,每个服务器都会初始化自身的选票,并且在初始化阶段,每台服务器都会将自己推举为 Leader。 + +  3. 发送初始化选票。完成选票的初始化后,服务器就会发起第一次投票。Zookeeper 会将刚刚初始化好的选票放入 sendqueue 中,由发送器 WorkerSender 负责发送出去。 + +  4. 接收外部投票。每台服务器会不断地从 recvqueue 队列中获取外部选票。如果服务器发现无法获取到任何外部投票,那么就会立即确认自己是否和集群中其他服务器保持着有效的连接,如果没有连接,则马上建立连接,如果已经建立了连接,则再次发送自己当前的内部投票。 + +  5. 判断选举轮次。在发送完初始化选票之后,接着开始处理外部投票。在处理外部投票时,会根据选举轮次来进行不同的处理。 + +    · 外部投票的选举轮次大于内部投票。若服务器自身的选举轮次落后于该外部投票对应服务器的选举轮次,那么就会立即更新自己的选举轮次 (logicalclock),并且清空所有已经收到的投票,然后使用初始化的投票来进行 PK 以确定是否变更内部投票。最终再将内部投票发送出去。 + +    · 外部投票的选举轮次小于内部投票。若服务器接收的外选票的选举轮次落后于自身的选举轮次,那么 Zookeeper 就会直接忽略该外部投票,不做任何处理,并返回步骤 4。 + +    · 外部投票的选举轮次等于内部投票。此时可以开始进行选票 PK。 + +  6. 选票 PK。在进行选票 PK 时,符合任意一个条件就需要变更投票。 + +    · 若外部投票中推举的 Leader 服务器的选举轮次大于内部投票,那么需要变更投票。 + +    · 若选举轮次一致,那么就对比两者的 ZXID,若外部投票的 ZXID 大,那么需要变更投票。 + +    · 若两者的 ZXID 一致,那么就对比两者的 SID,若外部投票的 SID 大,那么就需要变更投票。 + +  7. 变更投票。经过 PK 后,若确定了外部投票优于内部投票,那么就变更投票,即使用外部投票的选票信息来覆盖内部投票,变更完成后,再次将这个变更后的内部投票发送出去。 + +  8. 选票归档。无论是否变更了投票,都会将刚刚收到的那份外部投票放入选票集合 recvset 中进行归档。recvset 用于记录当前服务器在本轮次的 Leader 选举中收到的所有外部投票(按照服务队的 SID 区别,如 {(1, vote1), (2, vote2)...})。 + +  9. 统计投票。完成选票归档后,就可以开始统计投票,统计投票是为了统计集群中是否已经有过半的服务器认可了当前的内部投票,如果确定已经有过半服务器认可了该投票,则终止投票。否则返回步骤 4。 + +  10. 更新服务器状态。若已经确定可以终止投票,那么就开始更新服务器状态,服务器首选判断当前被过半服务器认可的投票所对应的 Leader 服务器是否是自己,若是自己,则将自己的服务器状态更新为 LEADING,若不是,则根据具体情况来确定自己是 FOLLOWING 或是 OBSERVING。 + +  以上 10 个步骤就是 FastLeaderElection 的核心,其中步骤 4-9 会经过几轮循环,直到有 Leader 选举产生。 + +#### 17.zookeeper 负载均衡和 nginx 负载均衡区别 + +- zk 的负载均衡是可以调控,nginx 只是能调权重,其他需要可控的都需要自己写插件;但是 nginx 的吞吐量比 zk 大很多,应该说按业务选择用哪种方式。 + +#### 18.集群支持动态添加机器吗? + + 其实就是水平扩容了,Zookeeper 在这方面不太好。两种方式: + +全部重启:关闭所有 Zookeeper 服务,修改配置之后启动。不影响之前客户端的会话。 + +逐个重启:在过半存活即可用的原则下,一台机器重启不影响整个集群对外提供服务。这是比较常用的方式。 + +3.5 版本开始支持动态扩容。 + +#### 19.集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗? + + 集群规则为 2N+1 台,N>0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用 + +#### 20.Zookeeper 对节点的 watch 监听通知是永久的吗?为什么不是永久的? + +不是。官方声明:一个 Watch 事件是一个一次性的触发器,当被设置了 Watch 的数据发生了改变的时候,则服务器将这个改变发送给设置了 Watch 的客户端,以便通知它们。 + + + +为什么不是永久的,举个例子,如果服务端变动频繁,而监听的客户端很多情况下,每次变动都要通知到所有的客户端,给网络和服务器造成很大压力。 +一般是客户端执行 getData(“/ 节点 A”,true),如果节点 A 发生了变更或删除,客户端会得到它的 watch 事件,但是在之后节点 A 又发生了变更,而客户端又没有设置 watch 事件,就不再给客户端发送。 +在实际应用中,很多情况下,我们的客户端不需要知道服务端的每一次变动,我只要最新的数据即可。 + +#### 参考 + +https://blog.csdn.net/weixin_43122090/article/details/103645642 + +https://www.cnblogs.com/lanqiu5ge/p/9405601.html \ No newline at end of file diff --git "a/\345\274\202\345\270\270&\345\217\215\345\260\204.md" "b/\345\274\202\345\270\270&\345\217\215\345\260\204.md" new file mode 100644 index 0000000..5cd7859 --- /dev/null +++ "b/\345\274\202\345\270\270&\345\217\215\345\260\204.md" @@ -0,0 +1,167 @@ +## 异常&反射 + +![image-20200414175020396](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/image-20200414175020396.png) + +#### 1.**error和exception有什么区别**? + +error表示系统级的错误,是java运行环境内部错误或者硬件问题,不能指望程序来处理这样的问题,除了退出运行外别无选择,它是Java虚拟机抛出的。 + +exception 表示程序需要捕捉、需要处理的异常,是由与程序设计的不完善而出现的问题,程序必须处理的问题。 + +#### 2.说出5个常见的**RuntimeException**? + +(1)Java.lang.NullPointerException 空指针异常;出现原因:调用了未经初始化的对象或者是不存在的对象。 + +(2)Java.lang.NumberFormatException 字符串转换为数字异常;出现原因:字符型数据中包含非数字型字符。 + +(3)Java.lang.IndexOutOfBoundsException 数组角标越界异常,常见于操作数组对象时发生。 + +(4)Java.lang.IllegalArgumentException 方法传递参数错误。 + +(5)Java.lang.ClassCastException 数据类型转换异常。 + +#### 3.**throw和throws的区别**? + +throw: + +  (1)throw 语句用在方法体内,表示抛出异常,由方法体内的语句处理。 + +  (2)throw 是具体向外抛出异常的动作,所以它抛出的是一个异常实例,执行 throw 一定是抛出了某种异常。 + +throws: + +​ (1)@throws 语句是用在方法声明后面,表示如果抛出异常,由该方法的调用者来进行异常的处理。 + +​ (2)throws 主要是声明这个方法会抛出某种类型的异常,让它的使用者要知道需要捕获的异常的类型。 + +​ (3)throws 表示出现异常的一种可能性,并不一定会发生这种异常。 + +#### 4.Java中异常分类 + +按照异常处理时机: + +编译时异常(受控异常(CheckedException))和运行时异常(非受控异常(UnCheckedException)) + +#### 5.如何自定义异常 + +继承Exception是检查性异常,继承RuntimeException是非检查性异常,一般要复写两个构造方法,用throw抛出新异常 + +如果同时有很多异常抛出,那可能就是异常链,就是一个异常引发另一个异常,另一个异常引发更多异常,一般我们会找它的原始异常来解决问题,一般会在开头或结尾,异常可通过initCause串起来,可以通过自定义异常 + +#### 6.Java中异常处理 + +首先处理异常主要有两种方式:一种try catch,一种是throws。 + +1. try catch: + +- try{} 中放入可能发生异常的代码。catch{}中放入对捕获到异常之后的处理。 + +2.throw throws: + +- throw是语句抛出异常,出现于函数内部,用来抛出一个具体异常实例,throw被执行后面的语句不起作用,直接转入异常处理阶段。 +- throws是函数方法抛出异常,一般写在方法的头部,抛出异常,给方法的调用者进行解决。 + +#### 7.常见的异常 + +NullPointException:空指针异常,对象是null时会抛出,在调用传入对象时尽量判断是否为null,Jdk8里面可以用Optional对象来避免 + +IndexOutOfBoundsException:数组下标越界,数组的下标超过了最大值时会抛出,在迭代循环时检查下标是否越界 + +NumberFormatException:数字类型转化异常,将非数字类型转成数字类型,将类型转化的代码catch住 + +ClassCastException:类型转换异常,发生在强转时,将不同类型转成同一类型,尽量少用强转,或用instanceof(判断继承中子类的实例是否是父类的实现)做类型判断,或多用泛型 + +FileNotFoundException:找不到指定文件,文件路径错误或文件不存在,可能用了绝对路径检查文件是否存在,路径是否写错,多用相对路径 + +ClassNotFoundException:在classpath中找不到引用的类缺乏引用当前类的jar或没有设置classpath或jar损坏-,找到jar并放入classpath中或检查jar是否损坏 + +OutOfMemoryError:内存溢出异常,产生对象太多,内存不够->不要在循环体重创建大量对象,或对象及时回收,增大初始化堆:-Xms 增加最大值:-Xmx + +NoClassDefFoundError:找不到相应的类错误,缺乏当前引用类的jar或jar版本不对->找到jar并放入classpath中或找到合适的版本 + +ConcurrentModificationException:并发修改异常,在集合迭代时修改里面的元素->在迭代时不要修改集合或用并发集合做遍历(如:ConcurrentHashMap) + +NoSuchMethodError:类里找不到相应的方法,一般是jar版本不对,当前引用的jar版本中没有这个方法->检查jar版本是否正确 + +UnsupportedClassVersionError:版本不支持错误,编译class的jdk和运行时候的jdk版本不一致或比较高->将低版本换成高版本 + +StackOverflowError:栈溢出错误,一般是函数的死循环,或递归调用无法退出->检查死循环的代码,或让递归有退出值,或加大栈初始化参数 + +#### 8.异常打印信息组成 + +所处线程名字、异常类名、异常信息、异常堆栈、异常的源码,包名,类名,方法名,行数 + +#### 9.常见方法 + +getMessage:错误信息的字符串解释 + +getCause:返回异常产生的原因,一般是原始异常如果不知道原因返回null + +printStackTrace:打印异常出现的位置或原因 + +toString:返回String格式的Throwable信息,此信息包括Throwable的名字和本地化信息 + +initCause:初始化原始异常 + +PrintStream和PrintWriter作为产生实现重载,这样就能实现打印栈轨迹到文件或流中 + +#### 10.如何自定义异常 + +继承Exception是检查性异常,继承RuntimeException是非检查性异常,一般要复写两个构造方法,用throw抛出新异常 + +如果同时有很多异常抛出,那可能就是异常链,就是一个异常引发另一个异常,另一个异常引发更多异常,一般我们会找它的原始异常来解决问题,一般会在开头或结尾,异常可通过initCause串起来,可以通过自定义异常 + +#### 11.什么是Java反射机制? + +Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。 + +#### 12.举例什么地方用到反射机制? + +1. JDBC中,利用反射动态加载了数据库驱动程序。 +2. Web服务器中利用反射调用了Sevlet的服务方法。 +3. Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。 +4. 很多框架都用到反射机制,注入属性,调用方法,如Spring。 + +#### 13.java反射机制的作用 + +- 在运行时判定任意一个对象所属的类 +- 在运行时构造任意一个类的对象; +- 在运行时判定任意一个类所具有的成员变量和方法; +- 在运行时调用任意一个对象的方法; +- 生成动态代理; + +#### 14.Java反射机制类 + +``` +java.lang.Class; //类 +java.lang.reflect.Constructor;//构造方法 +java.lang.reflect.Field; //类的成员变量 +java.lang.reflect.Method;//类的方法 +java.lang.reflect.Modifier;//访问权限 +``` + +#### 15.反射机制优缺点? + +优点:运行期类型的判断,动态加载类,提高代码灵活度。 + +缺点:性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。 + +#### 16.利用反射创建对象? + +1.通过一个全限类名创建一个对象 Class.forName(“全限类名”); + +例如:com.mysql.jdbc.Driver Driver类已经被加载到 jvm中,并且完成了类的初始化工作就行了 类名.class; 获取Class<?> clz 对象 对象.getClass(); + +2.获取构造器对象,通过构造器new出一个对象 Clazz.getConstructor([String.class]); Con.newInstance([参数]); 3.通过class对象创建一个实例对象(就相当与new类名()无参构造器) Cls.newInstance(); + + + +#### 参考: + +https://blog.csdn.net/qq_37875585/article/details/89340495 + +https://www.cnblogs.com/whoislcj/p/6038511.html + +https://blog.csdn.net/Yang_Hui_Liang/article/details/90238678 + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file diff --git "a/\347\256\200\345\216\206.md" "b/\347\256\200\345\216\206.md" new file mode 100644 index 0000000..7d2a44a --- /dev/null +++ "b/\347\256\200\345\216\206.md" @@ -0,0 +1,167 @@ +## 简历 + + + +原文链接:https://www.zhihu.com/question/25002833 ThoughtWorks中国回答 + +大家伙让一让,这个问题让老司机先答!作为一个潜入IT圈五年之久、看过数万份简历的HR,在这个问题上还是有点发言权的。HR在筛选简历时主要从公司需求出发,重点不一,不过还是有很多“通用”的套路,为了在30秒内判断出这份简历是否值得跟进,我认为程序员写简历的正确姿势是这样的: + +#### **一、基本格调** + +即打开简历之后的第一印象。就好比我们看见一个人,会有一个整体的感觉,他是fashion的、小清新的还是老道的?有了第一印象之后再慢慢分解来看。 + +**加分写法:** + +- 简洁明了,逻辑结构清晰。 +- 字体,排版,顺畅,清晰整齐就好。 +- 最好是PDF格式,兼容性强且不易乱序。 + +**减分写法:** + +- 设计的过于浮夸或者过于简单的。(eg.有的简历五颜六色、非常酷炫,却半天找不到联系方式,抑或是只有个人基本信息和公司名称) +- 写了十几页,半天打不开的,或者加载了半天,打开还乱码。 + +#### **二、基本信息(姓名/性别/毕业院校/电话/邮箱/居住地/期望地)** + +**加分写法:** + +- 清晰罗列出以上信息,这样HR就不用在接下来的电话沟通或面试中再去追问这些内容,建立我们接下来电话沟通对你的熟悉度。 +- 再额外能加上QQ或者微信就更好了(以防有时候电话打不通哦,时不时会遇到这种情况) + +**减分写法:** + +- 大部分的基本信息没有写 +- 甩给我一个Github链接,极致简洁的几句描述,需要通过你的链接来找你的联系方式。(如果不是博客写的特别好,基本是要放弃你了) + +#### **三、工作经历&项目经历** + +**加分写法:** + +- 工作经历项目经历可参照万能的STAR法则来写,STAR不清楚的童鞋点[这里](https://link.zhihu.com/?target=http%3A//www.xuexila.com/success/story/509956.html)啦 +- 效力过哪些公司,我们匹配的公司? BAT? 知名大型互联网公司? +- 做过什么行业领域,和我们目前的行业是否匹配 +- 擅长的技术语言,应用了哪些技术栈,(Java, Scala,Ruby, React, Vue, Microservice…) +- 经历的项目复杂度,及在项目中承担什么样的角色(人的变化/技术的变化/环境的变化/不同工作经历相同角色的不同点) +- 时间节点(空档期) + +**减分写法:** + +- 看了半天,不知所云,没有任何亮点,没有让人有去和你聊一聊深扒的信息。 + +**来几个栗子** + +栗子1错误打开方式: + +- XX(全栈工程师)2013.06 — 至今 +- 参与需求分析及实现方案设计。 +- 设计数据库表结构,实现后台功能及web页面展示。 +- 产品线上部署及运维。 +- ay 配置管理工程师 2010.03 — 2013.03 +- 负责公司产品性能测试,及线上数据分析 +- 负责公司配置管理,环境维护等工作 + +**点评:看不出来他做的什么事情,没有逻辑性,甚至不知道他做的什么技术语言。** + +**栗子2正确打开方式:** + +**西安XXX公司 Java工程师 — 2016.2月-2017.2月** + +1、MOGU推荐架构数据与缓存层设计开发 + +- MOGU是一款时尚资讯app,负责推荐页面资讯feed流的展示及用户历史的展示 +- 负责数据层,处理前端逻辑整个开发工作,分布式rpc服务搭建 +- 负责进行压测监测、缓存处理,对接又进行改进优化,主用redis缓存 + +2、基于JAVA的电商爬虫开发 + +- 使用java搭建爬虫server平台,进行配置和开发,进行网页改版监测功能开发 +- 爬取淘宝时尚品牌与其他电商网站商品品牌与详情等 +- 通过频率、ip池、匿名代理等应对一些网站的反爬 + +3、同图搜索Solr服务开发 + +基于算法组的同图策略,使用solr做java接又实现rpc服务搭建,进行索引构建和solr实现 + +**北京XXX** + +java大数据工程师— 2013.4月-2015.12月 + +1、负责实时流消息处理应用系统构建和实现 + +- 在调研了kafka的优势和我们的具体需求之后,用kafka作为消费者,保证高吞吐处理消息,并持久化消息的同时供其它服务使用,进行了系统的设计和搭建使用。 本地日志保证消息不丢失,并通过记录游标滑动重复读取数据。 +- 使用storm 负责搭建消息处理架构,并完成基于业务的消息落地,提供后续的数据 统计分析实时和离线任务,诸如pv、uv等数据,为运营做决策 +- 网站用户行为埋点和基于js的日志收集器开发,定义接又和前端部门配合。主用go 2、hadoop集群搭建和数据分析处理 + +2、基于CDH的集群搭建工作,后期进行维护 + +编写MapReduce程序,能将复杂工作逻辑化,尽最大能力发挥大数据应用的特点, 对程序高要求,监控自己程序运行情况,使用内存合理,注重增量和全量运算的利弊 + +3、调度系统设计与实现 基于quartz2搭建调度平台,带徒弟实现相关功能并定期review代码 + +4、数据库调优 负责主从搭建,并掌握主从搭建的利弊,了解业界mycat原理,有数据库优化经验,能 正确并擅长使用索引,对锁有深刻的认识 + +5、网站开发 java web网站业务开发,并能很好的使用缓存技术,对重构有实际的经验,并对面向对 象开发有全面的实战经验。了解java数据结构的使用场景,虽然对于大并发没有太大的 发挥余地,但是掌握了数据结构,对于并发和阻塞等有自己的见解。 + +**点评:非常清晰的告诉简历阅读者自己做了什么事情,负责了什么样的事情,用了什么技术栈,且逻辑连贯。** + +#### **四、工作期望&个人评价** + +加分写法: + +- 对自己有一个全方位的一个描述总结,让别人更好的解读你。或者在此处,高亮你的优点特长有哪些。 +- 即使不写个人评价,也一定记得写上工作期望。 + +减分写法: + +完全看不出个性特点,写和没写没什么区别。 来几个栗子 + +**栗子1 错误打开方式** + +为人性格,诚实谦虚,勤奋,能吃苦耐劳,有耐心,有团队意识,能和同学和谐相处,能虚心接受别人的建议的人。 + +责任心强,善于沟通,具有良好的团队合作精神;专业扎实,具有较强的钻研精神和学习能力;性格比较乐观外向,喜欢打羽毛球。 + +**栗子2正确打开方式** + +- 我对自己的定位: 主攻前端,同时在其他方面打打辅助。我不希望过于依赖别人,即使没有后端没有设计没有产品经理,我依然想要把这个产品做到完美。毕竟全栈才能最高效地解决问题。 +- 我对工作的态度: 第一,要高效完成自己的本职工作。第二,要在完成的基础上寻找完美。第三,要在完美的基础上,与其他同事 互相交流学习,互相提升。工作是一种生活方式,不是一份养家糊口的差事。 +- 我怎样克服困难: 不用百度是第一原则,在遇到技术问题时我往往会去Google、Stack over flow上寻找答案。但通常很多问题 并不一定已经被人解决,所以熟练地阅读源码、在手册、规范甚至 REPL的环境自己做实验才是最终解决问题的办法。相信事实的结果,自己动手去做。 +- 怎样保持自己的视野:我一直认为软件开发中视野极其重要,除了在 Twitter 上关注业界大牛,Github Trending 也是每周必刷。 另外 Podcast、Hacker News、Reddit 以及TechRadar 也是重要的一手资料。保持开阔视野才能找到更酷的解决方案。 +- 我的优势: 热爱技术、自学能力强,有良好的自我认知。全面的技能树与开阔的视野,良好的心态、情商与沟通能力。 +- 我的劣势: 非科班出身没有科班同学对算法的熟练掌握,但我决定死磕技术,弥补不足。 + +**栗子3正确打开方式** + +- 极客、热爱技术、热爱开源 +- Ruby on Rails:精通 +- Agile/Lean:精通 +- ReactJS:掌握 +- Docker:掌握 +- AWS:掌握 + +#### **五、 是否有博客,个人技术栈点等** + +1. 看到有这项的HR两眼已经放光了,加分加分项,说明你真正的热爱技术,善于学习总结,乐于分享,且有投入自己的业余时间到软件事业中。 +2. 我喜欢的书籍:《重构》《卓有成效的程序员》《代码整洁之道》等 +3. 我喜欢的社区: 图灵社区,知乎,博客园,Stack Over flow,Google Developer Group等 +4. 我的博客链接、个人作品链接如下: + +- [https://github.com/github](https://link.zhihu.com/?target=https%3A//github.com/github) +- [http://www.oschina.net/](https://link.zhihu.com/?target=http%3A//www.oschina.net/) +- [https://www.cnblogs.com/](https://link.zhihu.com/?target=https%3A//www.cnblogs.com/) +- [https://itunes.apple.com/app/battle-of-crab/id1121917063?l=en&amp;mt=8](https://link.zhihu.com/?target=https%3A//itunes.apple.com/app/battle-of-crab/id1121917063%3Fl%3Den%26mt%3D8) + +#### **六、简历内容真实性** + +老司机提醒你,你简历的任意一个细节将会是后面面试中的呈堂证供。 + +基本就这些了,希望对大家能有帮助,看起简历来几十秒,码字还是个体力活。 + +##### 微信搜索Java小咖秀 回复 “简历1” 获取404份简历模版 + + + +写在最后: 希望大家都能拿到自己心仪的offer。面试笔记会继续更新优化。 + +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) + diff --git "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200.md" new file mode 100644 index 0000000..579808d --- /dev/null +++ "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200.md" @@ -0,0 +1,287 @@ +# 计算机基础 + +#### 1.ICMP 是什么协议?处于哪一层? + +ICMP是(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议簇的一个子协议,用于在IP主机、路由器之间传递控制消息。属于网络层协议 + +控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。 + + + +#### 2.什么是程序局部性?为什么会有程序的空间局部性? + +程序局部性是指程序在运行时呈现出局部性规律,在一段时间间隔内,程序的执行是局限在某个部份,所访问的存储空间也只局限在某个区域。 + +程序的空间局部性是指若一个存储单元被访问,那么它附近的单元也可能被访问,这是由于程序的顺序执行引起的。 + + + +#### 3.谈一谈 TCP 与 UDP 的区别。 + +TCP与UDP都是传输层的协议,且都用端口号标识数据所达的进程。 + +TCP提供的是面向连接服务,提供可靠交付。且具有流量控制和拥塞控制。可用于可靠要求高的场合如:SMTP,FTP,HTTP等 + +UDP提供的是无连接服务,提供不可靠交付,且无确认机制。主要用于即时强的场合如:视频聊天,语音电话等。 + + + +#### 4.网络协议的三个核心要素是什么?各有什么作用? + +答: + +语法,定义了数据与控制信息的格式; + +语义,定义了需要发出何种控制信息,完成何种响应动作以及作出何种响应; + +同步,定义了事件实现顺序的详细说明; + + + +#### 5.为了实现重定位,需要哪些硬件? + +答: + +最简单的方式是在系统中增设一个重定位寄存器,用来存放正在执行作业的内存地址,每次访问数据时,由硬件自动将相对地址与重定位寄存器中的起始地址相加,形成实际的特理地址。当然在分页式与分段式系统中,具地址变换机构,以及快表等硬件。 + + + +#### 6.在交互式系统中,非剥夺是不是一个好的策略?为什么? + +答: + +非剥夺方式:分派程序一旦把处理机分配给某进程后便让它一直运行下去,直到进程完成或发生某事件而阻塞时,才把处理机分配给另一个进程。 + +剥夺方式:当一个进程正在运行时,系统可以基于某种原则,剥夺已分配给它的处理机,将之分配给其它进程。剥夺原则有:优先权原则、短进程、优先原则、时间片原则。 + +在分时系统中不剥夺并不是一个好的策略。因为,在分时系统中,除了交互性以外,及时性是很重要的性能因素。当一个作业被阻塞后,CPU就完全空闲了,别的用户的及时性就无法保证了,而完全可以把这些时间分配给别的作业运行。以提高整体的吞吐量。 + + + +#### 7.何为死锁?何为系统调用? + +答: + +死锁:指多个有关进程由于争夺资源而造成的一种僵局,在无外力的情况下这些进程都将无法再向前推进的状态。 + +系统调用:系统调用是OS与应用程序之间的接口,它是用户程序取得OS服务的惟一途径。 + +它与一般的过程调用的区别: + +运行在不同的系统状态。调用程序在运行在用户态,而被调用的程序运行在系统态; + +通过软中断机制,先由用户态转为系统态,经枋心分析后,才能转向相应的系统调用处理子程序;一般的过程调用返回后继续执行,但对系统调用,当调用的进程仍具有最高优先权时,才返回到调用进程继续处理;否则只能等被重新调度; + + + +#### 8.CPU 不执行程序的时候在干什么? + +答: + +当没有被任何程序使用的时候,计算机的处理器被认为是空闲的。当然下面提到的空闲任务不在此列中。当有程序利用CPU空闲时间的时候,就意味着它以较低的优先权运行着,以便不会影响那有正常优先权的程序运行。一般来讲,这会引起CPU消耗更多的电能,而大多数的现代CPU当它们空闲的时候是能够进入省电模式的。大多数操作系统都有个空闲任务,它是一个特殊的任务。仅当CPU无事可做的时候由操作系统调度器载入它。在现代的处理器中,HLT停机指令节省了大量的电能与执量,而空闲任务几乎总是由一个重复执行HLT停机指令的循环组成。 + + + +#### 9.试举例解释一下同步和互斥 + +答: + +同步表现为直接制约,如管道通信,一个进程写,一个进程读,它们是相互制约的。 + +互斥表现为间接制约,比如多个进程同时请求打印机(没使用SPOOLing技术)、多个进程同时请求一张网卡发送数据包等。 + + + +#### 10.在可变分区管理中,需要哪些硬件机制? + +答: + +采用可变分区方式管理时,一般均采用动态重定位方式装入作业。地址变换要靠硬件支持,主要是两个寄存器:基址寄存器和限长寄存器,限长寄存器存放作业所占分区的长度,基址寄存器则存放作业所占分区的起始地址,这两个值确定了一个分区的位置和大小。 + +转换时根据逻辑地址与限长值比较,如果不有超过这个值,表示访问地址合法,再加上基址寄存器中的值就得到了绝对地址了,否则形成“地址越界”中断。 + +#### 11.谈一谈中断和陷入的区别 + +外中断时指来自处理机和内存外部的中断,如I/O中断、定时器中断、外部信号中断等。狭义上也叫中断; + +内中断主要指在处理机和内存内部产生的中断,也称陷入,如校验错、页面失效、溢出、除数为零等; + +中断和陷阱的主要区别: + +(1)陷入通常由处理机正在执行的现行指令引起,而中断则是由与现行指令无关的中断源引起的。 + +(2) 陷阱处理程序提供的服务为当前进程所用,而中断处理程序提供的服务则不是为了当前进程的。 + +(3) CPU在执行完一条指令之后,下一条指令开始之前响应中断,而在一条指令执行中也可以响应陷阱。 + +#### 12.数据库系统和文件系统相比有什么优点? + +答: + +文件系统 + +数据库管理系统 + +某一应用 + +现实世界 + +共享性差,冗余度大 + +共享性高,冗余度小 + +记录内有结构,整体无结构 + +整体结构化,用数据模型描述 + +应用程序自己控制 + +由数据库管理系统提供数据安全性,完整性,并发控制和恢复能力 + +独立性差 + +具有高度的物理独立性和一定的逻辑独立性 + +#### 13.谈一谈计算机网络和分布式计算机系统的区别 + +两者在计算机硬件连接、系统拓朴结构和通信控制等方面基本都是一样的,它们都具有通信和资源共享的功能。 + +区别关键在于:分布式计算机系统是在分布式计算机操作系统支持下,进行分布式数据库处理的,也就是说各互联的计算机可以互相协调工作,共同完成一项任务,多台计算机上并行运行。且具有透明性,用户不知道数据、资源的具体位置,整个网络中所有计算机就像是一台计算机一样;而计算机网络却不具备这种功能,计算机网络系统中的各计算机通常是各自独立进行工作的。 + +#### 14.为什么要引入多道程序技术? + +因为引入多道程序技术后,可以进一步提高了CPU利用率(阻塞),提高内存和I/O设备利用率(小作业把内存浪费了),增加系统吞吐量(两都提高后的必然)。 + +#### 15.何为管态和目态?它们与进程运行状态的关系是什么? + +CPU交替执行操作系统程序和用户程序。管态又叫特权态,系统态或核心态。CPU在管态下可以执行指令系统的全集。通常,操作系统在管态下运行。 + +目态又叫常态或用户态。机器处于目态时,程序只能执行非特权指令。用户程序只能在目态下运行,如果用户程序在目态下执行特权指令,硬件将发生中断,由操作系统获得控制,特权指令执行被禁止,这样可以防止用户程序有意或无意的破坏系统。 + +#### 16.何为网络延时?何为完整性约束? + +时延(delay或latency)是指一个报文或分组从一个网络(或一条链路)的一端传送到另一端所需的时间。 + +数据完整性约束指的是为了防止不符合规范的数据进入数据库,在用户对数据进行插入、修改、删除等操作时,DBMS自动按照一定的约束条件对数据进行监测,使不符合规范的数据不能进入数据库,以确保数据库中存储的数据正确、有效、相容。 + +简单理解:按照一定的约束条件防止不符合规范的数据进入数据库 + +#### 17.谈一谈你对当前 5G 技术和云计算技术的理解 + +5G,第五代移动通信技术,有三个关键特征,超高速率,实现每秒10Gb的下载速率,是4G的100倍。超可靠超低时延,实现1ms的低时延,是4G时延的40分之一;超大连接,实现每平方公里100万的连接数,是4G的100倍。 + +云计算技术:分布式计算的一种,指的是通过网络“云”将巨大的数据计算处理程序分解成无数个小程序,然后,通过多部服务器组成的系统进行处理和分析这些小程序得到结果并返回给用户。云计算具有很强的扩展性和需要性,可以为用户提供一种全新的体验,云计算的核心是可以将很多的计算机资源协调在一起,因此,使用户通过网络就可以获取到无限的资源,同时获取的资源不受时间和空间的限制。 + +#### 18.点对点和端对端工作在哪层?工作机制是什么? + +点对点协议(Point to Point Protocol)的缩写为PPP,是TCP/IP网络协议包的一个成员。PPP是TCP/IP的扩展,它增加了两个额外的功能组: + +(1)它可以通过串行接口传输TCP/IP包; + +(2)它可以安全登录。 + +数据传输的可靠性是通过数据链路层和网络层的点对点和传输层的端对端保证的。点对点是基于MAC地址或者IP地址,是指一个设备发数据给另外一个设备,这些设备是指直连设备包括网卡,路由器,交换机。端对端是网络连接,应用程序之间的远程通信。端对端不需要知道底层是如何传输的,是一条逻辑链路。 + +端到端与点到点是针对网络中传输的两端设备间的关系而言的。端到端传输指的是在数据传输前,经过各种各样的交换设备,在两端设备问建立一条链路,就像它们是直接相连的一样,链路建立后,发送端就可以发送数据,直至数据发送完毕,接收端确认接收成功。点到点系统指的是发送端把数据传给与它直接相连的设备,这台设备在合适的时候又把数据传给与之直接相连的下一台设备,通过一台一台直接相连的设备,把数据传到接收端。 端到端传输的优点是链路建立后,发送端知道接收设备一定能收到,而且经过中间交换设备时不需要进行存储转发,因此传输延迟小。端到端传输的缺点是直到接收端收到数据为止,发送端的设备一直要参与传输。如果整个传输的延迟很长,那么对发送端的设备造成很大的浪费。端到端传输的另一个缺点是如果接收设备关机或故障,那么端到端传输不可能实现。 点到点传输的优点是发送端设备送出数据后,它的任务已经完成,不需要参与整个传输过程,这样不会浪费发送端设备的资源。另外,即使接收端设备关机或故障,点到点传输也可以采用存储转发技术进行缓冲。点到点传输的缺点是发送端发出数据后,不知道接收端能否收到或何时能收到数据。 在一个网络系统的不同分层中,可能用到端到端传输,也可能用到点到点传输。如Internet网,IP及以下各层采用点到点传输,IP层以上采用端到端传输。 + +端对端,点对点,只是称为问题,本质区别很小 + +端对端,主要服务于Application Layer,是说两台主机(终端),跨过网络直接连接 + +点对点,是说两台主机(终端)在局域网中传输。 + +#### 19.DBMS 支持哪几种数据模型?SQL 的四个组成部分是什么? + +常用的是层次模型,网状模型和关系模型(最重要) + +SQL的四个组成部分: + +1、数据库模式定义语言DDL:create用来创建数据库中的各种对象——表、视图、索引、同义词、聚簇等 + +2、数据查询语言dql:基本结构是由SELECT子句,FROM子句和WHERE子句组成的查询块 + +3、数据操纵语言dml:插入INSERT、更新UPDATE和删除DELETE + +4、数据控制语言dcl:用来授予或回收访问数据库的某种特权,并控制数据库操纵事物发生的时间和效果,对数据库实行监视等 + +#### 20.谈一谈网络时延由哪几个部分组成?各产生于何处? + +网络时延主要由发送时延,传播时延,处理时延组成。发送时延是指结点在发送数据时使数据块从结点进入到传输媒体所需的时间,也就是从数据块的第一个比特开始发送算起,到最后一个比特发送完毕所需的时间。发送时延又称为传输时延,它的计算公式是: + +发送时延=数据块长度/信道带宽 + +信道带宽就是数据在信道上的发送速率,它也常称为数据在信道上的传输速率。 + +传播时延是指从发送端发送数据开始,到接收端收到数据(或者从接收端发送确认帧,到发送端收到确认帧),总共经历的时间。 + +传播时延 = d/s + +d = 物理链路的长度 + +s = 介质的信号传播速度 (~2x108 m/sec) + +处理时延是指计算机处理数据所需的时间,与计算机CPU的性能有关。 + +#### 21.TCP/IP 网络协议的核心是什么?如何引出"over everything"和"everythingover"。 + +TCP/IP协议的核心是TCP、UDP和IP协议 + +分层次画出具体的协议来表示TCP/IP协议族,它的特点是上下两头大而中间小:应用层和网络接口都有很多协议,而中间的IP层很小,上层的各种协议都向下汇聚到一个IP协议中。这种很像沙漏计时器形状的TCP/IP协议族表明:TCP/IP协议可以为各种各样的应用提供服务(everything over ip) 同时TCP/IP协议也允许IP协议在各种各样的网络构成的互联网上运行(IP over everything)。 + +#### 22.谈一谈 ARP 地址解析协议的工作原理。 + +网络层以上的协议用IP地址来标识网络接口,但以太数据帧传输时,以物理地址来标识网络接口。因此我们需要进行IP地址与物理地址之间的转化。 + +对于IPv4来说,我们使用ARP地址解析协议来完成IP地址与物理地址的转化(IPv6使用邻居发现协议进行IP地址与物理地址的转化,它包含在ICMPv6中)。 + +ARP协议提供了网络层地址(IP地址)到物理地址(mac地址)之间的动态映射。ARP协议 是地址解析的通用协议。 + +ARP地址解析实现原理 + +每个主机都会在自己的 ARP 缓冲区中建立一个 ARP 列表,以表示 IP 地址和 MAC 地址之间的对应关系。 +主机(网络接口)新加入网络时(也可能只是mac地址发生变化,接口重启等), 会发送免费ARP报文把自己IP地址与Mac地址的映射关系广播给其他主机。 +网络上的主机接收到免费ARP报文时,会更新自己的ARP缓冲区。将新的映射关系更新到自己的ARP表中。 +某个主机需要发送报文时,首先检查 ARP 列表中是否有对应 IP 地址的目的主机的 MAC 地址,如果有,则直接发送数据,如果没有,就向本网段的所有主机发送 ARP 数据包,该数据包包括的内容有:源主机 IP 地址,源主机 MAC 地址,目的主机的 IP 地址等。 +当本网络的所有主机收到该 ARP 数据包时: + +(A)首先检查数据包中的 IP 地址是否是自己的 IP 地址,如果不是,则忽略该数据包。 + +(B)如果是,则首先从数据包中取出源主机的 IP 和 MAC 地址写入到 ARP 列表中,如果已经存在,则覆盖。 + + (C) 然后将自己的 MAC 地址写入 ARP 响应包中,告诉源主机自己是它想要找的 MAC 地址。 + + 6.源主机收到 ARP 响应包后。将目的主机的 IP 和 MAC 地址写入 ARP 列表,并利用此信息发送数据。如果源主机一直没有收到 ARP 响应数据包,表示 ARP 查询失败。 + +#### 23.谈一谈 DNS 域名系统的工作原理 + +DNS(Domain Name System,域名系统) + +因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。通过主机名,最终得到该主机名对应的IP地址的过程叫做域名解析(或主机名解析) + +每个IP地址都可以有一个主机名,主机名由一个或多个字符串组成,字符串之间用小数点隔开。有了主机名,就不要死记硬背每台IP设备的IP地址,只要记住相对直观有意义的主机名就行了。这就是DNS协议所要完成的功能。 + +主机名到IP地址的映射有两种方式:DNS + +1)静态映射,每台设备上都配置主机到IP地址的映射,各设备独立维护自己的映射表,而且只供本设备使用; + +2)动态映射,建立一套域名解析系统(DNS),只在专门的DNS服务器上配置主机到IP地址的映射,网络上需要使用主机名通信的设备,首先需要到DNS服务器查询主机所对应的IP地址。 + +通过主机名,最终得到该主机名对应的IP地址的过程叫做域名解析(或主机名解析)。在解析域名时,可以首先采用静态域名解析的方法,如果静态域名解析不成功,再采用动态域名解析的方法。可以将一些常用的域名放入静态域名解析表中,这样可以大大提高域名解析效率。 + + 比如我们游览一个网站的时候,打开一个网址, 这个时候 我们的电脑会首先发送一个数据包个 DNS系统 ,DNS系统回应一个数据包给我们,然后再转到我们游览的网站! 那个数据包里面就包含这个 我们访问的这个网站,然后返回来的数据包是 解析成了IP地址,然后就能通过TCP/IP协议通信了! + + 再比如我们发送baidu.com到DNS服务器,DNS服务器发给我们百度的服务器的IP,如果我们直接输入IP就绕开了解析这一个步骤 + +#### 24.何为网桥?防火墙的端口防护是指什么? + +网桥:网桥(Bridge)像一个聪明的中继器。中继器从一个网络电缆里接收信号, 放大它们,将其送入下一个电缆。相比较而言,网桥对从关卡上传下来的信息更敏锐一些。网桥是一种对帧进行转发的技术,根据MAC分区块,可隔离碰撞。网桥将网络的多个网段在数据链路层连接起来。 + +网桥也叫桥接器,是连接两个局域网的一种存储/转发设备,它能将一个大的LAN分割为多个网段,或将两个以上的LAN互联为一个逻辑LAN,使LAN上的所有用户都可访问服务器。 + +防火墙端口防护:指通过对防火墙的端口开关的设置,关闭一些非必需端口,达到一定安全防护目的行为。 + + + +#### 参考 + +https://blog.csdn.net/lingess/article/details/98479704 \ No newline at end of file From fae4055eafc5b7e131c8e59a110eab32ffac1f95 Mon Sep 17 00:00:00 2001 From: javaxiaokashow <64669886+javaxiaokashow@users.noreply.github.com> Date: Thu, 29 Jul 2021 13:01:19 +0800 Subject: [PATCH 19/19] Update Readme.md --- Readme.md | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/Readme.md b/Readme.md index 1b36cf4..59686b2 100644 --- a/Readme.md +++ b/Readme.md @@ -1,31 +1,20 @@ -最近很多童鞋在到处搜集面试题,但是面试题都比较零散,所以博主整理了一下,前4个专题多来源于互联网,从第4部分往后,基本是小编从阅读电子书以及专栏学习所得。目前有17个专题。全文阅读时间可能几个小时,但是整理和创作要花的时间就要比阅读的时间多出至少五十倍,目前全文大概15万字左右,166页。 - -小编后面阅读电子书以及专栏等所得,请不要直接发到互联网申请原创,如果要发请注明来源。小编会持续的更新维护。大家可以加我微信进入群内,vx:baiseyumaoxx。一旦更新,我会通知大家。 - -并且还会整理成一个题库,本来想将md文件上传到github上,但由于文件太大有的都无法显示所以直接整理成一个PDF,供大家学习。 - -好了,来了,本手册目前为第一版,内容有以下板块: - -- [**Java基础**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Java基础.md) -- [**Java集合**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/java集合.md) -- [**异常&反射**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/%20异常%26反射.md) -- [**IO&NIO**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/IO%26NIO.md) -- [**多线程**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/多线程.md) -- [**JVM**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/JVM.md) -- [**Linux**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Linux.md) -- [**MySql**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/MySql.md) -- [**Spring**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Spring.md) -- [**Mybatis**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Mybatis.md) -- [**Nginx**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Nginx.md) -- [**Redis**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Redis.md) -- [**Dubbo**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Dubbo.md) -- [**SpringBoot**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/SpringBoot.md) -- [**Kafka**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/Kafka.md) -- [**Spring Cloud**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/SpringCloud.md) -- [**简历**](https://github.com/javaxiaokashow/JavaFaceNotes/blob/master/Docs/简历.md) +最近很多小伙伴问我要面试题,但面试题大多都比较零散,所以博主整理了一下,有一部分是从互联网收集整理所得,还有一部分是小咖自己从专栏、书籍学习所得。 +目前有31个专题,277页。全文阅读时间可能几个小时,但是整理和创作要花的时间就要比阅读的时间多出至少十倍。 + +小编后面阅读书籍以及专栏等所得的部分,请不要直接发到互联网申请原创,如果要发请注明来源。 +小编会持续的更新维护。大家可以加我微信进入群内,vx:baiseyumaoxx。一旦更新,我会通知大家。 + +手册是第二个版本了已经,原来是17个专题,新增了14个专题,希望对大家有用。 + +目前有如下专题: + +Java基础、Java集合、异常&反射、IO&NIO、Java多线程、JVM、Linux、MySql、Spring、Mybatis、Nginx、Redis、Dubbo、SpringBoot、Kafka、Spring Cloud、简历 + +Netty、计算机基础、MongoDB、JSP、Servlet、Elasticsearch、Web安全、Tomcat、Zookeeper、Maven、Git、RabbitMQ、Java8、JVM + 具体大家需要看的部分点击目录即可查看。pdf给大家准备好了。小编会持续更新... -扫描二维码回复:“面试”即可获取pdf +扫描二维码回复:“面试2”即可获取pdf -![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png) \ No newline at end of file +![WechatIMG360](https://gitee.com/yizhibuerdai/Imagetools/raw/master/images/common1.png)