大家好,又见面了,我是你们的朋友全栈君。
问题大多取自点击打开链接 在网上找了一些答案,也添加了一些几乎是必问的题
一、 基础知识:
1) HashMap,LinkedHashMap,TreeMap的区别
1. HashMap,LinkedHashMap,TreeMap都属于Map。
2. Map的主要作用是用于存储键(key)值(value)对,根据键得到值,因此不允许键重复,但允许值重复。
3. HashMap是一个最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有最快的访问速度。HashMap最多只允许一条记录的键为Null;允许多条记录的值为Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用Collections的synchronizedMap方法使HashMap具有同步的能力。
4. LinkedHashMap也是一个HashMap,但是内部维持了一个双向链表,可以保持顺序;
5. TreeMap不仅可以保持顺序,而且可以用于排序;
2) ArrayList和LinkedList的区别
1. ArrayList是实现了基于动态数组的数据结构,LinkedList是基于链表的数据结构。
2. 对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针。
3. 对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。
3) 概述一下SpringMVC的工作原理
1. 客户端发出一个HTTP请求给web服务器,web服务器对HTTP请求进行解析,如果匹配DispatcherServlet的请求映射路径(在web.xml中指定,或者使用注解),web容器将请求转交给dispatcherServelet。
2. DispacherServelet接受到这个请求之后根据请求的信息(包括URL、Http方法、请求报文头和请求参数Cookie等)以及HandleMapping的配置找到处理请求的处理器Haddler。
3. DispatcherServlet根据HandlerMapping找到对应的Handler,将处理权交给Handler(Handler将具体的处理进行封装),再由具体的HandlerAdapter对Handler进行具体的调用。
4. Handler对数据处理完成以后将返回一个ModelAndView()对象给DispatcherServlet。
5. Handler返回的ModelAndView()只是一个逻辑视图并不是一个正式的视图,DispatcherSevlet通过ViewResolver将逻辑视图转化为真正的视图View。
6. Dispatcher通过model解析出ModelAndView()中的参数进行解析最终展现出完整的view并返回给客户端。
4) 集合类:List和Set比较
List是有序(存储元素的顺序和取出元素的顺序一致)的,Set是无序的;List集合中的元素都有索引,可以存储重复数据;Set不能存储重复数据。简单来说当存入的的对象有重复时,用List,没有重复元素时,用Set。
5) HashMap的底层实现;
首先有一个每个元素都是链表(可能表述不准确)的数组,当添加一个元素(key-value)时,就首先计算元素key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,但是形成了链表,同一各链表上的Hash值是相同的,所以说数组存放的是链表。而当链表长度太长时,链表就转换为红黑树,这样大大提高了查找的效率。
6) 如何实现HashMap顺序存储
LinkedHashMap里面有一个模拟的“双向循环链表”,用来保存entry的插入顺序,我也可以采用这种方法来在插入的时候保存key和value的有序。这里暂定名为OrderedHashMap,主要代码是从LinkedHashMap抄过来的,它也维护着两个模拟“双向循环链表”:keyHeader和valueHeader,保持key或value由小到大的顺序。当有个元素put进来后,除把它存在散列桶中外,还要在keyHeader按key的大小插入,也要在valueHeader上按value的顺序插入。想要输出的话,可以从这两个“头指针”向前或向后来迭代得到key或value有序的entry。(可以实现key和value,正序逆序的输出,只对数值型比较,如果不是数值型的话,如HashMap般正常处理)
7) HashMap、HashTable和ConcurrentHashMap的区别
HashMap和HashTable的区别一种比较简单的回答是:
1. HashMap是非线程安全的,HashTable是线程安全的,内部的方法基本都经过synchronized修饰。
2. 因为同步、哈希性能等原因,性能肯定是HashMap更佳。
3. HashMap允许有null值的存在,而在HashTable中put进的键值只要有一个null,直接抛出NullPointerException。
HashMap和ConCurrentHashMap的对比:
先对ConcurrentHashMap进行一些介绍吧,它是线程安全的HashMap的实现。
HashTable里使用的是synchronized关键字,这其实是对对象加锁,锁住的都是对象整体,当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。
1. ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁进行保护,相对于HashTable的syn关键字锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。
2. HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。
8) String,StringBuffer和StringBuilder的区别
String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)
9) wait和sleep的区别
对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。
sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
在调用sleep()方法的过程中,线程不会释放对象锁。
而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
10)JVM的内存结构,JVM的算法
JVM内存结构主要有三大块:堆内存、方法区和栈。堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分成三部分,Eden空间、From Survivor空间、To Survivor空间,默认情况下年轻代按照8:1:1的比例来分配;
方法区存储类信息、常量、静态变量等数据,是线程共享的区域,为与Java堆区分,方法区还有一个别名Non-Heap(非堆);栈又分为java虚拟机栈和本地方法栈主要用于方法的执行。
11)用过哪些设计模式,手写一个(除单例);
单例模式
实现方式:
a)将被实现的类的构造方法设计成private的。
b)添加此类引用的静态成员变量,并为其实例化。
c)在被实现的类中提供公共的CreateInstance函数,返回实例化的此类,就是b中的静态成员变量。
策略模式
实现方式:
a)提供公共接口或抽象类,定义需要使用的策略方法。(策略抽象类)
b)多个实现的策略抽象类的实现类。(策略实现类)
c)环境类,对多个实现类的封装,提供接口类型的成员量,可以在客户端中切换。
d)客户端 调用环境类 进行不同策略的切换。
注:Jdk中的TreeSet和 TreeMap的排序功能就是使用了策略模式。
观察者模式
观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
实现方式:
a)角色抽象类(提供对观察者的添加,删除和通知功能)。
b)角色具体类,实现a,维护一个c的集合(对角色抽象类的实现)。
c)观察者抽象类(被角色通知后实现的方法)。
d)观察者实现类,实现c(多个)。
注:JDK提供了对观察者模式的支持,使用Observable类和Observer接口
单例模式代码
- public class Singleton {
- private Singleton(){}
- private static class SingletonBuild{
- private static Singleton value = new Singleton();
- }
- public Singleton getInstance(){ return SingletonBuild.value ;}
- }
工厂模式代码
- interface food{}
- class A implements food{}
- class B implements food{}
- class C implements food{}
- public class StaticFactory {
- private StaticFactory(){}
- public static food getA(){ return new A(); }
- public static food getB(){ return new B(); }
- public static food getC(){ return new C(); }
- }
- class Client{
- //客户端代码只需要将相应的参数传入即可得到对象
- //用户不需要了解工厂类内部的逻辑。
- public void get(String name){
- food x = null ;
- if ( name.equals(“A”)) {
- x = StaticFactory.getA();
- }else if ( name.equals(“B”)){
- x = StaticFactory.getB();
- }else {
- x = StaticFactory.getC();
- }
- }
- }
12)SpringMVC的核心是什么,请求的流程是怎么处理的;
SpringMVC的核心是什么:
1、 IoC:控制反转
概念:控制权由对象本身转向容器;由容器根据配置文件去创建实例并创建各个实例之间的依赖关系
核心:bean工厂;在Spring中,bean工厂创建的各个实例称作bean
2、 AOP(Aspect-Oriented Programming): 面向切面编程
概念:简单来说AOP就是,每个人各司其职,灵活组合,达到一种可配置的、可插拔的程序结构。
从Spring的角度看,AOP最大的用途就在于提供了事务管理的能力。事务管理就是一个关注点,你的正事就是去访问数据库,而你不想管事务(太烦),所以,Spring在你访问数据库之前,自动帮你开启事务,当你访问数据库结束之后,自动帮你提交/回滚事务
请求的流程是怎么处理的:
SpringMVC框架是一个基于请求驱动的Web框架,并且使用了‘前端控制器’模型来进行设计,再根据‘请求映射规则’分发给相应的页面控制器进行处理。
1. 首先用户发送请求—— >DispatcherServlet , 分发器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;
2. DispatcherServlet ——>HandlerMapping , HandlerMapping 将会把请求映射为 HandlerExecutionChain 对象(包含一个 Handler 处理器(Controller)对象、多个HandlerInterceptor 拦截器)对象,通过这种策略模式,很容易添加新的映射策略;
3. DispatcherServlet ——>HandlerAdapter , HandlerAdapter 将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;
4. HandlerAdapter —— > 处理器功能处理方法的调用,HandlerAdapter 将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理(在调用处理器前会先执行spring的前置拦截器preHandle);并返回一个 ModelAndView 对象(包含模型数据、逻辑视图名),返回视图后会执行spring的后置拦截器postHandle;
5. ModelAndView 的逻辑视图名—— >ViewResolver , ViewResolver 将把逻辑视图名解析为具体的 View,通过这种策略模式,很容易更换其他视图技术;
6. View —— > 渲染 ,View 会根据传进来的 Model模型数据进行渲染,此处的 Model 实际是一个 Map 数据结构,因此很容易支持其他视图技术(这步处理完后执行spring的完成后拦截器);
7. 返回控制权给 DispatcherServlet ,由 DispatcherServlet 返回响应给用户,到此一个流程结束。
13)spring里面的aop的原理是什么
Spring实现AOP:JDK动态代理和CGLIB代理 JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理;其核心的两个类是InvocationHandler和Proxy。 CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强;需要引入包asm.jar和cglib.jar。 使用AspectJ注入式切面和@AspectJ注解驱动的切面实际上底层也是通过动态代理实现的。
14)mybatis如何处理结果集
把结果集拿到以后,从配置文件里获取对应的bean,类反射
15)java的多态表现在哪里;
多态:就是指相同的事物的不同状态,比如:水。水可以有三种状态:
气体、液体和固体。那么在JAVA中的多态也可以理解成这个意思,就是:
将父对象设置成为和一个或多个它的子对象相等的技术,
比如Parent=Child;
多态性使得能够利用同一类(父类)引用不同类的对象,
以及根据所引用对象的不同,以不同的方式执行相同的操作。
多态实现包括两种方式:重载和重写
例如:Animal a = new Tiger(); 这是一个老话题了,呵呵……
父类引用指向子类对象,Animal类中包含一个eat()方法,而Tiger类继承自
Animal类,如果子类重写了父类的eat()方法,则调用的时候,就可以按照子类
的形式调用,本质上是父类的方法,但是子类重写之后,就成了另一种方式,
这就是多态。
16)说说http,https协议;
HTTP 和 HTTPS 的相同点
大多数情况下,HTTP 和 HTTPS 是相同的,因为都是采用同一个基础的协议,作为 HTTP 或 HTTPS 客户端——浏览器,设立一个连接到 Web 服务器指定的端口。当服务器接收到请求,它会返回一个状态码以及消息,这个回应可能是请求信息、或者指示某个错误发送的错误信息。系统使用统一资源定位器 URI 模式,因此资源可以被唯一指定。而 HTTPS 和 HTTP 唯一不同的只是一个协议头(https)的说明,其他都是一样的。
HTTP 和 HTTPS 的不同之处
1.HTTP 的 URL 以 http:// 开头,而 HTTPS 的 URL 以 https:// 开头
2.HTTP 是不安全的,而 HTTPS 是安全的
3.HTTP 标准端口是 80 ,而 HTTPS 的标准端口是 443
4.在 OSI 网络模型中,HTTP 工作于应用层,而HTTPS 工作在传输层
5.HTTP 无需加密,而 HTTPS 对传输的数据进行加密
6.HTTP 无需证书,而 HTTPS 需要认证证书
17)tcp/ip协议簇是什么意思
TCP/IP是一个网络通讯协议群,它包含了很多通信协议。这些协议制定了网络设备、计算机连入网络以及数据是如何在它们之间进行传输的标准。TCP协议又叫传输控制协议,作用于传输层。IP协议又叫网络间互联协议,工作在网络层。它们都是TCP/IP协议簇中非常重要的协议。
18)osi五层网络协议;
OSI七层模型
OSI中的层功能 TCP/IP协议族
应用层文件传输,电子邮件,文件服务,虚拟终端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet
表示层数据格式化,代码转换,数据加密没有协议
会话层解除或建立与别的接点的联系没有协议
传输层提供端对端的接口 TCP,UDP
网络层为数据包选择路由 IP,ICMP,RIP,OSPF,BGP,IGMP
数据链路层传输有地址的帧以及错误检测功能 SLIP,CSLIP,PPP,ARP,RARP,MTU
物理层以二进制数据形式在物理媒体上传输数据 ISO2110,IEEE802,IEEE802.2
19)cookie和session的区别,分布式环境怎么保存用户状态;
Cookie
通俗讲,Cookie是访问某些网站以后在本地存储的一些网站相关的信息,下次再访问的时候减少一些步骤。另外一个更准确的说法是:Cookies是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器,是一种在客户端保持状态的方案。
Session
Session是存在服务器的一种用来存放用户数据的类HashTable结构。
区别:
最明显的不同是一个在客户端一个在服务端。因为Cookie存在客户端所以用户可以看见,所以也可以编辑伪造,不是十分安全。
Session过多的时候会消耗服务器资源,所以大型网站会有专门的Session服务器,而Cookie存在客户端所以没什么问题。
域的支持范围不一样,比方说a.com的Cookie在a.com下都能用,而www.a.com的Session在api.a.com下都不能用,解决这个问题的办法是JSONP或者跨域资源共享。
分布式环境怎么保存用户状态:
第一种:粘性session (Nginx)
原理:粘性Session是指将用户锁定到某一个服务器上,比如上面说的例子,用户第一次请求时,负载均衡器将用户的请求转发到了A服务器上,如果负载均衡器设置了粘性Session的话,那么用户以后的每次请求都会转发到A服务器上,相当于把用户和A服务器粘到了一块,这就是粘性Session机制。
优点:简单,不需要对session做任何处理。
缺点:缺乏容错性,如果当前访问的服务器发生故障,用户被转移到第二个服务器上时,他的session信息都将失效。
适用场景:发生故障对客户产生的影响较小;服务器发生故障是低概率事件。
实现方式:以Nginx为例,在upstream模块配置ip_hash属性即可实现粘性Session。
upstream mycluster{
#这里添加的是上面启动好的两台Tomcat服务器
ip_hash;#粘性Session
server192.168.22.229:8080 weight=1;
server192.168.22.230:8080 weight=1;
}
第二种:session持久化到数据库
原理:就不用多说了吧,拿出一个数据库,专门用来存储session信息。保证session的持久化。
优点:服务器出现问题,session不会丢失
缺点:如果网站的访问量很大,把session存储到数据库中,会对数据库造成很大压力,还需要增加额外的开销维护数据库。
20)git,svn区别
相同:
能记录文件的所有更改记录。这样是为了大量更改后,但是最后觉得还是原来的版本代码好,可以有记录回到过去,而不用采用 Copy 旧代码另存为某文件,然后某个时间从大量文件中找你需要的历史记录,版本控制帮我们做到了历史记录的存储,可以方便地查询及回滚到过去的某一版本。
不同:
git和其他版本控制系统(如 CVS)有不少的差别,git本身关心文件的整体性是否有改变,但多数的 CV S或 Subversion 系统则在乎文件内容的差异。因此git更像一个文件系统,直接在本机上获取数据,不必连接到主机端获取数据。
git 是用于Linux内核开发的版本控制工具。与CVS、Subversion(SVN) 一类的集中式版本控制工具不同,它采用了分布式版本库的作法,不需要服务器端软件,就可以运作版本控制,使得源代码的发布和交流极其方便。git的速度很快,这对于诸如Linux内核这样的大项目来说自然很重要。git最为出色的是它的合并追踪(merge tracing)能力。
SVN 是集中式或者有中心式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首先要从中央服务器哪里得到最新的版本,然后干活,干完后,需要把自己做完的活推送到中央服务器。集中式版本控制系统是必须联网才能工作,如果在局域网还可以,带宽够大,速度够快,如果在互联网下,如果网速慢的话,就纳闷了。
Git 是分布式版本控制系统,那么它就没有中央服务器的,每个人的电脑就是一个完整的版本库,这样,工作的时候就不需要联网了,因为版本都是在自己的电脑上。既然每个人的电脑都有一个完整的版本库,那多个人如何协作呢?比如说自己在电脑上改了文件A,其他人也在电脑上改了文件A,这时,你们两之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。
21)请写一段栈溢出、堆溢出的代码
- public class Test {
- //堆
- public void testHeap(){
- for(;;){
- ArrayList list = new ArrayList (2000);
- }
- }
- //栈
- int num=1;
- public void testStack(){
- num ;
- this.testStack();
- }
- public static void main(String[] args){
- Test t = new Test ();
- t.testHeap();
- t.testStack();
- }
- }
22)动态代理:JDK动态代理和CGLIB代理的区别
当一个对象(客户端)不能或者不想直接引用另一个对象(目标对象),这时可以应用代理模式在这两者之间构建一个桥梁–代理对象。按照代理对象的创建时期不同,可以分为两种:
静态代理:程序员事先写好代理对象类,在程序发布前就已经存在了;
动态代理:应用程序发布后,通过动态创建代理对象。
1.JDK动态代理
此时代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑。
代理模式在实际使用时需要指定具体的目标对象,如果为每个类都添加一个代理类的话,会导致类很多,同时如果不知道具体类的话,怎样实现代理模式呢?这就引出动态代理。
JDK动态代理只能针对实现了接口的类生成代理。
2.CGLIB代理
CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。
如果目标对象没有实现接口,则默认会采用CGLIB代理;
如果目标对象实现了接口,可以强制使用CGLIB实现代理(添加CGLIB库,并在spring配置中加入<aop:aspectj-autoproxy proxy-target-class=”true”/>)。
AOP包括切面(aspect)、通知(advice)、连接点(joinpoint),实现方式就是通过对目标对象的代理在连接点前后加入通知,完成统一的切面操作。
22) 转发与重定向的区别
转发是服务器行为,重定向是客户端行为。
二、 IO:
1) bio,nio,aio的区别
同步阻塞IO(JAVA BIO):
同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
同步非阻塞IO(Java NIO) :
同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。用户进程也需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问。
异步阻塞IO(Java NIO):
此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄(如果从UNP的角度看,select属于同步操作。因为select之后,进程还需要读写数据),从而提高系统的并发性!
(Java AIO(NIO.2))异步非阻塞IO:
在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。
2) 核心组件 Buffer,Channel,Selector等,用处是什么
Buffer
本质: 一块可写入数据,也可以从中读取数据的内存, 这块内存被包装成NIO buffer对象,并提供相关访问以便进行访问
Channel
与流类似,又有些不同:
1. 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
2. 通道可以异步地读写。
3. 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入
Seletor
Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。
3) NIO对比IO的有点在哪里
Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。
1. io是面向流的,也就是读取数据的时候是从流上逐个读取,所以数据不能进行整体以为,没有缓冲区;nio是面向缓冲区的,数据是存储在缓冲区中,读取数据是在缓冲区中进行,所以进行数据的偏移操作更加方便
2. io是阻塞的,当一个线程操作io时如果当前没有数据可读,那么线程阻塞,nio由于是对通道操作io,所以是非阻塞,当一个通道无数据可读,可切换通道处理其他io
3. nio有selecter选择器,就是线程通过选择器可以选择多个通道,而io只能处理一个
4) 哪些开源项目中用到了NIO
dubbo里的netty就大量用了nio
5) nio框架:dubbo的实现原理
dubbo作为rpc框架,实现的效果就是调用远程的方法就像在本地调用一样。如何做到呢?就是本地有对远程方法的描述,包括方法名、参数、返回值,在dubbo中是远程和本地使用同样的接口;然后呢,要有对网络通信的封装,要对调用方来说通信细节是完全不可见的,网络通信要做的就是将调用方法的属性通过一定的协议(简单来说就是消息格式)传递到服务端;服务端按照协议解析出调用的信息;执行相应的方法;在将方法的返回值通过协议传递给客户端;客户端再解析;在调用方式上又可以分为同步调用和异步调用;简单来说基本就这个过程。
三、 算法
1) 冒泡排序
原理:以从小到大排序为例,每一轮排序就找出未排序序列中最大值放在最后。
设数组的长度为N: (1)比较前后相邻的二个数据,如果前面数据大于后面的数据,就将这二个数据交换。
(2)这样对数组的第0个数据到N-1个数据进行一次遍历后,最大的一个数据就“沉”到数组第N-1个位置。
(3)N=N-1,如果N不为0就重复前面二步,否则排序完成。
以上就是冒泡排序的基本思想,按照这个定义很快就能写出代码:
- /**
- * 冒泡排序的第一种实现, 没有任何优化
- * @param a
- * @param n
- */
- public static void bubbleSort1(int [] a, int n){
- int i, j;
- for(i=0; i<n; i ){ //表示n次排序过程。
- for(j=1; j<n-i; j ){
- if(a[j-1] > a[j]){ //前面的数字大于后面的数字就交换
- //交换a[j-1]和a[j]
- int temp;
- temp = a[j-1];
- a[j-1] = a[j];
- a[j]=temp;
- }
- }
- }
- }// end
2) 快速排序
快速排序是冒泡排序的升级,他们都属于交换类排序,都是采用不断的比较和移动来实现排序的。快速排序是一种非常高效的排序算法,它的实现,增大了记录的比较和移动的距离,将关键字较大的记录从前面直接移动到后面,关键字较小的记录从后面直接移动到前面,从而减少了总的比较次数和移动次数。同时采用“分而治之”的思想,把大的拆分为小的,小的拆分为更小的,其原理如下:对于给定的一组记录,选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分,直到序列中的所有记录均有序为止。
Java实现:
- publicclass QuickSort {
- publicstaticvoidsort(int a[], int low, int hight) {
- int i, j, index;
- if (low > hight) {
- return;
- }
- i =low;
- j =hight;
- index = a[i]; // 用子表的第一个记录做基准
- while (i < j) { // 从表的两端交替向中间扫描
- while (i < j && a[j] >= index)
- j–;
- if (i < j)
- a[i ] = a[j];// 用比基准小的记录替换低位记录
- while (i < j && a[i] < index)
- i ;
- if (i < j) // 用比基准大的记录替换高位记录
- a[j–] = a[i];
- }
- a[i] = index;// 将基准数值替换回 a[i]
- sort(a, low, i – 1); // 对低子表进行递归排序
- sort(a, i 1, hight); // 对高子表进行递归排序
- }
- publicstaticvoidquickSort(int a[]) {
- sort(a, 0, a.length – 1);
- }
- publicstaticvoidmain(String[] args) {
- int a[] = { 49, 38, 65, 97, 76, 13, 27, 49 };
- quickSort(a);
- System.out.println(Arrays.toString(a));
- }
- }
3) 二分查找
二分查找也称折半查找(BinarySearch),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。
首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
Java实现:
四、 多线程相关
1) 阻塞队列的实现 参考ArrayBlockingQueue的底层实现
ArrayBlockingQueue:基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象时必须制定容量大小。并且可以指定公平性与非公平性,默认情况下为非公平的,即不保证等待时间最长的队列最优先能够访问队列。
LinkedBlockingQueue:基于链表实现的一个阻塞队列,在创建LinkedBlockingQueue对象时如果不指定容量大小,则默认大小为Integer.MAX_VALUE。
ArrayBlockingQueue基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长数组,以便缓存队列中的数据对象,这是一个常用的阻塞队列,除了一个定长数组外,ArrayBlockingQueue内部还保存着两个整形变量,分别标识着队列的头部和尾部在数组中的位置。 ArrayBlockingQueue在生产者放入数据和消费者获取数据,都是共用同一个锁对象,由此也意味着两者无法真正并行运行,这点尤其不同于LinkedBlockingQueue;按照实现原理来分析,ArrayBlockingQueue完全可以采用分离锁,从而实现生产者和消费者操作的完全并行运行。Doug Lea之所以没这样去做,也许是因为ArrayBlockingQueue的数据写入和获取操作已经足够轻巧,以至于引入独立的锁机制,除了给代码带来额外的复杂性外,其在性能上完全占不到任何便宜。 ArrayBlockingQueue和LinkedBlockingQueue间还有一个明显的不同之处在于,前者在插入或删除元素时不会产生或销毁任何额外的对象实例,而后者则会生成一个额外的Node对象。这在长时间内需要高效并发地处理大批量数据的系统中,其对于GC的影响还是存在一定的区别。而在创建ArrayBlockingQueue时,我们还可以控制对象的内部锁是否采用公平锁,默认采用非公平锁。
2) 为什么要用线程池
线程的执行过程:
创建(t1) 运行(t2) 销毁(t3)
线程运行的总时间 T= t1 t2 t3;
假如,有些业务逻辑需要频繁的使用线程执行某些简单的任务,那么很多时间都会浪费t1和t3上。
为了避免这种问题,JAVA提供了线程池
在线程池中的线程可以复用,当线程运行完任务之后,不被销毁。
因为创建线程开销比较大,当你的程序需要频繁地创建销毁一些相同的线程时,就可以先创建一定数量的线程,让他们睡眠,当需要线程的时候,就从里面拿一个出来跑,跑完了再放回去,这样就增加了效率
3) volatile关键字的用法
使多线程中的变量可见
4) 进程通讯的方式
消息队列,共享内存,信号量,socket通讯等
五、 数据库相关
1) msyql优化经验
1. 为查询缓存优化查询
2. 为搜索字段建索引
3. 为每张表设置一个ID
4. 使用 ENUM 而不是 VARCHAR
5. 表的垂直拆分
解决表字段过多的问题
拆分原则:
把不常用的字段放在一个表中
把大字段独立放在一个表中
把经常使用的字段放在一起
6. 表的水平拆分
解决表数据量的问题,拆分后表结构是一样的。
存在问题:跨分区表查询、统计及后台报表操作
2) mysql的语句优化
1. 尽量避免在列上运算,这样会导致索引失效
2. 使用 JOIN 时,应该用小结果集驱动大结果集,同时把复杂的 JOIN 查询拆分成多个query,因为 JOIN 多个表,可能导致更多的锁定和堵塞
3. 使用 LIKE 时,避免使用 %%
4. select 指定查询字段,不要全查出来,节省内存
5. 使用批量插入语句节省交互
6. limit的基数比较大时,使用 between,between 限定比 limit 快,但是between也有缺陷,如果id中间有断行或是中间部分id不读取的情况,数据会少
7. 不要使用 rand 函数取多条随机记录
8. 避免使用 NULL
9.
不要使用 count(id)
, 而应该是 count(*)
10. 不要做无谓的排序操作,而应尽可能在索引中完成排序
3) 说说事务的特性和隔离级别
隔离级别:
1. 脏读
脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。例如:用户A向用户B转账100元,对应SQL命令如下
update account set money=money 100 where name=’B’; (此时A通知B)
update account set money=money – 100 where name=’A’;
当只执行第一条SQL时,A通知B查看账户,B发现确实钱已到账(此时即发生了脏读),而之后无论第二条SQL是否执行,只要该事务不提交,则所有操作都将回滚,那么当B以后再次查看账户时就会发现钱其实并没有转。
2. 不可重复读
不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发送了不可重复读。
不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。
在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主。但在另一些情况下就有可能发生问题,例如对于同一个数据A和B依次查询就可能不同
3. 虚读(幻读)
幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。
幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。
MySQL数据库为我们提供的四种隔离级别:
1) Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
2) Repeatable read (可重复读):可避免脏读、不可重复读的发生。
3) Read committed (读已提交):可避免脏读的发生。
4) Read uncommitted (读未提交):最低级别,任何情况都无法保证。
4) 悲观锁和乐观锁的区别,怎么实现
1. 悲观锁:即很悲观,每次拿数据的时候都觉得数据会被人更改,所以拿数据的时候就把这条记录锁掉,这样别人就没法改这条数据了,一直到你的锁释放。
2. 乐观锁:即很乐观,查询数据的时候总觉得不会有人更改数据,等到更新的时候再判断这个数据有没有被人更改,有人更改了则本次更新失败。
一个典型的依赖数据库的悲观锁调用:
select * from account where name=”rica” forupdate
悲观锁,也是基于数据库的锁机制实现。
乐观锁,大多是基于数据版本(Version)记录机制实现,需要为每一行数据增加一个版本标识(也就是每一行数据多一个字段version),每次更新数据都要更新对应的版本号 1。
乐观锁的工作原理:读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
乐观锁还可以基于timestamp、和ALL等;
all 就是让该记录所有的字段都为版本控制信息 更新的时候把该记录所有更新前的数据作为WHERE条件。
timestamp 就是把查询出来的时候的时间作为更新的时候的条件与数据库记录进行比对是否相等
select * from users whereuserName=’fudong’;
update users set userSex=’女’,updateDate=sysDate where userName=’fudong’ and updateDate=oldDate;
5) 乐观锁会产生什么问题
ABA问题
乐观锁只在提交时会对原始数据进行对比
但是如果期间发生过由A—B—A 的问题,乐观锁会认为数据是正常的
解决这个问题的办法是加入版本号,然后提交时做对比
六、 nosql相关(主要是redis)
1) redis和memcache的区别
1. Redis中,并不是所有的数据都一直存储在内存中的,这是和Memcached相比一个最大的区别。
2. Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储。
3. Redis支持数据的备份,即master-slave模式的数据备份。
4. Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
2) redis是如何持久化的:rdb和aof
RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照
优势
RDB 文件要比AOF小
RDB的性能更好
劣势
RDB的持久化不够及时
RDB持久化时如果文件过大可能会造成服务器的阻塞,停止客户端请求
AOF redis会将每一个收到的写命令都通过write函数追加到文件中(默认是appendonly.aof)。
优势
AOF的持久性更加的耐久(可以每秒 或 每次操作保存一次)
AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis协议的格式保存,因此 AOF 文件的内容非常容易被人读懂,对文件进行分析(parse)也很轻松。
AOF是增量操作
劣势
对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积
根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。
当 Redis 启动时,如果 RDB 持久化和 AOF 持久化都被打开了,那么程序会优先使用 AOF 文件来恢复数据集,因为 AOF 文件所保存的数据通常
七、 linux相关
1) linux常用的命令有哪些
1. cd命令
2. ls命令
3. grep命令
4. find命令
5. cp命令
6. mv命令
7. rm命令
8. kill命令
9. file命令
10. tar命令
11. cat命令
12. chmod命令
2) 如何获取java进程的pid
ps -ef | grep java
kill -9 XXXXX XXXXX为上述查出的序号
3) 如何实时打印日志
cat /var/log/*.log
如果日志在更新,如何实时查看 tail -f /var/log/messages
还可以使用 watch -d -n 1 cat /var/log/messages
-d表示高亮不同的地方,-n表示多少秒刷新一次。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/147946.html原文链接:https://javaforall.cn