java面试1


1.List和Set的区别:
List继承自Collection,是有序的,其实现类ArrayList,是一个动态数组,擅长随机访问,使用数组存储,查询效率会高一些,因为是连续的地址,ArrayList要移动数据,所以插入和删除的时候效率较低。ArrayList的使用会比Vector快,他是非同步的,多线程安全的【多线程访问同一个代码块,不会产生不确定的结果】如果涉及到多线程,使用Vector会较好一些,Vector类中有很多方法是被Synchornize进行修饰,这样就导致了在效率上低于 ArrayList;LinkList基于链表的书数据机构,所以在开辟内存空间上不需要连续的地址,对于添加或者删除新的节点,效率会高一些,LinkList适用于要头尾操作或者指定位置的产插入或者删除的场景,继而,因为LinkList需要移动指针,所以查询效率会更底一些。
当然LinkList和ArrayList的使用场景还是可以这样理解,在需要对数据频繁的删除或者添加时候,用采用LinkList,相反对数据访问的场景较多,选择ArrayList较好。
TreeSet:是二叉树(红黑树的数据结构)实现的,其数据是排好序的,不允许加入空值。
HashSet:是哈希表实现的,HashSet中的数据是无序的,可以放入空值,但是只能放入一个空值,两者中的值都不能重复,就如同数据库主键约束一样。HashSet要求放入的对象必须实现HashCode方法,放入的对象,是以hashcode码作为标志的。hashset是基于hash算法实现的,查询效率要明显高于treeset,而如果需要排序的时候,才使用treeSet。
HashMap:基于Hash表实现,使用HashMap要求添加的键类明确定义了hashCode和equals方法。
TreeMap:基于红黑树实现的,没有调优选项,因为该树总是处于平衡状态的。
HashMap和HashTable:HashMap去掉了HashTable中的contains方法,但是加上了containsValue()和containsKey()方法,适用于插入,删除和定位元素。HashTable是同步的,线程安全的,HashTable是非线程安全的,效率上要比HashTable高,HashMap允许空键值,HashTable不允许。HashTable适用于按自然顺序或者自定义顺序遍历key。

2.HashTable和ConcurrentHashMap的区别是什么?
答:他们都适用于多线程环境,但是当HashTable当大小增加到一定时候,性能会急剧下降,因为迭代时需要被锁定很长的时间,但是ConcurrentHashMap引入了分割,不乱数据变的多大,仅仅需要锁定map中的某个部分,而其他线程不需要等到迭代完成才能访问map,它锁住的仅仅是map的部分,而HashTable是锁住整个map。

3.sleep和wait方法的区别?
答:首先sleep()是Thread类的静态方法,wait()是object的方法,这在本质上还是有区别的,sleep不能改变对象的机锁,所以当一个Synchronized块中调用Sleep()方法,线程虽然休眠了,但是对象的机锁并木有被释放,其他线程无法访问这个对象,即使睡着了也同样持有该对象锁。在sleep()结束休眠后,该线程并不一定会立即执行,因为其他线程可能正在执行而且没有被调度为放弃执行,除非线程有更高的优先级,对于CPU资源来说,不管哪个方式暂停的线程,都表示暂时不需要CPU时间,OS会将执行时间分配给别的线程,区别是使用wait()方法后,释放了该对象的机锁【暂时失去】,需要使用notify/notifyall来唤醒等待池中的线程才能重新获取CPU执行时间,wait()方法必须放在Synchornized 代码块中,否则会抛出异常。

多进程模型:
讨论两种模型,
多进程单线程模型 和 单进程多线程模型 。
多进程模型有更强的容错性,比起多线程的一个好出是一个进程奔溃了不会影响其他的进程。数据能到达到容错隔离,对于多线程架构的车程序一般可以做到一定程度的自动恢复能力,(master守护进程监控着所有的worker进程,发现进程挂了会将其重启)。
目前nginx主流的是多进程模型,,但是也同样支持多线程模型。几乎所有的web服务器也是多线程的,至少有一个守护进程配合一个worker线程,例如:apache,httpd等等以d结尾的进程,包括init.d本身就是0级总线程。Redis也可归类为多进程单线程模型,平时工作基本是单个进程。
多线程模型:创建速度快,方便高效的数据共享,共享数据:多线程可以共享同一个虚拟地址空间,多进程见的数据共享需要用到共享内存,信号量等IPC技术。常见的应用场景:键盘的输入,立刻响应。

4.JVM的内存模型?
答:JVM的内存结构主要有三块,堆内存,方法区和栈。

