java中高级面试


对于javaSE的理解
是整个JAVA的基础和核心,是刚接触java要学习的基础知识
1.1 控制数据的流向,将前台传过来的数据包起来,然后一个一个地插入数据库永久保存。
1.2 从数据库中用jdbc取出数据,然后包起来,最终传递到前台页面进行公开展览

where、having之间的区别和用法
聚合函数是比较where、having 的关键。
若须引入聚合函数来对group by 结果进行过滤 则只能用having
select sum(score) from student  where sex='man' group by name having sum(score)>210
where 后不能跟聚合函数,因为where执行顺序大于聚合函数

dubbo
调用关系说明:

0. 服务容器负责启动,加载,运行服务提供者。
1. 服务提供者在启动时,向注册中心注册自己提供的服务。                                            --写入自己的URL地址。
2. 服务消费者在启动时,向注册中心订阅自己所需的服务。                                            --订阅提供者的URL地址,并写入自己的URL地址。
3. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
4. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
5. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

Dubbo建议使用Zookeeper作为服务的注册中心。
Dubbo的将注册中心进行抽象,是得它可以外接不同的存储媒介给注册中心提供服务,有ZooKeeper,Redis等
首先是负载均衡,单注册中心的承载能力是有限的,在流量达到一定程度的时 候就需要分流,负载均衡就是为了分流而存在的,一个ZooKeeper群配合相应的Web应用就可以很容易达到负载均衡;资源同步,单单有负载均衡还不够,节点之间的数据和资源需要同步

负载均衡策略
RandomLoadBalance:随机负载均衡算法,Dubbo默认的负载均衡策略
按权重设置随机概率。在一个截面上碰撞的概率较高,但调用越大分布越均匀
RoundRobinLoadBalance:轮询负载均衡算法,按公约后的权重设置轮循比率
这种模式下,在权重设置不合理的情况下,会导致某些节点无法负载请求,另外,如果有些机器性能比较低,会存在请求阻塞的情况
ConsistentHashLoadBalance: 一致性Hash,相同参数的请求总是发到同一提供者。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。

Zookeeper的作用:
zookeeper用来注册服务和进行负载均衡,哪一个服务由哪一个机器来提供必需让调用者知道,简单来说就是ip地址和服务名称的对应关系。当然也可以 通过硬编码的方式把这种对应关系在调用方业务代码中实现,但是如果提供服务的机器挂掉调用者无法知晓,如果不更改代码会继续请求挂掉的机器提供服务。 zookeeper通过心跳机制可以检测挂掉的机器并将挂掉机器的ip和服务对应关系从列表中删除。

java小例子: int a=2,b=2;   
硬编码:if(a==2) return false;   
非硬编码 if(a==b) return true;   (就是把数值写成常数而不是变量 )
一个简单的版本:如求圆的面积 的问题 PI(3.14)   
那么3.14*r*r 就是硬编码,而PI*r*r 就不是硬编码。  

ZooKeeper的特性引进来。
首先是负载均衡,单注册中心的承载能力是有限的,在流量达到一定程度的时 候就需要分流,负载均衡就是为了分流而存在的,一个ZooKeeper群配合相应的Web应用就可以很容易达到负载均衡
资源同步 节点之间的数据和资源需要同步,ZooKeeper集群就天然具备有这样的功能

Zookeeper做了什么?
配置管理     如果程序分散部署在多台机器上,要逐个改变配置就变得困难。现在把这些配置全部放到zookeeper上去,保存在 Zookeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 Zookeeper 的通知
集群管理    是否有机器退出和加入、选举master。
分布式锁    一个是保持独占,另一个是控制时序。

dubbo协议
适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多(并发量高),单一消费者无法压满提供者,尽量不要用dubbo协议传输大文件或超大字符串。
适用场景:常规远程服务方法调用
1、dubbo默认采用dubbo协议,dubbo协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况
2、他不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
3、Dubbo协议缺省每服务每提供者每消费者使用单一长连接,如果数据量较大,可以使用多个连接。
4、为防止被大量连接撑挂,可在服务提供方限制大接收连接数,以实现服务提供方自我保护
为什么采用异步单一长连接?
因为服务的现状大都是服务提供者少,通常只有几台机器,而服务的消费者多,可能整个网站都在访问该服务,比如Morgan的提供者只有6台提供者,却有上百台消费者,每天有1.5亿次调用,如果采用常规的hessian服务,服务提供者很容易就被压跨,通过单一连接,保证单一消费者不会压死提供者,长连接,减少连接握手验证等

什么时候用长连接,短连接?    
长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,次处理时直接发送数据包就OK了,不用建立TCP连接。例如:数据库的连接用长连接,如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。
而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好。

短连接:通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接

