1.为什么要设计封装类,Integer 和int有什么区别?
1:java是面向对象的编程语言,我们一般都是操作对象,但是基本数据类型与对象是相违背的;而且在一些集合中(arraylist hashset hashmap hashtable)是只能存储object类型数据,基本类型是无法使用的。所以我们要设计封装类。
2:integer和int的区别:
(1):integer是引用数据类型,int是基本数据类型;他们通过拆箱和装箱进行相互转换
(2):integer是初始化的值是null(是一个对象),int的初始化是0
(3):integer内部封装了很多方法和属性,操作起来更加方便灵活
(4):integer存储在堆内存空间(是一个实例化对象),int存储在栈空间
(5):Integer的缓存机制,为了提高效率,integer会在-128-127之间进行数据的缓存,如果超出这个范围,integer会重新创建一个新的对象。
2.string str = new String("hello")之后,到底创建了几个对象
堆内存主要存放对象和数组
栈内存主要存放对象的引用变量和基本类型的变量
1:new string 一定会在堆上创建一个对象,如果常量池中存在‘hello’字符串,则将引用指向str,此时就只创建一个对象即可,如果没有就创建该对象,然后指向str,此时创建两个对象。
3.String、StringBuffer、StringBuilder 的区别是什么?
1:string finall修饰的,是不可变的;stringbuffer、stringbuilder是可变的
2:Stringbuffer 和stringbuilder 提供了16字符的缓冲区,在频繁的添加字符时性能更好
3:Stringbuffer是线程安全的,适用于多线程的情况
4:stringbuilder是线程不安全的,但是效率比stringbuffer更高,试用与单线程,频繁进行字符拼接。
4.为什么重写equals0方法就一定要重写 hashCode0方法?
1:equals方法是object类的方法,其实现是比较两个对象的内存地址是否相同。
2:hashCode方法本质上是一个hash函数,它会将不同对象生成不同的hashcode值,相同对象生成相同的hashcode。
3:如果我们值重写了equals方法,就会出现两个相同的对象hashcode值是不同的,如果此时我们将对象存储在散列集合,如hashmap hashset,散列集合是按照hashcode来确定存储位置的,这时就会出现相同对象存储在不同的位置,这就违反了散列集合的规则,这个对象就无法存储在散列集合中。
4:所以为了避免出现无法预料的错误,我们在重写equals方法是一定要重写hashcode方法。
5.什么是受检异常和非受检异常?
1:异常的父类是throwable,下面分为两类error和exception
2:error 和runtimeexception都属于非受检查异常,比如说空指针异常,数组越界异常,io异常等;除了以上两种都是受检查异常,此时我们需要手动处理常见的是try catch 或者 throws 。
6.什么是深克隆和浅克隆
1:拷贝之后两者之间是否有关联。
2:改变一个的值是否会影响到另一个数值变化。、
3:浅克隆是将原型对象中成员变量为值类型的属性都复制给克隆对象,把原型对象中成员变量为引用类型的引用地址也复制给克隆对象。
4:深克隆是将原型对象中成员变量为值类型的属性都复制给克隆对象,把原型对象中成员变量为引用类型的值和引用地址都复制给克隆对象。
5:深克隆与浅克隆最大的区别就在与引用数据类型的复制上,浅克隆复制的是引用地址,所以当原对象数据修改后,克隆对象的值也会跟随修改,深克隆修改原对象后,克隆对象的值是不会改变的。
6:beanutils中的copyProperties和数组的arrays.copy()都是浅克隆;常见的深克隆的方法,通过字节流进行对象的克隆。
7.强引用、软引用、弱引用、虚引用
1:强应用是只要引用存在就不会被回收,常见的是 Object obj = new Object();
2:软引用是在内存不足的时候会被垃圾回收器回收。
3:弱引用会存活在下次gc之前。
4:虚引用是最弱的引用关系,唯一的目的就是对象被收集器回收时收到一个系统通知。
5:强、软、弱、虚引用一次减弱。
8.一个空的Object对象占多大空间
1:对象存储结构分为对象头、实例数据、对齐填充
2:以64位系统:对象头:markword占8字节;开启压缩指针,类元指针占4字节,+对齐填充4个字节总共16字节;未开启压缩指针占8字节,总共16字节。
9.什么是序列化和反序列化
1:序列化是将对象转化为字节流的过程用于后台向前台传送数据(@jsonformat)
2:反序列化是将字节流转为对象的过程(@timeformat)
10.finally块一定会执行吗?
1:finally块在通常情况下,无论是否出发异常都会执行,常见的应用是io流等资源的关闭,日志打印等操作,但也有无法执行的情况
2:比如说没有进入try块就抛出异常导致程序终止;在try或者cache块中执行System.exit(0)语句导致jvm退出。
11.什么事内存泄漏,内存溢出
1:内存泄漏是指,在申请内存是,无法释放已申请的内存空间,内存泄漏会导致内存溢出。
2:内存溢出指,申请内存时,没有足够的内存供申请者使用
3:常见的内存泄漏的情况1:外部类引用内部类2:资源未关闭
12.重载和重写的区别
1:重载:发生在一个类中,方法名必须相同,参数类型不同,顺序不同、个数不同,方法返回值和访问修饰符可以不同(不构成重载,编译时报错)
2:重写:发生在父子类中,方法名必须相同,参数值和参数的类型必须相同,
修饰符范围必须大于等于父类,抛出异常的范围也要小于等于父类,返回值返回小于等于父类,finally修饰的方法不能被重写。
13.接口和抽象类的区别
1:抽象类可以存在普通成员函数,而接口只能存在public abstract修饰的方法(抽象方法)
2:抽象类的成员变量可以是各种类型的,而接口只能是public static finally修饰的变量
3:抽象类只能单继承,接口可以多实现
4:接口的设计目的是:约束行为,也就是你必须实现其中的方法,但是不约束你怎么去实现
5:抽像类的设计目的是为了代码的复用,他实现子类共同的方法,子类中不同的方法又子类去实现。但抽象类设计的代价太大(java是单继承)
6:抽象类是is a 的关系(子类都共有父类的一些具体方法);接口是 Like 的关系(飞机、小鸟都有飞的功能但实现原理不一样)
7:jdk1.8之后支持static、abstract方法可以不重写
14.List和set
1:list是有序的可以重复
2:set是无须的不可以重复
15.ArrayList和LinkedList的区别
1:Arraylist是基于动态数组实现,支持随机访问在内存地址中是一块固定大小的空间;查询速度较快,自动扩容机制:扩容时新建一个数组然后将原数组数据复制到新数组中,数组在没有指定初始化大小时默认为10,扩容时大约会到1.5倍。当新增数据数据在数组中间时,会将后面的数据依次移动然后再新增,此时效率是比较低的,因此插入时速度较慢,但是使用尾插法的时候效率是比较高的,甚至会超过linkedlist,(linkedlist会生成node对象)。
2:linkedList底层是双向链表实现的,只能顺序访问,插入速度较快,查询速度较慢,linkedlist优先使用迭代器访问,使用for循环时会遍历整个链表。linkedlist可以作为栈和队列使用。
16.hashmap与Hashtable的区别
区别:
1:hashmap是线程不安全的,(1.7使用的是基于数组+链表实现的1.8是基于数组+链表+红黑树实现的)
2:hashtable(过时接口,现在线程安全可以使用ConcurrentHashMap)是线程安全的
底层实现:
3:hashmap:在jdk1.8链表长度超过8数组长度超过64的时候链表会转换为红黑树;元素存储是Node对象,存储时先对key进行hash然后对hash值进行二次hash然后对数组长度进行取模得到对应的下标,如果没有产生hash冲突则创建node对象存入数组,否则,执行equals方法如果相同则取代原来的数据,否则判断链表高度,插入链表当高度超过8,并且数组长度超过64则将链表转换为红黑树,当长度低于6则转换回链表。key为null存在下标0的位置。
4:hashmap的默认初始值是16:扩容因子是0.75 最大长度是2的30次幂
数组扩容:
5:当数组的长度超过16*0.75的时候会扩容到原来的两倍32
hashtable是对hashmap的简单封装即在方法上加Synchronized
17.hashmap如何解决hash冲突
四种解决hash冲突的方法
1:开放地址法:一旦产生hash冲突然后按照一定的次序在hash表中查找空闲的位置然后插入数据
2:链式寻址法:将发生冲突的数据按照链表的形式插入(hashmap)
3:在hash法:继续进行hash直到不再发生hash冲突为止
4:建立公共溢出区:将发生hash表分为基础表和溢出表,hash冲突的数据都放在冲突表里
18.hashmap为什么会出现死循环
1:在多线程情况下,jdk1.7的情况下,当两个线程A、B同时指向链表abc的a并且next指向b时,此时A线程休眠,B线程执行扩容,当B线程执行完后,A线程仍然指向b,B线程指向a,此时就出现了死循环的问题
2:在jdk1.8hashmap使用尾插法后此问题就解决了
3:头插法,插入循序会颠倒,尾插法会保持原来的循序
19.线程生命周期及状态
1:五个生命周期:新建状态、就绪状态、运行状态、阻塞状态、结束状态
2:阻塞状态分为三种
(1):等待阻塞:线程执行wait方法,此时会释放所有资源,jvm将线程放入”等待池“中,没有其他线程调用notify或者notifyall方法是不会唤醒的,wait是object的方法
(2):同步阻塞:运行的线程在获取对象的同步锁时,若该锁被别的线程占用,则jvm会将线程放入“锁池中”
(3):其他阻塞:运行的线程执行sleep或join方法,或者发出i/o操作是,jvm会把线程设置为阻塞状态,当sleep超时,或者join其他线程超时/终止,或者i/o操作结束时,线程会转换为就绪状态。sleep方法是thread中的方法
3:新建状态:创建一个先线程
就绪状态:线程创建对象后,调用对象的start方法,线程位于可运行的线程池中,线程变得可运行,等待cpu的使用权
运行状态:线程获取cpu执行代码
阻塞状态:线程由于某种原因放弃了cpu的执行权,只有当线程重新转为就绪状态,才可以继续争取cpu的使用权,转为运行状态
结束状态:当线程结束或者由于异常退出了run方法,该线程结束生命周期
20:sleep、wait、join、yield
1:锁池:放哪些需要竞争同步锁,又没拿到同步锁的线程
2:等待池:线程调用wait方法进入等发状态,不会进行同步锁的竞争,只有调用notify或者notifyall方法才会开始竞争同步锁
执行notify会有一个线程进入锁池中
执行notifyall方法会将所有的线程加入锁池中
3:sleep():是thread类中的方法,sleep()执行是会让出cpu资源,可以使优先级较低的线程执行,不会释放锁资源,当等待时间结束后,会进入就绪状态,然后进入运行状态
wait():必须线程拿到锁后才能调用,在synchronized代码块中执行,会进入阻塞状态并且释放锁资源,只有调用notify或者notifyall方法后才会执行。
join():等待调用的进程结束后,才能继续执行,用于需要别的进程拿到执行结果后,才能执行的情况。
yield():和sleep一样都是暂停执行当前线程,不会释放锁资源,跟sleep不一样的是不会进入阻塞状态,会进入就绪状态,将竞争下一次cpu的执行权,只能对同优先级或者高优先级的进程有执行机会。
4:sleep和wait的区别
sleep是thread的静态本地方法,wait是object的本地方法
sleep不糊释放lock资源,wait会释放锁资源
sleep方法不依赖synchronized,但是wait需要依赖synochronized关键字
21:对线程安全的理解
1:在多线程的状态下,调用对象的行为都可以拿到正确的结果
2:造成线程不安全的原因主要是堆内存是共享的,可以被所有的线程访问
3:堆所存在的内存区域的唯一目的就是存放对象实例的,几乎所有的对象实例以及数组都是在这里分配内存。
4:栈是内个线程独有的,保存其运行状态和局部自动变量的。栈是内存安全的。
22:Thread和runnable
1:thread是类,runnable是接口,java支持单继承,多实现
2:thread实现了runnable接口,提供了更多的扩展
23:创建线程的方式
1:继承thread类重写run方法,调用start方法
2:实现runnable方法,重写run方法,代用start方法
3:实现callable接口,重写call方法,使用futuretask类包装callable对象,使用futuretask对象作为thread度喜爱那个的参数创建线程,调用futuretask对象的get()方法获取返回值
4:使用线程池创建线程
24:你对守护线程的理解
1:守护线程是所有jvm其他线程的保姆,当线程结束守护线程就会结束,我们不需要关系守护线程何时结束
25:并发并行和串行
串行可以理解为所有选手都在一条赛道上跑步,只能按顺序跑步;并行就是多个选手在各自的赛道上跑步,互不影响;并发可以理解为多个人竞争一条赛道;由于cpu是按时间片执行的,当线程执行完这个时间片后就需要让出cpu重新竞争cpu执行
26:并发编程的三大特性
1:原子性:多线程操作下临界资源(同一时间只能被一个线程或者进程使用的资源)时,得到的结果和预期的结果是一样的
cas(company and Swap)java Atomic包提供了相关操作,只能对一个变量的操作是原子性的
如何保证原子性
1:Synchronized 2:cas 3:lock 4:threadlocal
2:可见性:
3:有序性:
27:你对线程池的理解及线程池的参数
1:由于创建线程和销毁线线程是消耗资源的操作,所以采用了线程池进行线程处理,线程池可以提供对线程的统一管理
参数:
核心线程数:是线程池的常住线程,线程池存在就不会被销毁
最大线程数:线程池所能创建的最大线程数量
超时时间:除核心线程外其他线程空闲时间超过后就会销毁
工作队列:当超过核心线程数后线程在队列中等待
线程工厂:创建线程的方式
handler:拒绝策略,线程超过最大线程后执行的策略
28:你对spring的理解
1:spring是一个容器,他主要提供了对javabean的管理(spring应用上下文)
2:spring是一个轻量级的框架,提供了web、jdbc等
3: spring两大新概念:ioc aop
4:ioc是一种控制反转的概念,spring通过xml文件或者注解实现注入
5:spring是一个大的bean工厂,通管对象的创建、初始化、销毁
spring是轻量级的j2ee框架,他是一个容器框架,用来装javabean(java对象),也是一个中间层框架(万能胶)可以起到一个粘合作用,比如说mybatis
29:你对ioc的理解
1:ico的意思是控制反转,意思是自身对象中的内置对象的控制反转
2:ioc以来DI实现,也就是依赖注入,我们通过xml或者注解的方式实现DI
3:bean工厂、DI共同实现了控制反转思想
30:aop的理解
1:面向切面编程,实现解耦,和代码复用,应用场景:日志打印
31:beanfactory和applicationcontext的区别
1:applicationcontext是beanfactory的子接口
2:beanfactory是延迟加载,app是在编译起就会加载所有的
32:面试常见问题:
1:延迟双删
32:mysql事务
四大特性acid
原子性 一致性 隔离性 永久性
原子性:要么都成功要么都失败
一致性:事务前后数据库的数据是一样的
隔离行:事务并发执行时,感觉不到另一个事务也在执行
永久性:事务执行成功后,数据库的数据是永久改变的
脏读、不可重复读、幻读
脏读:是指事务A读取到了事务B未提交的数据
不可重复读:在一个事务中两次读取取到的数据是不一样的
幻读:是指事务A在两次读取中事务B进行了提交,事务A读取数据是一样的,但是数据库里的数据时不一样的,此时执行插入操作时不成功的
隔离级别
1:serializable(在一个事务未提交时,另一个事务会等待,不会出现问题)
2:repeatable read(可重复读,在一个事务中多次读取的数据是一样的,mysql默认,会出现幻读情况)
3:read committed(可以看到别的事务提交后的数据,会导致不可重复读)
4:read uncommitted(读,不提交,可以看到别的事务未提交的数据,会出现脏读)
33.spring事务的7种传播
required、supports、mandatory、requires_new、nested
常用的三种required:当事务已经存在时,会加入到事务中,如果当前不存在事务就创建一个事务
requirs_new:会创建一个新的事务,原事务会挂起,相当于创建一个子事务,外面的事务回滚,不影响里面的事务
nested:会创建一个嵌套事务,当外面的事务回滚时,嵌套事务也会回滚
34:spring的bean实例化过程
整体流程:将读取到的xml配置加载bean标签信息到beanfactory的BeanDefinitionMap中,此时设置bean创建的参数,然后将创建的bena对象加到singletonObjects(单例池)中
通过<bena>标签的形式设置bean参数,其中中要的参数有scope(singleton,prototype )为bean的作用范围,单例和原型,原型不会注入到单例池中,getbean的时候会创建实例,使用结束会注销。
<bean>标签注入属性的方式1:<property>标签注入参数2:<constructor-arg>注入构造方法需要的参数
bean实例化的两种方式:
构造方法实例化:通过构造方法实例化,默认是无参构造
工厂方式实例化:通过自定义的工厂方法进行实例化
工厂方法分为两种:1:静态工厂方法实例化:2:实例工厂方法实例化
工厂使用到的标签是<factory_bean> 和<factory_method>
35:spring的生命周期
1:beanfactoryPostProcesser(bean工厂后处理器):实在bean标签加载到beanDefinitionMap的时候执行的用于动态设置beanDefinitionMap的参数
2:beanPostProcesser(bean后处理器):在bean实例化过程中调用
3:bean的循环依赖,对象AB相互注入
解决方式:三级缓存 1:singletonObjects 2:earlySingletonObjects 3:singletonFactories
解决流程:对象初始化将对象加入到三级缓存中,当注入别的对象时从123级 缓存取数据,如果没有就创建创建改对象,进入这个对象的创建过程,此时如果需要注入原来的对象则从三级缓存中取到,此时该对象注入到一级缓存中,删除23级缓存,原来的对象注入到二级缓存删除三级缓存,然后继续原对象的创建,当都创建完之后注入到一级缓存中。
4:bean的生命周期
36:你对spring的理解
spring是一个轻量型的框架,通过ioc、aop的功能实现了一个类似于万能胶的作用,可以可方便的融合多个框架
37:springIoc的工作流程
ioc是控制反转的意思,将对象的创建控制权交给spring容器
1:注册bean,可以通过bean标签或者注解的方式将类注入到beanFactory的beanDefinitionMap中
2:依赖注入:可以用过<property>标签的方式将属性注入到对象中,生成对象
38:springBean的理解
1:bean是spring的中最基本的组成单元
2:bean是一个或着多个对象
39:spring bean的定义有哪些内容
spring bean创建时需要那些参数
1:scop(singleton、prototype,原型)单例会创建一个对象,原型是使用时创建,用完销毁 2:class(对象的全限定名)3:id(bean的唯一命名,不可重复)4:layze_init(是否懒加载)
5:properties(参数,bean的属性,成员变量)6:constructor-arg(有参构造方法传递参数)
40:spring中bean的作用域
1:singleton(单例)
2:propotype(原型)
41:bean的生命周期(bean的实例化阶段)
1:准备阶段:生成beanDefinition阶段;读取bean的注册信息,然后调用beanfactoryPostPropercess方法进行bean注册信息的修改。
2:实例化加入的singtonObjects之前,1:初始化对象 2:属性注入 3:执行aware注入参数4:执行beanPostProcessor(bean后处理器)的befor()方法 5:看是否实现了initializingBean接口,执行afterPropertiesSet()方法6:init()方法 7:执行后处理器中的after()方法
42:spring如何解决循环依赖问题(三级缓存)
1:singtonObjects(一级缓存)
2:earlySingletonObjects(二级缓存)
3:singtonFactories(三级缓存)
4:初始化对象创建实例(未完成)放入三级缓存中,然后执行属性注入,当注入另一个bean时,会在123级缓存中查找时候有,如果没有创建实例到三级缓存中,此时如果注入了刚开始的bean则在三级缓存中取出注入到该实例,然后该实例会放入到二级缓存中,删除三级缓存中的实例,继续执行完该实例后放入一级缓存,删除23集缓存,继续开始的实例创建,放入一级缓存,清楚23级缓存数据
43:spring以来注入的方式
1:构造器注入(通过有参构造的方法,将对象传递过来)
2:通过set方法注入(设置参数,添加set的方法)
3:注解方式(autowried)
44:spring中用到了那些设计模式
1:单例模式:
饿汉式和懒汉式
2:适配器
3:工厂模式
4:装饰器模式
5:策略模式
6:代理模式
7:模板方式模式
8:观察者模式
45:spring事务失效的场景
1:同一个类中的普通方法A调用加了事务的B方法时。
2:方法没有使用public修饰
3:数据库不支持事务
46:springaop思想
1:被调用的类不再直接生成该类对象然后调用,而是创建代理类,通过代理类调用该类的方法,jdk通过实现接口的方式,调用被代理类的方法,代理类的方法名与参数类型与被代理类一样
2:动态代理与静态代理的区别是,动态代理是运行是决定代理对象的
47:springaop的参数
1:target:目标对象(被增强的方法所在的对象)
2:proxy:增强对象(目标对象增强后形成的对象)
3:jijonpoint:连接点(可以被增强的方法)
4:pointcut:切入点(被增强的方法)
5:advice:通知(增强部分)
6:aspect:切面(增强点和通知的组合)
7:weaving:织入(将通知和切入点动态组合的过程)
48:反射获取字节码文件(class)的方式
1:对象.getclass
2:类.class
3:class.forName(""),传入类的全限定名称
49:反射的作用
获取类中的所有信息
反射中获取属性的方法
construct:构造方法
parameter:参数
field:成员变量
modifoers:修饰符
Method:方式
Declared:私有的
50:动态代理
java提供的动态代理api是什么?
java 反射包(reflxy)包下的Proxy
动态代理newProxyInstance的参数有哪些
1:通知类的类加载器(代理的接口的classloader())
2:通知类的接口(代理的接口)
3:增强的调用处理程序(invocation)
invocationHandler中invoke()方法被谁调用了,参数是什么?
1:proxy:代理的类的对象
2:args:参数
51:五种常见的运行时异常
1:数组下标越界异常
2:空指针异常
3:ClassCastException(类型转换异常)
4:outofmemoryexception(内存不足异常)
5:NumberFormatException(数据格式异常)
52:aspectj面向切面编程
1:<aop>标签注册aop
2:切入点表达式 expression(execution :"方法路径"):就是你要增强的方法
*:代表所有 .. 代表本包下及子包下多有
3:通知的五种类型
1:beforeAdvice:前置通知
2:after_return_Advice:后置通知
3:around:环绕通知,如果出现异常,后置通知不在执行
4:afterThrowingAdvice: 抛出异常后通知
5:afterAdvice:最终通知,无论异常发生不发生都执行
53:动态代理生成代理对象的两种方式
1:使用springaop生成,aspectj,根据接口创建接口的实现类成为代理对象
2:使用cglib:需要创建子类为代理对象,所以代理类不能时finally修饰的
54: redis的物种数据结构
1:string
2:list(双向链表)
3:hash(hash)
4:set(底层是hash)
5:Zset(带权重的set)
55:redis持久化
1:rdb:快照模式,会线程阻塞,不宜频繁操作
2:aof:存储新增修改删除操作,具有压缩功能,适用于关键数据存储,不允许数据丢失或丢失极少时间段;缺点是文件较大,恢复较慢
56:redis的主从复制
1:主服务(master) 从服务(slave)
2:三个阶段:建立连接 数据同步阶段 命令传播阶段
3:slave连接master ,master生成缓冲区,使用bgsave创建一个子线程进行rdb;然后将rdb数据发送给slave(全量复制);slave接受rdb文件清空数据,写入数据;执行完毕后,告诉mast执行完毕了,mast将缓冲区中的操作指令发送给slave,slave执行操作更新数据(部分复制)
4:命令传播阶段,mast和slave都会存储当前指令的索引,slave告诉mast上次数据的位置,mast发送之后的指令给slave,当出现网络延迟现象后,由于mast缓冲区是有大小的,slave无法拿到网络延迟时的数据,此时就要重新执行rdb操作
57:redisrdb三种方式
1:save 2:bgsave 3 save 配置
1:阻塞客户端指令,无法读写数据,没有额外的资源消耗
2:bgsave,创建新进程,不影响客户端指令
3:本质时bgsave
58:redis aof的三种策略
aof具有重写操作,在保证结果不出错的情况下,将多个指令合并为较少的指令
1:always 2:everysec 3:no
1:每个指令都存储,性能较差,数据不会丢失
2:每秒执行,数据最多丢失一秒内的
3:系统执行,不受控制
59:nacos的服务注册与发现
1:什么时服务注册,时服务将服务信息发送给服务治理者
2:服务发现,是指服务从注册中心获取到服务列表(一次请求,有更新,服务治理者会发送新的给你)
3:
60:nacos的配置中心
1:如何区分不同运行环境,date id的命名格式有严格要求,为包名➕运行环境(dev)
2:共享配置,date id 为报名则在相同服务间是共享的
3:共享服务,date is 名字随便起,需要在配置文件配置名称
61:feign如何进行通信
1:使用@feignclint注解,方法上加上请求路径
2:feign内置了ribbon实现负载均衡
62:es倒排索引
1:根据关键词建立索引,快速定位到包含关键词的文档
63:hashmap、hashset插入相同的数据
1:hashmap会覆盖
2:hashset不会覆盖