java字节码 (byte code):是java虚拟机(JVM)可执行的一种虚拟指令格式,也就是class文件,然后能在JVM中执行,机器码(machine code)只能有电脑CPU来执行,java在运行的时候把字节码转换为机器码。
程序计数器:可以看作是当前线程执行 字节码行号 的指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、
线程恢复等基础功能都需要依赖这个计数器来完成。
java虚拟机中的 还可理解为:java虚拟机栈或者说是描述成java虚拟机中局部变量表部分,存放了编译期间可知的各种基本数据类型(boolean,int, byte,short ,long,float,double,char)和对象的引用(reference)和returnAddress,
是java虚拟机中所有线程共享的一部分内存,在虚拟机启动的时候创建,所有的对像实例以及数组都要在堆上进行分配,而且堆也是垃圾回收的主要区域,(Darbage Collect Heap)GC堆,“垃圾堆”。
方法区 可以存储一些运行期的常量池(编译期间产生的字变量和)还有对象类型数据,包括对象类型,父类,实现的接口,方法等。
数组中简单的值类型数组类型,每个数组是一个引用,引用到栈上的空间,引用类型,类类型的数组,每个数组成员任是一个引用,引用到堆上的空间,因为类的实例分配在堆上。

5.String和StringBuffer,StringBuilder的区别?
答:其实讨论String到底是可变还是不可变,本质上是值对象中的value[]字符数组可不可变,而不是对象的引用可不可变。关于StringBuilder和StringBuffer的效率问题,两者可谓是前世今生,StringBuilder的前世是StringBuffer,自1.5引入,StringBuilder的效率比StringBuffer稍微高,如果不考虑线程安全,StringBuilder应该是首选。JVM运行程序主要耗费在对象的创建和回收对象上。

HTTP的三次握手和四次挥手
HTTP的三次握手:
第一次:客户端向服务器发送SYN报文(SYN一般是用来进行同步的)此时客户端进入了 SYN_SEND状态,服务器进入SYN_RECE等待确认状态。
第二次:服务器接收到SYN报文后同时向客户端发送接收确认报文,并带上ACK报文,(用于应答的)。
第三次:客户端接收到ACK报文并检查ACK报文是否正确,如果正确的话客户端再次发送ACK,服务器收到后确认验证通过,表示连接已经成功建立,可以发送数据包了。

断开的四次挥手:
由于TCP是全双工的,因此每个方向都必须单独进行关闭才可,这个原则是当一方完成数据发送任务后就能发送一个FIN的标志来结束这个发送通道,收到FIN后只意味着这一个方向上没有数据流动(没有需要的数据要发送了),一个TCP可以在收到一个FIN后仍然能发送数据。
第一次挥手:客户端向服务器发送FIN,用来告诉服务器你发的东西我收到了,我要关闭通道了。
第二次挥手:服务器从客户度收到FIN,并发回一个ACK报文(应答用的),确认收到客户端的消息。
第三次挥手:服务器关闭与客户端的连接,发送一个FIN给客户端。
第四次关闭连接:客户端收到后发回ACK报文确认。

Spring AOP
要理解AOP我们可以对照着OOP(面向对象的思想来说) 面向对象是在万物皆是对象 利用面向对象的特性 继承 多态 抽象 封装 来构建对象,是一种自上而下的对象层次结构,但是细粒度到每个对象事务的内部,OOP就显得有些力不从心,考虑到系统的高聚集 低耦合,例如系统日志功能 权限判断 事务管理(日志)它几乎是水平散列分布在每个对象中,却与核心功能毫无关系。这样会导致了大量的代码重复,不利于各个模块的重用。
AOP思想:将通用的逻辑从代码业务逻辑中分离出来。是一种编程范式,也是一种编程思想,跟语言无关。
基于注解的方式:首先在一个类上@Aspect 代表的是一个切面类,@Pointcut 要切入的点是什么 一般用在方法上,还有@Advice 表示需要在方法的哪个时机进行切入。
例如:@pointcut("within(com.immoc.service.PointcutService)")或者@PointCut("within(com.immoc..*)")扫描这个包下面的所有子包及包下的所有方法。
AOP中 通配符
* 用于匹配一些任意数量的字符
+ 用于指定类及其子类
.. 用于匹配人意数量的子包或者参数


基本类型
==
equals
字符串变量
对象在内存中的首地址
字符串内容
非字符串变量
对象在字符串中的首地址
对象在字符串中的首地址
基本类型【原生类型】
不可用
包装类
地址
内容
6.== 和equals区别?
答:注意要点:当“==”运算符的两个操作数都是包装器类型的引用,则是比较执行的是否是同于个对象,而如果两者比较中有一个操作数是表达式(含运算符)则比较的是数值(会自动触发拆箱的过程),
装箱:即调用包装类型valueOf(),拆箱即XXXValue()。XXX可对应到不同的包装类型。
注意:Integer,Short,Byte,Charater,Long这几个类的valueOf()实现是类似的,Double,Float的valueOf方法的实现是类似的。

7.Session和Cookie的区别?
答:首先我先不比较Session和Cookie的区别,先说一些关于Session的主要知识点,在服务器的集群中Session的安全和同步是最大的问题,一般是使用客户端cookie加密方式,用的较少,不推荐,另一种是Session的复制---参与集群的节点上的session状态需要同步到其他所有的节点上,只要是session的状态一经改变,session数据都要被复制到其余节点上,如果服务器集群数量过大,在session复制的过程中会消耗大量的带宽,是服务器的效率明显降低;另一种方式是session共享,将所有的session信息一台服务器进行统一的管理,