单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
枚举(单例模式)
public enum Singleton {
    INSTANCE;
    public void whateverMethod() {

    }
}
// 饿汉式单例
public class Singleton1 {
    // 私有构造
    private Singleton1() {}

    private static Singleton1 single = new Singleton1();

    // 静态工厂方法
    public static Singleton1 getInstance() {
        return single;
    }
}
应用刚启动的时候,不管外部有没有调用该类的实例方法,该类的实例就已经创建好了。以空间换时间
写法简单,在多线程下也能保证单例实例的唯一性,不用同步,运行效率高。
当类被加载时,静态变量instance会被初始化,此时类的私有构造函数会被调用,单例类的唯一实例将被创建。
懒汉式:
应用刚启动的时候,并不创建实例,当外部调用该类的实例或者该类实例方法的时候,才创建该类的实例。是以时间换空间。
实例在被使用的时候才被创建,可以节省系统资源,体现了延迟加载的思想。

/**
 * 饱汉式(懒汉式)----就是有钱,豪,用的时候再new(线程不安全)
 * <p>
 * Created by lxk on 2017/3/23
 */
public class SingletonPattern2 {
    private static SingletonPattern2 singletonInstance;
    private SingletonPattern2() {
    }
    public static SingletonPattern2 getSingletonInstance() {
        if (singletonInstance == null) {
            singletonInstance = new SingletonPattern2();
        }
        return singletonInstance;
    }
 
    ///**
    // * 为了应对上述的不安全,可以简单的如下操作给方法添加[synchronized],使之成为同步函数。
    // * 但是:
    // * 在很多线程的情况下,就每个线程访问都得判断锁,效率就是问题。所以,才有后面的[双重锁形式]
    // */
    //public static synchronized SingletonPattern2 getSingletonInstance() {
    //    if (singletonInstance == null) {
    //        singletonInstance = new SingletonPattern2();
    //    }
    //    return singletonInstance;
    //}
在任何需要生成复杂对象的地方,都可以使用工厂方法模式(比如 计算器的加减乘除)
步骤 1
创建一个接口:
Shape.java
public interface Shape { void draw(); }
步骤 2

创建实现接口的实体类。
Rectangle.java
public class Rectangle implements Shape { @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } }
Square.java
public class Square implements Shape { @Override public void draw() { System.out.println("Inside Square::draw() method."); } }
Circle.java
public class Circle implements Shape { @Override public void draw() { System.out.println("Inside Circle::draw() method."); } }
步骤 3

创建一个工厂,生成基于给定信息的实体类的对象。
ShapeFactory.java
public class ShapeFactory { //使用 getShape 方法获取形状类型的对象
    public Shape getShape(String shapeType){
        if(shapeType == null){ return null; }
        if(shapeType.equalsIgnoreCase("CIRCLE")){ return new Circle(); } else if(shapeType.equalsIgnoreCase("RECTANGLE")){ return new Rectangle(); } else if(shapeType.equalsIgnoreCase("SQUARE")){ return new Square(); } return null; } }
步骤 4

使用该工厂,通过传递类型信息来获取实体类的对象。
FactoryPatternDemo.java
public class FactoryPatternDemo {
    public static void main(String[] args) {
        ShapeFactory shapeFactory = new ShapeFactory(); //获取 Circle 的对象,并调用它的 draw 方法
        Shape shape1 = shapeFactory.getShape("CIRCLE"); //调用 Circle 的 draw 方法 shape1.draw(); //获取 Rectangle 的对象,并调用它的 draw 方法
        Shape shape2 = shapeFactory.getShape("RECTANGLE"); //调用 Rectangle 的 draw 方法 shape2.draw(); //获取 Square 的对象,并调用它的 draw 方法
        Shape shape3 = shapeFactory.getShape("SQUARE"); //调用 Square 的 draw 方法 shape3.draw();
    }
}
步骤 5

执行程序,输出结果:

Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.


sql优化
SELECT子句中避免使用‘*’;
表设计--合理的增加冗余的字段(减少表的联接查询);
在业务密集的SQL当中尽量不采用IN操作符,用EXISTS替代IN、用NOT EXISTS替代NOT IN;
用UNION-ALL 替换UNION /or( 如果有可能的话);
最左匹配原则
两个字段(name,age)建立联合索引,如果where age=12这样的话,是没有利用到索引的,这里我们可以简单的理解为先是对name字段的值排序,然后对age的数据排序,如果直接查age的话,这时就没有利用到索引了,查询条件where name=’xxx’ and age=xx 这时的话,就利用到索引了

