Java面试题
01 数据库是怎么优化的
答:
1.数据库I/O方面硬件性能
- 扩大虚拟内存,并保证有足够可以扩充的空间
- 把数据库服务器上的不必要服务关闭掉
- 把SQL数据库服务器的吞吐量调为最大
2.调整数据库
- 若对该表的查询频率比较高,则建立索引。
- 分区(如MySQL,按时间分区)
- 尽量使用固定长度字段和限制字段长度(如 varchar(10))优势:
- 降低物理存储空间
- 提高数据库处理速度
- 附带校验数据库是否合法功能
3.使用存储过程
应用程序的实现过程中,能够采用存储过程实现的对数据库的操作尽量通过存储过程来实现。
因为存储过程是存放在数据库服务器上的一次性被设计、编码、测试,并被再次使用,需要执行该任务的应用可以简单地执行存储过程,并且只返回结果集或者数值。
这样不仅可以使程序模块化,同时提高响应速度,减少网络流量,并且通过输入参数接受输入,使得在应用中完成逻辑的一致性实现。
4.SQL语句方面
比如使用连接查询代替子查询、使用联合(UNION)来代替手动创建的临时表、使用索引。
02 你使用SpringBoot的时候各种配置文件是怎么引入的
答:
- 如过时yml配置中的属性,@ConfigurationProperties注解设置与yml配置文件中对应的属性名,吧文件中的值通过自动注入,获取值。
- 配置类:我们需要进行手动创建一个类,然后在这个类中使用@Configuration注解:指定当前的类是配置类,配置类就相当于配置文件。
- @Bean注解将一些类或对象交由Spring容器管理
- @Value注入配置文件的属性值
03 你看底层代码吗?
答:
看过Spring自动配置的相关源码,
1.在spring-boot-autoconfigure包下保存大量的自动配置类。
2.类名都保存 META-INFO/spring.factotiries文件下
3.自动配置类生效需要一定条件,如@ConditionalOnClass,引入某些类时(导入了对应的框架依赖)
Spring Boot应用的启动入口是@SpringBootApplication注解标注类中的main()方法,在@SpringBootApplication 中有三个重要的注解.
@SpringBootConfiguration表明该类为配置类。
@EnableAutoConfiguration表示自动配置功能。
@ComponentScan 包的扫描
@EnableAutoConfiguration --> AutoConfigurationImportSelector --> loadFactoryNames —> classLoader.getResources(“META-INF/spring.factories”)
其中classLoader.getResources通过反射的机制可以帮我们找到相关的配置类。
看过线程池相关的源码
ThreadPoolExecutor实现的顶层接口是Executor,顶层接口Executor提供了一种思想:将任务提交和任务执行进行解耦。用户无需关注如何创建线程,如何调度线程来执行任务,用户只需提供Runnable对象,将任务的运行逻辑提交到执行器(Executor)中,由Executor框架完成线程的调配和任务的执行部分。
在线程池中拥有一套任务调度的机制。
1.其中包含核心线程和工作线程数,核心线程可以理解为一个公司的核心员工,当核心线程的数量大于2.工作线程的时候,就启动一个线程来执行新提交的任务。
2.如果工作线程数大于等于核心线程数,并且线程池的阻塞队列未满,则将任务加到该阻塞队列中。
3.如果工作线程数大于等于核心线程数,工作线程数小于等于最大线程总数,并且阻塞队列已经满,则启动一个非核心线程来执行新提交的任务。
4.如果工作线程数大于等于最大线程总数,并且阻塞队列已经满了,则根据拒绝策略来处理该任务。
4. 说说你做过的项目和你主要负责的模块。
答:
我做过一个xx在线教育系统,这是一个类似于中国慕课网的一个在线学习网站,该项目是一个基于Alibaba的分布式体系的。用户可以根据自己的需要在网站上搜索自己想要学习的课程,其中部分课程是收费课程,用户需要通过vx支付后才可以观看课程视频。其中我负责的模块是一个用户的多条件搜索模块和一个单点登录的模块。其中在多条件搜索服务中我选择使用Elasticsearch来实现搜索功能。因为商品数量庞大,并且搜索条件十分的灵活多变,因此传统的数据库搜索的方式会显得不太合适,并且es是基于倒排索引的方式来搜索数据,就是在查询数据的时候,通过分词器将查询的数据分词处理,然后通过分词来查找索引的value,然后通过value找到key。这样搜索数据更快,不需要对整个数据库进行全文搜索。
在搜索模块中,首先我将课程相关的数据在Mysql中通过关联查询查询到,然后在搜索服务中通过feign来调用课程服务中的所有全部课程。然后在es中将数据初始化到索引中,导入的思想是首先我们先判断es中是否存在该索引,如果存在则删除之前的索引,然后导入现在查询到的数据。如果不存在则直接导入数据。这样我们Mysql中的课程相关的全部信息就导入到es中了。在多条件查询的过程中,我们首先将前台用户输入的多条件查询信息封装到HashMap集合中,然后生成布尔查询构造器,将查询对象封装到过滤器链中,将排序条件封装到属性排序构造器中。将查询的当前页 和 页数也封装进查询构造器。最后执行查询请求,得到响应数据。创建ES索引包装对象 id为索引id data为索引的数据。然后通过jackson将我们查询到的数据解析为javaBean对象集合。最后封装到我们的索引包装类中。其中我们还是用了RabbitMQ来实现了ES和Mysql数据的同步。其中实现的步骤为我们生成了一个topic主题类型的交换机,然后分别绑定了两条队列在该交换机上,两条队列分别设置对应的key,一个队列用于增改,一个队列用于删除。当我们的后台对Mysql的数据进行了增改或删除时就会向对应key的队列中发送消息,消息内容就是操作的数据,然后我们的Es服务方会对我们的MQ的两条队列进行实时的监听。当在对应的队列中收到消息,就会执行操作,对我们的es执行增改或查询的操作。这样我们就实现了ES和Mysql的数据同步。
在单点登录模块中,我选择的是使用JWT来对我们用户的登录信息进行加密生成token,其中我们还使用了RSA的非对称加密算法来作为JWT的密钥。其中我们使用公钥对登录信息进行加密,使用私钥对登录信息进行解密。其中登录我们使用的是SpringSecurity,当我们的用户登录成功后,会跳转到我们登录成功的逻辑中,我们会接受到用户的登录信息然后将用户信息进行jwt加密,封装到一个对象中。返回前台。然后前台将此对象添加到请求头中。每次进行数据访问的时候,都将携带请求头,我们在网关中会实现一个全局的过滤器对用户的请求进行鉴权处理,获取每次请求的请求头,然后解析出请求头中的加密信息,如果解析成功则放行,如果解析失败则进行拦截。
5. 讲讲Redis,你在项目中有用过Redis吗?
redis是一个高性能的内存数据库,以key-value方式存储数据,可以作为缓存使用。它性能高,支持多种存储类型,支持持久化,单线程。
其中数据类型有:
- 字符串:简单的key-value数据,适合保存单个数据,如:用户数量、库存数、商品数
- hash 哈希(适合保存复杂类型数据,如:用户对象)
- list 列表(链表结构,适合保存有序的、可重复的数据,如:商品列表、评论列表)
- set 无序集合(适合保存无序的,不可重复的数据)
- zset 有序集合(适合保存有序的,不可重复的数据)
常见问题:
1)缓存击穿
高并发的情况下,短时间内缓存会被穿过,请求直接打到数据库上,可能导致数据库压力过大。
解决方案:对代码上锁(双重检查锁)
2)缓存穿透
高并发的情况下,如果查询不存在的数据,因为缓存和数据库都不存在,请求都会打到数据库上,可能导致系统崩溃。
解决方案:
1) 保存不存在的数据到缓存中,设置一定过期时间
2) 布隆过滤器(直接过滤掉不存在数据的请求) 不能准确判断是否存在数据,能准确判断数据不存在
3)缓存雪崩
高并发的情况下,缓存服务器重启或热点数据同时过期,全部访问数据库,导致数据库宕机
解决方案:
1)配置缓存集群
2)尽量给热点数据设置不一样的过期时间,相对均匀
在项目中我们的广告模块使用了redis,因为用户每次进入页面都会请求广告的模块数据,因此我们将广告模块的数据存入redis缓存中,每次用户进入页面的时候都请求我们redis缓存的数据,来减轻Mysql的压力。
6. 你对我们公司还有什么想了解的吗?
想了解一下贵公司的最近的项目是什么,运用的什么技术。
7. 数据库建表的时候int(1)和int(2)的区别
INT(M),M默认为11;M表示最大显示宽度, 建表若设置了zerofill(0填充), 会在数字前面补充0.int(1)和int(2)从本身长度还是存储方式上都是一样的。mysql里,int(1)和int(2)的区别就是显示的长度,但是要设置一个参数:如果列制定了zerofill 就会用0填充显示,如2 int(3)指定后就会显示为002
8. #和$的区别
在Mybatis中#是以占位符的形式拼接sql中的参数,$是以字符串拼接的方式拼接sql中的参数,可能产生sql注入问题。
9. Springmvc的执行流程
1、 用户发送请求至前端控制器DispatcherServlet。
2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、 DispatcherServlet调用HandlerAdapter处理器适配器。
5、 HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6、 Controller执行完成返回ModelAndView。
7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9、 ViewReslover解析后返回具体View。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、 DispatcherServlet响应用户。
10.讲讲ioc
ioc是spring的一个重要组件,名为控制反转。它是一种程序的设计思想。控制是控制javaBean对象的创建和管理。反转是一般情况下对象的创建和管理由开发者控制,反转是把对象的创建和管理交给容器完成,然后再交给开发者。 ioc的主要作用是解耦,解耦,是降低程序耦合度,也就是减少程序代码之间的依赖性,如果代码之间的依赖性很高,修改一处代码会影响很多其他的代码,这就给项目的稳定性带来的问题,不利于代码的扩展和维护。没有IOC的程序中,我们使用new来完成对象的创建,如果需要的对象的类型发生改变,就需要手动修改代码。有了IOC后,对象的创建由第三方(Spring容器)完成,由Spring来管理应用中所有对象的生命周期,开发者只需要关注自己的业务逻辑,代码更利于扩展和维护。ioc是通过反射机制+工厂模式实现。
11.反射的三种创建方式
- 类.class
- 对象.getClass()
- Class.forName(“类的全路径名”)
12. Arraylist和linklist的区别
ArrayList的结构是动态数组,Linklist的结构是链表。ArrayList查询快,因为可以通过下标索引查询。Linklist增删快,因为是链表结构所以增删不需要移动数据。
13.Arraylist一次扩容多少
Arraylist一次扩容1.5倍,默认初始大小为10。ArrayList 的内部实现,其实是用一个对象数组进行存放具体的值,然后用一种扩容的机制,进行数组的动态增长。其扩容机制可以理解为,如果元素的个数,大于其容量,则把其容量扩展为原来容量的1.5倍。
14.Vector是如何实现的安全的
加上了synchronized关键字修饰(this对象锁,锁住整个容器对象)。
15.jvm参数
一、jvm配置参数分为3大类:
1、跟踪参数:跟踪、监控JVM状态,用于程序员JVM调优及故障排查
2、堆分配参数:分配堆内存
3、栈分配参数:分配栈内存
二、各类配置参数常用参数:
1、常用跟踪监控参数:
①打印gc简要信息
第一个:-XX:+PrintGC
第二个:-verbose:gc
这2个参数效果一样,都是在发生gc时,打印gc简要信息的。以
1: [GC (Allocation Failure) 32686K->1648K(123904K), 0.0007230 secs] 2: [GC (Allocation
Failure) 34034K->1600K(123904K), 0.0009652 secs]
②打印gc详细信息及堆使用详细信息:-XX:+PrintGCDetails
③将gc日志记录到外部文件中去:-Xloggc:log/gc.log(参数中gc.log就是外部文件的名称)
当java程序出现OOM异常时,总是想查看当时gc的情况,那么使用这个参数记录下gc日志会非常
便于故障排查。也可以进行日常JVM监控。
④监控类的加载情况:-XX:+TraceClassLoading
使用这个参数可以监控java程序加载的类。
2、常用堆分配参数
①最大堆:-Xmx,java程序最大能使用多少内存大小,如果超过这个大小,那么java程序会报:
out of memory(OOM错误)
②最小堆:-Xms
③指定新生代的内存:-Xmn
④总的堆:目前程序已经配置到的内存大小。一般而言程序一启动,会按照-Xms5m先分配5M的空
间,这时总的堆大小就是5M。
⑤空闲堆:程序已经分配的内存大小减去已经使用的内存大小
⑥新生代(eden+from+to)和老年代(不包含永久区)的比值:-XX:NewRatio
Survivor区与Eden区的比值:-XX:SurvivorRatio
⑦官方推荐:
新生代占堆的3/8
幸存代占新生代的1/10
⑧发生OOM异常时把堆栈信息打印到外部文件
第1个:-XX:+HeapDumpOnOutOfMemoryError
第2个:-XX:+HeapDumpPath
⑨将堆的最小值-Xms 参数与最大值-Xmx 参数设置为一样即可避免堆自动扩展。因为JVM初始分
配的内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的内存由-Xmx指 定,默认是物理内
存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于
70%时,JVM会减少堆直到 -Xms的最小限制。因此服务器一般设置-Xms、-Xmx相等以避免在每
次GC 后调整堆的大小。对象的堆内存由称为垃圾回收器的自动内存管理系统回收。
3、常用栈分配参数
①栈大小参数为-Xss
栈大小通常只有几百k,决定于函数调用的深度。每个线程都有自己独立的栈空间(私有栈空间,
不共享)。如果函数调用太深,超过了栈的大小,则会抛出java.lang.StackOverflowError。遇到这
种错误,通常情况下,不是去调整-Xss参数,而是应该去检查是否是函数调用太深导致的,是否使
用了递归,能不能保证递归出口等。
16.讲讲你知道的那些排序方法,并说说他们的时间复杂度。
17.stringbuild跟stringbuffer的区别
1.在执行速度方面的比较:StringBuilder > StringBuffer
2.StringBuffer与StringBuilder,他们是字符串变量,是可改变的对象,每当我们用它们对字符串做操作时,实际上是在一个对象上操作的,不像String一样创建一些对象进行操作,所以速度就快了。
3.StringBuilder:线程非安全的
StringBuffer:线程安全的
当我们在字符串缓冲去被多个线程使用是,JVM不能保证StringBuilder的操作是安全的,虽然他的速度最快,但是可以保证StringBuffer是可以正确操作的。当然大多数情况下就是我们是在单线程下进行的操作,所以大多数情况下是建议用StringBuilder而不用StringBuffer的,就是速度的原因。
对于三者使用的总结:
1.如果要操作少量的数据用 String
2.单线程操作字符串缓冲区 下操作大量数据 StringBuilder
3.多线程操作字符串缓冲区 下操作大量数据 StringBuffer
18.常见的设计模式知道几种
装饰者设计模式
开闭原则:程序开发过程中,对功能的扩展开放,对功能的修改关闭,提高程序的稳定性
目的:在不修改原有类的代码基础上,对类的功能进行扩展
在Java的IO包下,大量应用了装饰者模式
实现
1.装饰者和被装饰者都要实现相同的接口或继承相同的父类
2.装饰者中定义一个被装饰者的对象
3.给装饰者传入被装饰者对象
4.调用装饰者的方法时,也调用被装饰者的方法,同时进行功能的扩展
单例设计模式
目的:保证一个类只有一个实例(对象)
应用场景:
对于某些大的对象,单例模式能节省系统资源
应用某些特定业务需求,如:保证公司只有一个CEO
实现 PS : Runtime类就是单例模式的
将所有构造方法定义为private
在类中创建一个静态的对象
在类中定义一个静态方法来返回该对象
两种单例模式
饿汉式:类中一开始就创建对象,不管后面是否使用对象,都消耗了内存。
懒汉式:类中一开始不创建对象,调用返回对象方法时再创建对象。
代理模式
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
代理模式的作用
代理模式的作用
1)中介的作用,当调用者不能或不方便调用某个对象时,代理起到中介的作用,帮助调用者间接
的调用对象。
2)符合开闭原则,在不修改原有类代码的前提下,对类的功能进行增强。
代理模式的分类
代理模式分为两种:
1) 静态代理,在运行前,通过编写代码的方式生成代理类
2) 动态代理,在运行后,通过反射机制生成代理类
静态代理
1)代理者和被代理者都实现相同的接口
2)代理者包含被代理者的对象
3)创建代理对象时传入被代理对象
4)代理者执行方法时,会调用被代理者的方法,同时扩展新的功能
静态代理的问题:一个代理类只能代理一种业务,如果有多种业务,就必须创建大量的代理类。
动态代理
和静态代理不同,动态代理是在运行时,通过反射机制动态生成代理类。开发者不需要手动编写新
的代理类。
动态代理分类
JDK动态代理
JDK自带的,前提是:被代理类必须实现过接口。
实现步骤
1) 实现InvocationHandler接口
2)实现invoke方法
3)通过Proxy.newProxyInstance方法返回代理对象
CGLib动态代理
需要引入CGLib依赖,它的原理是:通过反射+继承机制动态生成被代理类的子类,所以被代理类
不能是final的。
实现步骤
1)引入cglib
2)实现MethodInterceptor接口
3)实现intercept方法
4)通过Ehancer返回代理对象
19.hashMap和hashTable的区别
1.Hashtable是线程安全的,HashMap是非线程安全的
2.HashMap的性能高于Hashtable
3.Hashtable不能接收null作为键和值,HashMap可以
4.HashMap可以存null的键值对,hashtable不能,HashMap没有多线程机制,所以多线程不安
全,hashtable有线程锁,是线程安全的,hashtable多线程下,会锁住整个code表,效率很
低,为了提高效率,将code表分成了十二个桶,每次锁时只会锁一个桶,提高了效率
20.一些常见的集合的区别
1、list和set是实现了collection接口的。
2、list可以允许重复的对象。可以插入多个null元素。是一个有序容器,保持了每个元素的插入顺
序,输出的顺序就是插入的顺序。Set不允许重复对象,无序容器,你无法保证每个元素的存储顺
序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序。只允许一个 null 元素。
3、Map不是collection的子接口或者实现类。Map是一个接口。Map 的 每个 Entry 都持有两个对
象,也就是一个键一个值,Map 可能会持有相同的值对象但键对象必须是唯一的。TreeMap 也通
过 Comparator 或者 Comparable 维护了一个排序顺序。Map 里你可以拥有随意个 null 值但最多
只能有一个 null 键。
21.vue子夫项目怎么实现请求同步
父组件通过 props 传入状态给子组件,子组件通过 props 来初始化另外一个内部的状态,子组件每次更改状态之后都通知父组件,然后由父组件来更改自己的状态,就是 props on emit 的应用。
22.es的使用为什么要用
就是在项目中的搜索模块要使用到的一个搜索引擎,它可以帮助我们
更加快速查找到有效信息,因为商品数量庞大,并且搜索条件十分的灵活多变,因此传统的数据库搜索的方式会显得不太合适,并且es是基于倒排索引的方式来搜索数据,就是在查询数据的时候,通过分词器将查询的数据分词处理,然后通过分词来查找索引的value,然后通过value找到key。这样搜索数据更快,不需要对整个数据库进行全文搜索。
23.redis为什么要用
redis是一种noSql类型的数据库,存储在内存中,读写效率高,可以减轻数据库的压力。作为数据库的一个缓存。
24.消息队列的用法
RabbitMQ的五种消息模型
-
一对一模型
最基本的队列模型:
一个生产者发送消息到一个队列,一个消费者从队列中取消息。 -
工作队列
工作队列,生产者将消息分发给多个消费者,如果生产者生产了100条消息,消费者1消费50条,消费者2消费50条。(消费者平均消费消息) -
发布/订阅
发布/订阅模式和Work模式的区别是:Work模式只存在一个队列,多个消费者共同消费一个队列中的消息;而发布订阅模式存在多个队列,不同的消费者可以从各自的队列中处理完全相同的消息。
实现步骤:
- 创建交换机(Exchange)类型是fanout
- 交换机需要绑定不同的队列
- 不同的消费者从不同的队列中获得消息
- 生产者发送消息到交换机
- 再由交换机将消息分发到多个队列
-
路由
路由模式的消息队列可以给队列绑定不同的key,生产者发送消息时,给消息设置不同的key,这样交换机在分发消息时,可以让消息路由到key匹配的队列中。 -
主题
主题模式和路由模式差不多,在key中可以加入通配符:
- * 匹配任意一个单词
- # 匹配逗号隔开的多个单词
使用的大致过程就是生产者会向队列里面发消息,消费者会实时监听队列在队列里取消息,然后进行消费。
24.spring跟springcloud区别
Spring是一个生态体系(也可以说是技术体系),是集大成者,它包含了Spring Framework、Spring Boot、Spring Cloud等(还包括Spring Cloud data flow、spring data、spring integration、spring batch、spring security、spring hateoas)
Spring Cloud事实上是一整套基于Spring Boot的微服务解决方案。它为开发者提供了很多工具,用于快速构建分布式系统的一些通用模式,例如:配置管理、注册中心、服务发现、限流、网关、链路追踪等。
25.vue的生命周期
1、beforeCreate(创建前)
表示实例完全被创建出来之前,vue 实例的挂载元素$el和数据对象 data 都为 undefined,还未初
始化。
2、created(创建后)
数据对象 data 已存在,可以调用 methods 中的方法,操作 data 中的数据,但 dom 未生成,$el
未存在 。
3、beforeMount(挂载前)
vue 实例的 $el 和 data 都已初始化,挂载之前为虚拟的 dom节点,模板已经在内存中编辑完成
了,但是尚未把模板渲染到页面中。data.message 未替换。
4、mounted(挂载后)
vue 实例挂载完成,data.message 成功渲染。内存中的模板,已经真实的挂载到了页面中,用户
已经可以看到渲染好的页面了。实例创建期间的最后一个生命周期函数,当执行完 mounted 就表
示,实例已经被完全创建好了,DOM 渲染在 mounted 中就已经完成了。
5、beforeUpdate(更新前)
当 data 变化时,会触发beforeUpdate方法 。data 数据尚未和最新的数据保持同步。
6、updated(更新后)
当 data 变化时,会触发 updated 方法。页面和 data 数据已经保持同步了。
7、beforeDestory(销毁前)
组件销毁之前调用 ,在这一步,实例仍然完全可用。
8、destoryed(销毁后)
组件销毁之后调用,对 data 的改变不会再触发周期函数,vue 实例已解除事件监听和 dom绑定,
但 dom 结构依然存在。
26.list去重,返回去重结果
使用java8新特性stream进行List去重
双重for循环去重
set集合判断去重,不打乱顺序
遍历后判断赋给另一个list集合
set和list转换去重
27.常用的函数
random();//返回一个随机数。
isLetter();//是否是一个字母
rint(); //返回与参数最接近的整数
abs();//绝对值
uuid 得到一个随机字符串
28.jq选择器,如何选中元素
jQuery 属性选择器
jQuery 使用 XPath 表达式来选择带有给定属性的元素。
$("[href]") 选取所有带有 href 属性的元素。
$("[href=’#’]") 选取所有带有 href 值等于 “#” 的元素。
$("[href!=’#’]") 选取所有带有 href 值不等于 “#” 的元素。
( " [ h r e f ("[href("[href=’.jpg’]") 选取所有 href 值以 “.jpg” 结尾的元素。
29.spring事务配置的具体流程
- 定义事务管理器
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
- DAO上需加上@Transactional注解
30. 面向对象6大原则
1.单一原则。一个类应该有且只有一个变化的原因。单一职责原则将不同的职责分离到单独的类,每一个职责都是一个变化的中心。需求变化时,将通过更改职责相关的类来体现。如果一个类拥有多于一个的职责,则多个职责耦合在一起,会有多于一个原因来导致这个类发生变化。一个职责的变化可能会影响到其他的职责,另外,把多个职责耦合在一起,影响复用性。
2.里氏替换原则,就是要求继承是严格的is-a关系。所有引用基类的地方必须能透明地使用其子类的对象。在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。例如:我喜欢动物,那我一定喜欢狗,因为狗是动物的子类;但是我喜欢狗,不能据此断定我喜欢动物,因为我并不喜欢老鼠,虽然它也是动物。
3.依赖倒置原则。依赖倒置原则的核心就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。低层模块尽量都要有抽象类或接口,或者两者都有。变量的声明类型尽量是抽象类或接口。
4.接口分离原则。一个类对另一个类的依赖应该建立在最小的接口上,通俗的讲就是需要什么就提供什么,不需要的就不要提供。接口中的方法应该尽量少,不要使接口过于臃肿,不要有很多不相关的逻辑方法。
5.多用组合(has-a),少用继承(is-a)。如果新对象的某些功能在别的已经创建好的对象里面已经实现,那么应当尽量使用别的对象提供的功能,使之成为新对象的一部分,而不要再重新创建。可以降低类与类之间的耦合程度。
6.开闭原则。对修改关闭,对扩展开放。在软件的生命周期内,因为变化,升级和维护等原因需要对软件原有代码进行修改,可能会给旧代码引入错误,也有可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。解决方案:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现。不过这要求,我们要对需求的变更有前瞻性和预见性。其实只要遵循前面5中设计模式,设计出来的软件就是符合开闭原则的。
31.创建线程的三种方式
- 继承Thread类,重写run方法,调用start方法
- 实现Runnable接口,实现run方法,创建实现Runnable接口的对象,传入Thread对象中启动线程。
- 实现callable接口,实现call方法,创建Callable对象,传入FutureTask对象,创建FutureTask对象,传入Thread对象,启动线程,调用get方法得到返回结果。
32.线程中常用的方法
start方法: 让一个线程进入就绪状态
run方法:线程真正要执行的逻辑,让一个线程分配的cpu资源,开始启动就会调用run方法执行
sleep(n)方法:让线程休眠n毫秒,休眠时会让出cpu但不会释放锁。
join方法:当前线程等待目标线程运行结束,再开始运行。可以设置超时时间
interrupt()方法:打断线程,如果打断正在sleep,wait,join的方法会抛出InterruptedException异常,并清除打断标记。
如果打断正在执行的线程,会设置打断标记但不会真的打断它,如果打断正在park的线程会真的打断park状态,而且设置打断标记。
yield()方法:让当前线程从执行态变为就绪态。
setDaemon(true)方法:设置一个线程为守护线程。
setPriority(int)方法:修改线程的优先级。
33.用过es的什么查询
用过es的布尔查询,将查询条件封装为map集合放入布尔查询的过滤器链中。
34.Jdk8比以前有什么新特性
Lambda表达式 类似于ES6中的箭头函数
新的日期API Datetime
引入Optional 防止空指针异常
使用Base64
接口的默认方法和静态方法
新增方法引用格式
新增Stream类
注解相关的改变
支持并行(parallel)数组
对并发类(Concurrency)的扩展。
35.Hashmap+list什么时候触发红黑树
一个是链表的长度达到8个,一个是数组的长度达到64个