一: 前言
二: 面试挑战
在文章开始前,首先安利下"面试挑战": 凡是满足下面的挑战条件的,如果一个月内没有拿到一个Offer的,免费提供简历封装建议和相关面试题目解答。
如果面试通过的,可以截图分享领取奖励,让大家一起见证,具体条件如下:
1、持续学习本人《面试大全》至少两个月且对其中的内容基本掌握的。
2、计算机相关专业或者经过计算机相关专业的培训(不少于3个月,正常来说培训机构培训时间不会少于三个月),准备从事JAVA开发人员。
3、 打算从事的工作是JAVA开发,年限是1-3年(大神的话就忽略我说的,可以直接退出了)或者是符合计算机相关专业,准备从学校出来就业的。
4、需要提供相关面试证据或者是面试题目。
如果大家对这个感兴趣的,可以关注【IT学习日记】回复【面试挑战】即可参与,现在参与还免费赠送一份面试资料。
三: JAVA基础知识(四)
一: Error和Exception的区别
相同点: 两者都继承自Throwable类。
(一): Error
错误,表示系统级别的错误,如系统崩溃、虚拟机错误、内存不做等,程序是不能够改变和处理的,是在程序编译或者运行时出现的错误,需要修改程序,一般指的是虚拟机相关的问题。
(二): Exception
异常,表示程序可以处理的异常,可以在代码进行捕获处理可以恢复的,遇到这类异常,尽可能处理,让程序恢复正常。
(三): Exception被划分为check exception和uncheck exception两种。
Check Exception: 可以检测的异常,在编译的时候就会提示,这种异常必须处理,如IOException。
Uncheck Exception: 不可检查异常,表示程序中编译其中无法检测出的异常,只有在运行时才可能出现,这种异常都归属于:Runtime Exception如常见的:NullPoint Exception,这种异常能够被发现和提前处理是依赖于开发者的经验。
(四): 常见的异常有哪些:
1、Null Point Exception(NPE): 空指针异常
2、ArrayIndexOutOfBoundsException: 数组索引越界异常
3、StringIndexOutOfBOundsException: 字符串索引超出范围,越界
4、ClassCastException: 类型转换异常
5、IllegalArgumentsException: 非法参数异常
ClassNotFoundException属于check exception,在编译时就会被检测出,必须处理。
二: JAVA中堆和栈的区别
堆栈/栈:
栈又名堆栈,是一种数据结构,其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底,有先进后出的特点。
堆:
一种特别的树形数据结构,它总是满足堆中某个节点的值总是不大于或不小于其父节点的值和堆总是一棵完全二叉树,其中将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
区别:
1、空间分配:
栈的空间是由操作系统自动分配和释放,主要是用于存储局部变量、方法调用等功能。
堆的空间是在程序运行时自动分配的而不需要告诉编译器,回收时通过垃圾回收器进行处理,主要是用于存储对象。
2、存取效率
因为堆的大小是运行时动态分配的,所以它的存取效率会比较慢,而栈的大小需要在编译器就定下来,缺乏灵活性,但是它的存取效率会更高,且栈数据可以共享
三: 在浏览器输入一个网址到得到服务器响应的经历的过程
(一) 延伸知识:
1、DNS即域名解析系统,可以简单的理解为是存储域名和IP地址映射关系的一个数据表,被设计出来的目的是为了让用户不用记住繁琐的IP地址,而是可以直接通过容易记住的域名进行访问网站。
2、域名的TTL值:
指的是一条域名解析记录在域名服务器存留的时间,TTL值只能在权威服务器修改。
3、存在的场景:
当本地域名服务器接收到解析请求,但是缓存中不存在对应的解析记录时,会将解析请求发送到根域名服务器,根域名服务器则会返回解析请求中对应的主域名/顶级服务器地址,然后本地服务器再将解析请求发送给顶级域名服务器进行解析获取到响应的结果,本地服务器再获取到这个结果后会缓存在服务器一段时间,如果在缓存时间内再获取到对应的结果,则会直接响应解析请求,这个存留的时间就称为TTL值。
(二) 经历步骤:
1、检查浏览器缓存看是否存在域名和IP的映射,如果存在,则直接返回对象的IP,然后通过IP向服务器访问地址栏的资源。
2、如果没有命中浏览器缓存,则会检查当前操作系统的缓存是否存在对应的解析过程。在操作系统中,也存在着如下的解析过程: C盘 -> window文件夹 -> System32 -> drivers -> etcs -> hosts。如果在对应的hosts中配置了域名对应的ip,则会优先使用配置的,这是hosts文件被设置只读的原因(因为如果hosts被恶意修改,这样会在访问时被指向恶意的第三方地址,从而实现域名劫持)
3、如果操作系统缓存和hosts也没有命中,则会请求本地域名解析系统(LDNS)进行解析,这个解析系统分布在各个区域,一般是找离当前机器最近的。
4、如果第3步完成也没有找到域名对应的IP,此时LDNS则会将解析请求发送给根域名解析器(Root Server)进行解析。
5、根域名解析接收到LDNS的解析请求后,会返回一个当前解析请求查询域中的主域名服务器地址(gTLD Servcer)
6、本地域解析系统在接收到这个地址后,将解析请求再发送给对应的主域名服务器进行解析。
7、主域名服务器根据这个解析请求返回域名对应的网站注册的域名服务器。
8、网站注册域名服务器则根据域名去查询到对应的IP地址并返回给本地域名解析系统,本地域名解析系统缓存当前域名和IP的映射关系,然后将结果返回给用户。
9、用户接收到解析结果并根据对应的TTL值缓存到本地系统中,域名解析过程结束。
10、域名解析后,得到需要访问的IP地址,然后通过IP地址,客户端访问指定服务器的资源。
11、服务器接收到用户请求,进行业务处理,并向客户端响应请求结果。
12、客户端接收到响应,进行界面渲染,请求-响应的流程完成。
四: 水平拓展详解
一: 典型的互联网系统架构图
二: 如上图所示,常见的系统架构分层如下
1、客户端层: 就是常见的浏览器、APP。
2、反向代理层: 此层用于反向代理和负载均衡,常见的软件:nginx,硬件F5。
3、前端应用层: 现在的系统一般是前后端分离并单独部署,此处属于前端项目。
4、服务层: 后端服务群,提供给前端项目调用。
5、缓存层: 介于服务层和数据库层之间,通过缓存,减少IO次数、提高吞吐量和响应速度。
6、数据库层: 实际的数据存储区域,可以进行读写分离,通过binlog日志进行同步数据。
五: 系统架构不同层级的水平拓展方式
1、针对客户端层优化
此处的逻辑主要是用户通过域名访问应用,但是在访问前,需要通过DSN进行域名和IP地址的转换。
优化方案: 利用DNS负载均衡,给同一个域名配置多个IP地址,在应答的时候,DNS会对每个查询按DNS中配置的IP地址顺序返回,从而将客户端引导到不同的服务器上。
CDN(Content Delivery Network)内容分发网络也是利用DNS的重定向技术,DNS会返回一个离用户最近的IP地址作为响应,CDN节点的服务器负责响应用户的请求,提供所需的内容。
2、反向代理层优化
如果反向代理层成为了性能的瓶颈时,可以通过添加新服务器,安装新的反向代理来水平拓展反向代理的性能(如果是使用硬件的话,则需要购买新的硬件),理论上来说可以承受无限制的并发量。
3、前端应用层优化
前后端的项目分离部署,将前端项目部署到处理静态文件更好的服务器(如nginx处理静态文件的效率就远大于tomcat服务器)这本身就是对性能的一种优化,同时,也降低了系统的耦合性。
同时,前端层面也可以在代码层级做性能优化,如缓存条件,如果界面界面切换时条件没有改动,可以直接读取之前的数据,不再向后端发起请求。
4、服务层优化
前端应用层通过RPC远程调用服务,当性能成为瓶颈时,可以添加服务器数量,将服务部署到新机器中,理论上来说,可以兼容无限制的并发(可以使用如euraka注册中心这种自动注册和发现的组件来实现服务层自动扩容)。
5、缓存层优化
缓存层本身就是使用”空间换时间”的方式来提高系统的效率和吞吐量,如果缓存层因为数据量出现性能瓶颈时,可以添加新的服务器来实现水平拓展。
6、数据层的优化
当系统数据量上升,单机性能达到瓶颈时,可以添加新的机器,通过读写分离,数据水平拆分存储到不同服务器来提高系统的性能。
六: 数据库常见的水平拆分的方式
一: 按指定规则划分
定义:
如根据字段取值范围划分为不同模块,属于某个范围的数据存储在库1,另外一个范围的存储在库2,以此类推,这样将单库单表的数据转移到了多库多表,提高了系统的性能。
优点:
划分方式简单,实现相对容易。
比较容易拓展,如果数据量继续增大,可以将后面的数据划分到新的库表。
数据的分配相对均匀。
缺点:
数据的负载不一定均匀,因为可能某一部分数据非常活跃(如新用户),这部分数据的请求压力会很大。
二: 按哈希取模规则水平拆分
定义:
对指定字段(或者部分字段)进行哈希取模运算,根据奇偶数存储在不同的库中,这样也可以将单库数据水平拆分到多库,提高系统的性能。
优点:
规则简单,实现相对容易。
数据分配均匀。
请求均匀性比第一种方式更好(一般不会出现都是对某个字段取模后只是奇数或者偶数请求非常活跃的清理)
缺点:
数据的拓展性比较差,如果数据量持续增大,hash规则需要变动时,可能需要进行数据迁移。
七: 水平拆分和读写分离的区别
作用:
提高数据库的性能
区别:
1、水平拆分中每个库只是存储部分数据,但是读写分离中库存储的是全部的数据。
2、水平拆分中各个服务器数据的总和就是数据的总和。
3、理论上来说,水平拆分后读写性能都会随之提升n倍(n是拓展的机器),但是读写分离中写的逻辑还是在主数据库,且每个库存储的数据都是全量,性能提升不会提升很高,但是读的性能会提升很高。
八: 谈谈懒加载
定义:
懒加载模式是bean在第一次调用时候被实例,而不是spring容器启动时候,默认是不开启的,官方描述:A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup。
作用:
是使用时再加载资源,可以提升加载速度,减轻服务器压力,提高用户体验
实现方式:
xml方式: 在bean的配置文件中设置lazy-init=true
注解方式: 在延迟加载类中使用@Lazy注解
九: 过滤器和拦截器的区别
过滤器(Filter):
在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是AOP的一种实现策略。
拦截器(Interceptor):
过滤器是一个程序,它先于与之相关的servlet或JSP页面运行在服务器上。过滤器可附加到一个或多个servlet或JSP页面上,并且可以检查进入这些资源的请求信息。
相似之处:
1、两者都是AOP编程思想的一个落地实现。
2、两者都可以实现权限鉴定,日志记录等。
区别:
1、过滤器是基于函数回调,拦截器是基于反射。
2、过滤器依赖于Servlet容器,而拦截器不依赖于Servlet容器,它是spring框架支持的,所以它可以使用spring中的任何资源、对象如数据源、事务管理等。
3、拦截器可以获取IOC容器中的各个bean,而过滤器就不行,在拦截器里注入一个service,可以调用业务逻辑。
4、使用细节、范围不一样,过滤器只是在Servlet前后起作用,而拦截器能够在方法前后、异常抛出前后等地方使用,它具有更大的弹性。
5、拦截器是被包裹在过滤器之中的,具体图形如下((图来源于网络,侵删)
十: 如何给每个请求的url创建 一个唯一ID,这个代码应该创建在哪里
(一) 生成唯一ID的方案:
1、使用UUID(Universally Unique ldentifier),它是标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符, 示例:110e8400-e29b-41d4-a716-446655440011
特点:
性能非常高:本地生成,没有网络消耗
唯一性但无序
2、使用Redis,通过INCR或者INCRBY命令生成唯一ID,因为Redis是单线程执行天然保证了原子性。
特点:
需要搭建集群保证高可用
生成唯一ID的地方:
因为需要对每个请求的url都生成一个唯一的ID,所以生成ID的代码应该存放在拦截器中,对所有的请求进行拦截并生成唯一的ID。
四: 总结
由于文章篇幅的限制,面试大全的第四章暂时到这里就告一段落。如果有意见或者建议,可以在下方或者私信留言,看到会及时回复,也欢迎大家参加面试挑战和面试题投稿,希望大家早日获得心仪的Offer,如果觉得文字对你有帮助,欢迎关注和点赞。
面试大全系列文章会保持稳定的更新速度,大概每周两更到三更,感兴趣和可以关注我。
如果想参加面试挑战,可以私信回复【面试挑战】即可,如果想进行面试题目投稿,可以私信回复【面试题目投稿】即可,如果想获取更多面试问题和资料,查看最新的面试题目更新进度,可以关注我,私信【面试资料】即可,谢谢大家的阅读和关注。
五: 热门推荐
1、跳槽者、应届生必看JAVA面试题系列(一)
2、跳槽者、应届生必看JAVA面试题系列(二)
3、跳槽者、应届生必看JAVA面试题系列(三)
4、面试宝典(一) - 让你不再错过“金九银十“的求职浪潮之简历包装篇
5、轻松写出优雅的Restful风格API