失效条件
条件是or,如果还想让or条件生效,给or每个字段加个索引
避免在索引列上使用计算。WHERE子句中,如果索引列是函数的一部分。优化器将不使用索引而使用全表扫描;
低效: SELECT … FROM DEPT WHERE SAL * 12 > 25000; 高效: SELECT … FROM DEPT WHERE SAL > 25000/12;
模糊查询like 关键词%yue%,由于yue前面用到了“%”,因此该查询必然走全表扫描,除非必要,否则不要在关键词前加%;
注意:like ‘…%’,是会使用索引的;左模糊like

考虑索引重建场合
索引最大的好处是提高查询速度,
缺点是更新数据时效率低,因为要同时更新索引
对数据进行频繁查询进建立索引,如果要频繁更改数据不建议使用索引。
表上频繁发生update,delete操作。
表上发生了alter table ..move操作(move操作导致了rowid变化)

为什么要分库分表,主从复制,读写分离
针对大数据量并且访问频繁的表,将其分为若干个表
当一张表的数据达到几千万时,你查询一次所花的时间会变多,如果有联合查询的话,我想有可能会死在那儿了。分表的目的就在于此,减小数据库的负担,缩短查询时间;
主从结构,一主多备,读写分离,读从库,写主库
在实际的应用中,绝大部分情况都是读远大于写。Mysql提供了读写分离的机制,所有的写操作都必须对应到Master,读操作可以在 Master和Slave机器上进行,Slave与Master的结构完全一样,
所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟
提高数据库的吞吐量   

非关系型数据库中,我们查询一条数据,结果出来一个数组,关系型数据库中,查询一条数据结果是一个对象。
关系型数据库
SQLite、Oracle、mysql
特性:关系型数据库的最大特点就是事务的一致性;
优点:通用的SQL语言使得操作关系型数据库非常方便,大大减低了数据冗余和数据不一致的概率;
缺点:为了维护一致性所付出的巨大代价就是其读写性能比较差,海量数据的高效率读写;
非关系型数据库
MongoDb、redis、HBase
特性:使用键值对存储数据;严格上不是一种数据库,应该是一种数据结构化存储方法的集合
优点:无需经过sql层的解析,读写性能很高,nosql的存储格式是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,而关系型数据库则只支持基础类型。
缺点:不提供sql支持,学习和使用成本较高,无事务处理,附加功能bi和报表等支持也不好

数据库事务必须具备ACID特性,ACID是Atomic原子性,Consistency一致性,Isolation隔离性,Durability持久性
事务的四大特性
原子性--事务包含的所有操作要么全部成功,要么全部失败回滚
一致性--事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
隔离性--多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离.
持久性--一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

脏读:事务B读取事务A还没有提交的数据
不可重复读:两次事务读的数据不一致
幻读:事务A修改了数据,事务B也修改了数据,这时在事务A看来,明明修改了数据,昨不一样,
隔离级别
① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
② Repeatable read (可重复读):可避免脏读、不可重复读的发生。
③ Read committed (读已提交):可避免脏读的发生。
④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。

Spring 框架

spring管理的bean在默认情况下是会在服务器启动的时候初始化的。 
bean设置了scope为prototype(原型)之后,会每次使用时生产一个 
bean设置了lazy-init=”true”后,启动服务器不会马上实例化,而是在用到的时候被实例化
具体来说Spring是一个轻量级的容器,用于管理业务相关对象的。核心功能主要为:IOC,AOP,MVC。
Spring 的基础是ioc和 aop,ioc 提供了依赖注入的容器, aop解决了面向横切面的编程
控制反转IoC(Inversion of Control)
是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系,让容器管理对象的生命周期如创建,初始化,销毁等。
DI(依赖注入)其实就是IOC的另外一种说法
Spring MVC提供了一种轻度耦合的方式来开发web应用。它是Spring的一个模块,是一个web框架。通过Dispatcher Servlet, ModelAndView 和 View Resolver,开发web应用变得很容易
SpringBoot实现了自动配置,降低了项目搭建的复杂度。它主要是为了解决使用Spring框架需要进行大量的配置太麻烦的问题,所以它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具
spring-colud是一种云端分布式架构解决方案,基于spring boot,在spring boot做较少的配置,便可成为 spring cloud 中的一个微服务。

什么是AOP?
面向切面编程(AOP)完善spring的依赖注入(DI),面向切面编程在spring中主要表现为两个方面
1.面向切面编程提供声明式事务管理
2.spring支持用户自定义的切面
Spring AOP的核心实现原理就是采用的动态代理,根据被代理对象是否实现了所要被代理的接口这个条件,动态代理会选择不同的实现方案。
java中最常见的动态代理模式:jdk原生动态代理和cgLlb动态代理
JDK原生:不需要任何外部依赖,但只能基于接口进行代理,实现一个InvocationHandler,方法调用会被转发到该类的invoke()方法,在需要使用对象实例的时候,通过JDK动态代理获取实例的代理对象
cgLlb:通过继承的方式实现,无论目标对象有没有实现接口都可以代理,但是无法处理final的情况
其实最核心的还是java的反射机制,通过获取对象实例,调用对应的方法,前者通过接口来做桥梁,后者通过中间类转发,都只是形式而已

