程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2023-06(2)

Java面试:2021.05.23

发布于2021-05-29 20:38     阅读(1527)     评论(0)     点赞(19)     收藏(4)


1、常用的线程池有几种?

 

常用的线程池有6种.

1. newCachedThreadPool:缓冲线程池。没有核心线程,最大线程上限为 Integer.MAX_VALUE,新任务来了不排队,总是新建临时线程来处理任务,临时线程如果空闲超过60秒则被回收。适合突发的大量短任务,但缺点是没有线程上限,还是会因为线程数太多,而造成内存溢出;

2. newFixedThreadPool:固定线程池。核心线程固定,没有临时线程,新任务来了如果没有空闲线程则进入阻塞队列排队。缺点是队列没有上限(Integer.MAX_VALUE),如果任务处理不够快,都堆积到队列当中,而造成内存溢出;

3. newSingleThreadExecutor :单线程线程池。只有一个核心线程,队列也是没有上限(Integer.MAX_VALUE);

4. newSingleThreadScheduledExecutor:单线程任务调度线程池。只有一个核心线程,最大线程数没有上限,用来延时或定时执行任务;

5. newScheduledThreadPool:固定线程任务调度线程池。核心线程数固定,最大线程数没有上限,用来延时或定时执行任务;

6. newWorkStealingPool:任务窃取线程池。使用多个队列来减少竞争,当一个线程执行完本队列中的任务后,会窃取其它队列中未执行的任务。但不能保证任务的执行顺序。

 

2、谈谈你对Threadlocal的理解。

 

它的本质是通过【线程隔离】的思想来避免对象被多线程共享,每个线程都用它们自己的对象,自然没有并发问题。

  • 每个线程中有一个 ThreadLocalMap 的弱键 map 集合,键即为 ThreadLocal 对象,值为希望线程隔离的对象(如数据库连接、SqlSession 等)   

使用方法 static ThreadLocal tl = new ThreadLocal();

  • 使用 tl.set(obj) 方法将对象设置入当前线程的 ThreadLocalMap,ThreadLocal 对象自己作为 key

  • 使用 tl.get() 方法就将它自己作为 key 到当前线程的 ThreadLocalMap 中去查询之前设置的对象,作为结果返回

虽然 tl 只有一个,但它是被关联的值是各个线程独有的,做到了值的线程隔离。

 

3、Spring的Aop的实现原理。

 

Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理:

(1)JDK 动态代理只提供接口代理,不支持类代理,核心 InvocationHandler 接口和 Proxy 类,InvocationHandler 通过 invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起,Proxy 利用 InvocationHandler 动态创建一个符合某一接口的的实例, 生成目标类的代理对象。

(2) 如果代理类没有实现 InvocationHandler 接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现 AOP。CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final,那么它是无法使用 CGLIB 做动态代理的。

 

 

4、Springmvc的流程。

 

1. 用户发送请求到前端控制器(DispatcherServlet)

2. 前 端 控 制 器 ( DispatcherServlet ) 收 到 请 求 调 用 处 理 器 映 射 器 (HandlerMapping),去查找处理器(Handler)