session是存放在服务器端的,cookie是存放在客户端的,Session作为两个两个设备之间的状态保持者,至少需要一方需要保持另一方的会话状态,我们可以把用户访问页面产生的session放到cookie里面,就是以cookie为中转站。你访问web服务器A,产生了session然后把它放到cookie里面,当你的请求被分配到B服务器时,服务器B先判断服务器有没有这个session,如果没有,再去看看客户端的cookie里面有没有这个session,如果也没有,说明session真的不存,如果cookie里面有,就把cookie里面的sessoin同步到服务器B,这样就可以实现session的同步了。
说明:这种方法实现起来简单,方便,也不会加大数据库的负担,但是如果客户端把cookie禁掉了的话,那么session就无从同步了,这样会给网站带来损失;cookie的安全性不高,虽然它已经加了密,但是还是可以伪造的。
 
由于Http是无状态的协议,但是多数需要保持应用状态的程序,需要保证客户端和服务端的交互状态一致,对于浏览器发起的请求,仅基于http协议,比如:客户端对服务器请求同一个URL,请求1次和一万次服务器是无法记住用户身份的,是无法识别出是否为同一个用户的请求,为了保持这种交互的状态,就需要采取一系列的措施:如:
Cookie,隐藏form表单域,Session,URL,Https。那就先说说Tomcat中session的用法。
分布式Session中的处理方式:
第一:粘性Session,原理是把一个用户锁定在都一台服务器上,当用户第一次请求时,负载均衡器将用户的请求转发到了A服务器上,以后每次用户请求都会打在A服务器上,这就是Session的粘性原理,其现方式为在nginx的配置文件中这样配置:
upstearm mycluster{
  ip_hash; ## 粘性Session
 server 192.168.0.1:8080 weight:1;
 server 192.168.0.2:9090 weight:1;
}
第二种是Session的复制:
参与集群的节点上的session状态需要同步到其他所有的节点上,只要是session的状态一经改变,session数据都要被复制到其余节点上,如果服务器集群数量过大,在session复制的过程中会消耗大量的带宽,是服务器的效率明显降低;另一种方式是session共享,将所有的session信息一台服务器进行统一的管理,
第三种是Sessio共享机制:
使用分布式的Redis和Memcache实现,两者必须是集群方案。也可细分为两种Session共享方式:
① 粘性Session共享: 链接地址 https://my.oschina.net/u/1774673/blog/871912【实在没时间去深入理解了】

死锁产生发生:
死锁检测算法:设置一张进程等待表【进程号等待资源号】和一张资源等待表【资源号和进程号】
线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。

死锁的产生的一些特定条件:

1、 互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放 。

2、 请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。

3、 不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用。

4、 循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。

如何避免:

1、加锁顺序:
当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生。如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。当然这种方式需要你事先知道所有可能会用到的锁,然而总有些时候是无法预知的。

2、加锁时限:
加上一个超时时间,若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。但是如果有非常多的线程同一时间去竞争同一批资源,就算有超时和回退机制,还是可能会导致这些线程重复地尝试但却始终得不到锁。

3、死锁检测:
死锁检测即每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。

谈谈你对MysqlInnoDB的认识?
InnoDB是mysql一个重要的存储引擎,跟其他的存储引擎相比较,其特点是:
  1. 具有较好的事务支持,事务的原子性,支持四个事务隔离级别,多版本读。
  2. 行级锁定:通过索引实现,全表扫描仍然会表锁,注意间隙锁的影响。
  3. 读写阻塞与事务隔离级别相关。
  4. 整个表和主键以Cluster方式存储,组成一颗平衡树。
  5. 具有能缓存索引,也能缓存数据的性能
      6. 所有的Secondary Index都会保存主键信息。
使用场景:
  1. 需要事务支持
  2. 行级锁对高并发有很好的适应能力,但是要确保查询是通过索引完成的。
  3. 数据更新较为频繁。
  4. 数据一致性要求较高。
  5. 硬件设备内存较大,可以使用InnoDB较高的缓存能力来提高内存利用率,尽量减少磁盘的IO。

数据库事务的隔离级别?
  1. 读未提交:一个事务可以读取另一个未提交事务的数据。【read uncommited】。
  2. 读提交:一个事务只有能到另一个事务提交完之后才可读取该数据【read commited】。
  3. 重复读:一个事务在开启之时,不允许修改操作【Reattable read】。
  4. 序列化:事务串行执行,可避免 脏读,患读,不可重复读,是数据库事务的最高隔离级别,但是会影响数据库性能,代价过大【Serializable】。
      
可重复读:(MySql的默认隔离级别)
表示同一个事务中多次读取同样的记录的结果是一致的。但在理论上,可重复读还是无法解决幻读问题,幻读(当某个事务在读取某个范围内的记录时另一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时产生幻读)

在可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复 读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会 发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免。需要Serializable隔离级别 ,读用读锁,写用写锁,读锁和写锁互斥,这么做可以有效的避免幻读、不可重复读、脏读等问题,但会极大的降低数据库的并发能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值