Java面试必备

1 、hashmap的底层,和hashtable,hashset有什么区别?currenthashtable的底层?
hashmap的实现方式数组加链表,在jdk1.8以后使用数组加二叉树的结构。
hashcode采用31的原因是,因为可以利用移位操作代替乘法和减法操作,hashmap的初始容量为16加载因子为0.75。加载因子越大,就是启用扩容的阈值越大,这表明hashmap所占的内存越少,但是某一数组上挂载的链表长度越长,所以搜索速度越慢。hashset理论实现和hashmap是相同的,只是hashset的value值是确定的,而且hashmap属于map而hashset属于collection。
hashmap是线程不安全的,hashtable是线程安全的,并且hashtable的实现方式是拉链法。另外hashmap是快速失败的,而hashtable不是,hashtable的键可以为null,但是hashtable不允许。
currenthashmap和hashmap一样都是线程安全的,但是currenthashmap是对每一个段数据都配有一个锁,而hashtable如果发生写操作时,将会对整个表加锁,相对于currenthashmap而言,可靠性要高,但是速度慢。
concurrenthashmap是二级hashmap,第一层维护segment,第二层是每个segment维护的元素组成的hashmap结构。
一个currenthashmap里包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构, 一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素, 每个Segment守护者一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。
ConcurrentHashMap的get操作
Segment的get操作实现非常简单和高效。先经过一次再哈希,然后使用这个哈希值通过哈希运算定位到segment,再通过哈希算法定位到元素,代码如下:

public V get(Object key) {
    int hash = hash(key.hashCode());
    return segmentFor(hash).get(key, hash);
}

get方法定义成volatile的变量,能够在线程之间保持可见性,能够被多线程同时读,并且保证不会读到过期的值,但是只能被单线程写(有一种情况可以被多线程写,就是写入的值不依赖于原值),在get操作里只需要读不需要写共享变量count和value,所以可以不用加锁。之所以不会读到过期的值,是根据java内存模型的happen before原则,对volatile字段的写入操作先于读操作,即使两个线程同时修改和获取volatile变量,get操作也能拿到最新的值,这是用volatile替换锁的经典应用场景。

transient volatile int count;
volatile V value;

在定位元素的代码里我们可以发现定位HashEntry和定位Segment的哈希算法虽然一样,都与数组的长度减去一相与,但是相与的值不一样,定位Segment使用的是元素的hashcode通过再哈希后得到的值的高位,而定位HashEntry直接使用的是再哈希后的值。其目的是避免两次哈希后的值一样,导致元素虽然在Segment里散列开了,但是却没有在HashEntry里散列开。

hash >>> segmentShift) & segmentMask//定位Segment所使用的hash算法
int index = hash & (tab.length - 1);// 定位HashEntry所使用的hash算法

ConcurrentHashMap的Put操作
由于put方法里需要对共享变量进行写入操作,所以为了线程安全,在操作共享变量时必须得加锁。Put方法首先定位到Segment,然后在Segment里进行插入操作。插入操作需要经历两个步骤,第一步判断是否需要对Segment里的HashEntry数组进行扩容,第二步定位添加元素的位置然后放在HashEntry数组里。
是否需要扩容。在插入元素前会先判断Segment里的HashEntry数组是否超过容量(threshold),如果超过阀值,数组进行扩容。值得一提的是,Segment的扩容判断比HashMap更恰当,因为HashMap是在插入元素后判断元素是否已经到达容量的,如果到达了就进行扩容,但是很有可能扩容之后没有新元素插入,这时HashMap就进行了一次无效的扩容。
如何扩容。扩容的时候首先会创建一个两倍于原容量的数组,然后将原数组里的元素进行再hash后插入到新的数组里。为了高效ConcurrentHashMap不会对整个容器进行扩容,而只对某个segment进行扩容。

ConcurrentHashMap的size操作
ConcurrentHashMap的做法是先尝试2次通过不锁住Segment的方式来统计各个Segment大小,如果统计的过程中,容器的count发生了变化,则再采用加锁的方式来统计所有Segment的大小。那么ConcurrentHashMap是如何判断在统计的时候容器是否发生了变化呢?使用modCount变量,在put , remove和clean方法里操作元素前都会将变量modCount进行加1,那么在统计size前后比较modCount是否发生变化,从而得知容器的大小是否发生变化。

2 、spring 的aop你怎么看?Spring的aop怎么实现?Spring的aop有哪些实现方式
aop是spring两大特点中的一个,aop翻译过来是面向切面,而与面向对象不同的是面向切面是一个寄生编程方式。主要包括,切入点,连接点,切面,织入,通知。
aop动态代理的方法可以使用jdk动态代理或者CGLIB动态代理,前者基于接口,后者基于子类。
xml开发例子:

<bean id="helloworld" class="myaop.HelloWorldImpl1">
	</bean>
	<bean id="printtime" class="myaop.printtime"></bean>
	<aop:config>
		<aop:aspect id="dotime" ref="printtime">切面类
			<aop:pointcut id="adddoMethod"
				expression="execution(* myaop.HelloWorldImpl1.do*(..))" />运行时
			<aop:before method="syso" pointcut-ref="adddoMethod" />
			前一个是切面的方法,后一个是被切的方法,意思是在adddpmethod之前运行syso方法,是poincut。
			<aop:after method="syso" pointcut-ref="adddoMethod" />
			
		</aop:aspect>
				<aop:aspect id="printtime" ref="printtime">
			<aop:pointcut id="addprintMethod"
				expression="execution(* myaop.HelloWorldImpl1.print*(..))" />
			<aop:before method="logging" pointcut-ref="addprintMethod" />
		</aop:aspect>
	</aop:config>
</beans>

3 、索引有什么用?索引失效问题?索引分类?用途?
http://www.javashuo.com/article/p-sptdtuym-k.html
传统无索引情况下,如果需要查询某一字段的数据时需要
http://www.javashuo.com/article/p-zrcracrw-k.html
4 、数据库引擎,存储结构
mysql常用数据引擎有
InnoDB 默认引擎,支持事务,支持外键,支持行级锁。mysql在运行innodb时会再内存中建立缓冲池,用于缓冲数据和索引。但该引擎不支持FULLTEXT类型的索引,而且不保存行数,因此当select count(*) form table需要扫描全表。另外索引采用是聚集索引,因此必须确定primary key,如果用户不指定,将自动选定一个字段作为主键,如果不存在这种列,将使用隐含字段。
Isam
Myisam isam的扩展版本,没有事务,索引采用非聚集索引,可以没有主键。
memory 与redis类似,是内存数据库,使用hash索引

myisam和innodb选择?
innodb
1 可靠性要求高,需要事务
2 插入和查询都很频繁
myisam
1 没有事务
2 做count运算
3 插入不频繁,查询频繁

5 、spring 的理解?bean的实现方式?bean的生命周期,作用域
1 core 是最基础的部分,主要提供ioc功能,基础概念是bean工厂
2 context 构筑于core包之上,国际化,事务传播,资源加载,以及透明创建上下文
3 aop 面向切面编程的包
4 dao 提供了jdbc的抽象层,并且提供了事务管理
5 orm 提供了hibernate,mybaits的接口
6 mvc提供了mvc 的实现
7 web 提供了对structs2 或者webword 的接口
bean的实现详见https://blog.csdn.net/wuzhengfei1112/article/details/74056324
bean的创建分为两部分,
第一部是解析xml
第二部是生成beanfactory
bean的生命周期和作用域
生命周期:Spring 只帮我们管理单例模式 Bean 的完整生命周期,对于 prototype 的 bean ,Spring 在创建好交给使用者之后则不会再管理后续的生命周期
下图是singleton的生命周期:
Alt text在这里插入图片描述
作用域:singleton,prototype,session,globalsession,request

6、快排和归并和堆排的算法。
快排

public class QuickSort {
	public static void quickSortHelp(int[] arr) {
		quickSort(arr,0, arr.length-1);
	}
	public static void quickSort(int[] arr,int low, int high) {
		if(low<high) {
			int partition = partition(arr,low,high);
			quickSort(arr,low, partition-1);
			quickSort(arr,partition+1, high);
		}
		
	}
	public static int partition(int[] arr,int low,int high) {
		while(low<high) {
			while(arr[high]>=arr[low]&&low<high){
				high--;
			}
			Swap(arr,high,low);
			while(arr[low]<=arr[high]&&low<high) {
				low++;
			}
			Swap(arr,high,low);
		}
		return low;
	}
	public static void Swap(int[] arr,int high,int low) {
		int temp = arr[low];
		arr[low] =arr[high];
		arr[high] = temp;
	}
	public static void main(String[] args) {
		int[] array = { 2, 8, 5, 6, 10, 5, 4, 6, 11, 15, 3 };
		quickSortHelp(array);
		for (int s : array) {
			System.out.println(s);
		}
	}
}

7、代理模式

package daili;

public interface Account {
	//查询功能
    public void queryAccount();
    //修改功能
    public void updateAccount();

}
package daili;

public class AccountImpl implements Account {
	 @Override
	    public void queryAccount() {
	        System.out.println("委托类的查询方法...");
	    }
	 
	    @Override
	    public void updateAccount() {
	        System.out.println("委托类的修改方法.....");
	    }
}
package daili;

