java中高级面试
Posted 风有衡
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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会被初始化,此时类的私有构造函数会被调用,单例类的唯一实例将被创建。
懒汉式:
应用刚启动的时候,并不创建实例,当外部调用该类的实例或者该类实例方法的时候,才创建该类的实例。是以时间换空间。
实例在被使用的时候才被创建,可以节省系统资源,体现了延迟加载的思想。
在任何需要生成复杂对象的地方,都可以使用工厂方法模式(比如 计算器的加减乘除)
步骤 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是一个轻量级的容器,用于管理业务相关对象的。核心功能主要为: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支持用户自定义的切面
面向切面编程(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区别?
当“==”运算符的两个操作数都是包装器类型的引用,则是比较执行的是否是同于个对象,而如果两者比较中有一个操作数是表达式(含运算符)则比较的是数值(会自动触发拆箱的过程)
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语句的预编译,所以从安全性和性能的角度出发,能使用#{}的情况下就不要使用${}。
分布式锁一般有三种实现方式:(乐观锁,自己实现,通过版本号 )
1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁
首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:
互斥性。在任意时刻,只有一个客户端能持有锁。
不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
redis加锁代码
public class RedisTool {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
/**
* 尝试获取分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @param expireTime 超期时间
* @return 是否获取成功 */
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
}
1. 当前没有锁(key不存在),那么就进行加锁操作,并对锁设置个有效期,同时value表示加锁的客户端。2. 已有锁存在,不做任何操作。
解锁代码
public class RedisTool {
private static final Long RELEASE_SUCCESS = 1L;
/**
* 释放分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @return 是否释放成功 */
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
String script = "if redis.call(‘get‘, KEYS[1]) == ARGV[1] then return redis.call(‘del‘, KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
}
首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)
深入探索SpringApplication执行流程
如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:
根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
推断并设置main方法的定义类。
Spring 框架中的单例 Beans 是线程安全的么?
Spring 框架并没有对单例 bean 进行任何多线程的封装处理。关于单例 bean 的线程安全和并
发问题需要开发者自行去搞定。但实际上,大部分的 Spring bean 并没有可变的状态(比如
Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean
有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。
最浅显的解决办法就是将多态 bean 的作用域由“singleton”变更为“prototype”。
@Autowired和@Resource开启基于注解的自动装配
@Autowired是Spring的注解,@Resource是J2EE的注解
@Component是所有受Spring 管理组件的通用形式,@Component注解可以放在类的头上,@Component不推荐使用。
@Service对应的是业务层Bean
@Controller对应表现层的Bean
@RequestMapping 表明所有的请求路径都要基于该路径
Java集合类 io 多线程
1) Collection
一组"对立"的元素,通常这些元素都服从某种规则
1.1) List必须保持元素特定的顺序
1.2) Set不能有重复元素
1.3) Queue保持一个队列(先进先出)的顺序
Collection 每个位置只能保存一个元素(对象)
2) Map
一组成对的"键值对"对象
Map保存的是"键值对",就像一个小型数据库。我们可以通过"键"找到该键对应的"值"
Set继承自Collection接口,不能包含有重复元素(记住,这是整个Set类层次的共有属性)
Set判断两个对象相同不是使用"=="运算符,而是根据equals方法。也就是说,我们在加入一个新元素的时候,如果这个新元素对象和Set中已有对象进行注意equals比较都返回false,
则Set就会接受这个新元素对象,否则拒绝,因此对Set的构造函数,传入的Collection参数不能包含重复的元素
HashSet是Set接口的典型实现,HashSet使用HASH算法来存储集合中的元素,因此具有良好的存取和查找性能。当向HashSet集合中存入一个元素时,HashSet会调用该对象的
hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值决定该对象在HashSet中的存储位置。
Set:
1.不允许重复对象
2. 无序容器,你无法保证每个元素的存储顺序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序。
3. 只允许一个 null 元素
4.Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,因此 TreeSet 是一个根据其 compare() 和 compareTo() 的定义进行排序的有序容器。
List:
1.可以允许重复的对象。
2.可以插入多个null元素。
3.是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序。
4.常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于经常需要从 List 中添加或删除元素的场合更为合适。
map:
1.Map不是collection的子接口或者实现类。Map是一个接口。
2.Map的每个 Entry 都持有两个对象,也就是一个键一个值,Map 可能会持有相同的值对象但键对象必须是唯一的。
3.TreeMap也通过 Comparator 或者 Comparable 维护了一个排序顺序。
4.Map里你可以拥有随意个 null 值但最多只能有一个 null 键。
5.Map接口最流行的几个实现类是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)
如果你经常会使用索引来对容器中的元素进行访问,那么 List 是你的正确的选择。如果你已经知道索引了的话,那么 List 的实现类比如 ArrayList 可以提供更快速的访问,如果经常添加删除元素的,那么肯定要选择LinkedList。
如果你想容器中的元素能够按照它们插入的次序进行有序存储,那么还是 List,因为 List 是一个有序容器,它按照插入顺序进行存储。
如果你想保证插入元素的唯一性,也就是你不想有重复值的出现,那么可以选择一个 Set 的实现类,比如 HashSet、LinkedHashSet 或者 TreeSet。所有 Set 的实现类都遵循了统一约束比如唯一性,而且还提供了额外的特性比如 TreeSet 还是一个 SortedSet,所有存储于 TreeSet 中的元素可以使用 Java 里的 Comparator 或者 Comparable 进行排序。LinkedHashSet 也按照元素的插入顺序对它们进行存储。
如果你以键和值的形式进行数据存储那么 Map 是你正确的选择。你可以根据你的后续需要从 Hashtable、HashMap、TreeMap 中进行选择。
jdk1.8和jdk1.7 集合结构
aop动态代理详解
jdk1.8 lambam表达式
kafaka
nginx
Tomcat在高并发环境下处理动态请求时性能很低,而在处理静态页面更加脆弱,但是通过Nginx来处理静态页面要比通过Tomcat处理在性能方面好很多
Nginx可以通过两种方式来实现与Tomcat的耦合。
将静态页面请求交给Nginx,动态请求交给后端Tomcat处理。
将所有请求都交给后端的Tomcat服务器处理,同时利用Nginx自身的负载均衡功能,进行多台Tomcat服务器的负载均衡。
以上是关于java中高级面试的主要内容,如果未能解决你的问题,请参考以下文章