Site
Site
文章目录
  1. 面向对象的优点和特性
  2. JDK 与 JRE 的区别
  3. MVC 体系结构
  4. servlet 执行流程
  5. servlet 的生命周期
  6. springMVC 执行流程
  7. AOP 与 IOC 的概念(即 spring 的核心原理)
  8. Hibernate 原理
  9. Hibernate 的三种状态和区别
  10. Mybatis 原理
  11. JDBC 操作步骤
  12. 数据库优化
  13. Get 与 Post 的区别
  14. ArrayList 与 LinkedList 的区别
  15. ArrayList 与 Vector 的区别
  16. HashMap 和 HashTable 的区别
  17. HTTP 协议的了解
  18. 类的实例化顺序
  19. 抽象类和接口的区别
  20. JAVA 集合类
    1. 1. List 接口
    2. 2. Set 接口
    3. 3. Map 接口
  21. Spring 加载流程
  22. Spring 支持的几种 bean 的作用域
  23. Spring 的通知类型
  24. Java 经典的设计模式
  25. Hibernate 的核心思想
  26. Java 中 HashMap 的工作原理
  27. Java 堆的结构
  28. 你知道的锁有哪些
  29. 对 Restful 的理解
  30. Dubbo 框架
  31. 消息队列
  32. Memcached 的原理
  33. Redis 和 Memcached 的区别
    1. 1.数据类型支持不同
    2. 2.内存管理机制不同
    3. 3.数据持久化支持不同
    4. 4.集群管理不同
  34. Mybatis 与 Hibernate 的区别
    1. Hibernate
    2. Mybatis
  35. GC 的定义/优点/原理
  36. 分布式Session的几种实现方式
  37. springBoot 自动配置的原理
  38. 什么是微服务
  39. springCloud 如何实现服务的注册和发现
  40. Ribbon 和 Feign 的区别
  41. springCloud 断路器的作用
  • 多线程方面
    1. Volatile 和 Synchronized 的不同
    2. 线程与进程的区别
    3. 线程在执行过程中的状态
    4. 多线程的优点
    5. Thread 类中的start() 和 run() 方法有什么区别
    6. 一个线程在运行时发生异常会怎样
    7. i++ 与 ++i 的区别
    8. JAVA9 的新特性
    9. JAVA10 新特性
  • JAVA面试题

    面向对象的优点和特性

    • 优点
    1. 代码开发模块化,更易维护和修改
    2. 代码复用
    3. 增强代码的可靠性和灵活性
    4. 增加代码的可理解性
    • 特性
    1. 封装:封装给对象提供了隐藏内部特性和行为的能力。对象提供一些能被其他对象访问的方法来改变它内部的数据。访问权限修饰符:public,private,protected
      封装的好处:
      • 通过隐藏对象的属性来保护对象内部的状态
      • 提高了代码的可用性和维护性,因为对象的行为可以被单独的改变或者是扩展
      • 禁止对象之间的不良交互提高模块化
    2. 继承:继承给对象提供了从基类获取字段和方法的能力。继承提高了代码的重用性,也可以在不修改类的情况下给现存的类添加新特性。
    3. 抽象:抽象是把想法从具体的实例中分离出来的步骤,因此要根据他们的功能而不是实现细节来创建类。Java支持创建只暴露接口而不包含方法实现的抽象的类。这种抽象技术的主要目的是把类的行为和实现细节分离开。
    4. 多态:多态是同一个行为具有多个不同的表现形式或形态的能力,就是同一个接口,使用不同的实例而执行不同的操作。

    JDK 与 JRE 的区别

    1. JRE是Java运行时环境,是将要执行Java程序的Java虚拟机。JDK是Java开发工具包,包含了JRE,编译器和其他工具,可以让开发者开发,编译,执行Java应用程序。

    MVC 体系结构

    模型-视图-控制器模式,也称为MVC模式(Model View Controller)。用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。它把软件系统分为三个基本部分:

    • 模型(Model):负责存储系统的中心数据。
    • 视图(View):将信息显示给用户(可以定义多个视图)。
    • 控制器(Controller):处理用户输入的信息。负责从视图读取数据,控制用户输入,并向模型发送数据,是应用程序中处理用户交互的部分。负责管理与用户交互交互控制。

    servlet 执行流程

    客户端发起 http 请求,web 服务器将请求转发到 servlet 容器,servlet 解析 url 并根据 web.xml 找到对应的 servlet,并将 request、response对象传递给找到的 servlet,servlet根据 request 就可以知道是谁发出的请求,请求信息及其他信息,当 servlet 处理完业务逻辑后会将信息放入到 response 并响应到客户端。

    servlet 的生命周期

    Servlet加载—实例化—服务—销毁

    • init():在服务器装入servlet时执行,负责初始化servlet对象,仅执行一次。
    • service():它是servlet的核心,负责响应客户端的请求,每当一个客户端请求一个httpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个请求(ServletRequest)对象和一个响应(ServletResponse)对象作为参数,然后调用与HTTP请求的方法相应的do功能(doGet,doPost)
    • destroy():仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期时,负责释放占用的资源。一个servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程都已经终止或完成。

    springMVC 执行流程

    springMVC 是由 dispatchServlet 为核心的分层控制框架。首先客户端发出一个请求,web 服务器解析请求 url 并去匹配 dispatchServlet 的映射 url,如果匹配上就将这个请求放入dispatchServlet,dispatchServlet根据 mapping 映射配置去寻找相对应的 handler,然后把处理权交给 handler,handler 封装了处理业务逻辑的代码,当 handler 处理完后会返回一个逻辑视图modelAndView给dispatchServlet,此时的modelAndView不是物理视图,所以dispatchServlet会通过viewResource视图资源去解析modelAndView,然后将解析后的参数放到view中返回给客户端。

    AOP 与 IOC 的概念(即 spring 的核心原理)

    • IOC : 控制反转。主要强调的是程序之间的关系是由容器控制的,容器控制对象,控制了对外部资源的获取,我们的spring就像一个容器一样。在传统的编程中我们都是通过创建对象来获取对象的属性和依赖,而在IOC中,是容器帮我们创建对象,并注入依赖,容器帮我们查找和注入对象,对象是被获取,所以叫反转。
    • AOP : 面向切面编程,主要是管理系统层的业务,比如日志,权限,事物等。AOP是将封装好的对象剖开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块为切面,切面将那些与业务逻辑无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块之间的耦合度,同时提高了系统的可维护性。在项目中使用AOP的地方有很多,比如,我们会使用AOP校验请求参数,验证 token,管理员权限拦截,日志信息,response状态码改变等等。

    Hibernate 原理

    1. 读取并解析hibernate.cfg.xml配置文件
    2. 读取并解析映射信息
    3. 创建SessionFactory
    4. 负责被持久化对象CRUD操作,打开Session
    5. 创建并启动事物Transaction
    6. 操作数据,持久化操作
    7. 提交事物,关闭Session,关闭SessionFactory

    Hibernate 的三种状态和区别

    1. 瞬时(transient):也叫自由状态。数据库中没有数据组与之对应,超过作用域会被JVM垃圾回收器回收,一般是new出来且与session没有关联的对象
    2. 持久(persistent):数据库中可能有数据与之对应,当前与session有关联,并且相关联的session没有关闭,事物没有提交;持久对象状态发生改变,在事物提交时会影响到数据库。
    3. 脱管(detached):也叫游离状态,数据库中可能有数据与之对应,但是当前没有session与之关联,但是有oid;托管对象状态发生改变,hibernate不能检测到。
    4. 区别:瞬时状态的实体缺乏与数据库表记录之间的联系,而脱管状态的的实体恰恰相反。

    Mybatis 原理

    1. 加载配置,包括配置文件,代码中的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括传入参数映射配置,执行的SQL语句,结果映射配置),存储在内存中。
    2. SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象,Mybatis根据SQL的ID找到对应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。
    3. SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。
    4. 结果映射:将操作数据库的结果按照映射的配置进行转换,返回最终结果。
      Mybatis框架结构

    JDBC 操作步骤

    1. 加载驱动:Class.forName(“com.mysql.jdbc.Driver”)
    2. 创建连接
    3. 创建Statement对象,创建语句
    4. 执行sql语句
    5. 处理结果
    6. 释放资源

    数据库优化

    1. 选择合适的字段,设置合适的长度,尽量把字段设置为notNull,这样查询的时候数据库就不需要比较null值。
    2. 使用关联查询代替子查询,不仅提高性能,而且避免子查询的锁问题。
    3. 使用union联合查询手动创建临时表。
    4. 开启事物,当数据库执行多条语句出现错误时,事物会回滚,可以维护数据库的完整性。
    5. 使用外键,事物可以维护数据的完整性但是它不能保证数据的关联性,使用外键可以保证数据的关联性。
    6. 使用索引,索引是提高数据库性能的常用方法,它可以让数据库以更快的速度检索特定的行,特别是对于max,min,order by查询时,效果很明显。
    7. 优化查询语句。

      • 对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引
      • 尽量避免在 where 子句中对字段进行 null 值判断,尽可能的使用 NOT NULL填充数据库
      • 尽量避免在 where 子句中使用 != 或 <> 操作符
      • 尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描,如:

        select id from t where num=10 or Name = ‘admin’
        可以这样查询:

        select id from t where num = 10
        union all
        select id from t where Name = ‘admin’

      • in 和 not in 也要慎用,否则会导致全表扫描,用 exists 代替 in 是一个好的选择,select num from a where exists(select 1 from b where num=a.num)
      • 强制查询使用索引:

        select id from t with(index(索引名)) where num = @num

      • 尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描
      • select count(*) from table;这样不带任何条件的count会引起全表扫描,并且没有任何业务意义,是一定要杜绝的。
      • 对于多张大数据量(这里几百条就算大了)的表JOIN,要先分页再JOIN,否则逻辑读会很高,性能很差
      • 索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率
      • 任何地方都不要使用 select from t ,用具体的字段列表代替“”,不要返回用不到的任何字段
      • 尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理
      • 拆分大的 DELETE 或INSERT 语句,批量提交SQL语句
    8. 创建视图或存储过程,这样可以聚焦特定的数据,简化数据操作。

    Get 与 Post 的区别

    1. get是从服务器上获取数据,post是向服务器传送数据。
    2. get 传送的数据量小,post传送的数据量较大。
    3. get方式服务器端用request.queryString获取变量的值,post方式服务器端用request.form获取提交的数据。
    4. get携带的参数和访问地址用户可见,post将字段与对应值封装在实体中传送,用户不可见,但是我们可以抓包看到。
    5. get幂等,post不幂等(幂等是指同一个请求方法执行多次和仅执行一次的效果完全相同),这就是我们浏览器在刷新/后退遇到post时会给用户提示的原因。
    6. 网上有资料说get产生一个tcp数据包,post产生2个tcp数据包,这样的说法是不准确的,此说法指出: get请求浏览器会把http header 和 data 一并发送出去,服务器响应200返回数据,post方式请求浏览器会先发送header,服务器响应100 continue,浏览器再发送 data,在RFC(互联网规范)说明了如果你想在get 请求里带body,一样可以发送并等待 100 continue,这样是符合标准的,所以这样的说法并不准确。

    ArrayList 与 LinkedList 的区别

    ArrayList是线性表,底层是使用数组实现的,它在尾端插入和访问数据时效率较高,LinkedList是双向链表,它在中间插入或者头部插入时效率较高,在访问数据时效率较低。
    下面你可以深入解析数组与链表的区别:

    1.数组
    数组是将元素在内存中连续存放(连序),由于每个元素占用内存相同,可以通过下标迅速访问数组中任何元素,但是如果要在数组中增加或者删除一个元素,需要移动大量的元素,来空出或者填补空间。

    2.链表
    链表中的元素在内存中不是顺序存储的(无序),而是通过存在元素中的指针联系到一起,每个节点包括两个部分: 一个是存储数据元素的数据域,另一个是存储下一个节点地址的指针(这一点可以查看链表底层)。如果要访问链表中的一个元素,需要从第一个元素开始,一直找到需要的元素位置(查询修改慢),但是增加和删除操作就很容易,只需要修改元素中的指针就可以了。

    3.区别

    1. 存储位置:数组逻辑上相邻的元素在物理位置上也相邻,而链表不一定。
    2. 存储空间:链表存放的内存空间可以是连续的,也可以是不连续的,数组则是连续的一段内存空间。一般情况下存放相同多的数据,数组占用较小的内存,而链表还需要存放其前驱和后继的空间。
    3. 长度的可变性:链表的长度是按实际需要可以伸缩的,而数组的长度是在定义是要给定的,如果超出初始大小,则会出现溢出现象。
    4. 按序号查找时:数组可以随机访问,时间复杂度为O(1),而链表不支持随机访问,平均需要O(n)。
    5. 按值查找时:若数组无序,数组和链表的时间复杂度均为O(1),但是当数组有序时,可以采用二分法查询(这个不懂的同学先哭会),将时间复杂度将为O(logn)。
    6. 插入和删除时:数组平均需要移动n/2个元素,而链表只需要修改指针即可。
    7. 空间分配:数组在静态存储分配情形下,存储元素数量受限制,动态存储分配情形下,虽然存储空间可以扩充,但需要移动大量元素,数组从栈中分配空间,方便快捷但自由度小。链表在堆中分配空间,灵活高效,但是申请管理比较麻烦。

    ArrayList 与 Vector 的区别

    1. Vector可实现同步,ArrayList不考虑同步的安全问题,但是效率要高。
    2. 如果两者的容量已满,Vector会按其容量的一倍增长,而ArrayList则按其容量的5%增加,所以Vector更能节省资源。

    HashMap 和 HashTable 的区别

    • HashMap 允许键和值是null,而HashTable不允许。
    • HashTable是同步的,适合多线程环境,而HashMap只适合单线程环境
    • HashMap提供了可供应用迭代的键的集合,HashMap是快速失败的,另一方面,HashTable提供了对键的猎取

    HTTP 协议的了解

    详见 HTTP协议

    类的实例化顺序

    1. 父类静态变量
    2. 父类静态代码块
    3. 子类静态变量
    4. 子类静态代码块
    5. 父类非静态变量(父类实例成员变量)
    6. 父类构造函数
    7. 子类非静态变量(子类实例成员变量)
    8. 子类构造方法

    抽象类和接口的区别

    1. 抽象类和接口都不能实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
    2. 抽象类要被子类继承,接口要被类实现。
    3. 接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现。
    4. 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
    5. 抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
    6. 抽象方法只能申明,不能实现。
    7. 抽象类里可以没有抽象方法 。
    8. 如果一个类里有抽象方法,那么这个类只能是抽象类 。
    9. 抽象方法要被实现,所以不能是静态的,也不能是私有的。
    10. 接口可继承接口,并可多继承接口,但类只能单继承。

    JAVA 集合类

    Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素。由Collection接口派生的两个接口是List和Set。

    1. List 接口

    List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(类似于数组中的下标),来访问list中的元素,List允许有相同的元素。
    实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。

    2. Set 接口

    Set 是一种不包含重复元素的Collection,Set最多有一个null元素。

    3. Map 接口

    Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。

    Spring 加载流程

    这里其实是在考核你对spring源码的理解,描述的深入清晰就能获得加分。
    通过listener入口,核心是在AbstractApplicationContext的refresh方法,在此处构建BeanFactory,注册可能感兴趣的事件,创建Bean实例随想,触发被监听的事件等。AbstractApplicationContext 有一个抽象方法refreshBeanFactory,其中的一个方法loadBeanDefinitions(beanFactory) 将开始加载/解析Bean的定义,也就是把用户定义的数据结构转化为IOC容器中的特定数据结构,创建好BeanFactory后,AbstractApplicationContext的prepareBeanFactory的方法会添加一些工具类,支持对已构建的BeanFactory的配置修改和添加一些自定义的操作。

    Spring 支持的几种 bean 的作用域

    1. singleton : bean在每个Spring ioc 容器中只有一个实例。
    2. prototype : 一个bean的定义可以有多个实例。
    3. request : 每次http请求都会创建一个bean,该作用域仅在基于web的springApplicationContext情形下有效。
    4. session : 在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的springApplicationContext情形下有效。
    5. global-session : 在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的springApplicationContext情形下有效。

    Spring 的通知类型

    1. before : 前置通知,在一个方法执行前被调用。
    2. after : 在方法执行之后调用的通知,无论方法执行收否成功
    3. after-returning : 仅当方法完成之后执行
    4. after-throwing : 在方法抛出异常退出时执行的通知
    5. around : 在方法执行之前和之后调用的通知

    Java 经典的设计模式

    • 单例模式:核心是只需要new一个实例对象,比如数据库连接,在线人数等。把一个计时器存放在数据库或者内存中,当有人登陆的时候取出来加一再放回去,有人退出登陆的时候取出来减一再放回去,但是当有两个人同时登陆的时候,会同时取出计数器,同时加一,同时放回去,这样的话数据就会错误,所以需要一个全局变量的对象给全部人使用,只需要new出一个实例对象,这就是单例模式的应用,并且单例模式节省资源,因为它控制了实例对象的个数,并有利于gc回收。
    • 策略模式:将几个类中公共的方法提取到一个类中,从而使扩展更容易,保证代码的可移植性,减少冗余的代码,可维护性强。
    • 工厂模式:简单的工厂模式主要是统一提供示例对象的引用,通过工厂模式接口获取实例是对象的引用。
      完整设计模式见Java设计模式

    Hibernate 的核心思想

    Hibernate的核心思想是ROM对象关系映射机制。他是将表与表之间的操作映射成对象与对象之间的操作。也就是从数据库中提取的信息会自动按照你设置的映射要求封装成特定的对象,我们对对象的修改对应数据行的修改。

    Java 中 HashMap 的工作原理

    HashMap的源码中可以发现,HashMap底层就是一个数组结果,数组中每一项又是一个链表,当新建一个HashMap的时候,就会初始化一个数组,Entry就是数组中的元素,每个Map.Entry就是一个key-value对,它持有一个指向下一个元素的引用(指针),这就构成了链表。
    实现原理:

    • 利用key的hashCode重新hash计算出当前对象的元素所在数组中的下标
    • 存储时,如果出现hash值相同的key,会覆盖原始值,若不同则会添加新的Entry,与集合中的Entry行程Entry链,而且新Entry位于其头部。
    • 获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到对应值。
    • 出现冲突后,冲突的key的对象将放入链表中,然后在链表中做进一步的对比。

    Java 堆的结构

    Jvm的堆是运行时数据区,所有类的实例和数组都是在堆上分配内存。它在JVM启动的时候被创建。对象所占的堆内存是由自动内存管理系统也就是垃圾收集器回收。
    堆内存是由存活和死亡的对象组成的。存活的对象是应用可以访问的,不会被垃圾回收,死亡的对象是应用不可访问尚且还没有被垃圾回收器回收的对象,一直到回收之前他们都会占据堆内存空间。

    你知道的锁有哪些

    1. 自旋锁:可以在使线程没有取得锁的时候,不被挂起,而转去执行一个空循环(即所谓的自旋),若在若干个空循环后,线程如果就可以获得锁,则继续执行,若线程依然不能获得锁,才会被挂起。适用于锁竞争不是很激烈,锁占用时间很短的并发编程,对于锁竞争激烈,单线程锁占用很长时间的并发程序,反而会浪费CPU时间和系统资源。
    2. 阻塞锁:让线程进入阻塞状态进行等待,当获得相应的信号(唤醒,时间)时,才可以进入线程的准备就绪状态,准备就绪状态的所有线程通过竞争进入运行状态。Java中能够进入/退出阻塞状态或包含阻塞锁的方法有,synchronized关键字,ReentrantLock,Object.wait()/notify()
    3. 可重入锁:也叫做递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然有货的该锁的代码,但不受影响。
    4. 悲观锁:悲观锁认为,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库就用到了很多这种机制,比如行锁,表锁,读锁,写锁等,都是在操作之前先上锁。独占锁是悲观锁的一种实现。
    5. 乐观锁:乐观锁认为,每次去拿数据的时候别人都不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。乐观锁适用于多读的应用类型。
    6. 轮询锁和定时锁:由tryLock实现,与无条件获取锁模式相比,他们具有更完善的错误恢复机制。可避免死锁的发生:boolean tryLock():仅在调用时锁为空闲状态才获取该锁。如果锁可用,则获取锁,并立即返回true,如果锁不可用,则返回false。boolean tryLock(long time,TimeUnit unit) throws InterruptedException:如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁。
    7. 显示锁和内置锁:显示锁用Lock来定义,内置锁用synchronized。内置锁:每个java对象都可以用作一个实现同步的锁,这些锁成为内置锁。获得内置锁的唯一途径就是进入这个锁保护的同步代码或方法,在推出同步代码或方法时会释放该锁。内置锁是互斥锁。
    8. 读写锁:Lock接口以及对象,使用它,很优雅的控制了竞争资源的安全访问,在读的地方使用读锁,写的地方使用写锁,在读写锁的加锁策略中,允许多个读操作同时进行,但每次只允许一个写操作。读写锁是一种性能优化的策略。
    9. 对象锁和类锁:对象锁使用于对象实例方法,或者一个对象实例上的,调用对象wait()方法时,会释放持有的对象锁,以便于调用notify方法,notify方法调用之后,会等到notify所在的线程执行完之后再释放锁,类锁是用于类的静态方法或者一个类的class对象上的,不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁,类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别。
    10. 锁粗化:锁粗化的概念应该比较好理解,就是将多次链接在一起的加锁,解锁操作合并为一次,将多个连续的锁扩展成一个范围更大的锁。
    11. 互斥锁:互斥锁指的是一次最多只能有一个线程持有的锁,如Lock

    对 Restful 的理解

    REST指的是一组架构约束条件和原则,满足这些约束条件和原则的应用程序或设计就是Restful。

    Dubbo 框架

    Dubbo分布式服务框架

    消息队列

    消息队列

    Memcached 的原理

    基于libeven的事件处理,内置内存存储方式SLab Allocation机制,并不单一个的数据删除机制,基于客户端的分布式系统。
    变化频繁,具有不稳定性的数据,不需要实时入库(在线人数),门户网站的新闻等,觉得页面静态化仍不能满足要求,可以放入到memcache中。

    Redis 和 Memcached 的区别

    1.数据类型支持不同

    Memcached仅支持简单的key-value结构的数据,Redis支持String,Hash,List,Set和Sorted Set。

    2.内存管理机制不同

    在Redis中,并不是所有的数据都一直存储在内存中,这是和Memcached最大的区别。当物理内存用完时,Redis可以将一些很久没用到的calue交换到磁盘,同时在内存中清除,如果读取的key对应的value不在内存中,那么则需要从swap文件中加载相应数据,这里就存在I/O线程池的问题,在默认情况下,Redis会出现阻塞,当swap文件加载完成后才会响应,这种策略只使用于客户端数量小的情况,无法满足大并发应用需求;Memcached默认使用Slab Allocation机制管理内存,按照预先规定的大小,将分配的内存分隔成特定长度的块以存储相应长度的key-value数据,解决内存碎片问题。

    3.数据持久化支持不同

    Redis虽然是基于内存的存储系统,但是它本身是支持内存数据的持久化的,而且提供两种主要的持久化策略:RDB快照和AOF日志。而memcached是不支持数据持久化操作的。

    4.集群管理不同

    Memcached本身并不支持分布式,因此只能在客户端通过像一致性哈希这样的分布式算法来实现Memcached的分布式存储。Redis更偏向于在服务器端构建分布式存储,Redis Cluster是一个实现了分布式且允许单点故障的Redis高级版本,Redis Cluster引入了Master节点和Slave节点,每个Master节点都会有对应的两个用于冗余的Slave节点,当Master节点退出后,集群会自动选择一个Slave节点成为新的Master节点.

    Mybatis 与 Hibernate 的区别

    • 开发速度:Hibernate的真正掌握要比Mybatis来得难些。Mybatis框架相对简单很容易上手,但也相对简陋些。个人觉得要用好Mybatis还是首先要先理解好Hibernate。
    • 开发工作量:Hibernate和MyBatis都有相应的代码生成工具。可以生成简单基本的DAO层方法。针对高级查询,Mybatis需要手动编写SQL语句,以及ResultMap。而Hibernate有良好的映射机制,开发者无需关心SQL的生成与结果映射,可以更专注于业务流程。
    • 调优方面:

    Hibernate的调优方案

    1. 制定合理的缓存策略
    2. 尽量使用延迟加载特性
    3. 采用合理的Session管理机制
    4. 使用批量抓取,设定合理的批处理参数
    5. 进行合理的O/R映射设计

    Mybatis的调优方案
    MyBatis在Session方面和Hibernate的Session生命周期是一致的,同样需要合理的Session管理机制。MyBatis同样具有二级缓存机制。(要开启二级缓存,你需要在你的 SQL 映射文件中添加一行: )

    • SQL优化方面:

    Hibernate的查询会将表中的所有字段查询出来,这一点会有性能消耗。Hibernate也可以自己写SQL来指定需要查询的字段,但这样就破坏了Hibernate开发的简洁性。而Mybatis的SQL是手动编写的,所以可以按需求指定查询的字段。
    Hibernate HQL语句的调优需要将SQL打印出来,而Hibernate的SQL被很多人嫌弃因为太丑了。MyBatis的SQL是自己手动写的所以调整方便。但Hibernate具有自己的日志统计。Mybatis本身不带日志统计,使用Log4j进行日志记录。

    • 扩展性方面:

    Hibernate与具体数据库的关联只需在XML文件中配置即可,所有的HQL语句与具体使用的数据库无关,移植性很好。MyBatis项目中所有的SQL语句都是依赖所用的数据库的,所以不同数据库类型的支持不好。

    Mybatis 的优势

    1. MyBatis可以进行更为细致的SQL优化,可以减少查询字段。
    2. MyBatis容易掌握,而Hibernate门槛较高。

    Hibernate 的优势

    1. Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。
    2. Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。
    3. Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。
    4. Hibernate有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。

    Hibernate

    标准的ORM框架,入门门槛高,不需要写sql,sql语句自动生成,对sql语句进行优化,修改比较困难,DAO层开发比Mybatis简单,缓存机制比Mybatis要好,对增删改查的对象维护更方便。
    适合需求变化不多的中小型项目,比如:后台管理系统,ERP等。

    Mybatis

    专注sql本身,需要程序员自己编写sql,可以减少查询的字段,sql修改优化比较方便,更加灵活。Mybatis是一个不完全的ORM框架,相比Hibernate较容易掌握。

    GC 的定义/优点/原理

    GC(Garbage Collection)是垃圾收集的意思,Java使用GC作为其内存管理机制。由于有垃圾回收机制,Java中的对象不再有”作用域”的概念,只有对象的引用才有作用域。垃圾回收可以有效地防止内存泄漏,有效利用可以使用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时地调用垃圾回收器对某个对象或所有对象进行垃圾回收,可以使用system.gc()或者Runtime.gc()进行手动垃圾回收,但是没有办法保证GC的执行。

    分布式Session的几种实现方式

    1.Session Replication 方式管理(session复制)

    • 简介:将一台机器上的Session数据广播复制到集群中其余机器上
    • 使用场景:机器较少,网络流量较小
    • 优点:实现简单,配置较少,当网络中有机器Down掉时不影响用户访问
    • 缺点:广播式复制到其余机器有一定延时,带来一定网络开销

    2.Session Sticky 方式管理

    • 简介:粘性Session,当用户访问集群中某台机器后,强制指定后续所有请求均落到此机器上
    • 使用场景:机器数适中,对稳定性要求不是非常苛刻
    • 优点:实现简单、配置方便、没有额外网络开销
    • 缺点:网络中有机器Down掉时、用户Session会丢失、容易造成单点故障

    3.缓存集中式管理

    • 简介:将Session存入分布式缓存集群中的某台机器上,当用户访问不同节点时先从缓存中拿Session信息
    • 使用场景:集群中机器数多、网络环境复杂
    • 优点:可靠性好
    • 缺点:实现复杂、稳定性依赖于缓存的稳定性、Session信息放入缓存时要有合理的策略写入

    springBoot 自动配置的原理

    在spring程序main方法中添加@SpringBootApplication或者@EnableAutoConfiguration,会自动去maven中读取每个starter中的spring.factories文件,该文件里配置了所有需要被创建的spring容器中的bean。

    什么是微服务

    以前的模式是所有的代码在同一个工程中,部署在同一个服务器中,同一个项目的不同模块不同功能相互抢占资源。微服务将工程根据不同的业务规则拆分成微服务,部署在不同的机器上,服务之间进行相互调用,Java微服务框架有Dubbo,SpringCloud等

    springCloud 如何实现服务的注册和发现

    服务在发布时指定对应的服务名(服务名包括了IP地址和端口)将服务注册到注册中心(eureka或者zookeeper),这一过程是springCloud自动实现的,只需要在main方法添加@EnableDiscoveryClient,同一个服务修改端口就可以启动多个实例。
    调用方法:传递服务名称通过注册中心获取所有的可用实例,通过负载均衡策略调用对应的服务。

    Ribbon 和 Feign 的区别

    Ribbon 和 Feign 都是用于调用其他服务的,不过方式不同。

    1. 启动类使用的注解不同,Ribbon用的是@RibbonClient,Feign用的是@EnableFeignClients
    2. 服务的指定位置不同,Ribbon实在@RibbonClient注解上声明,Feign则是在定义抽象方法的接口中使用@FeignClient声明。
    3. 调用方式不同,Ribbon需要自己构建http请求,模拟http请求然后使用RestTemplate发送给其他服务,步骤相当繁琐。Feign则是在Ribbon的基础上进行了一次改进,采用接口的方式,将需要调用的其他服务的方法定义成抽象方法即可。不需要自己构建http请求,不过要注意的是抽象方法的注解,方法签名要和提供服务的方法完全一致。

    springCloud 断路器的作用

    当一个服务调用另一个服务由于网络原因或者自身原因出现问题时,调用者就会等待被调用者的响应,当更多的服务请求这些资源时,导致更多的请求等待,这样就会发生连锁效应(雪崩效应),断路器的作用就是解决这一问题。一定时间内,达到一定的次数无法被调用,并且多次检测没有恢复的迹象,断路器完全打开,那么下次请求就不会请求到该服务,若段时间内有恢复迹象,断路器会将部分请求发给该服务,当能正常调用时,断路器关闭,服务一直处于正常状态是断路器是关闭的。

    多线程方面

    Volatile 和 Synchronized 的不同

    1. 粒度不同,前者针对变量,后者锁对象和类
    2. syn阻塞,volatile线程不阻塞
    3. syn保证三大特性,volatile不保证原子性
    4. syn编译器优化,volatile不优化

    线程与进程的区别

    线程和进程都是一个时间段的描述,是CPU工作时间段的描述,不过是颗粒大小不同。在CPU中,任务轮流方法是:先加载程序A的上下文,然后开始执行A,保存程序A的上下文,调入下一个要执行的程序B的程序上下文,然后开始执行B,保存程序B的上下文…
    进程就是包换上下文切换的程序执行时间总和 = CPU加载上下文 + CPU执行 + CPU保存上下文,加载上下文后,开始执行程序A的a小段,然后执行b小段,这里的ab就是线程,也就是说线程是共享了进程的上下文环境的更为细小的CPU时间段,一个进程可以包含很多个线程。

    线程在执行过程中的状态

    1. 就绪(Runnable):线程准备运行,不一定立马就能执行
    2. 运行中(Running):进程正在执行线程的代码
    3. 等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束
    4. 睡眠中(Sleeping):线程被强制睡眠
    5. I/O阻塞(Blocked on I/O):等待I/O操作完成
    6. 同步阻塞(Blocked on Synchronization):等待获取锁
    7. 死亡(Dead):线程完成了执行

    多线程的优点

    1. 相对于单线程而言,可以响应多任务的并发操作,多线程取消了主循环和轮流检测机制,一个线程可以暂停而不阻止系统其他部分的执行,而且当程序中一个线程阻塞时,只有那个被阻塞的线程暂停,所有其他的线程继续执行。
    2. 相对于进程而言,开销比较小,转换成本较小,所有线程共享同一地址空间,相互协作,彼此之间通信很容易。

    Thread 类中的start() 和 run() 方法有什么区别

    start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果是不一样的,调用run()方法的时候,只会在原来的方法中调用,没用新的线程启动,start()方法才会启动新线程。

    一个线程在运行时发生异常会怎样

    如果异常没有被捕获该线程将会停止执行。Thread.UncaughtExceptionHandler是用于处理未捕获异常造成线程突然中断情况的一个内嵌接口。当一个未捕获异常造成线程线程中断的时候JVM会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler并将线程和异常作为参数传递给Handler的uncaughtException()方法进行处理。

    i++ 与 ++i 的区别

    1. i++ 返回原来的值,++i返回 +1 后的值(在代码中我们理解为,i++先处理逻辑再+1,++i先+1再处理逻辑)
    2. i++ 不能作为左值,++i可以。左值:对应内存中有确定存储地址的对象的表达式的值,而右值是所有不是左值的表达式的值。左值与右值的根本区别在于是否允许取地址&运算符获得对应的内存地址。
      例如:

      1
      2
      3
      4
      5
      6
      int i = 0;
      int *p1 = &(++i); //正确
      int *p2 = &(i++); //错误

      ++i = 1; //正确
      i++ = 5; //错误

    我们从底层来看:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 前缀形式(++i):
    int& int::operator++() //这里返回的是一个引用形式,就是说函数返回值也可以作为一个左值使用
    {//函数本身无参,意味着是在自身空间内增加1的
    *this += 1; // 增加
    return *this; // 取回值
    }

    //后缀形式(i++):
    const int int::operator++(int) //函数返回值是一个非左值型的,与前缀形式的差别所在。
    {//函数带参,说明有另外的空间开辟
    int oldValue = *this; // 取回值
    ++(*this); // 增加
    return oldValue; // 返回被取回的值,返回的是一个临时变量,是右值。
    }

    JAVA9 的新特性

    1. Java 平台级模块系统:模块化的JAR文件都包含一个额外的模块描述器,在这个模块描述器中,对其他模块的依赖是通过requires来表示的,exports控制哪些包可以被其他模块访问到。
    2. Linking:通过java9中的jlink工具创建针对应用程序进行优化的最小运行时环境,而不需要完全加载JDK安装版本。
    3. JShell:交互式Java REPL,java语言的交互式编程环境,从控制台启动 jshell ,并直接启动输入和执行 Java 代码。
    4. 改进的Javadoc:现已支持在API文档中进行搜索,而且兼容HTML5标准。
    5. 集合工厂方法:使集合的填充更加方便快捷

      1
      2
      Set<Integer> ints = Set.of(1, 2, 3);
      List<String> strings = List.of("first", "second");
    6. 改进的Stream API:通过这套API可以在集合上建立用于转换的申明管道。

    7. 私有接口方法:java8为我们带来了接口的默认方法。接口现在也可以包含行为,而不仅仅是方法签名,但是如果在接口上有几个默认方法,代码几乎相同,则我们可以使用java9,想接口添加私有辅助方法来解决。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      public interface MyInterface {

      void normalInterfaceMethod();

      default void interfaceMethodWithDefault() { init(); }

      default void anotherDefaultMethod() { init(); }

      // This method is not part of the public API exposed by MyInterface
      private void init() { System.out.println("Initializing"); }
      }

    a. HTTP/2:这个特性用来代替HttpURLConnection API,并提供对WebSocket和HTTP/2的支持。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    HttpClient client = HttpClient.newHttpClient();

    HttpRequest req =
    HttpRequest.newBuilder(URI.create("http://www.google.com"))
    .header("User-Agent","Java")
    .GET()
    .build();

    HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandler.asString());

    b. 多版本兼容JAR

    JAVA10 新特性

    1. 局部变量类型推断:java引入在其他语言中很常见的var,只要编译器可以推断此种类型,你不在需要专门生命一个局部变量的类型,比如

      1
      var x = new ArrayList<String>();
    2. 应用类数据共享(CDS):CDS用于改善JVM启动,同时减少当多个虚拟机在同一个物理或虚拟的机器上运行时的资源占用。

    3. 并行垃圾回收器G1:G1是设计来作为一种低延时的垃圾回收器,可以减少在使用默认的收集器的应用性能配置文件的差异
    4. 垃圾回收器接口:不是开发者用来控制垃圾回收的接口,而是一个在JVM源代码中允许另外的垃圾回收快速方便的集成接口。
    支持一下
    扫一扫,支持xfan
    • 微信扫一扫
    • 支付宝扫一扫