public class AccountProxy implements Account {
	 private AccountImpl accountImpl;
	 
	    /**
	     * 
	     * @Title: AccountProxy
	     * @Description: 重写默认的构造函数。真正执行的业务对象是accountImpl
	     * @param: @param accountImpl
	     * @throws
	     */
	    public AccountProxy(AccountImpl accountImpl){
	        this.accountImpl=accountImpl;
	    }
	    @Override
	    public void queryAccount() {
	        System.out.println("查询业务处理之前...");
	        accountImpl.queryAccount();
	        System.out.println("查询业务处理之后....");
	    }
	 
	    @Override
	    public void updateAccount() {
	        System.out.println("修改业务处理之前...");
	        accountImpl.queryAccount();
	        System.out.println("修改业务处理之后....");
	 
	    }

}
package daili;

public class Test {

	 public static void main(String[] args) {
	        AccountImpl accountImpl=new AccountImpl();
	        AccountProxy accountProxy=new AccountProxy(accountImpl);
	        accountProxy.queryAccount();
	        accountProxy.updateAccount();
	 
	    }

}

动态代理https://www.cnblogs.com/baizhanshi/p/6611164.html
aop主要使用动态代理和反射

8、线程锁有哪些?数据库锁有哪些
synchronize,volatile,lock
行锁,表锁,悲观锁,乐观锁
表锁:行共享,行排他,共享锁,共享行排他,排他
https://www.cnblogs.com/sessionbest/articles/8689071.html
9、线程池类型?线程池设置参数,线程返回值
4种常用的线程池,其实是Threadpoolexecuter的通过参数设置后得到的定制的线程池
newScheduleThreadPool
可以根据计划进行线程运行。
** newSingleThreadExecutor**
线程池中只有一条线程,保证满足FIFO的形式
newFixedThreadPool
固定数量线程数的线程池,核心线程数和最大线程数相同,外部输入。如果大于这个就加入阻塞队列,阻塞队列满了就返回失败。
newCachedThreadPool
可以定制计划的线程池,使用syschronousqueue,有核心线程数量,最大线程为无穷大,即可以无穷创建,但是涉及内存问题
上面的线程池都是改变threadpoolexecutor参数实现,就是定制的threadpool,当然可以自己实现
常用threadpoolexecutor
int corePoolSize,核心线程数
int maximumPoolSize,最大维护线程池
long keepAliveTime,超过核心池数量的线程的时间
TimeUnit unit,时间单位
BlockingQueue workQueue,

五种阻塞队列
1SynchronousQueue:无缓存,使用cas轮询实现,消费者和生产者的交互
2LinkedBlockingQueue:有缓存的链表阻塞队列,在LinkedBlockingQueue中维护了一条链表,可以设置链表的最大长度。采用分离锁,可以并发执行,速度快
3ArrayBlockingQueue:有缓存的数组阻塞队列,在ArrayBlockingQueue中维护了一条定长的数组。生产者和消费者共用一个锁对象,两者无法真正意义上的并行,之所以不采用分离锁是因为这种阻塞队列已经足够轻量,所以加入分离锁后悔变复杂
4DelayQueue:只有达到延时时间才能取到元素,大小没有限制
5 PriorityBlockingQueue基于优先级的阻塞队列,注意的是只会阻塞消费者不阻塞生产者,因此如果生产者速度大于消费者,会耗尽堆内存空间。
10 、分页怎么实现的
分页有两种方式,一种物理分页,即使用limit 。另一种是使用游标,也称为逻辑分页

11、三次握手四次挥手都做了什么
三次握手:
1 TCP服务器首先创建传输控制块TCB,时刻准备接受客户进程的连接请求,进入LISTEN(监听状态)
2 TCP客户进程也先创建传输控制块TCB,然后向服务器发送连接请求,此时报文同部位的SYN=1,同时选择一个初始序列号seq=x;此时tcp客户端进入到SYN-SENT阶段。
3 TCP服务端收到连接请求,如果同意连接则,发出确认报文,SYN=1,ACK=1,seq=y,ack=x+1,此时服务器进入SYN-RCVD状态
4 TCP客户端收到确认后向服务器发出确认,报文ACK=1,seq=x+1,ack=y+1;
5 进入establish状态
进行三次握手而不是两次是因为如果发生延时在客户端向服务器端发送请求时,那么服务器将直接和客户机建立连接,而此时客户机可能已经不想要了,造成资源浪费,而进行三次握手相当于客户机向服务器再次确认需要建立连接,保证了实时性和有效性

四次挥手:
1原本是establish状态,客户端和服务端
2客户端主动关闭,发出关闭请求FIN=1,seq=u,进入FIN-WAIT-1状态
3服务器收到关闭请求,如果同意,发出ACK=1,seq=v,ack=u+1,进入close-wait状态
4客户机收到后进入FIN-WAIT-2状态
5服务器再次发出FIN=1,ACK=1,seq=w,ack=u+1,进入LAST-ACK
6客户机收到后发出ACK=1,seq=u+1.ack=w+1,进入TIME-WAIT状态
7服务器收到后关闭
8客户端等待2MSL关闭

等待2MSL
等待2MSL是因为首先保证客户端向服务发送的确认报文,服务器可以收到,如果没收到,服务器会再发一个确认报文,在这个时间内客户机人处于time-wait状态,可以重传确认报文
第二点,是保证在这个时间内,这个旧的报文段可以从网络中消失,保证新的报文的有效性。
建立连接的时候, 服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。
握手三次挥手四次问题
关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送,从而导致多了一次
参考:http://www.javashuo.com/article/p-ucrnmzvv-gg.html

12、java虚拟机内存管理,类的加载和双亲委派
堆,栈,静态成员
堆,本地方法栈,程序计数器,jvm栈,方法区。其中本地方法栈,jvm栈和程序计数器是不共享的,而堆和方法区是共享的。
本地方法栈主要是其他编码语言开发的一些方法和接口
jvm栈是局部的变量
程序计数器是字节码记录程序以及方法的入口
方法区是,类加载时存储静态成员的区域,包括编译后的代码,常量,静态常量,类信息。
堆是对象储存的区域,根据新生代和老生代分为三个区域,两个servior,一个eden,一个老生代区
类加载过程
加载连接和初始化。其中连接分为验证,准备,和解析
验证:确保加载的类信息符合JVM规范,没有安全方面的问题
准备:正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法去中进行分配
解析:虚拟机常量池的符号引用替换为字节引用过程
加载器有,启动类加载器,扩展类加载器,应用程序类加载器,用户自定义类加载器
双亲委派模型是指:当一个类加载器收到加载任务时,并不会立刻执行加载,而是把类加载向父加载器抛出,如果父类处理不了就继续向上抛出,直到顶层类加载器也处理不了,就尝试自己执行加载任务,这些做的好处是永远只加载唯一的类。

13、垃圾回收,回收什么?什么时候回收?怎样发现?怎样清理?新生代老生代永久代?
垃圾回收是回收对象 和方法区中的是不使用的对象,回收时间新建一个对象后,如果eden已满,就会在用minor gc回收不常用对象。如果年老代的满的时候,利用fullgc,进行垃圾回收。
发现包括两种方法:
一种是计数法,每当一个地方引用对象时,计数器值加一,引用失效计数器减1,任何时刻计数器为0的对象表示不再被使用,但是由于这种方法解决不了相互引用问题,所以jvm不使用这种方法。
另一种是可达性分析法,即以一系列的gc root的对象为初始对象,从这些节点向下搜索,搜索走过的路径为引用链,当一个对象到gcroot没有任何引用,即不可达时,则证明此对象不可达。可以选用方法区的常量应用对象,虚拟机栈,本地方法栈为gcroots。
清理:
标记-清除:这种方法通过上述的发现方法后,利用清除方法对对象进行清除,但是这种方法会留下大量的碎片,这可能导致需要连续内存时需要重复执行低效的算法。
复制算法:将内存分成两块,每次用一块内存,如果内存用完后,将存活对象复制到另一块中,而使用过的内存空间直接清理。但是这种方法非常耗费内存,因为要分成两块。一般利用这种算法清理新生代。
压缩算法:将活动的对象都向另一端移动,清理边界以外的内存。
结合上面三种基本算法:目前商用虚拟机采用分代收集算法。对于大批对象死去,少量存活的新生代使用复制算法,而对于存活率高的老年代使用压缩或者标记清楚
常见收集器
串行收集器,单条线程收集,暂停其他线程工作
并行收集器,多线程,服务器收集。
CMS并发收集器多线程标记清除,不暂停
G1 收集器多线程,主线程暂停并行,否则并发,取消物理层面的新生代和年老代的划分,并且有巨型对象直接放置在年老代
参考https://www.cnblogs.com/ASPNET2008/p/6496481.html

新生代利用复制算法,而老生代使用标记清除或者压缩算法,为fullgc。新生代有三个区,两个surivior,一个eden,比例为1:1:8。新建的对象一般位于eden中,如果在第一次垃圾回收后,仍然处于活跃状态,则将其放入surivior中,如果幸存15次,则将这个对象移入到老生代。surivior一个是from,一个是to,每一次回收都会清空eden,并将活动的surivior中的对象从一个复制到另一个。