面向切面编程(aop)是对面向对象编程(oop)的补充,
面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。
AOP从程序运行角度考虑程序的结构,提取业务处理过程的切面,oop是静态的抽象,aop是动态的抽象,
是对应用执行过程中的步骤进行抽象,,从而获得步骤之间的逻辑划分。

aop框架具有的两个特征:
1.各个步骤之间的良好隔离性
2.源代码无关性

SpringMVC工作流程
1、用户发送请求至前端控制器DispatcherServlet
2、DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、处理器映射器找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给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响应用户

简单:系统启动的时候根据配置文件创建spring的容器, 首先是发送http请求到核心控制器DispatcherServlet,spring容器通过映射器去寻找业务控制器,
使用适配器找到相应的业务类,在进业务类时进行数据封装,在封装前可能会涉及到类型转换,执行完业务类后使用ModelAndView进行视图转发,
数据放在model中,用map传递数据进行页面显示。

值传递和引用传递的区别
值传递仅仅传递的是值。
引用传递,传递的是内存地址,修改后会改变内存地址对应储存的值。
用数组来举例就最清楚了,例如我们定义一个数组a[]={1,2};那么a[0]=1, a[1=2].
如果我们把数组a里的元素值作为参数传递,实际上只是进行了值传递,对数组本身没有影响
如果我们把 数组a的指针作为参数传递,那么假如处理的函数就可以直接修改数组a里的值。

== 和equals区别?
当“==”运算符的两个操作数都是包装器类型的引用,则是比较执行的是否是同于个对象,而如果两者比较中有一个操作数是表达式(含运算符)则比较的是数值(会自动触发拆箱的过程)
equals 用于判断两个变量是否是对同一个对象的引用

Session和Cookie的区别?
session是存放在服务器端的,cookie是存放在客户端的,cookie的安全性不高,虽然它已经加了密,但是还是可以伪造的。

死锁产生发生:
线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。
如何避免:
1、加锁顺序:
2、加锁时限:
解决方案:简单粗暴 找到进程号,kill 进程
 
String,StringBuffer和StringBuilder的区别
1、运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > String。
2、线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的。

适用场景分析:
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

mybatis如何处理结果集
MyBatis的结果集是通过反射来实现的
MyBatis里面的核心处理类叫做SqlSession

MyBatis(IBatis)的好处是什么
把sql语句从Java源程序中独立出来,放在单独的XML文件中编写,给程序的维护带来了很大便利。
封装了底层JDBC API的调用细节,并能自动将结果集转换成JavaBean对象,大大简化了Java数据库编程的重复工作
需要程序员自己去编写sql语句,程序员可以结合数据库自身的特点灵活控制sql语句,更高的查询效率

#{}与${}的区别
1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #{user_id},如果传入的值是111,那么解析成sql时的值为order by “111”, 如果传入的值是id,则解析成的sql为order by “id”.
2. $将传入的数据直接显示生成在sql中。如:order by ${user_id},如果传入的值是111,那么解析成sql时的值为order by 111, 如果传入的值是id,则解析成的sql为order by id.
${}方式会引发SQL注入的问题、同时也会影响SQL语句的预编译,所以从安全性和性能的角度出发,能使用#{}的情况下就不要使用${}。

在mapper中如何传递多个参数
第一种:使用 @param 注解   第二种:多个参数封装成map

分布式锁一般有三种实现方式:(乐观锁,自己实现,通过版本号 )
1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁
首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:
互斥性。在任意时刻,只有一个客户端能持有锁。
不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

加锁
1. 当前没有锁(key不存在),那么就进行加锁操作,并对锁设置个有效期,同时value表示加锁的客户端。

2. 已有锁存在,不做任何操作。
解锁
首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)

深入探索SpringApplication执行流程
如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:
    根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
    使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。实现了这个接口的这些实例将在上下文初始化的时候prepareContext被使用。
    使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。这个接口表示这个接口将监听对应的ApplicationEvent事件
    推断并设置main方法的定义类。
    