3. 处理器映射器(HandlerMapping)找到具体的处理器(可以根据 xml 配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。

4. 前端控制器(DispatcherServlet)调用处理器映射器(HandlerMapping)

5. 处理器适配器(HandlerAdapter)去调用自定义的处理器类(Controller,也叫后端控制器)。

6.自定义的处理器类(Controller,也叫后端控制器)将得到的参数进行处理并返回结果给处理器映射器(HandlerMapping)

7. 处 理 器 适 配 器 ( HandlerAdapter ) 将 得 到 的 结 果 返 回 给 前 端 控 制 器 (DispatcherServlet)

8. DispatcherServlet( 前 端 控 制 器 ) 将 ModelAndView 传 给 视 图 解 析 器 (ViewReslover)

9. 视图解析器(ViewReslover)将得到的参数从逻辑视图转换为物理视图并返回给前端控制器(DispatcherServlet)54

10. 前端控制器(DispatcherServlet)调用物理视图进行渲染并返回

11. 前端控制器(DispatcherServlet)将渲染后的结果返回

 

 

5、谈谈你对java中集合的理解, 具体实现类有什么特点?

集合分为单例集合和双列集合, 如下是他们的继承接口。

(1)单列:Connection接口:
子接口: List 有序,可重复

实现类: ArrayList
优点: 底层数据结构是数组,查询快,增删慢。
缺点: 线程不安全,效率高
实现类:LinkedList
优点: 底层数据结构是链表,查询慢,增删快。
缺点: 线程不安全,效率高
子接口:Set 无序,唯一

实现类: HashSet
底层数据结构是哈希表。(无序,唯一)
如何来保证元素唯一性?
依赖两个方法:hashCode()和equals()

实现类:LinkedHashSet
底层数据结构是链表和哈希表。(FIFO插入有序,唯一)
1.由链表保证元素有序
2.由哈希表保证元素唯一

实现类:TreeSet
底层数据结构是红黑树。(唯一,有序)
1. 如何保证元素排序的呢?
自然排序, 比较器排序
2.如何保证元素唯一性的呢?
根据比较的返回值是否是0来决定

(2)双列:Map接口有四个实现类: 

实现类: HashMap 
基于 hash 表的 Map 接口实现,非线程安全,高效,支持 null 值和 null 键, 线程不安全。
实现类: HashTable 
线程安全,低效,不支持 null 值和 null 键; 
实现类: LinkedHashMap 
线程不安全,是 HashMap 的一个子类,保存了记录的插入顺序; 
实现类: TreeMap

能够把它保存的记录根据键排序,默认是键值的升序排序,线程不安全。

 

 

6、Hashmap底层原理。

 

 

一.、JDK1.8之前(数组+链表)

(1) new HashMap

创建一个空对象, 如果不指定数组长度, 默认数组长度16. 如果指定了数组长度, 会找一个和该数组临近的2的n次方数据作为长度。
数组中存入的是Entry<k, v>的entry对象.创建的时候还加入了0.75的负载因子. 这个负载因子和扩容rehash()方法有关。


(2) map.put(k,v)方法

首先,先判断key存放的位置, 判断出位置了, 然后将entry对象放到数组对应的位置中。
key通过hashcode方法(hashmap内部的hashcode扰动函数)算出hash值, 然后通过(数组长度-1)&hash值, 得到一个位于0-15区间的数字, 这就是对应数组中的下标了。

如果equals()方法为true, 则说明就是同一个key了, value值就会覆盖;
如果equals()方法为false,则不是一个key, 就在数组对应索引位置变为链表存储新Entry<key,value>。
上一步说到的链表是拉链法: 将链表和数组相结合.也就是说创建一个链表数组,数组中每一格就是一个链表.若约到哈希冲突,则将冲突的值加到链表中即可。

插入链表的时候是首插法, 也就是链表中的新元素排在旧元素前面. 因为都会认为新存入的数据是被应用最多的, 所以新数据排在旧数据前面。


(3) map.get(k)实现原理

先调用k的hashCode()方法得出hash值,并通过hash值&(数组长度-1)运算转换成数组的下标。
在通过数组下标快速定位到某个位置上, 这个位置上什么都没有,则返回null。
如果这个位置上有单向链表,那么它就会拿着参数K和单向链表上的每一个节点的K进行equals,如果所有equals方法都返回false,则get方法返回null。如果其中一个节点的K和参数K进行equals返回true,那么此时该节点的value就是我们要找的value了,get方法最终返回这个要找的value。

 

二.、JDK1.8之后(数组+链表+红黑树)

(1) new HashMap

JDK1.8以后只有在put数据的时候才会创建对象, 而且每个数组中存的是node对象。
如果不指定数组长度, 默认数组长度16. 如果指定了数组长度, 会找一个和该数组临近的2的n次方数据作为长度。
数组中存入的是Node<k, v>的node对象。
创建的时候还加入了0.75的负载因子. 这个负载因子和扩容rehash()方法有关。


(2) map.put(k,v)方法

和JDK1.8之前不同的是, 但是这里的链表当达到一定程度后会转为红黑树.

如果链表的长度超过8则转为红黑树, 也就是该位置存储的是数组+红黑树结构.
当链表长度小于等于6时又变为链表, 也就是该位置存储的是数组+链表结构.
链表法采用的是尾插法. 也就是新的元素排在当前元素的后面。
JDK1.8 的时候, 数组中存的是node对象, 而不是entry对象了, node对象包括三部分。

 

(3) map.get(k)原理

和JDK1.8之前一样。

 

 

7、List, Set, Map的区别是什么?

 

 

它们都是 Java 中表示集合的接口

  •  

    List,单列数据集合,允许重复对象,有序(保持插入顺序)

     

  •  

    Set,单列数据集合,不允许重复对象,有没有序与实现有关,例如

     

    •  

      LinkedHashSet 可以保持插入顺序

       

    •  

      TreeSet 可以按对象自然排序,或按比较器排序

       

    •  

      HashSet 无序

       

  •  

    Map,双列数据集合(包含键、值两部分),键不允许重复,值可以重复,同样根据实现类不同,key 可能有序或无序

     

    •  

      LinkedHashMap 可以让 key 按插入有序

       

    •  

      TreeMap 可以让 key 按自然排序,或按比较器排序

       

    •  

      HashMap 的 key 无序

       

Set 中的元素和 Map 中的 key 是否允许重复取决于正确实现了 hashCode 与 equals 方法。

 

 

 

8、Spring的核心是什么?

 

1. Spring 的两大核心是:IOC(控制反转)和 AOP(面向切面编程)

2. IOC 的意思是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到 Spring 容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。最直观的表达就是,IOC 让对象的创建不用去 new 了,可以由 spring 根据我们提供的配置文件自动生产,我们需要对象的时候,直接从 Spring 容器中获取即可.

Spring 的配置文件中配置了类的字节码位置及信息, 容器生成的时候加载配置文件识别字节码信息, 通过反射创建类的对象.

Spring 的 IOC 有三种注入方式 :构造器注入, setter 方法注入, 根据注解注入。

3. AOP,一般称为面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect). SpringAOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改原有目标对象的字节码,而是在运行时生成代理对象,这个代理对象负责结合切面中公共行为和目标对象中原始的行为,从而实现动态增强的效果

Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理:

  • JDK 动态代理:只提供接口代理,不支持类代理,核心是 InvocationHandler 接口和 Proxy 类,InvocationHandler 通过 invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起

  • CGLIB(Code Generation Library):如果目标没有实现接口,那么 Spring AOP会选择使用 CGLIB 来动态代理目标类。,是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现 AOP。CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final,那么它是无法使用 CGLIB 做动态代理的。

 

 