14、表的insert,delete,update,select,inner join on,left join on,right join on,以及sort 等基本操作,以及一对多,一对一,多对多的处理。

15、 事务的隔离级别和传输特性
三个问题,脏读,不可重复读,幻读
四种隔离级别
未提交读,不解决问题
提交读,解决脏读
可重复读,解决脏读和不可重复读
序列化,解决脏读,不可重复读,幻读
八种传输特性
如果有事务则使用事务如果没有则不使用 support
如果有事务使用事务如果没有则创建事务 requested
如果有事务使用事务,如果没有抛出异常
如果有事务挂起当前事务,另外创建事务,没有事务创建事务
如果有事务则挂起当前事务,不使用事务,没有事务也不创建
如果有事务则挂起当前事务,并抛出异常
事务嵌套

16、什么是redis?解决什么问题?redis的淘汰策略和调优,持久化?穿透和雪崩分别指什么?怎么处理?
Redis是一款k-v内存数据库,可以解决高并发和高性能问题
redis的淘汰策略主要有
从过期数据中剔除使用最少的数据 volatile-lru
从过期数据中剔除随机数据 volatile-lru
从所有数据中剔除使用频率最低的数据allkeys-lru
从所有数据中剔除随机数据 allkeys-random
从过期中选择即将过期数据 volatile-ttl
禁止剔除数据 noeviction
快照和aof·
快照是将数据直接保存,aof是利用日志保存
穿透是指黑客一直访问内存数据库中不存在的数据,导致这些数据都落在了硬盘数据库中,导致数据库压力增大,从而宕机
雪崩是指数据量过大,或者内存数据库有部分宕机,导致内存数据库处理不了这么大数据量,使得这些请求都在硬盘数据库中,使得服务器宕机
雪崩的解决方法
前:建立完善的redis集群,以及良好的内存更新方案,和适合的备份方案
中:使用访问控制系统,限流
后:快速恢复内存数据库

17、接口和抽象类的区别,注意JDK8的接口可以有实现。
1接口中所有的方法都是抽象的,但是抽象类中有普通方法
2可以实现多个接口但是只能继承一个类
3接口只有方法名,抽象类可以有方法体,但是在jdk1.8接口也可以有方法体,但必须是default修饰的。
4接口没有构造器,而抽象函数有构造器
5接口只能用public修饰,但是抽象类可以用default,public,prodected修饰
6接口中没有main方法,但是抽象类中可以有
7接口中添加方法,必须修改实现它的所有类,但是抽象类可以默认方法。

18、Java序列化的方式,如何在序列化过程中使得某些元素保密
序列化:对象转为二进制码传输
反序列化:将二进制编码转换回对象

19、什么是反射?为什么使用反射?你平时在哪些框架遇见过反射?
在运行时动态加载类。
Java反射机制主要提供了以下功能:在运行时构造一个类的对象;判断一个类所具有的成员变量和方法;调用一个对象的方法;生成动态代理。反射最大的应用就是框架
Java反射的主要功能:
1确定一个对象的类
2取出类的modifiers,数据成员,方法,构造器,和超类.
3找出某个接口里定义的常量和方法说明.
4创建一个类实例,这个实例在运行时刻才有名字(运行时间才生成的对象).
5取得和设定对象数据成员的值,如果数据成员名是运行时刻确定的也能做到.
6在运行时刻调用动态对象的方法.
7创建数组,数组大小和类型在运行时刻才确定,也能更改数组成员的值.
反射的应用很多,很多框架都有用到
spring 的 ioc/di 也是反射….
javaBean和jsp之间调用也是反射….
struts的 FormBean 和页面之间…也是通过反射调用….
JDBC 的 classForName()也是反射……
hibernate的 find(Class clazz) 也是反射….
反射还有一个不得不说的问题,就是性能问题,大量使用反射系统性能大打折扣。
**20、jdbc的使用,jdbc连接池,有什么封装好的jdbc工具

<!-- c3p0连接池配置 -->
     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
          <!-- 用户名-->
          <property name="user" value="${username}"/>
          <!-- 用户密码-->
          <property name="password" value="${password}"/>
          <property name="driverClass" value="${driver_class}"/>
          <property name="jdbcUrl" value="${url}"/>
 
           <!--连接池中保留的最大连接数。默认值: 15 --> 
          <property name="maxPoolSize" value="20"/>
          <!-- 连接池中保留的最小连接数,默认为:3-->
          <property name="minPoolSize" value="2"/>
          <!-- 初始化连接池中的连接数,取值应在minPoolSize与maxPoolSize之间,默认为3-->
          <property name="initialPoolSize" value="2"/>
 
          <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。默认值: 0 --> 
          <property name="maxIdleTime">60</property>
          
          <!-- 当连接池连接耗尽时,客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。默认: 0 --> 
          <property name="checkoutTimeout" value="3000"/>
          
          <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。默认值: 3 --> 
          <property name="acquireIncrement" value="2"/>
 
         <!--定义在从数据库获取新连接失败后重复尝试的次数。默认值: 30 ;小于等于0表示无限次--> 
          <property name="acquireRetryAttempts" value="0"/>
 
          <!--重新尝试的时间间隔,默认为:1000毫秒--> 
          <property name="acquireRetryDelay" value="1000" />
 
          <!--关闭连接时,是否提交未提交的事务,默认为false,即关闭连接,回滚未提交的事务 --> 
          <property name="autoCommitOnClose">false</property>
 
          <!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试使用。默认值: null --> 
          <property name="automaticTestTable">Test</property>
 
          <!--如果为false,则获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常,但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。默认: false--> 
          <property name="breakAfterAcquireFailure">false</property>
 
          <!--每60秒检查所有连接池中的空闲连接。默认值: 0,不检查 --> 
          <property name="idleConnectionTestPeriod">60</property>
          <!--c3p0全局的PreparedStatements缓存的大小。如果maxStatements与maxStatementsPerConnection均为0,则缓存不生效,只要有一个不为0,则语句的缓存就能生效。如果默认值: 0--> 
          <property name="maxStatements">100</property>
          <!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。默认值: 0 --> 
          <property name="maxStatementsPerConnection"></property>
     </bean>

21、日志的实现方式,安全的实现方式?
Java Logging API(Oracle)—— Java默认的日志框架
Log4j(Apache)——开源日志框架
Logback(Logback Project)——开源项目,被设计成Log4j版本1的后续版本 tinylog(tinylog)——轻量级开源logger
22、一共设计了多少表,每个表的字段是什么?mysql的优化手段?
管理员表 id,用户名,密码
用户表 id,用户名,密码,帖子数
博客表 id, 博客名,博客详情,用户外键
评论表 id,评论内容,评论时间,外键评论用户,外键博客用户名
登录人员表 id ,登录时间,登录次数。外键用户

23、hibernate的几种状态

24、多线程Java实现多线程有哪几种方式。Callable和Future的了解
继承Thread,实现runnable接口
之所以又有Callable和Future是因为前两种方式并没有返回值,为了获得返回值,可以采用callable和future,或者callable和futuretask。其他获得线程返回值的方法是共享变量,或者利用wait和notify和notifyall实现线程间的通信。

package mythread2;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

class SumTask implements Callable<Long> {

	@Override
	public Long call() throws Exception {

		long sum = 0;
		for (int i = 0; i < 9000; i++) {
			sum += i;
		}

		return sum;
	}
}