Spring 框架中的单例 Beans 是线程安全的么?
Spring 框架并没有对单例 bean 进行任何多线程的封装处理。关于单例 bean 的线程安全和并
发问题需要开发者自行去搞定。但实际上,大部分的 Spring bean 并没有可变的状态(比如
Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean
有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。
最浅显的解决办法就是将多态 bean 的作用域由“singleton”变更为“prototype”。
spring开发web 时要注意,默认Controller、Dao、Service都是单例的

@SpringBootApplication是一个复合注解,包括@ComponentScan,和@SpringBootConfiguration,@EnableAutoConfiguration。
@Autowired和@Resource(java原生)开启基于注解的自动装配
@Autowired是Spring的注解,@Resource是J2EE的注解
@Component是所有受Spring 管理组件的通用形式,@Component注解可以放在类的头上,@Component不推荐使用。
@Service对应的是业务层Bean
@Controller对应表现层的Bean
@RequestMapping 表明所有的请求路径都要基于该路径

Java集合类
(1)List  支持null元素和重复元素的动态扩容列表,jdk提供的实现类有:ArrayList, LinkedList, Stack,CopyOnWriteArrayList、Vector
(2)Set 不支持重复元素的动态扩容列表,jdk提供的实现类有:EnumSet, TreeSet, HashSet, LinkedHashSet、 NavigableSet、ConcurrentSkipListSet、CopyOnWriteArraySet
(3)map  是存储键/值对的映射集,jdk提供的实现类有:HashMap, TreeMap,LinkedHashMap、ConcurrentHashMap、HashTable、ConcurrentSkipListMap
(4)queue/deque  queue是在集合尾部添加元素,在头部删除元素的队列,deque是可在头部和尾部添加或者删除元素的双端队列

List 线程不安全的子类:ArrayList, LinkedList,
ArrayList:是一个容量动态扩张的集合,实现了RandomAccess接口,支持随机访问,可以通过List list = Collections.synchronizedList(new ArrayList(...))把它转成线程安全的集合,当然只是封装了对ArrayList的操作,保存同步而已,性能不是很高,所有的修改操作都要一个个同步。
LinkedList:内部是链表结果,非线程安全,添加元素的方法只是新增一个节点然后改变尾部节点和新增节点的引用链接,所以新增和删除操作比较快,但是不支持随机访问,同时实现List和Deque接口,所以即可以当一个双端队列使用,也可以当List使用

set 线程不安全的子类:EnumSet, TreeSet, HashSet, LinkedHashSet、 NavigableSet
treeSet是有序集合,内部通过TreeMap来存储元素,把元素存储在map的key里,通过TreeMap存储Key的有序性和无重复性来实现自己的有序性和Set的的元素无重复性
HashSet不是线程安全的

map    线程不安全的子类:HashMap, TreeMap,LinkedHashMap,键值对、键唯一、值不唯一
HashMap是线程不安全的提供所有Map操作的集合容器,支持Null为key或者value,并且是无序的。

HashMap是线程不安全的,不要在并发的环境中同时操作HashMap,建议使用ConcurrentHashMap。
jdk1.8后HashMap的存储结构,因为和1.7之前都不太一样,性能也不一样。
jdk1.8之前,hashMap的存储结构是数组+链表,也就是发生hash冲突后的元素后插入到链表里,而不是存储在数组,这样的的话如果对于hash冲突比较严重的数据时,hashMap的查询速度就不是o(1)了,而是o('n');
jdk1.8后,HashMap的存储结构是数组+链表or平衡树,因为平衡树的时间复杂度是o(log('n')),是1.8后HashMap的查询复杂度是O(1)~O(log('n' ))。
loadFactor是触发HashMap扩容的负载因子,默认是0.75,扩容是非常耗时的事情,所以这个负载因子很重要,0.75是性能比较好的一个数

如果你经常会使用索引来对容器中的元素进行访问,那么 List 是你的正确的选择。如果你已经知道索引了的话,那么 List 的实现类比如 ArrayList 可以提供更快速的访问,如果经常添加删除元素的,那么肯定要选择LinkedList。
如果你想容器中的元素能够按照它们插入的次序进行有序存储,那么还是 List,因为 List 是一个有序容器,它按照插入顺序进行存储。
如果你想保证插入元素的唯一性,也就是你不想有重复值的出现,那么可以选择一个 Set 的实现类,比如 HashSet、LinkedHashSet 或者 TreeSet。所有 Set 的实现类都遵循了统一约束比如唯一性,而且还提供了额外的特性比如 TreeSet 还是一个 SortedSet,所有存储于 TreeSet 中的元素可以使用 Java 里的 Comparator 或者 Comparable 进行排序。LinkedHashSet 也按照元素的插入顺序对它们进行存储。
如果你以键和值的形式进行数据存储那么 Map 是你正确的选择。你可以根据你的后续需要从 Hashtable、HashMap、TreeMap 中进行选择。

Comparable和Comparator区别比较
Comparable是排序接口,若一个类实现了Comparable接口,就意味着“该类支持排序”。而Comparator是比较器,我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。
Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
两种方法各有优劣, 用Comparable 简单, 只要实现Comparable 接口的对象直接就成为一个可以比较的对象,但是需要修改源代码。 用Comparator 的好处是不需要修改源代码, 而是另外实现一个比较器, 当某个自定义的对象需要作比较的时候,把比较器和对象一起传递过去就可以比大小了, 并且在Comparator 里面用户可以自己实现复杂的可以通用的逻辑,使其可以匹配一些比较简单的对象,那样就可以节省很多重复劳动了。

jdk1.8 lambam表达式
// 以前的循环方式  
for (String player : players) {  
     System.out.print(player + "; ");  
}  
// 使用 lambda 表达式以及函数操作(functional operation)  
players.forEach((player) -> System.out.print(player + "; "));  
使用Lambdas排序集合
// 1.3 也可以采用如下形式:  
Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2)));
readExcelList.stream().forEach(p->{ xxx });
java通过Stream对list集合分组
CompleteDtos.stream().collect(Collectors.groupingBy(CompleteRateDto::getIdKey)).values().forEach(p->{ xxx });