9、Springboot启动原理。

 

  • 当运行 SpringApplication.run(静态方法)时,会创建 SpringApplication 对象,并调用其 run 方法

  • 开始计时

  • 加载自动配置的监听器,向它们发布 starting 事件

  • 准备 Environment 对象,发布 environmentPrepared 事件

  • 打印 banner

  • 创建 ApplicationContext 容器

  • 准备 ApplicationContext 容器(应用 Initializers),发布 contextPrepared 事件

  • 加载 beanDefinition(来源有主引导类、META-INF/spring.factories中的自动配置类),发布 contextLoaded 事件

  • ApplicationContext refresh 发布 contextRefreshed 事件

  • 停止计时,发布 started 事件

  • 调用 ApplicationRunner 及 CommandLineRunner

  • 发布 running 事件

 

 

10、Springboot的优势有哪些?

基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。

版本锁定:解决是 maven 依赖版本容易冲突的问题,集合了常用的并且测试过的所有版本使用了 Starter(启动器)管理依赖并能对版本进行集中控制,如下的父工程带有版本号,就是对版本进行了集中控制。

起步依赖 :解决了完成某一个功能要整合的 jar 包过多的问题,集合了常用的 jar 包。

自动配置:解决了整合框架或者技术的配置文件过多,集合了所有的约定的默认配置。

内置 Tomcat:通过内置的 tomcat,无需再用其他外置的 Tomcat 就直接可以运行 javaEE 程序。

总之:人们把 Spring Boot 称为搭建程序的脚手架。其最主要作用就是帮我们快速的构建庞大的 spring 项目,并且尽可能的减少一切 xml 配置,做到开箱即用,迅速上手,让我们关注与业务而非配置。

 

11、Redis常用数据类型有什么?

常用数据类型有5种.。

1. String类型
String是Redis最基本的类型, 可以与内存存储一模一样的类型, 一个Key对应一个Value. Value不仅String类型的, 也可以是数字.
String 类型是二进制安全的,意思是 Redis 的 String 类型可以包含任何数据,比如 jpg 图片或者序列化的对象, String 类型的值最大能存储 512M.