public class CallableDemo {
	public static void main(String[] args) throws ExecutionException, InterruptedException {
	FutureTask<Long> futureTask=new FutureTask<>(new SumTask());
	Executor executor=   Executors.newSingleThreadExecutor();
	executor.execute(futureTask);
	System.err.println(futureTask.get());
		try {
			System.err.println();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

25、Lock接口有哪些实现类,使用场景是什么。可重入锁的用处及实现原理,写时复制的过程,读写锁,分段锁(ConcurrentHashMap中的segment)。悲观锁,乐观锁,优缺点,CAS有什么缺陷,该如何解决。ABC三个线程如何保证顺序执行。
lock 接口下有重入锁和读写锁两个,重入锁的使用场景和synchronize类似,但是具有更强大的功能,就是互斥锁。
读写锁,是将锁分为读锁和写锁,一般情况下,写锁只能由一个线程持有,读锁由多个线程持有,并且读读共享,读写互斥,写写互斥,即写的时候,其他线程不允许写或读,读的时候,其他线程不允许写。
悲观锁,就是不管是否发生多线程冲突,只要存在这种可能,就每次访问都加锁,加锁就会导致锁之间的争夺,有争夺就会有输赢,输者等待。 syncrhoized是一种独占锁,即:占用该锁的线程才可以执行,申请该锁的线程就只能挂起等待,直到占用锁的线程释放锁才唤醒,拿到锁并执行。由于在进程挂起和恢复执行过程中存在着很大的开销,并且当一个线程正在等待锁时,它不能做任何事。所以syncrhoized是一种悲观锁,凡是用syncrhoized加了锁的多线程之间都会因锁的争夺结果导致挂起、唤醒等开销。
乐观锁获得锁后一直持有锁以防本线程再次申请该锁造成无谓的解锁再加锁开销,或者假设没有冲突而去完成同步代码块如果冲突再循环重试,或者采取申请锁失败后不立刻挂起而是稍微等待再次尝试获取 等待策略,以减少线程因为挂起、阻塞、唤醒(发生CPU的调度切换) 而造成的开销。偏向锁、轻量级锁(CAS轮询)、自旋锁 就是基于上述思路的乐观锁。
在多线程的加锁机制中,JVM会首先尝试乐观锁,失败后才调用悲观锁。
CAS(Compare-and-Swap)实现乐观锁的手段,通过轮询实现乐观锁
目前大部分锁都是利用cas,如atomic类,以及lock底层,synchronize未转换为重量锁之前一直使用cas的手段。
cas的缺点:只能保证变量的原子性不能保证代码块的原子性
对cpu资源有压力
ABA问题,利用版本号解决
ABC三个线程如何保证顺序执行:用join解决,让并行变为串行。

package mythread2;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class mylock {
	static Lock lock = new ReentrantLock();

	public static void main(String[] args) throws InterruptedException {
		mythread2 mythread2 = new mythread2();
		mythread1 mythread1 = new mythread1(mythread2);
		
		mythread1.start();
		mythread2.start();

	}
}

class mythread1 extends Thread {
	mythread2 mythread2 = new mythread2();

	public mythread1(mythread2 mythread2) {
		// TODO Auto-generated constructor stub
		this.mythread2=mythread2;
	}

	@Override
	public void run() {
		for (int i = 0; i < 1000; i++) {
			try {
				mythread2.join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			mylock.lock.lock();

			System.out.print("国");
			System.out.print("亲");
			System.out.print("节");
			System.out.print("快");
			System.out.print("乐");
			System.out.println();
			mylock.lock.unlock();
		}

	}
}

class mythread2 extends Thread {

	@Override
	public void run() {
		for (int i = 0; i < 1000; i++) {
			mylock.lock.lock();
			System.out.print("好");
			System.out.print("运");
			System.out.print("连");
			System.out.print("连");
			System.out.print("连");
			System.out.println();
			mylock.lock.unlock();
		}
	}
}

重入锁reentrantlock和synchronize的区别

  1. ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。
  2. ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。
  3. ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。

重入锁使用

package mythread2;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class mylock {
	static Lock lock = new ReentrantLock();
	public static void main(String[] args) {
		new mythread1().start();
		new mythread2().start();

	}
}
class mythread1 extends Thread {

	@Override
	public void run() {
		for (int i = 0; i < 1000; i++) {
			mylock.lock.lock();
				System.out.print("国");
				System.out.print("亲");
				System.out.print("节");
				System.out.print("快");
				System.out.print("乐");
				System.out.println();
				mylock.lock.unlock();
		}

	}
}
class mythread2 extends Thread {
	@Override
	public void run() {
		for (int i = 0; i < 1000; i++) {
			mylock.lock.lock();
			System.out.print("好");
			System.out.print("运");
			System.out.print("连");
			System.out.print("连");
			System.out.print("连");
			System.out.println();
			mylock.lock.unlock();
		}
	}
}

26、线程的状态都有哪些。sleep和wait的区别。notify和notifyall的区别。ThreadLocal的了解,实现原理
新建状态,就绪状态,运行状态,阻塞状态,死亡状态,五种
阻塞状态又分为三种:等待阻塞(wait),同步阻塞(synchronize),其他阻塞(sleep,join以及io)
27、Jvm的参数设置
CATALINA_OPTS="
-server :一般应用于服务器端,启动慢,内存管理效率高,对应的是-client 应用于客户端,启动快,但是内存管理效率低
-Xms6000M 初始java堆的大小,一般将其设置为和xmx相同大小,避免jvm反复申请内存而导致性能起伏
-Xmx6000M 最大堆大小,如果应用程序内存大于该值就会使得jvm奔溃,一般设置为内存的80%
-Xss512k 栈大小,在jdk5以前设置为256,5以后设置为1m。减小此值将会容纳更多的线程,但是也会导致outofmemory,一般不设置为1m,256够用
-XX:NewSize=2250M
新生代堆大小,新生代堆包括eden,和survior,大小比一般为8:1:1
-XX:MaxNewSize=2250M
最大新生代堆大小
-XX:PermSize=128M
永久代堆大小
-XX:MaxPermSize=256M
永久代最大堆大小
-XX:+AggressiveOpts
加入最新的优化技术
-XX:+UseBiasedLocking
使用优化锁
-XX:+DisableExplicitGC
不允许使用system.gc防止性能不懂
-XX:+UseParNewGC
对新生代使用并行回收
-XX:+UseConcMarkSweepGC
年老代并行回收
-XX:MaxTenuringThreshold=31
升级为年老代的计数值
-XX:+CMSParallelRemarkEnabled

-XX:+UseCMSCompactAtFullCollection

-XX:LargePageSizeInBytes=128m
分页大小
-XX:+UseFastAccessorMethods
get,set方法转化为本地编码
-XX:+UseCMSInitiatingOccupancyOnly

-Duser.timezone=Asia/Shanghai
时区设置
-Djava.awt.headless=true"
-XX:NewRatio 4 表示年轻代比年老代为1:4

28、红黑树和b树,二叉查找树,堆,最大堆和最小堆
红黑树是一种自平衡二叉树
b树balance多叉树,b+树比b树多了同级引用
二叉查找树的性质是必须是没有重复的数字,所以适合map,当然是变形以后,其实二叉查找树的建立类似于插入排序。
**堆是满二叉树
29、内存屏障是什么
内存屏障(Memory Barrier,或有时叫做内存栅栏,Memory Fence)是一种CPU指令,用于控制特定条件下的重排序和内存可见性问题。Java编译器也会根据内存屏障的规则禁止重排序。
内存屏障可以被分为以下几种类型
LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。 在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。
有的处理器的重排序规则较严,无需内存屏障也能很好的工作,Java编译器会在这种情况下不放置内存屏障。
为了实现上一章中讨论的JSR-133的规定,Java编译器会这样使用内存屏障。
为了保证final字段的特殊语义,也会在下面的语句加入内存屏障。
x.finalField = v; StoreStore; sharedRef = x;
https://www.cnblogs.com/chenyangyao/p/5269622.html
30、Object类下的方法
tostring
equals
hashcode
wait
notify
notifyall
getclass
finalize
clone
31、bio、nio和aio的区别
bio 同步阻塞
nio 异步阻塞
aio 异步非阻塞
详情:https://blog.csdn.net/huangwenyi1010/article/details/75577091
32、Synchronize原理?lock原理?volitile原理?三者区别?i++线程安全?在1.6后synchronize的性能提升?
synchronize 是jvm控制的内置锁
lock是编程实现的显式锁
volitile是关键字
synchronize 和Reentrantlock都是同步的,而且都是阻塞式同步,并且都是可重入锁。synchronize是jvm实现的,而lock是api层面实现的,而且需要lock和unlock联合try…finally使用。同时synchronize是非公平锁,lock默认是非公平锁,但是可以通过设置,将lock转为公平锁。synchronize是独享锁,reentrantlock也是独享锁,但是readwritelock的读共享锁写是独享锁,通过aqs实现。同时lock等待可以中断,但是synchronize不行。

在jdk1.6,加入了偏向锁,轻量级锁,自旋锁和重量级锁,随着并发程度的增大,逐渐升级但是不能降级。
33、生产者和消费者模式

synchronized、wait和notify 方法实现

package producerConsumer;
//wait 和 notify
public class ProducerConsumerWithWaitNofity {
    public static void main(String[] args) {
        Resource resource = new Resource();
        //生产者线程
        ProducerThread p1 = new ProducerThread(resource);
        ProducerThread p2 = new ProducerThread(resource);
        ProducerThread p3 = new ProducerThread(resource);
        //消费者线程
        ConsumerThread c1 = new ConsumerThread(resource);
        //ConsumerThread c2 = new ConsumerThread(resource);
        //ConsumerThread c3 = new ConsumerThread(resource);
    
        p1.start();
        p2.start();
        p3.start();
        c1.start();
        //c2.start();
        //c3.start();
    }
    
    
    
}
/**
 * 公共资源类
 * @author 
 *
 */
class Resource{//重要
    //当前资源数量
    private int num = 0;
    //资源池中允许存放的资源数目
    private int size = 10;

    /**
     * 从资源池中取走资源
     */
    public synchronized void remove(){
        if(num > 0){
            num--;
            System.out.println("消费者" + Thread.currentThread().getName() +
                    "消耗一件资源," + "当前线程池有" + num + "个");
            notifyAll();//通知生产者生产资源
        }else{
            try {
                //如果没有资源,则消费者进入等待状态
                wait();
                System.out.println("消费者" + Thread.currentThread().getName() + "线程进入等待状态");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 向资源池中添加资源
     */
    public synchronized void add(){
        if(num < size){
            num++;
            System.out.println(Thread.currentThread().getName() + "生产一件资源,当前资源池有" 
            + num + "个");
            //通知等待的消费者
            notifyAll();
        }else{
            //如果当前资源池中有10件资源
            try{
                wait();//生产者进入等待状态,并释放锁
                System.out.println(Thread.currentThread().getName()+"线程进入等待");
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}
/**
 * 消费者线程
 */
class ConsumerThread extends Thread{
    private Resource resource;
    public ConsumerThread(Resource resource){
        this.resource = resource;
    }
    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.remove();
        }
    }
}
/**
 * 生产者线程
 */
class ProducerThread extends Thread{
    private Resource resource;
    public ProducerThread(Resource resource){
        this.resource = resource;
    }
    @Override
    public void run() {
        //不断地生产资源
        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.add();
        }
    }
    
}

方式二:lock和condition的await、signalAll

package producerConsumer;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * 使用Lock 和 Condition解决生产者消费者问题
 * @author tangzhijing
 *
 */
public class LockCondition {
        public static void main(String[] args) {
            Lock lock = new ReentrantLock();
            Condition producerCondition = lock.newCondition();
            Condition consumerCondition = lock.newCondition();
            Resource2 resource = new Resource2(lock,producerCondition,consumerCondition);
            
            //生产者线程
            ProducerThread2 producer1 = new ProducerThread2(resource);
            
            //消费者线程
            ConsumerThread2 consumer1 = new ConsumerThread2(resource);
            ConsumerThread2 consumer2 = new ConsumerThread2(resource);
            ConsumerThread2 consumer3 = new ConsumerThread2(resource);
            
            producer1.start();
            consumer1.start();
            consumer2.start();
            consumer3.start();
        }
}
/**
 * 消费者线程
 */
class ConsumerThread2 extends Thread{
    private Resource2 resource;
    public ConsumerThread2(Resource2 resource){
        this.resource = resource;
        //setName("消费者");
    }
    public void run(){
        while(true){
            try {
                Thread.sleep((long) (1000 * Math.random()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.remove();
        }
    }
}
/**
 * 生产者线程
 * @author tangzhijing
 *
 */
class ProducerThread2 extends Thread{
    private Resource2 resource;
    public ProducerThread2(Resource2 resource){
        this.resource = resource;
        setName("生产者");
    }
    public void run(){
        while(true){
                try {
                    Thread.sleep((long) (1000 * Math.random()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                resource.add();
        }
    }
}
/**
 * 公共资源类
 * @author tangzhijing
 *
 */
class Resource2{
    private int num = 0;//当前资源数量
    private int size = 10;//资源池中允许存放的资源数目
    private Lock lock;
    private Condition producerCondition;
    private Condition consumerCondition;
    public Resource2(Lock lock, Condition producerCondition, Condition consumerCondition) {
        this.lock = lock;
        this.producerCondition = producerCondition;
        this.consumerCondition = consumerCondition;
 
    }
    /**
     * 向资源池中添加资源
     */
    public void add(){
        lock.lock();
        try{
            if(num < size){
                num++;
                System.out.println(Thread.currentThread().getName() + 
                        "生产一件资源,当前资源池有" + num + "个");
                //唤醒等待的消费者
                consumerCondition.signalAll();
            }else{
                //让生产者线程等待
                try {
                    producerCondition.await();
                    System.out.println(Thread.currentThread().getName() + "线程进入等待");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }finally{
            lock.unlock();
        }
    }
    /**
     * 从资源池中取走资源
     */
    public void remove(){
        lock.lock();
        try{
            if(num > 0){
                num--;
                System.out.println("消费者" + Thread.currentThread().getName() 
                        + "消耗一件资源," + "当前资源池有" + num + "个");
                producerCondition.signalAll();//唤醒等待的生产者
            }else{
                try {
                    consumerCondition.await();
                    System.out.println(Thread.currentThread().getName() + "线程进入等待");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }//让消费者等待
            }
        }finally{
            lock.unlock();
        }
    }
    
}

BlockingQueue

package producerConsumer;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

//使用阻塞队列BlockingQueue解决生产者消费者
public class BlockingQueueConsumerProducer {
    public static void main(String[] args) {
        Resource3 resource = new Resource3();
        //生产者线程
        ProducerThread3 p = new ProducerThread3(resource);
        //多个消费者
        ConsumerThread3 c1 = new ConsumerThread3(resource);
        ConsumerThread3 c2 = new ConsumerThread3(resource);
        ConsumerThread3 c3 = new ConsumerThread3(resource);
 
        p.start();
        c1.start();
        c2.start();
        c3.start();
    }
}
/**
 * 消费者线程
 * @author tangzhijing
 *
 */
class ConsumerThread3 extends Thread {
    private Resource3 resource3;
 
    public ConsumerThread3(Resource3 resource) {
        this.resource3 = resource;
        //setName("消费者");
    }
 
    public void run() {
        while (true) {
            try {
                Thread.sleep((long) (1000 * Math.random()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource3.remove();
        }
    }
}
/**
 * 生产者线程
 * @author tangzhijing
 *
 */
class ProducerThread3 extends Thread{
    private Resource3 resource3;
    public ProducerThread3(Resource3 resource) {
        this.resource3 = resource;
        //setName("生产者");
    }
 
    public void run() {
        while (true) {
            try {
                Thread.sleep((long) (1000 * Math.random()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource3.add();
        }
    }
}
class Resource3{
    private BlockingQueue resourceQueue = new LinkedBlockingQueue(10);
    /**
     * 向资源池中添加资源
     */
    public void add(){
        try {
            resourceQueue.put(1);
            System.out.println("生产者" + Thread.currentThread().getName()
                    + "生产一件资源," + "当前资源池有" + resourceQueue.size() + 
                    "个资源");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    /**
     * 向资源池中移除资源
     */
    public void remove(){
        try {
            resourceQueue.take();
            System.out.println("消费者" + Thread.currentThread().getName() + 
                    "消耗一件资源," + "当前资源池有" + resourceQueue.size() 
                    + "个资源");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

34、.xml中配置什么
监听器,过滤器,welcome-file,servlet,
35、实现线程安全的i++;
1、利用synchronize;
2、利用reentranlock;
3、利用reentranwriteandreadlock;
4、利用atomicinteger和count.incrementAndGet.
36、快速失败和安全失败
快速失败是利用迭代器时,如果有其他线程妄图修改集合类/map类就抛出快速失败
安全失败是拷贝一份

37 数组在内存的具体实现?内存角度分析?那么数组属于类吗?分析
int[] a=new int[100];
在堆中开辟一段100个int型的数据单元,a创建在栈区,a是这段内存的头指针。
但是数组不属于类,但是实现了类的所有方法。
38 计算机网络簇

39 jvm反射和cglicb反射怎么实现

40密码加密

41 保留登录状态有哪几种方法,项目里的实现?
保留登录状态就是要获取session,session的获取方法一共有三种
42 mysql优化和sql语句优化,以及mysql的debug工具?
mysql的优化从底层到顶:sql和索引优化,数据库表,系统配置,硬件
max,limit,group by,order by是sql语句优化
索引建立在 1 字段长度短2 离散程度大3 需要经常查询和排序的
过多的索引 不仅会影响写入效率,还会影响查询效率
表的建立:1选择最小的形式进行储存
2 尽量使用简单的形式,int比时间戳和char简单
3 尽量少使用not null
4 少用text

43 spring 的事务?
事务分为两种方式:一种是声明式,一种是编程式
声明式又分xml和注解
其中xml事务必须先实现txmanager和aop两种

<!-- 配置事务管理器 -->配置一个bean,类名是datasourcetransactionmanager,注入了DataSource

	<bean id="cityPickTransactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource">
			<ref bean="dataSource_backend-product" />
		</property>
	</bean>
	 
<!-- 配置事务传播特性 -->注入上面的citypicktansactionmanager,然后对方法进行一个事务管理,记住事务管理的本质是在mysql操作的,即如果和数据库无关的就无需进行事务操作,但是几乎所有的后端的类都会和数据库发生交互,所以理论上都需要考虑数据库。一般对查的要求不高,所以使用support即可,如过要查的话,就使用required,可以用事务就用不用就算了。
	<tx:advice id="cityPickAdvice" transaction-manager="cityPickTransactionManager">
		<tx:attributes>
			<tx:method name="insert*" propagation="REQUIRED"
				rollback-for="Exception" />
			<tx:method name="add*" propagation="REQUIRED"
				rollback-for="Exception" />
			<tx:method name="create*" propagation="REQUIRED"
			           rollback-for="Exception" />
			<tx:method name="update*" propagation="NESTED"
				rollback-for="Exception" />
			<tx:method name="batchApply*" propagation="REQUIRED"
				rollback-for="Exception" />
			<tx:method name="save*" propagation="REQUIRED"
				rollback-for="Exception" />
			<tx:method name="delete*" propagation="REQUIRED"
				rollback-for="Exception" />
			<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
			<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
			<tx:method name="fetch*" propagation="SUPPORTS" read-only="true" />
			<tx:method name="is*" propagation="SUPPORTS" read-only="true" />
			<tx:method name="*_noTrans" propagation="NOT_SUPPORTED" />
			<tx:method name="*byTran" propagation="REQUIRED"/>
		</tx:attributes>
	</tx:advice>
<!-- 配置参与事务的类 -->
	<aop:config>
		<aop:pointcut id="cityPickManagerMethod"
			expression=" (
                            execution(* com.test.service.impl.CityPriceScheduleServiceImpl.*(..))
                         ) " />
		<aop:advisor advice-ref="cityPickAdvice" pointcut-ref="cityPickManagerMethod" />、、事务管理
	</aop:config>
 
</beans>

44 什么是hashcoede?hashcode的计算方法?为什么要重写equals时需要重写hashcode?
hashCode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值。
31(31(31*h+v0)+v1)+v2… 迭乘
因为约定就是两个相等的对象就必须拥有相同的hashcode。因为假设在equals中没有重写hashcode,那么如果在hashmap中使用到这两个对象,因为hashmap是将两个相等的对象放在一个地方,而此时虽然对象A==B,但是没有重写hashcode,导致hashcode不等,从而导致将两个相等的对象放在两个不同的hashmap位置。
45 TCP和UDP的区别,TCP的报文结构,UDP的报文结构

tcp报文结构

源端口  目标端口
序列号  确认号
TCP首部长度    保留   SYN  ACK  FIN URG RST PSH 窗口大小
校验和 紧急指针
选项
数据部分

udp报文结构

源端口  目标端口
UDP长度    UDP校验和
数据

ip报文

版本  首部长度  服务类型   总长度
标识   标志  片偏移
源地址
目标地址
长度可选字段  填充
数据

46 http 的结构以及http1.0和http1.1,2.0区别
(1)连接方面
HTTP1.0使用非持久连接,即在非持久连接下,一个tcp连接只传输一个Web对象。每次请求和响应都需要建立一个单独的连接,每次连接只是传输一个对象,严重影响客户机和服务器的性能。
HTTP1.1默认使用持久连接(然而,HTTP/1.1协议的客户机和服务器可以配置成使用非持久连接)在持久连接下,不必为每个Web对象的传送建立一个新的连接,一个连接中可以传输多个对象。在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟。HTTP 1.1的持续连接,也需要增加新的请求头来帮助实现,例如,Connection请求头的值为Keep-Alive时,客户端通知服务器返回本次请求结果后保持连接;Connection请求头的值为close时,客户端通知服务器返回本次请求结果后关闭连接。HTTP 1.1还提供了与身份认证、状态管理和Cache缓存等机制相关的请求头和响应头。
(2)缓存方面
HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准
HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略带宽优化及网络连接的使用
(3)状态码
在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除
(4)带宽优化
HTTP 1.1支持只发送header信息(不带任何body信息),如果服务器认为客户端有权限请求服务器,则返回100,否则返回401。客户端如果接收到100,才开始把请求body发送到服务器。
这样当服务器返回401的时候,客户端就可以不用发送请求body了,节约了带宽。
(5)Host头
HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。
HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
HTTP1.1 VS HTTP2.0
(1)多路复用:
HTTP/1.1协议中,浏览器客户端在同一时间针对同一域名的请求有一定数据限制。超过限制数目的请求会被阻塞。
HTTP2.0使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级。
当然HTTP1.1也可以多建立几个TCP连接,来支持处理更多并发的请求,但是创建TCP连接本身也是有开销的。
TCP连接有一个预热和保护的过程,先检查数据是否传送成功,一旦成功过,则慢慢加大传输速度。因此对应瞬时并发的连接,服务器的响应就会变慢。所以最好能使用一个建立好的连接,并且这个连接可以支持瞬时并发的请求。
(2)首部压缩:
HTTP1.1不支持header数据的压缩,HTTP2.0使用HPACK算法对header的数据进行压缩,这样数据体积小了,在网络上传输就会更快。
(3)服务器推送:
当我们对支持HTTP2.0的web server请求数据的时候,服务器会顺便把一些客户端需要的资源一起推送到客户端,免得客户端再次创建连接发送请求到服务器端获取。这种方式非常合适加载静态资源。
47 Linux下创建一个文件用什么命令,修改权限使用什么命令,修改所有者使用什么命令?查看网络连通?

48讲讲http协议,输入一个网址到浏览器呈现出界面的过程是什么样子的?
在我们点击一个网址,到它能够呈现在浏览器中,展示在我们面前,这个过程中,电脑里,网络上,究竟发生了什么事情。

服务器启动监听模式

客户端浏览器发送请求

  1. 解析URL
  2. 输入的是 URL 还是搜索的关键字
    当协议或主机名不合法时,浏览器会将地址栏中输入的文字传给默认的搜索引擎。大部分情况下,在把文字传递给搜索引擎的时候,URL会带有特定的一串字符,用来告诉搜索引擎这次搜索来自这个特定浏览器。
    3. 转换非 ASCII 的 Unicode 字符
    浏览器检查输入是否含有不是 a-z, A-Z,0-9, - 或者 . 的字符
    这里主机名是 google.com ,所以没有非ASCII的字符;如果有的话,浏览器会对主机名部分使用 Punycode 编码

4. 检查 HSTS 列表
浏览器检查自带的“预加载 HSTS(HTTP严格传输安全)”列表,这个列表里包含了那些请求浏览器只使用HTTPS进行连接的网站
5. dns查询

6. ARP 过程
要想发送 ARP(地址解析协议)广播,我们需要有一个目标 IP 地址,同时还需要知道用于发送 ARP 广播的接口的 MAC 地址。
首先查询 ARP 缓存,如果缓存命中,我们返回结果:目标 IP = MAC
如果缓存没有命中:
查看路由表,看看目标 IP 地址是不是在本地路由表中的某个子网内。是的话,使用跟那个子网相连的接口,否则使用与默认网关相连的接口。
查询选择的网络接口的 MAC 地址
我们发送一个二层( OSI 模型 中的数据链路层)ARP 请求:
ARP Request:
Sender MAC: interface:mac:address:here
Sender IP: interface.ip.goes.here
Target MAC: FF:FF:FF:FF:FF:FF (Broadcast)
Target IP: target.ip.goes.here
根据连接主机和路由器的硬件类型不同,可以分为以下几种情况:

直连:
如果我们和路由器是直接连接的,路由器会返回一个 ARP Reply (见下面)。
集线器:
如果我们连接到一个集线器,集线器会把 ARP 请求向所有其它端口广播,如果路由器也“连接”在其中,它会返回一个 ARP Reply 。
交换机:
如果我们连接到了一个交换机,交换机会检查本地 CAM/MAC 表,看看哪个端口有我们要找的那个 MAC 地址,如果没有找到,交换机会向所有其它端口广播这个 ARP 请求。
如果交换机的 MAC/CAM 表中有对应的条目,交换机会向有我们想要查询的 MAC 地址的那个端口发送 ARP 请求
如果路由器也“连接”在其中,它会返回一个 ARP Reply
ARP Reply:
Sender MAC: target:mac:address:here
Sender IP: target.ip.goes.here
Target MAC: interface:mac:address:here
Target IP: interface.ip.goes.here
现在我们有了 DNS 服务器或者默认网关的 IP 地址,我们可以继续 DNS 请求了:
使用 53 端口向 DNS 服务器发送 UDP 请求包,如果响应包太大,会使用 TCP 协议
如果本地/ISP DNS 服务器没有找到结果,它会发送一个递归查询请求,一层一层向高层 DNS 服务器做查询,直到查询到起始授权机构,如果找到会把结果返回
7. 使用套接字建立三次握手
当浏览器得到了目标服务器的 IP 地址,以及 URL 中给出来端口号(http 协议默认端口号是 80, https 默认端口号是 443),它会调用系统库函数 socket ,请求一个 TCP流套接字,对应的参数是 AF_INET/AF_INET6 和 SOCK_STREAM 。

这个请求首先被交给传输层,在传输层请求被封装成 TCP segment。目标端口会被加入头部,源端口会在系统内核的动态端口范围内选取(Linux下是ip_local_port_range)
TCP segment 被送往网络层,网络层会在其中再加入一个 IP 头部,里面包含了目标服务器的IP地址以及本机的IP地址,把它封装成一个TCP packet。
这个 TCP packet 接下来会进入链路层,链路层会在封包中加入 frame头部,里面包含了本地内置网卡的MAC地址以及网关(本地路由器)的 MAC 地址。像前面说的一样,如果内核不知道网关的 MAC 地址,它必须进行 ARP 广播来查询其地址。
到了现在,TCP 封包已经准备好了,可以进行tcp三次握手(在地址解析完毕后首先进行的是三次握手,就是客户端和服务器端只发送SYN包,建立三次连接后,在对HTTP的引用进行响应)

最终封包会到达管理本地子网的路由器。在那里出发,它会继续经过自治区域(autonomous system, 缩写 AS)的边界路由器,其他自治区域,最终到达目标服务器。一路上经过的这些路由器会从IP数据报头部里提取出目标地址,并将封包正确地路由到下一个目的地。IP数据报头部 time to live (TTL) 域的值每经过一个路由器就减1,如果封包的TTL变为0,或者路由器由于网络拥堵等原因封包队列满了,那么这个包会被路由器丢弃。

8. TLS 握手
客户端发送一个 Client hello 消息到服务器端,消息中同时包含了它的 Transport Layer Security (TLS) 版本,可用的加密算法和压缩算法。

服务器端向客户端返回一个 Server hello 消息,消息中包含了服务器端的TLS版本,服务器选择了哪个加密和压缩算法,以及服务器的公开证书,证书中包含了公钥。客户端会使用这个公钥加密接下来的握手过程,直到协商生成一个新的对称密钥

客户端根据自己的信任CA列表,验证服务器端的证书是否有效。如果有效,客户端会生成一串伪随机数,使用服务器的公钥加密它。这串随机数会被用于生成新的对称密钥
服务器端使用自己的私钥解密上面提到的随机数,然后使用这串随机数生成自己的对称主密钥
客户端发送一个 Finished 消息给服务器端,使用对称密钥加密这次通讯的一个散列值
服务器端生成自己的 hash 值,然后解密客户端发送来的信息,检查这两个值是否对应。如果对应,就向客户端发送一个 Finished 消息,也使用协商好的对称密钥加密
从现在开始,接下来整个 TLS 会话都使用对称秘钥进行加密,传输应用层(HTTP)内容
9. HTTP 服务器请求处理
HTTPD(HTTP Daemon)在服务器端处理请求/响应。最常见的 HTTPD 有 Linux 上常用的 Apache 和 nginx,以及 Windows 上的 IIS。
HTTPD 接收请求
服务器把请求拆分为以下几个参数:
HTTP 请求方法(GET, POST, HEAD, PUT, DELETE, CONNECT, OPTIONS, 或者 TRACE)。直接在地址栏中输入 URL 这种情况下,使用的是 GET 方法
域名:google.com
请求路径/页面:/ (我们没有请求google.com下的指定的页面,因此 / 是默认的路径)
服务器验证其上已经配置了 google.com 的虚拟主机
服务器验证 google.com 接受 GET 方法
服务器验证该用户可以使用 GET 方法(根据 IP 地址,身份信息等)
如果服务器安装了 URL 重写模块(例如 Apache 的 mod_rewrite 和 IIS 的 URL Rewrite),服务器会尝试匹配重写规则,如果匹配上的话,服务器会按照规则重写这个请求
服务器根据请求信息获取相应的响应内容,这种情况下由于访问路径是 “/“ ,会访问首页文件(你可以重写这个规则,但是这个是最常用的)。
服务器会使用指定的处理程序分析处理这个文件,假如 Google 使用 PHP,服务器会使用 PHP 解析 index 文件,并捕获输出,把 PHP 的输出结果返回给请求者
请求进入处理函数之后,如果客户端所请求需要浏览的内容是一个动态的内容,那么处理函数会相应的从数据源里面取出数据,这个地方一般会有一个缓存,例如 memcached 来减小 db 的压力,如果引入了 orm 框架的话,那么处理函数直接向 orm 框架索要数据就可以了,由 orm 框架来决定是使用内存里面的缓存还是从 db 去取数据,一般缓存都会有一个过期的时间,而 orm 框架也会在取到数据回来之后,把数据存一份在内存缓存中的。

orm 框架负责把面向对象的请求翻译成标准的 sql 语句,然后送到后端的 db 去执行,db 这里以 mysql 为例的话,那么一条 sql 进来之后,db 本身也是有缓存的,不过 db 的缓存一般是用 sql 语言 hash 来存取的,也就是说,想要缓存能够命中,除了查询的字段和方法要一样以外,查询的参数也要完全一模一样才能够使用 db 本身的查询缓存,sql 经过查询缓存器,然后就会到达查询分析器,在这里,db 会根据被搜索的数据表的索引建立情况,和 sql 语言本身的特点,来决定使用哪一个字段的索引,值得一提的是,即使一个数据表同时在多个字段建立了索引,但是对于一条 sql 语句来说,还是只能使用一个索引,所以这里就需要分析使用哪个索引效率最高了,一般来说,sql 优化在这个点上也是很重要的一个方面。

sql 由 db 返回结果集后,再由 orm 框架把结果转换成模型对象,然后由 orm 框架进行一些逻辑处理,把准备好的数据,送到视图层的渲染引擎去渲染,渲染引擎负责模板的管理,字段的友好显示,也包括负责一些多国语言之类的任务。对于一条请求在 mvc 中的生命周期,在视图层把页面准备好后,再从动态脚本解释器送回到 http 服务器,由 http 服务器把这些正文加上一个响应头,封装成一个标准的 http 响应包,再通过 tcp ip 协议,送回到客户机浏览器。

10. 客户端渲染

11. 浏览器

12. HTML 解析

49https和http有什么区别,区别的细节是什么样子的?
https协议和http有着很大的区别。总的来说,http效率更高,https安全性更高。
首先谈谈什么是HTTPS:
HTTPS(Secure Hypertext Transfer Protocol)安全超文本传输协议 它是一个安全通信通道,它基于HTTP开发,用于在客户计算机和服务器之间交换信息。它使用安全套接字层(SSL)进行信息交换,简单来说它是HTTP的安全版。 它是由Netscape开发并内置于其浏览器中,用于对数据进行压缩和解压操作,并返回网络上传送回的结果。HTTPS实际上应用了Netscape的安 全全套接字层(SSL)作为HTTP应用层的子层。(HTTPS使用端口443,而不是象HTTP那样使用端口80来和TCP/IP进行通信。)SSL使 用40 位关键字作为RC4流加密算法,这对于商业信息的加密是合适的。HTTPS和SSL支持使用X.509数字认证,如果需要的话用户可以确认发送者是谁。
HTTPS和HTTP的区别:
https协议需要到ca申请证书,一般免费证书很少,需要交费。 http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议 http和https使用的是完全不同的连接方式用的端口也不一样,前者是80,后者是443。
http的连接很简单,是无状态的 HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议 要比http协议安全 HTTPS解决的问题:
1 . 信任主机的问题. 采用https 的server 必须从CA 申请一个用于证明服务器用途类型的证书. 改证书只有用于对应的server 的时候,客户度才信任次主机. 所以目前所有的银行系统网站,关键部分应用都是https 的. 客户通过信任该证书,从而信任了该主机. 其实这样做效率很低,但是银行更侧重安全. 这一点对我们没有任何意义,我们的server ,采用的证书不管自己issue 还是从公众的地方issue, 客户端都是自己人,所以我们也就肯定信任该server.
2 . 通讯过程中的数据的泄密和被窜改
一般意义上的https, 就是 server 有一个证书.
a) 主要目的是保证server 就是他声称的server. 这个跟第一点一样.
b) 服务端和客户端之间的所有通讯,都是加密的. i. 具体讲,是客户端产生一个对称的密钥,通过server 的证书来交换密钥. 一般意义上的握手过程. ii. 加下来所有的信息往来就都是加密的. 第三方即使截获,也没有任何意义.因为他没有密钥. 当然窜改也就没有什么意义了.
少许对客户端有要求的情况下,会要求客户端也必须有一个证书.
a) 这里客户端证书,其实就类似表示个人信息的时候,除了用户名/密码, 还有一个CA 认证过的身份. 应为个人证书一般来说上别人无法模拟的,所有这样能够更深的确认自己的身份.
b) 目前少数个人银行的专业版是这种做法,具体证书可能是拿U盘作为一个备份的载体.像我用的交通银行的网上银行就是采取的这种方式。 HTTPS 一定是繁琐的. a) 本来简单的http协议,一个get一个response. 由于https 要还密钥和确认加密算法的需要.单握手就需要6/7 个往返. i. 任何应用中,过多的round trip 肯定影响性能. b) 接下来才是具体的http协议,每一次响应或者请求, 都要求客户端和服务端对会话的内容做加密/解密. i. 尽管对称加密/解密效率比较高,可是仍然要消耗过多的CPU,为此有专门的SSL 芯片. 如果CPU 信能比较低的话,肯定会降低性能,从而不能serve 更多的请求.
符:SSL的简介:
SSL是Netscape公司所提出的安全保密协议,在浏览器(如Internet Explorer、Netscape Navigator)和Web服务器(如Netscape的Netscape Enterprise Server、ColdFusion Server等等)之间构造安全通道来进行数据传输,SSL运行在TCP/IP层之上、应用层之下,为应用程序提供加密数据通道,它采用了RC4、MD5 以及RSA等加密算法,使用40 位的密钥,适用于商业信息的加密。同时,Netscape公司相应开发了HTTPS协议并内置于其浏览器中,HTTPS实际上就是SSL over HTTP,它使用默认端口443,而不是像HTTP那样使用端口80来和TCP/IP进行通信。HTTPS协议使用SSL在发送方把原始数据进行加密,然 后在接受方进行解密,加密和解密需要发送方和接受方通过交换共知的密钥来实现,因此,所传送的数据不容易被网络黑客截获和解密。 然而,加密和解密过程需要耗费系统大量的开销,严重降低机器的性能,相关测试数据表明使用HTTPS协议传输数据的工作效率只有使用HTTP协议传输的十 分之一。假如为了安全保密,将一个网站所有的Web应用都启用SSL技术来加密,并使用HTTPS协议进行传输,那么该网站的性能和效率将会大大降低,而 且没有这个必要,因为一般来说并不是所有数据都要求那么高的安全保密级别,所以,我们只需对那些涉及机密数据的交互处理使用HTTPS协议,这样就做到鱼与熊掌兼得。总之不需要用https 的地方,就尽量不要用
50有哪些索引,它们底层是采用什么数据结构去实现的
索引类型有:
HASH 索引
B树索引
R 树索引
fulltext索引
索引种类:
主键索引
唯一索引
一般索引
组合索引
全文索引
myisam和innodb索引区别
两者都是b+索引,但是myisam是非聚簇,innodb是聚簇的InnoDB使用的是聚簇索引,
将主键组织到一棵B+树中,而行数据就储存在叶子节点上,若使用"where id = 14"这样的条件查找主键,则按照B+树的检索算法即可查找到对应的叶节点,之后获得行数据。若对Name列进行条件搜索,则需要两个步骤:第一步在辅助索引B+树中检索Name,到达其叶子节点获取对应的主键。第二步使用主键在主索引B+树种再执行一次B+树检索操作,最终到达叶子节点即可获取整行数据。
  MyISM使用的是非聚簇索引,非聚簇索引的两棵B+树看上去没什么不同,节点的结构完全一致只是存储的内容不同而已,主键索引B+树的节点存储了主键,辅助键索引B+树存储了辅助键。表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别。由于索引树是独立的,通过辅助键检索无需访问主键的索引树。
51数据库同步你做过哪些方案,各有什么优缺点
52http的常用的状态有哪些,301和302的区别是什么,503是什么意思
200:请求被正常处理
204:请求被受理但没有资源可以返回
206:客户端只是请求资源的一部分,服务器只对请求的部分资源执行GET方法,相应报文中通过Content-Range指定范围的资源。
301:永久性重定向
302:临时重定向
303:与302状态码有相似功能,只是它希望客户端在请求一个URI的时候,能通过GET方法重定向到另一个URI上
304:发送附带条件的请求时,条件不满足时返回,与重定向无关
307:临时重定向,与302类似,只是强制要求使用POST方法
400:请求报文语法有误,服务器无法识别
401:请求需要认证
403:请求的对应资源禁止被访问
404:服务器无法找到对应资源
500:服务器内部错误
503:服务器正忙

53你们项目中上传图片是采用post的哪种方式

54百度的查询框中,假设你打了一个“中”,那么下面的一串的提示字符,类似“中国/中间”这些是如何出现的

55倒排索引的介绍
常规的索引是文档到关键词的映射: 文档——>关键词但是这样检索关键词的时候很费力,要一个文档一个文档的遍历一遍。(这事不能忍)于是人们发明了倒排索引倒排索引是关键词到文档的映射 关键词——>文档这样,只要有关键词,立马就能找到她在那个文档里出现过,剩下的事就是把她揪出来了~~~可能是因为将正常的索引倒过来了吧,所以大家叫他倒排索引,可我依然喜欢叫他反向索引~
56Lucene的实现原理

57Linux中查看服务的命令
1、ps aux 或netstat -tlunp
ps是进程查看命令,netstat是端口查看命令,在Linux系统中,服务一定是有进程的,所以使用ps命令可以查看服务运行情况,另外,Linux服务多数是网络服务,所以通过netstat命令也可以查看服务运行状态。
2service 服务名 status
比如查看httpd的Web服务的运行状态,执行service httpd status,如下图所示:
3、/sbin/service --status-all |grep “服务名”
比如查看httpd的web服务,执行 /sbin/service --status-all |grep "httpd"即可。如下图所示。
4、chkconfig --list
比如查看httpd的web服务,执行 chkconfig --list |grep "httpd"即可。如下图所示。
58死锁的概念,导致死锁的原因。导致死锁的四个必要条件。处理死锁的四个方式。预防死锁的方法、避免死锁的方法。
1.互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放
2.请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
3.不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用
4.循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞
59 springboot的理解和使用?

60权限 RBAC
https://www.cnblogs.com/yly-blog/archive/2017/08/04/7283541.html
61.TreeMap如何保证有序,红黑树基本原理,红黑树存在的意义

62.hashmap线程不安全体现在什么方面?hashmap死循环产生的原因?
主要是两个方面,
63.g1垃圾收集器的全称和原理,cms垃圾收集器的原理,串行垃圾收集器原理,并行垃圾收集器原理

64. 手写代码: 给一个任意树的节点序列,树的节点中包括了节点的值和他所有的孩子节点,请找出这棵树的父节点

66. volatile知道吗,底层怎么实现的?
volatile 修饰的变量具有两层含义,
1 对其他线程可见
2 禁止指令重排序
volatile 的过程是,对本cpu的高速缓存内的变量进行修改,然后把修改写入主存中,并利用mesi协议将数据的其他缓存的备份全部修改,但是volatile只保证了可见性,并不保证原子性,即对volatile修饰的变量进行自增操作仍然是不安全的,可以采用autointerger。https://www.jianshu.com/p/157279e6efdb
67. java集合的几种接口,分别说说

68. 内存溢出以及如何解决,如何监控,内存泄漏呢?
1 内存溢出是指超过了规定内存的大小
2 内存泄漏是指没有及时回收内存,导致最后无可用内存,最后内存溢出
内存溢出的产生条件
1 递归,递归算法由于一直需要引用上一层的对象,对象不能被及时的gc,最后内存溢出。循环也会产生类似的情况,相对来说递归更加危险。
2 启动内存设置过小,在jvm虚拟机可以设置。包括堆区以及栈区。
3 硬件内存过小
内存泄漏的产生条件
1 连接未关闭,io流或者线程之类的
2 监听器未关闭
3 大量的一次性String,如果可以使用stringbuffer
4 静态集合类,使用完置null
监控
Jprofiler
visualvm
69. 集合遍历的时候进行修改如何解决
一般而言快速失败
可以copy一份集合,修改原版的集合
备份用于遍历。

70 如何进行对象的拷贝
拷贝分为深拷贝和浅拷贝和多层克隆
1、被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常), 该接口为标记接口(不含任何方法)
2、覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象。(native为本地方法)

深拷贝
除了之前的clone方法,需要将对象进行拷贝

多层拷贝
实现cloneable和Serializable方法实现序列化克隆

71、线程如何实现同步和共享?
同步概念和异步是相对的 synchronize是实现同步的最简单的方法
共享的本质是
72 struts2的总体架构及流程?

在这里插入图片描述

73 线程和进程
线程是共享内存的,而进程是拥有独立的内存区域
线程是cpu执行的最小单位,而进程是线程的集合
线程是轻量级的,而进程是重量级的。

75单例模式的注意点 volatile和synchronized构造单例
饿汉式(线程安全,但是费内存)

public class Singleton_E {
	private  Singleton_E() {
		// TODO Auto-generated constructor stub
	}
	private static Singleton_E e=new Singleton_E();
	public static Singleton_E getsingleton() {
		return e;
		
	}
}

懒汉式(有判断null所以不安全,所以不可用多线程)

public class Singleton_E {
	private Singleton_E() {
		// TODO Auto-generated constructor stub
	}

	private static Singleton_E e = null;

	public static Singleton_E getsingleton() {
		if (e == null) {
			e=new Singleton_E();
		}
		return e;

	}
}

**线程安全:**懒汉式
final

public class Singleton_E {
	private Singleton_E() {
		// TODO Auto-generated constructor stub
	}

	private static Singleton_Einstance {
	private static final Singleton_E E=new Singleton_E();
	}

	public static Singleton_E getsingleton() {
		return Singleton_Einstance.Singleton_E;

	}
}

volatile+synchronize(代码块级)

public class Singleton_E {
	private Singleton_E() {
		// TODO Auto-generated constructor stub
	}

	private volatile static Singleton_E e = null;

	public static Singleton_E getsingleton() {
		if (e == null) {
			synchronized(Singleton_E.class)
			if(e==null){
				e=new Singleton_E();
				}
		}
		return e;

	}
}

synchronize 方法级

public class Singleton_E {
	private Singleton_E() {
		// TODO Auto-generated constructor stub
	}

	private static Singleton_E e = null;

	public synchronized static Singleton_E getsingleton() {
		if (e == null) {
			e=new Singleton_E();
		}
		return e;

	}
}

枚举

76 hashMap存取的操作,如何解决哈希冲突,modCount是干啥的,先插入元素还是先扩容,扩容时大量元素位置失效,如何进行元素迁移

77长连接短连接,在请求头响应头里面时如何体现的
在首部字段设置connection :close为短连接
设为connection:keep-alive 和keep-alive:timeout=60;表明连接为长连接
78redis 数据类型,如果要你做一个排序的功能,需要用redis哪个类型
string
set
hash
sortset
list

81 B和B+树说一下区别,和二叉树啥区别

82什么时候索引失效
使用函数
前一个有索引后一个没有

83你项目里面有数据库优化的内容,怎么优化的

86 AQS的理解

异常种类
算数异常类:ArithmeticExecption
空指针异常类型:NullPointerException
类型强制转换类型:ClassCastException
数组负下标异常:NegativeArrayException
数组下标越界异常:ArrayIndexOutOfBoundsException
违背安全原则异常:SecturityException
文件已结束异常:EOFException
文件未找到异常:FileNotFoundException
字符串转换为数字异常:NumberFormatException
操作数据库异常:SQLException
输入输出异常:IOException
方法未找到异常:NoSuchMethodException
下标越界异常:IndexOutOfBoundsExecption
系统异常:SystemException
创建一个大小为负数的数组错误异常:NegativeArraySizeException
数据格式异常:NumberFormatException
安全异常:SecurityException
不支持的操作异常:UnsupportedOperationException

排序算法的复杂度
Alt text

Spring ioc 的理解以及Spring aop理解
ioc 的两种实现方式一种是DL 依赖查找另一类是DI依赖注入
springmvc的实现框架
在这里插入图片描述