kafaka
Apache开发的一种发布订阅消息系统,它是一个分布式的、分区的和重复的日志服务。

nginx

 

在正向代理中,Proxy和Client同属于一个LAN(图中方框内),隐藏了客户端信息;

在反向代理中,Proxy和Server同属于一个LAN(图中方框内),隐藏了服务端信息;

负载均衡调度算法:

weight轮询(默认):接收到的请求按照顺序逐一分配到不同的后端服务器,即使在使用过程中,某一台后端服务器宕机,Nginx会自动将该服务器剔除出队列,请求受理情况不会受到任何影响。

ip_hash:每个请求按照发起客户端的ip的hash结果进行匹配,这样的算法下一个固定ip地址的客户端总会访问到同一个后端服务器

(1)保证内网的安全,通常将反向代理作为公网访问地址,Web服务器是内网
(2)负载均衡,通过反向代理服务器来优化网站的负载

Tomcat在高并发环境下处理动态请求时性能很低,而在处理静态页面更加脆弱,但是通过Nginx来处理静态页面要比通过Tomcat处理在性能方面好很多
Nginx可以通过两种方式来实现与Tomcat的耦合。
将静态页面请求交给Nginx,动态请求交给后端Tomcat处理。
将所有请求都交给后端的Tomcat服务器处理,同时利用Nginx自身的负载均衡功能,进行多台Tomcat服务器的负载均衡。

Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:
  ①Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。
  ②装载并创建该Servlet的一个实例对象。 
  ③调用Servlet实例对象的init()方法。
  ④创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
  ⑤WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。 

Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度。
  针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,servlet实例对象才会销毁。

ConcurentHashMap原理?

线程不安全的HashMap-- 因为多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap。 底层数组+链表实现,线程不安全

效率低下的HashTable容器 -- HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable的效率非常低下。因为当一个线程访问HashTable的同步方法时,其他线程访问HashTable的同步方法时,可能会进入阻塞或轮询状态

底层数组+链表实现 线程安全

ConcurrentHashMap --底层采用分段的数组+链表实现,线程安全,jdk 1.8后 变更为table数组+单向链表+红黑树的结构,负载因子恒定为0.75

通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。

锁分段技术:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

ConcurrentHashMap提供了与Hashtable和SynchronizedMap不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而在同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。

诸如get、put、remove等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的

 spring事务和jdbc事务的优缺点

Spring提供了对事务的声明式事务管理,只需要在配置文件中做一些配置,即可把操作纳入到事务管理当中,解除了和代码的耦合。

Spring声明式事务管理,核心实现就是基于Aop。

Spring声明式事务管理是粗粒度的事务控制,只能给整个方法应用事务,不可以对方法的某几行应用事务。

1.xml方式声明事务  2.注解方式声明事务

JDBC事务

JDBC的一切行为包括事务是基于一个Connection的,在JDBC中是通过Connection对象进行事务管理。在JDBC中,常用的和事务相关的方法是: setAutoCommitcommitrollback等。

JDBC为使用Java进行数据库的事务操作提供了最基本的支持。通过JDBC事务,我们可以将多个SQL语句放到同一个事务中,保证其ACID特性。JDBC事务的主要优点就是API比较简单,可以实现最基本的事务操作,性能也相对较好。

但是,JDBC事务有一个局限:一个 JDBC 事务不能跨越多个数据库

 

http原理

HTTP的工作过程

一次HTTP操作称为一个事务,其工作过程可分为四步:

1)首先客户机与服务器需要建立连接。只要单击某个超级链接,HTTP的工作开始。

2)建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和可能的内容。

3)服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。

4)客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客户机与服务器断开连接。

如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,有显示屏输出。对于用户来说,这些过程是由HTTP自己完成的,用户只要用鼠标点击,等待信息显示就可以了。

 