应用场景
    缓存功能:String字符串是最常用的数据类型, 不仅仅是Redis, 各个语言都是最基本类型,因此,利用Redis作为缓存,配合其它数据库作为存储层,利用Redis支持高并发的特点,可以大大加快系统的读写速度、以及降低后端数据库的压力。
    计数器:许多系统都会使用Redis作为系统的实时计数器,可以快速实现计数和查询的功能。而且最终的数据结果可以按照特定的时间落地到数据库或者其它存储介质当中进行永久保存。
    共享用户Session:用户重新刷新一次界面,可能需要访问一下数据进行重新登录,或者访问页面缓存Cookie,但是可以利用Redis将用户的Session集中管理,在这种模式只需要保证Redis的高可用,每次用户Session的更新和获取都可以快速完成。大大提高效率。
2. Hash
Hash是一个键值(key-value)的集合。Redis 的 Hash 是一个 String 的 Key 和 Value 的映射表,Hash 特别适合存储对象。常用命令:hget,hset,hgetall 等。

应用场景:可以封装对象格式的数据
3. List
List 列表是简单的字符串列表(双向列表), 按照插入顺序排序, 可以添加一个元素到列表的头部(左边)或者尾部(右边) 常用命令:lpush、rpush、lpop、rpop、lrange(获取列表片段)等。

应用场景:
    链表特点查询快, 可以作为消息队列使用.
    用户的粉丝列表
    博客首页,博主的文章列表
4. Set
Set 是无序不可重复的, 底层是通过 hashtable 实现的, 常用命令:sdd、spop、smembers、sunion 等。

应用场景:
    和List一样,区别在于 Set 是自动去重. 而且 Set 提供了判断某个成员是否在一个 Set 集合中, 比如: 统计访问网站的所有Ip.
可以基于 Set 玩儿交集、并集、差集的操作,比如交集吧,我们可以把两个人的好友列表整一个交集,看看俩人的共同好友是谁?
5. Zset
Zset 特点是有序且不可重复。常用命令:zadd、zrange、zrem、zcard 等。

应用场景:
    排行榜:有序不重复数据类型典型应用场景做排行榜. 例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面:按照时间、按照播放量、按照获得的赞数等。
    用Zset来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务, 让重要的任务优先执行。

 

 

12、重定向和转发有什么区别?

 

转发:用 request 的 getRequestDispatcher()方法得到 ReuqestDispatcher 对象,调用 forward()方法 request.getRequestDispatcher("other.jsp").forward(request, response);

重定向:调用 response 的 sendRedirect()方法 response.sendRedirect("other.jsp");

1> 重定向 2 次请求,请求转发 1 次请求;

2> 重定向地址栏会变,请求转发地址栏不变;

3> 重定向是浏览器跳转,请求转发是服务器跳转;

4> 重定向可以跳转到任意网址,请求转发只能跳转当前项目。

 

 

13、Cookie和Session有什么区别?

 

1.存储位置不同

cookie 的数据信息存放在客户端浏览器上。session 的数据信息存放在服务器上。

2.存储容量不同

单个 cookie 保存的数据<=4KB,每个域名保存的cookie个数有限(依浏览器不同而不同)。对于 session 来说并没有上限,但出于对服务器端的性能考虑,session 内不要存放过多的东西,并且设置合理 session 过期时间。

3.存储方式不同

cookie 仅能存字符串数据,并且需要通过URL编码后保存。session 中能够存储任何类型的数据,包括且不限于 string,integer,list,map 等。

4.隐私策略不同

cookie 对客户端是可见的,别有用心的人可以分析存放在本地的 cookie 并进行 cookie 欺骗,所以它是不安全的。session 存储在服务器上,不存在敏感信息泄漏的风险。

5. 有效期上不同

开发可以通过设置 cookie 的属性,达到使 cookie 长期有效的效果。session 依赖于名为 JSESSIONID 的 cookie,而 cookie JSESSIONID 的过期时间默认为-1,只需关闭窗口该 session 就会失效,因而 session 不能达到长期有效的效果。

6.服务器压力不同

cookie 保管在客户端,不占用服务器资源。对于并发用户十分多的网站,cookie 是很好的选择。

session 是保管在服务器端的,每个用户都会产生一个 session。假如并发访问的用户十分多,会产生十分多的 session,耗费大量的内存。



所属网站分类: 技术文章 > 博客

作者:skdk

链接:http://www.javaheidong.com/blog/article/207277/f45602fcd4acf40bbbb5/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

19 0
收藏该文
已收藏

评论内容:(最多支持255个字符)