设计模式分哪几种

总体来说设计模式分为三大类:

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

Socket并发解决方案?

多线程同步实现?

synchronized和volatile的使用区别?

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是
    立即可见的。
2)禁止进行指令重排序。
   volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;
   synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
1.volatile仅能使用在变量级别;
   synchronized则可以使用在变量、方法、和类级别的
2.volatile仅能实现变量的修改可见性,并不能保证原子性;

   synchronized则可以保证变量的修改可见性和原子性
3.volatile不会造成线程的阻塞;
   synchronized可能会造成线程的阻塞。

mybatis和hibernate的优缺点

Hibernate的优点:

1、hibernate是全自动,hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql。

2、数据库移植性良好。

Hibernate的缺点:

1、学习门槛高,精通门槛更高,程序员如何设计O/R映射,在性能和对象模型之间如何取得平衡,以及怎样用好Hibernate方面需要的经验和能力都很强才行

2、hibernate的sql很多都是自动生成的,无法直接维护sql;虽然有hql查询,但功能还是不及sql强大,见到报表等变态需求时,hql查询要虚,也就是说hql查询是有局限的;hibernate虽然也支持原生sql查询,但开发模式上却与orm不同,需要转换思维,因此使用上有些不方便。总之写sql的灵活度上hibernate不及mybatis。

Mybatis的优点:

1、易于上手和掌握,提供了数据库查询的自动对象绑定功能,而且延续了很好的SQL使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。

2、sql写在xml里,便于统一管理和优化, 解除sql与程序代码的耦合。

3、提供xml标签,支持编写动态sql。

4、速度相对于Hibernate的速度较快

Mybatis的缺点:

1、关联表多时,字段多的时候,sql工作量很大。

2、sql依赖于数据库,导致数据库移植性差。

3、对象关系映射标签和字段映射标签仅仅是对映射关系的描述,具体实现仍然依赖于sql。

4、编写动态sql时,不方便调试,尤其逻辑复杂时。

java线程池有哪些?线程池的原理,什么场景下不适用线程池

ThreadLocal的理解

向ThreadLocal里面存东西就是向它里面的Map存东西的,然后ThreadLocal把这个Map挂到当前的线程底下,这样Map就仅仅属于这个线程了。

mysql和oracle的区别

最重要的区别
    MySQL是轻量型数据库,并且免费,没有服务恢复数据。
    Oracle是重量型数据库,收费,Oracle公司对Oracle数据库有任何服务。

对事务的提交
    MySQL默认是自动提交,而Oracle默认不自动提交,需要用户手动提交,需要在写commit;指令或者点击commit按钮

分页查询
    MySQL是直接在SQL语句中写"select... from ...where...limit  x, y",有limit就可以实现分页;而Oracle则是需要用到伪列ROWNUM和嵌套查询

JVM

类加载的几个过程

加载、验证、准备、解析、初始化。然后是使用和卸载了

堆里面的分区:Eden,survival (from+ to),老年代,各自的特点?

堆里面分为新生代和老生代(java8取消了永久代,采用了Metaspace),新生代包含Eden+Survivor区,survivor区里面分为from和to区,内存回收时,如果用的是复制算法,从from复制到to,当经过一次或者多次GC之后,存活下来的对象会被移动到老年区,当JVM内存不够用的时候,会触发Full GC,清理JVM老年区

当新生区满了之后会触发YGC,先把存活的对象放到其中一个Survice
区,然后进行垃圾清理。因为如果仅仅清理需要删除的对象,这样会导致内存碎
片,因此一般会把Eden 进行完全的清理,然后整理内存。那么下次GC 的时候,
就会使用下一个Survive,这样循环使用。如果有特别大的对象,新生代放不下,
就会使用老年代的担保,直接放到老年代里面。因为JVM 认为,一般大对象的存
活时间一般比较久远。

 

容器

HashMap 底层实现。

HashMap的底层通过位桶实现,位桶里面存的是链表(1.7以前)或者红黑树(有序,1.8开始) ,其实就是数组加链表(或者红黑树)的格式,通过判断hashCode定位位桶中的下标,通过equals定位目标值在链表中的位置,,当添加一个元素(key-value)时,就首先计算元素key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,但是形成了链表,同一各链表上的Hash值是相同的,所以说数组存放的是链表。而当链表长度太长时,链表就转换为红黑树,这样大大提高了查找的效率。
当链表数组的容量超过初始容量的0.75时,再散列将链表数组扩大2倍,把原链表数组的搬移到新的数组中。

HasMap的扩容机制resize()?

构造hash表时,如果不指明初始大小,默认大小为16(即Node数组大小16),如果Node[]数组中的元素达到(填充比*Node.length)重新调整HashMap大小 变为原来2倍大小,扩容很耗时

什么时候扩容:当向容器添加元素的时候,会判断当前容器的元素个数,如果大于等于阈值---即当前数组的长度乘以加载因子的值的时候,就要自动扩容啦。

扩容(resize)就是重新计算容量,向HashMap对象里不停的添加元素,而HashMap对象内部的数组无法装载更多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。方法是使用一个新的数组代替已有的容量小的数组,就像我们用一个小桶装水,如果想装更多的水,就得换大水桶。

Linux系统查看当前运行java的进程?

在Linux下查看所有java进程命令:ps -ef | grep java

停止特定java进程命令:kill -9 java进程序号

停止所有java进程命令:pkill - 9 java

HashSet类是如何实现添加元素保证不重复的?---哈希码的原理

当你利用HashSet创建一个对象时,在HashSet的构造方法中,先用hashCode方法计算出该对象的哈希码。
比较顺序以及原理
        (1).如果该对象哈希码与集合已存在对象的哈希码不一致,则该对象没有与其他对象重复,添加到集合中!
        (2).如果存在于该对象相同的哈希码,那么通过equals方法判断两个哈希码相同的对象是否为同一对象(判断的标准是:属性是否相同)
                1>.相同对象,不添加!
                2>.不同对象,添加!
final finally finalize区别

1、final修饰符(关键字)。被final修饰的类,就意味着不能再派生出新的子类,不能作为父类而被子类继承

2、finally是在异常处理时提供finally块来执行任何清除操作。不管有没有异常被抛出、捕获,finally块都会被执行

3、finalize是方法名。在垃圾收集器删除对象之前对这个对象调用的,将对象从内存中清除出去之前做必要的清理工作。

java的反射机制原理?

一  反射机制的概念:

指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能调用它的任意一个方法.这种动态获取信息,以及动态调用对象方法的功能叫java语言的反射机制.

二  反射机制的应用:

生成动态代理,面向切片编程(在调用方法的前后各加栈帧).

三  反射机制的原理:

1  首先明确的概念: 一切皆对象----类也是对象.

2  然后知道类中的内容 :modifier  constructor  field  method.

3  其次明白加载: 当Animal.class在硬盘中时,是一个文件,当载入到内存中,可以认为是一个对象,是java.lang.class的对象.

Hash冲突?

由于哈希算法被计算的数据是无限的,而计算后的结果范围有限,因此总会存在不同的数据经过计算后得到的值相同,这就是哈希冲突

Spring框架分为哪七大模块以及各模块的主要功能作用?

1. Spring Core: Core封装包是框架的最基础部分,提供IOC和依赖注入特性。这里的基础概念是BeanFactory,它提供对Factory模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。
2.Spring Context: 构建于Core封装包基础上的 Context封装包,提供了一种框架式的对象访问方法,有些象JNDI注册器。Context封装包的特性得自于Beans封装包,并添加了对国际化(I18N)的支持(例如资源绑定),事件传播,资源装载的方式和Context的透明创建,比如说通过Servlet容器。
3.Spring DAO:  DAO (Data Access Object)提供了JDBC的抽象层,它可消除冗长的JDBC编码和解析数据库厂商特有的错误代码。 并且,JDBC封装包还提供了一种比编程性更好的声明性事务管理方法,不仅仅是实现了特定接口,而且对所有的POJOs(plain old Java objects)都适用。
4.Spring ORM: ORM 封装包提供了常用的“对象/关系”映射APIs的集成层。 其中包括JPA、JDO、Hibernate 和 iBatis 。利用ORM封装包,可以混合使用所有Spring提供的特性进行“对象/关系”映射,如前边提到的简单声明性事务管理。
5.Spring AOP: Spring的 AOP 封装包提供了符合AOP Alliance规范的面向方面的编程实现,让你可以定义,例如方法拦截器(method-interceptors)和切点(pointcuts),从逻辑上讲,从而减弱代码的功能耦合,清晰的被分离开。而且,利用source-level的元数据功能,还可以将各种行为信息合并到你的代码中。
6.Spring Web: Spring中的 Web 包提供了基础的针对Web开发的集成特性,例如多方文件上传,利用Servlet listeners进行IOC容器初始化和针对Web的ApplicationContext。当与WebWork或Struts一起使用Spring时,这个包使Spring可与其他框架结合。
7.Spring Web MVC: Spring中的MVC封装包提供了Web应用的Model-View-Controller(MVC)实现。Spring的MVC框架并不是仅仅提供一种传统的实现,它提供了一种清晰的分离模型,在领域模型代码和Web Form之间。并且,还可以借助Spring框架的其他特性。

posted on 2019-01-14 10:57  风有衡  阅读(1697)  评论(0编辑  收藏  举报