这个是我为了面试准备的一些资料,分享出来,供参考。本身有word版,还有面试的一些录音文件,如果需要联系微信:wangxiaozao_nannan
多线程
什么叫进程和线程
进程是系统进行资源分配和调度的一个独立单位
线程是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位
进程在执行过程中拥有独立的内存单元,一个进程崩溃后,在保护模式下不会对其它进程产生影响
线程只拥有一点在运行中必不可少的资源,没有单独的内存单元
一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮
但是多个线程共享内存,可以减少处理机的切换时间,从而提高系统的执行效率
参考:
http://www.cnblogs.com/way_testlife/archive/2011/04/16/2018312.html
http://www.cnblogs.com/lmule/archive/2010/08/18/1802774.html
创建线程的几种方式
三种方式:
1. 继承Thread类创建线程类
继承Thread类,重写run方法,调用start()方法来启动该线程
Thread.currentThread()方法返回当前正在执行的线程对象
1. new FirstThreadTest().start();
2. new FirstThreadTest().start();
2. 实现Runnable接口创建线程类
实现Runnable接口,实现run方法的线程执行体
创建Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象
调用线程对象的start()方法来启动该线程
RunnableThreadTest实现了Runnable接口
1. RunnableThreadTest rtt = new RunnableThreadTest();
2. new Thread(rtt,"新线程1").start();
3. new Thread(rtt,"新线程2").start();
3. 实现Callable接口,通过Callable和Future创建线程
实现Callable接口,实现call方法
创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值
使用FutureTask对象作为Thread对象的target创建并启动新线程
调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
1. CallableThreadTest ctt = new CallableThreadTest();
2. FutureTask<Integer> ft = new FutureTask<>(ctt);
3. new Thread(ft,"有返回值的线程").start();
区别对比:
采用实现Runnable、Callable接口的方式创见多线程时
优势:
线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
劣势:
编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。
使用继承Thread类的方式创建多线程时
优势:
编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
劣势:
线程类已经继承了Thread类,所以不能再继承其他父类。
实际应用:
List<Future<AccountDiagData>> threadRlt = new ArrayList<>();
ExecutorServiceexec = Executors.newFixedThreadPool(EXENUMS);
threadRlt.add(exec.submit(new FsAccountDiagThread(……)));
FsAccountDiagThread implements Callable<AccountDiagData>
死锁
两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象
死锁的发生必须具备以下四个必要条件。造成死锁的原因
1)互斥条件:某份资源同时只能给一个进程使用,其余申请者,只能等待资源释放。
2)请求和保持条件:进程占有了一个资源,又申请另一个被其他进程占有的资源,造成阻塞,但又不释放已经占有的资源
3)不剥夺条件:进程占有的资源在使用完之前,不能被其他进程剥夺
4)环路等待条件:进程A在等待B占用的资源,B又在等待A占有的资源。
Sleep和wait的区别
① 这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类。
sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。
② 锁: 最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU。一般wait不会加时间限制,因为如果wait线程的运行资源不够,再出来也没用,要等待其他线程调用notify/notifyAll唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。sleep(milliseconds)可以用时间指定使它自动唤醒过来,如果时间不到只能调用interrupt()强行打断。
Thread.sleep(0)的作用是“触发操作系统立刻重新进行一次CPU竞争”。
a线程想要停止b线程,设置优先级,a的优先级高于b
③ 使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用。
synchronized(x){
x.notify()
//或者wait()
}
由于sleep()方法是Thread类的方法,因此它不能改变对象的机锁。所以当在一个Synchronized方法中调用sleep()时,线程虽然休眠了,但是对象的机锁没有被释放,其他线程仍然无法访问这个对象。而wait()方法则会在线程休眠的同时释放掉机锁,其他线程可以访问该对象。
sleep()使当前线程进入停滞状态(阻塞当前线程),让出CPU的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会。所以不会占用cpu。
区别:
参考:http://blog.csdn.net/u012050154/article/details/50903326
1.每个对象都有一个锁来控制同步访问,Synchronized关键字可以和对象的锁交互,来实现同步方法或同步块。sleep()方法正在执行的线程主动让出CPU(然后CPU就可以去执行其他任务),在sleep指定时间后CPU再回到该线程继续往下执行(注意:sleep方法只让出了CPU,而并不会释放同步资源锁!!!);wait()方法则是指当前线程让自己暂时退让出同步资源锁,以便其他正在等待该资源的线程得到该资源进而运行,只有调用了notify()方法,之前调用wait()的线程才会解除wait状态,可以去参与竞争同步资源锁,进而得到执行。(注意:notify的作用相当于叫醒睡着的人,而并不会给他分配任务,就是说notify只是让之前调用wait的线程有权利重新参与线程的调度)
2.sleep()方法可以在任何地方使用;wait()方法则只能在同步方法或同步块中使用;
3.sleep()是线程线程类(Thread)的方法,调用会暂停此线程指定的时间,但监控依然保持,不会释放对象锁,到时间自动恢复;wait()是Object的方法,调用会放弃对象锁,进入等待队列,待调用notify()/notifyAll()唤醒指定的线程或者所有线程,才会进入锁池,不再次获得对象锁才会进入运行状态;
Join
join() 定义在Thread.java中。
join() 的作用:让“主线程”等待“子线程”结束之后才能继续运行。
wait()的作用是让“当前线程”等待,而这里的“当前线程”是指当前在CPU上运行的线程。
多线程学习目录:
http://www.cnblogs.com/skywang12345/p/java_threads_category.html
线程池
1. import java.util.concurrent.ExecutorService;
2. import java.util.concurrent.Executors;
3. import java.util.concurrent.Future;
ExecutorService exec= Executors.newFixedThreadPool(EXENUMS);
List<Future<AccountDiagData>> threadRlt = new ArrayList<>();
1. threadRlt.add(exec.submit(new FsAccountDiagThread(……)));
2. exec.shutdown();
3.
4. for (Future<AccountDiagData> f : threadRlt) {
5. ……
6. }
线程的类:
public class FsAccountDiagThread implements Callable<AccountDiagData>{
构造函数
}
Callable<AccountDiagData> 其中AccountDiagData 就是这个线程类的返回类型
Callable和runnable接口的区别
参考:http://blog.csdn.net/zyl1042635242/article/details/48267635
http://www.cnblogs.com/frinder6/p/5507082.html
相同点:
1.都是接口
2.都可用来编写多线程
3.都需要调用Thread.start()启动线程
区别:
1.Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;
2.callable接口的线程任务能够返回执行结果,Runnable接口的任务线程不能返回结果
Callable与executors联合在一起,在任务完成时可立刻获得一个更新了的Future。而Runable却要自己处理
Future接口,一般都是取回Callable执行的状态用的。其中的主要方法:
cancel,取消Callable的执行,当Callable还没有完成时
get,获得Callable的返回值
isCanceled,判断是否取消了
isDone,判断是否完成
Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取‘将来’结果;当不调用此方法时,主线程不会阻塞!
线程池
参考:http://blog.csdn.net/u011519624/article/details/69263460
创建ThreadPoolExecutor可以通过构造方法和Executors的静态方法。
public ThreadPoolExecutor(intcorePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnitunit, BlockingQueue<Runnable> workQueue, ThreadFactorythreadFactory, RejectedExecutionHandler handler)
corePoolSize,线程池里最小线程数
maximumPoolSize,线程池里最大线程数量,超过最大线程时候会使用RejectedExecutionHandler
keepAliveTime,unit,线程最大的存活时间
workerQueue,缓存异步任务的队列
ThreadFactory,用来构造线程池里的worker线程
Executors.newCachedThreadPool
core是0,max是无限,队列是SynchronousQueue。无限创建线程,线程60S过期被销毁。风险点是线程数不可控。
Executors.newFixedThreadPool
core是thread数量,max是thread数量,队列是LinkedBlockingQueue。因为LinkedBlockingQueue是无界的,所以max不会起作用。风险点是workerQueue的长度不可控
参考:http://blog.csdn.net/zyl1042635242/article/details/48267635
用Executor来构建线程池,应该要做的事:
1).调用Executors类中的静态方法newCachedThreadPool(必要时创建新线程,空闲线程会被保留60秒)或newFixedThreadPool(包含固定数量的线程池)等,返回的是一个实现了ExecutorService 接口的ThreadPoolExecutor类或者是一个实现了ScheduledExecutorServiece接口的类对象。
2).调用submit提交Runnable或Callable对象。
3).如果想要取消一个任务,或如果提交Callable对象,那就要保存好返回的Future对象。
4).当不再提交任何任务时,调用shutdown方法。
线程池的大小如何设置:
任务的性质:CPU密集型任务、IO密集型任务、混合型任务。
任务的优先级:高、中、低。
任务的执行时间:长、中、短。
任务的依赖性:是否依赖其他系统资源,如数据库连接等。
CPU密集型任务应配置尽可能小的线程,如配置CPU个数+1的线程数,IO密集型任务应配置尽可能多的线程,因为IO操作不占用CPU,不要让CPU闲下来,应加大线程数量,如配置两倍CPU个数+1,而对于混合型的任务,如果可以拆分
若任务对其他系统资源有依赖,如某个任务依赖数据库的连接返回的结果,这时候等待的时间越长,则CPU空闲的时间越长,那么线程数量应设置得越大,才能更好的利用CPU。
当然具体合理线程池值大小,需要结合系统实际情况,在大量的尝试下比较才能得出,以上只是前人总结的规律。
最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间)* CPU数目
线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。
(1)高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换
(2)并发不高、任务执行时间长的业务要区分开看:
a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以适当加大线程池中的线程数目,让CPU处理更多的业务
b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换
怎么实现线程的同步
参考:http://blog.csdn.net/wenwen091100304/article/details/48318699
为什么要同步
因为当我们有多个线程要同时访问一个变量或对象时,如果这些线程中既有读又有写操作时,就会导致变量值或对象的状态出现混乱,从而导致程序异常。
实现方法
1.同步方法
有synchronized关键字修饰的方法。由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类
2.同步代码块
有synchronized关键字修饰的语句块。被该关键字修饰的语句块会自动被加上内置锁,从而实现同步
注:同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
3.使用特殊域变量volatile实现线程同步
4.使用重入锁实现线程同步
在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。
ReenreantLock类的常用方法有:
ReentrantLock() : 创建一个ReentrantLock实例
lock() : 获得锁
unlock() : 释放锁
注:ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用
如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码。如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁
5.使用局部变量实现线程同步
private static ThreadLocal<Integer> count = newThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 0;
}
};
使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响
ThreadLocal与同步机制
a.ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题
b.前者采用以"空间换时间"的方法,后者采用以"时间换空间"的方式
ThreadLocal的应用场景
为了说明ThreadLocal的应用场景。我们来看一个框架的样例。Spring的事务管理器通过AOP切入业务代码,在进入业务代码前,会依据相应的事务管理器提取出相应的事务对象,假如事务管理器是DataSourceTransactionManager,就会从DataSource中获取一个连接对象,通过一定的包装后将其保存在ThreadLocal中。而且Spring也将DataSource进行了包装,重写了当中的getConnection()方法,或者说该方法的返回将由Spring来控制,这样Spring就能让线程内多次获取到的Connection对象是同一个。
为什么要放在ThreadLocal里面呢?由于Spring在AOP后并不能向应用程序传递參数。应用程序的每一个业务代码是事先定义好的,Spring并不会要求在业务代码的入口參数中必须编写Connection的入口參数。此时Spring选择了ThreadLocal,通过它保证连接对象始终在线程内部,不论什么时候都能拿到,此时Spring很清楚什么时候回收这个连接,也就是很清楚什么时候从ThreadLocal中删除这个元素(在9.2节中会具体解说)。
从Spring事务管理器的设计上能够看出。Spring利用ThreadLocal得到了一个非常完美的设计思路,同一时候它在设计时也十分清楚ThreadLocal中元素应该在什么时候删除。由此,我们简单地觉得ThreadLocal尽量使用在一个全局的设计上。而不是一种打补丁的间接方法。
Java中线程的锁有哪几种实现方式
参考:http://www.cnblogs.com/xrq730/p/4851530.html
Java中每个对象都有一个内置锁,一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。
1、synchronized同步方法
(1)对其他synchronized同步方法或synchronized(this)同步代码块呈阻塞状态
(2)同一时间只有一个线程可以执行synchronized同步方法中的代码
2、synchronized同步代码块
(1)对其他synchronized同步方法或synchronized(this)同步代码块呈阻塞状态
(2)同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码
高并发接口调用
一般如果高并发接口调用,会在接口的最开始,使用redis 来保证有些正在操作
MYSQL
MYSQL索引类型
1.普通索引 INDEX
这是最基本的索引,它没有任何限制
2.唯一索引 UNIQUE INDEX
与普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值(注意和主键不同)。如果是组合索引,则列值的组合必须唯一
3.主键索引
它是一种特殊的唯一索引,不允许有空值
4.组合索引
组合索引,其实是相当于分别建立了下面两组组合索引
组合索引:title(50),time(10)
–title,time
–title
主键和唯一索引的比较:
(1)对于主健/unique constraint , oracle/sql server/mysql等都会自动建立唯一索引;
(2)主键不一定只包含一个字段,所以如果你在主键的其中一个字段建唯一索引还是必要的;
(3)主健可作外健,唯一索引不可;
(4)主健不可为空,唯一索引可;
(5)主健也可是多个字段的组合;
(6)主键与唯一索引不同的是:
a.有not null属性;
b.每个表只能有一个。
索引的不足之处
上面都在说使用索引的好处,但过多的使用索引将会造成滥用。因此索引也会有它的缺点:
1.虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。
2.建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件的会膨胀很快。
索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询语句。
数据库的搜索引擎
Innodb引擎
1.对数据库ACID事务的支持
2.实现了SQL标准的四种隔离级别
3.提供了行级锁和外键约束
4.设计目标是处理大容量数据库系统
MySQL运行时Innodb会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎不支持FULLTEXT类型的索引,而且它没有保存表的行数,当SELECT COUNT(*)FROM TABLE时需要扫描全表。当需要使用数据库事务时,该引擎当然是首选。由于锁的粒度更小,写操作不会锁定全表,所以在并发较高时,使用Innodb引擎会提升效率。但是使用行级锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表。
MyIASM引擎
MyIASM是MySQL默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当INSERT(插入)或UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。
不过和Innodb不同,MyIASM中存储了表的行数,于是SELECT COUNT(*) FROM TABLE时只需要直接读取已经保存好的值而不需要进行全表扫描。
如果表的读操作远远多于写操作且不需要数据库事务的支持,那么MyIASM也是很好的选择。
因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。
并且和MyISAM不同,InnoDB的辅助索引数据域存储的也是相应记录主键的值而不是地址,所以当以辅助索引查找时,会先根据辅助索引找到主键,再根据主键索引找到实际的数据。所以Innodb不建议使用过长的主键,否则会使辅助索引变得过大。建议使用自增的字段作为主键,这样B+Tree的每一个结点都会被顺序的填满,而不会频繁的分裂调整,会有效的提升插入数据的效率。
数据库的隔离级别
参考:http://xm-king.iteye.com/blog/770721
Read Uncommitted(读取未提交内容)
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。
Read Committed(读取提交内容)
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。
Repeatable Read(可重读)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
Serializable(可串行化)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
乐观锁和悲观锁
悲观锁
悲观锁的特点是先获取锁,再进行业务操作,即“悲观”的认为获取锁是非常有可能失败的,因此要先确保获取锁成功再进行业务操作。通常所说的“一锁二查三更新”即指的是使用悲观锁。通常来讲在数据库上的悲观锁需要数据库本身提供支持,即通过常用的select … for update操作来实现悲观锁。
乐观锁
乐观锁的特点先进行业务操作,不到万不得已不去拿锁。即“乐观”的认为拿锁多半是会成功的,因此在进行完业务操作需要实际更新数据的最后一步再去拿一下锁就好。
乐观锁在数据库上的实现完全是逻辑的,不需要数据库提供特殊的支持。一般的做法是在需要锁的数据上增加一个版本号,或者时间戳
适用情况:
乐观锁在不发生取锁失败的情况下开销比悲观锁小,但是一旦发生失败回滚开销则比较大,因此适合用在取锁失败概率比较小的场景,可以提升系统并发性能
1.响应速度:如果需要非常高的响应速度,建议采用乐观锁方案,成功就执行,不成功就失败,不需要等待其他并发去释放锁
2.冲突频率:如果冲突频率非常高,建议采用悲观锁,保证成功率,如果冲突频率大,乐观锁会需要多次重试才能成功,代价比较大
3.重试代价:如果重试代价大,建议采用悲观锁
慢SQL的优化:索引,分库分表,读写分离,分区,缓存
数据库的一致性
Mycat的事务处理,读写分离
读写分离,编程上要注意什么
数据库的乐观锁和悲观锁
Oracle有哪些索引
什么场景用乐观锁(高频操作),什么场景用悲观锁
实时排名—抽样排名
调用存储过程的好处:
为什么要用代码,而不用存储过程:
Java概念
多态
Java实现多态有三个必要条件:继承、重写、向上转型。
比如:
Class animal – 方法 run
Class cat extends animal – 重写方法run—说明是猫在跑
Class dog extends animal – 重写方法run—说明是狗在跑
使用的时候,
New animal = cat 或者 dog
调用animal 的run方法
不同的对象可以执行相同的行为,但是他们都需要通过自己的实现方式来执行
多态的表现形式:
1. 重写
子类对父类的函数进行重新定义
2. 重载
同一个类中,同样的方法名,但是参数类型和个数不一样,返回值类型可以相同也可以不相同
Java的反射机制
概念:
指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能调用它的任意一个方法.这种动态获取信息,以及动态调用对象方法的功能叫java语言的反射机制.
应用:
生成动态代理,面向切片编程(在调用方法的前后各加栈帧).
序列化和反序列化
· 序列化:将数据结构或对象转换成二进制串的过程
· 反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程
XML是一种常用的序列化和反序列化协议,具有跨机器,跨语言等优点。
JSON正快速成为最广泛使用的序列化协议之一,优点:
它保持了XML的人眼可读(Human-readable)的优点。
相对于XML而言,序列化后的数据更加简洁。XML所产生序列化之后文件的大小接近JSON的两倍。
与XML相比,其协议比较简单,解析速度比较快。
序列化的实现:
将需要被序列化的类实现Serializable接口,标注该对象是可被序列化
什么时候使用序列化:
一:对象序列化可以实现分布式对象。主要应用例如:RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。
二:java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的"深复制",即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。
Java的垃圾回收机制
Java的垃圾回收机制是Java虚拟机提供的能力,用于在空闲时间以不定时的方式动态回收无任何引用的对象占据的内存空间。
垃圾回收回收的是无任何引用的对象占据的内存空间而不是对象本身
空闲时间,对无任何引用的对象,进行内存空间的回收
System.gc()
Runtime.getRuntime().gc()
上面的方法调用时用于显式通知JVM可以进行一次垃圾回收,但真正垃圾回收机制具体在什么时间点开始发生动作这同样是不可预料的
对哪些java框架或者多线程比较了解
Java框架就是封装好方便程序员操作的类,使项目的开发更简单,维护起来也更容易。
Spring是一个轻量级的控制反转(IoC——InversionofControl)和面向切面(AOP——AspectOriented Programming)的容器框架。spring充当了管理容器的角色。
所谓的控制反转就是我们在设计模式中学到的依赖倒置原则,不依赖具体,要依赖抽象。
而面向切面是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
Java的容器/集合
对Java来说,一切皆是对象,他的容器就是能保存java的对象的类。
JAVA的容器---List,Map,Set
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
Collection: 存放独立元素的序列。
Map:存放key-value型的元素对。(这对于一些需要利用key查找value的程序十分的重要!)
从类体系图中可以看出,Collection定义了Collection类型数据的最基本、最共性的功能接口,而List对该接口进行了拓展。
其中各个类的适用场景有很大的差别,在使用时,应该根据需要灵活的进行选择。此处介绍最为常用的四个容器:
LinkedList :其数据结构采用的是链表,此种结构的优势是删除和添加的效率很高,但随机访问元素时效率较ArrayList类低。
ArrayList:其数据结构采用的是线性表,此种结构的优势是访问和查询十分方便,但添加和删除的时候效率很低。
HashSet: Set类不允许其中存在重复的元素(集),无法添加一个重复的元素(Set中已经存在)。HashSet利用Hash函数进行了查询效率上的优化,其contain()方法经常被使用,以用于判断相关元素是否已经被添加过。
HashMap: 提供了key-value的键值对数据存储机制,可以十分方便的通过键值查找相应的元素,而且通过Hash散列机制,查找十分的方便。
HashMap和HashTable的区别
相同点:HashMap和Hashtable都实现了Map接口
主要的区别有:线程安全性,同步(synchronization),以及速度。
HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。HashMap 实现了Serializable接口,因此它支持序列化,实现了Cloneable接口,能被克隆。
Hashtable同样是基于哈希表实现的,同样每个元素是一个key-value对,其内部也是通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。Hashtable也是JDK1.0引入的类,是线程安全的,能用于多线程环境中。Hashtable同样实现了Serializable接口,它支持序列化,实现了Cloneable接口,能被克隆。
1、继承的父类不同
Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类(HashMap的子类是LinkedHashMap)。但二者都实现了Map接口。
2、线程安全性不同
Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省情况下是非Synchronize的。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步,但使用HashMap时就必须要自己增加同步处理。
3、是否提供contains方法
HashMap把Hashtable的contains方法去掉了,改成containsValue和containsKey,因为contains方法容易让人引起误解。
Hashtable则保留了contains,containsValue和containsKey三个方法,其中contains和containsValue功能相同。
4、key和value是否允许null值
Hashtable中,key和value都不允许出现null值。但是如果在Hashtable中有类似put(null,null)的操作,编译同样可以通过,因为key和value都是Object类型,但运行时会抛出NullPointerException异常,这是JDK的规范规定的。
HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。
5、两个遍历方式的内部实现上不同
Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。
6、hash值不同
哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
7、内部实现使用的数组初始化和扩容方式不同
HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。
Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。
hashmap |
线程不安全 |
允许有null的键和值 |
效率高一点、 |
方法不是Synchronize的要提供外同步 |
有containsvalue和containsKey方法 |
HashMap 是Java1.2 引进的Map interface 的一个实现 |
HashMap是Hashtable的轻量级实现 |
hashtable |
线程安全 |
不允许有null的键和值 |
效率稍低、 |
方法是是Synchronize的 |
有contains方法方法 |
、Hashtable 继承于Dictionary 类 |
Hashtable 比HashMap 要旧 |
HashMap的工作原理
HashMap是基于hashing的原理,我们使用put(key,value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。
Bucket [ˈbʌkɪt] 水桶;一桶(的量);大量; 〈俚〉交通工具;
Entry英[ˈentri] 进入,入场;入口处,门口;登记,记录;参加比赛的人;
当两个对象的hashcode相同会发生什么?
因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中。
如果两个键的hashcode相同,你如何获取值对象?
找到bucket位置之后,会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象。一些优秀的开发者会指出使用不可变的、声明作final的对象,并且采用合适的equals()和hashCode()方法的话,将会减少碰撞的发生,提高效率。不可变性使得能够缓存不同键的hashcode,这将提高整个获取对象的速度,使用String,Interger这样的wrapper类作为键是非常好的选择。
如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?
默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。
你了解重新调整HashMap大小存在什么问题吗?
多线程的情况下,可能产生条件竞争(race condition)。
当重新调整HashMap大小的时候,确实存在条件竞争,因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了。这个时候,你可以质问面试官,为什么这么奇怪,要在多线程的环境下使用HashMap呢?
为什么String, Interger这样的wrapper类适合作为键?
因为String是不可变的,也是final的,而且已经重写了equals()和hashCode()方法了。其他的wrapper类也有这个特点。不可变性是必要的,因为为了要计算hashCode(),就要防止键值改变,如果键值在放入时和获取时返回不同的hashcode的话,那么就不能从HashMap中找到你想要的对象。不可变性还有其他的优点如线程安全。如果你可以仅仅通过将某个field声明成final就能保证hashCode是不变的,那么请这么做吧。因为获取对象的时候要用到equals()和hashCode()方法,那么键对象正确的重写这两个方法是非常重要的。如果两个不相等的对象返回不同的hashcode的话,那么碰撞的几率就会小些,这样就能提高HashMap的性能。
我们可以使用自定义的对象作为键吗?
只要它遵守了equals()和hashCode()方法的定义规则,并且当对象插入到Map中之后将不会再改变了。如果这个自定义对象时不可变的,那么它已经满足了作为键的条件,因为当它创建之后就已经不能改变了。
我们可以使用CocurrentHashMap来代替Hashtable吗?
我们知道Hashtable是synchronized的,但是ConcurrentHashMap同步性能更好,因为它仅仅根据同步级别对map的一部分进行上锁。ConcurrentHashMap当然可以代替HashTable,但是HashTable提供更强的线程安全性。
总结
HashMap的工作原理
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
当两个不同的键对象的hashcode相同时会发生什么?它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。
要牢记以下关键点:
HashMap有一个叫做Entry的内部类,它用来存储key-value对。
上面的Entry对象是存储在一个叫做table的Entry数组中。
table的索引在逻辑上叫做“桶”(bucket),它存储了链表的第一个元素。
key的hashcode()方法用来找到Entry对象所在的桶。
如果两个key有相同的hash值,他们会被放在table数组的同一个桶里面。
key的equals()方法用来确保key的唯一性。
value对象的equals()和hashcode()方法根本一点用也没有。
HashMap获取key
方法1:keySet()
HashMap hashmp = ne HashMap();
hashmp.put("aa","111");
Set set = hashmp.keySet();
Iterator iter = set.iterator();
while (iter.hasNext()) {
String key = (String) iter.next();
// printkey
}
// traverse
for (String key : list.get(pos).keySet() ){
myKey = key;
}
方法2:entrySet()
HashMap map;
Iterator i = map.entrySet().iterator();
while (i.hasNext()) {
Object obj = i.next();
String key = obj.toString();
}
// or
while (i.hasNext()) {
Entry entry =(java.util.Map.Entry)it.next();
entry.getkey();
entry.getValue();
}
ArrayList和LinkedList的区别
参考:http://tieba.baidu.com/p/2777462369
http://www.cnblogs.com/huzi007/p/5550440.html
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
ArrayList的get(i)不需要遍历可以直接通过索引获取数据,而LinkedList需要遍历,遍历到i索引位置时将当前位置数据返回。
LinkedList的get,遍历可能会从列表头部或末尾开始进行,取决于i在列表的前半部还是后半部分
同样在特定索引位置插入数据时,也是先遍历到那个位置再进行插入。这样很容易理解,当很大数据量时(比如1W),在LinkedList前部和后部进行删除插入操作,速度会很快,但是如果是在中部进行操作,测试结果就不那么乐观了
遍历LinkedList必须使用iterator不能使用for循环,因为每次for循环体内通过get(i)取得某一元素时都需要对list重新进行遍历,性能消耗极大
==号和equals的区别
1. ==号在比较基本数据类型时比较的是值,而用==号比较两个对象时比较的是两个对象的地址值:
2. quals()方法存在于Object类中,因为Object类是所有类的直接或间接父类,也就是说所有的类中的equals()方法都继承自Object类,而通过源码我们发现,Object类中equals()方法底层依赖的是==号,那么,在所有没有重写equals()方法的类中,调用equals()方法其实和使用==号的效果一样,也是比较的地址值,然而,Java提供的所有类中,绝大多数类都重写了equals()方法,重写后的equals()方法一般都是比较两个对象的值:
Set集合
String,StringBuffer 和 StringBuilder的区别
① 可变与不可变
String类中使用字符数组保存字符串,如下就是,因为有“final”修饰符,所以可以知道string对象是不可变的。
rivate final char value[];
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,如下就是,可知这两种对象都是可变的。
char[] value;
② 否多线程安全
String中的对象是不可变的,也就可以理解为常量,显然线程安全。
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
③ StringBuilder与StringBuffer共同点
StringBuilder与StringBuffer有公共父类AbstractStringBuilder(抽象类)。
抽象类与接口的其中一个区别是:抽象类中可以定义一些子类的公共方法,子类只需要增加新的功能,不需要重复写已经存在的方法;而接口中只是对方法的申明和常量的定义。
StringBuilder、StringBuffer的方法都会调用AbstractStringBuilder中的公共方法,如super.append(...)。只是StringBuffer会在方法上加synchronized关键字,进行同步。
最后,如果程序不是多线程的,那么使用StringBuilder效率高于StringBuffer。
参考:http://blog.csdn.net/shineflowers/article/details/40047479
分布式开发
依赖注入几种实现方式
IOC容器就是具有依赖注入功能的容器,IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。应用程序无需直接在代码中new相关的对象,应用程序由IOC容器进行组装。在Spring中BeanFactory是IOC容器的实际代表者。
依赖注入的三种实现方式
1. 构造函数注入(Contructor Injection)
2. setter注入
3. 接口注入
容器是spring的核心,使IoC管理所有和组件
spring的两种容器:a、BeanFactoy b、ApplicationContext应用上下文
BeanFactory:BeanhFactory使用延迟加载所有的Bean,为了从BeanhFactory得到一个Bean,只要调用getBean()方法,就能获得Bean
ApplicationContext:a、提供文本信息解析,支持I18N
b、提供载入文件资源的通用方法
c、向注册为监听器的Bean发送事件
d、ApplicationContext接口扩展BeanFactory接口
e、ApplicationContext提供附加功能
ApplicationContext的三个实现类:a、ClassPathXmlApplication:把上下文文件当成类路径资源 b、FileSystemXmlApplication:从文件系统中的XML文件载入上下文定义信息
c、XmlWebApplicationContext:从Web系统中的XML文件载入上下文定义信息
在默认情况下,Bean全都是单态,在<bean>中的singleton为false
ApplicationContext和BeanFactory的区别
三种获取ApplicationContext对象引用的方法
1.ClassPathXmlApplicationContext 通过类路径。
2.FileSystemXmlApplicationContext 通过文件路径。
ApplicationContext ac =FileSystemXmlApplicationContext("d:\\com\\bean.xml");
3.XmlWebApplicationContext:从web系统中加载。
1.从ApplicationContext中取bean
ApplicationContext ac = newClassPathXmlApplicationContext("/com/bean.xml");
当我们实例化bean.xml时,该文件中配置的bean都会被实例化。(该bean scope是singleton)
2. 从BeanFactory中取bean
BeanFactory factory= new XmlBeanFactory(newClassPathResource("/com/bean.xml"));
factory.getBean("student"); //取bean的时候才会实例化
当使用beanfactory去获取bean,当你只是实例化该容器,那么容器内的bean不会被实例化,只有当使用某个bean(getBean)时,才会实时去实例化该bean。
总结
1.如果使用ApplicationContext,则配置的bean如果是singleton不管你用还是不用,都被实例化。好处是可以预先加载,坏处是浪费内存。
2.BeanFactory,当使用BeanFactory实例化对象时,配置的bean不会马上被实例化。当你使用该bean时才会被实例化(getBean)。好处是节约内存,缺点是速度比较慢。多用于移动设备的开发上。
3.一般没有特殊要求,应当使用ApplicationContext完成。
参考:http://blog.sina.com.cn/s/blog_5f8ac4b70101jmkc.html
Spring注解
Spring集成ibatis,一对多的情况
参考:http://wyang2008.iteye.com/blog/540369
1. <?xml version="1.0" encoding="UTF-8" ?>
2. <!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN" "http://www.ibatis.com/dtd/sql-map-2.dtd">
3. <sqlMap namespace="account">
4. <typeAlias alias="Account" type="ibatis.one.many.Account" />
5. <typeAlias alias="OrderInfo" type="ibatis.one.many.OrderInfo" />
6.
7. <resultMap class="Account" id="accountResultMap">
8. <result property="accountId" column="accountId" />
9. <result property="accountNo" column="accountNo" />
10. <result property="orderList" column="accountId"
11. select="getOrdersByAccountId" />
12. </resultMap>
13. <resultMap class="OrderInfo" id="orderResultMap">
14. <result property="orderId" column="orderId" />
15. <result property="orederName" column="orederName" />
16. <result property="accountId" column="accountId" />
17. </resultMap>
18. <select id="getAccounts" resultMap="accountResultMap">
19. <![CDATA[select * from Account]]>
20. </select>
21. <select id="getOrdersByAccountId" resultMap="orderResultMap">
22. <![CDATA[
23. select o.* from Account a,orderinfo o where o.accountId = a.accountId and a.accountId=#value# ]]>
24. </select>
25. </sqlMap>
Spring4.0 @RestController与@Controller区别
参考:http://blog.csdn.net/hj7jay/article/details/52261200
http://blog.csdn.net/gg12365gg/article/details/51345601
@RestController注解相当于@ResponseBody+ @Controller合在一起的作用
1. 如果只是使用@RestController注解Controller,则Controller中的方法无法返回jsp页面,配置的视图解析器InternalResourceViewResolver不起作用,返回的内容就是Return 里的内容。
2. 如果需要返回到指定页面,则需要用 @Controller配合视图解析器InternalResourceViewResolver才行。
3. 如果需要返回JSON,XML或自定义mediaType内容到页面,则需要在对应的方法上加上@ResponseBody注解。
Spring的自动装配置
set注入和构造注入有时在做配置时比较麻烦。所以框架为了提高开发效率,提供自动装配功能,简化配置。Spring框架式默认不支持自动装配的,要想使用自动装配需要修改spring配置文件中<bean>标签的autowire属性
自动装配属性有6个值可选,分别代表不同的含义。
1,byName
从Spring环境中获取目标对象时,目标对象中的属性会根据名称在整个Spring环境中查找<bean>标签的id属性值。如果有相同的,那么获取这个对象,实现关联。整个Spring环境:表示所有的spring配置文件中查找,那么id不能有重复的。
2,byType
从Spring环境中获取目标对象时,目标对象中的属性会根据类型在整个spring环境中查找<bean>标签的class属性值。如果有相同的,那么获取这个对象,实现关联。
缺点:如果存在多个相同类型的bean对象,会出错。
如果属性为单一类型的数据,那么查找到多个关联对象会发生错误。
如果属性为数组或集合(泛型)类型,那么查找到多个关联对象不会发生异常。
3,constructor
使用构造方法完成对象注入,其实也是根据构造方法的参数类型进行对象查找,相当于采用byType的方式。
4,autodetect
自动选择:如果对象没有无参数的构造方法,那么自动选择constructor的自动装配方式进行构造注入。如果对象含有无参数的构造方法,那么自动选择byType的自动装配方式进行setter注入。
5,no
不支持自动装配功能
6,default
表示默认采用上一级标签的自动装配的取值。如果存在多个配置文件的话,那么每一个配置文件的自动装配方式都是独立的。
Spring @Autowired,@Resource,@Required注解的用法和作用
@component
@component,基于注解,标示一个受spring管理的组件
@Repository、@Service 和 @Controller分别和持久层、业务层和控制层(Web 层)相对应,这三个注解和 @Component 是等效的
@Component把这个类交给Spring管理,由于不好说这个类属于哪个层面,比较中立的情况下,一般就用@Component
@Autowired的用法和作用
这个注解就是spring可以自动帮你把bean里面引用的对象的setter/getter方法省略,它会自动帮你set/get。
Spring 2.5 引入了@Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。通过 @Autowired的使用来消除 set ,get方法。
在applicationContext.xml中加入:
<!-- 该 BeanPostProcessor 将自动对标注@Autowired 的 Bean 进行注入 -->
<beanclass="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
Spring 通过一个BeanPostProcessor对 @Autowired 进行解析,所以要让 @Autowired 起作用必须事先在 Spring 容器中声明 AutowiredAnnotationBeanPostProcessor Bean。
修改在原来注入spirng容器中的bean的方法。
在域变量上加上标签@Autowired,并且去掉相应的get 和set方法
@Resource的作用和用法
@Resource 的作用相当于 @Autowired,只不过 @Autowired 按 byType 自动注入,面 @Resource 默认按 byName自动注入罢了。
@Resource 有两个属性是比较重要的,分别是 name和 type,Spring将 @Resource 注释的 name属性解析为 Bean的名字,而 type属性则解析为 Bean的类型。所以如果使用name属性,则使用 byName的自动注入策略,而使用 type属性时则使用 byType自动注入策略。如果既不指定 name也不指定 type属性,这时将通过反射机制使用 byName自动注入策略。
// 自动注入类型为 Car 的 Bean
@Resource
private Car car; // 自动注入 bean 名称为 office 的 Bean
@Resource(name = "office")
private Office office;
@Required注解用于检查特定的属性是否设置
@required 负责检查一个bean在初始化时其声明的 set方法是否被执行, 当某个被标注了@Required 的 Setter 方法没有被调用,则 Spring 在解析的时候会抛出异常,以提醒开发者对相应属性进行设置。 @Required 注解只能标注在 Setter 方法之上。因为依赖注入的本质是检查 Setter 方法是否被调用了,而不是真的去检查属性是否赋值了以及赋了什么样的值。如果将该注解标注在非 setXxxx() 类型的方法则被忽略。
标注一个方法(通常是一个JavaBean的setter方法)是@Required,也就是,这个setter方法必须定义为通过一个值来依赖注入。
1.RequiredAnnotationBeanPostProcessor 为该注解的处理器,即bean后置处理器,检查所有带有该解的bean属性是否设置,如果未设置则抛出异常。
2.在spring配置文件中可以通过<context:annotation-config/>元素自动注册RequiredAnnotationBeanPostProcessor处理器。
3.RequiredAnnotationBeanPostProcessor处理器还能自定义注解用于检查属性,功能与@Required一致
@Autowired 与@Resource的区别:
参考:http://blog.csdn.net/lf_software_studio/article/details/8256510
1. @Autowired与@Resource都可以用来装配bean.都可以写在字段上,或写在setter方法上。
2. @Autowired默认按类型装配(这个注解是属于spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false),如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下:
@Autowired() @Qualifier("baseDao")
private BaseDao baseDao;
3. @Resource(这个注解属于J2EE的),默认安装名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
@Resource(name="baseDao")
private BaseDao baseDao;
Spring单例和线程安全
Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式,这是在多线程开发的时候要尤其注意的地方。
单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
当多用户同时请求一个服务时,容器会给每一个请求分配一个线程,这是多个线程会并发执行该请求多对应的业务逻辑(成员方法),此时就要注意了,如果该处理逻辑中有对该单列状态的修改(体现为该单列的成员属性),则必须考虑线程同步问题
线程安全问题都是由全局变量及静态变量引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
1) 常量始终是线程安全的,因为只存在读操作。
2)每次调用方法前都新建一个实例是线程安全的,因为不会访问共享的资源。
3)局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量和方法内变量。
Hibernate
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。
Hibernate的二级缓存
参考:http://www.iteye.com/topic/18904
spring 的优点?
1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦
2.可以使用容易提供的众多服务,如事务管理,消息服务等
3.容器提供单例模式支持
4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能
5.容器提供了众多的辅助类,能加快应用的开发
6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等
7.spring属于低侵入式设计,代码的污染极低
8.独立于各种应用服务器
9.spring的DI机制降低了业务对象替换的复杂性
10.Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部
什么是DI机制?
依赖注入(Dependecy Injection)和控制反转(Inversion of Control)是同一个概念,具体的讲:当某个角色需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在spring中创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由spring来完成,然后注入调用者因此也称为依赖注入。
spring以动态灵活的方式来管理对象,注入的两种方式,设置注入和构造注入。
设置注入的优点:直观,自然
构造注入的优点:可以在构造器中决定依赖关系的顺序。
什么是AOP?
面向切面编程(AOP)完善spring的依赖注入(DI),面向切面编程在spring中主要表现为两个方面
1.面向切面编程提供声明式事务管理
2.spring支持用户自定义的切面
面向切面编程(aop)是对面向对象编程(oop)的补充,
面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。
AOP从程序运行角度考虑程序的结构,提取业务处理过程的切面,oop是静态的抽象,aop是动态的抽象,是对应用执行过程中的步骤进行抽象,,从而获得步骤之间的逻辑划分。
aop框架具有的两个特征:
1.各个步骤之间的良好隔离性
2.源代码无关性
Spring4.0
IOC反转资源控制的方向,容器主动将资源推送给它所管理的组件,组件所要做的就是选择一种合适的方式来接收资源
DI(依赖注入)组件以一种定义好的方式(setter方法)接受容器资源的注入
Class:bean的全类名,通过反射的方式在IOC容器中创建Bean,所以要求Bean中必须有无参数的构造器
Id:标识容器中的,id必须唯一
ApplicationContext代表IOC的容器
两种类型的IOC容器实现:
BeanFactory:IOC容器的基本实现
ApplicationContext:提供了更多的高级特性,是BeanFactory的子接口
Java两种比较方法:
equals()比较的是对象的内容(区分字母的大小写格式)
“==”比较两个对象时,比较的是两个对象的内存地址
HTTP
HTTP 协议是以 ASCII 码传输,建立在TCP/IP 协议之上的应用层规范。
规范把 HTTP 请求分为三个部分:状态行、请求头、消息主体。类似于下面这样:
<method> <request-URL><version>
<headers>
<entity-body>
协议规定 POST 提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必须使用什么编码方式。实际上,开发者完全可以自己决定消息主体的格式,只要最后发送的 HTTP 请求满足上面的格式就可以。
JSON
按照自己添加的顺序,指定为true即可:
JSONObject object1 = new JSONObject(true);
按照自己指定的顺序:
public static class VO {
@JSONField(ordinal = 3)
private int f0;
@JSONField(ordinal = 2)
private int f1;
@JSONField(ordinal = 1)
private int f2;
}
@JSONField作用在Field时可指定名称,其name不仅定义了输入key的名称,同时也定义了输出的名称。
@JSONField(name="name")
private String name;
当作用在setter方法上时,就相当于根据 name 到 json中寻找对应的值,并调用该setter对象赋值。当作用在getter上时,在bean转换为json时,其key值为name定义的值。
@JSONField(name="name")
public String getProductName() {
return productName;
}
@JSONField(name="NAME")
public void setProductName(String productName) {
this.productName = productName;
}
跨域问题
跨域问题的解决方案:
1.nginx配置文件解决
[html] view plain copy
1. location /compet-biz-trade {
2. add_header 'Access-Control-Allow-Origin' '*';
3. add_header 'Access-Control-Allow-Credentials' 'true';
4. add_header 'Access-Control-Allow-Methods' 'OPTION, POST, GET';
5. add_header 'Access-Control-Allow-Headers' 'X-Requested-With, Content-Type,authorization';
6. proxy_pass http://127.0.0.1:8011/compet-biz-trade;
7. }
主要是添加:
[html] view plain copy
1. add_header 'Access-Control-Allow-Origin' '*';
Access-Control-Allow-Origin这个Header在W3C标准里用来检查该跨域请求是否可以被通过,如果值为*则表明当前页面可以跨域访问。默认的情况下是不允许的。
但是,如果程序猿偷懒将Access-Control-Allow-Origin设置为:Access-Control-Allow-Origin:* 允许任何来自任意域的跨域请求,那么就存在被 DDoS攻击的可能。
2.自己写filter类,在业务项目中配置web.xml 中配置想要的xml 文件
[html] view plain copy
1. public class CorsFilter implements Filter{
2.
3. @Override
4. public void init(FilterConfig filterConfig) throws ServletException {
5. // TODO Auto-generated method stub
6.
7. }
8.
9. @Override
10. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
11. ServletException {
12. // TODO Auto-generated method stub
13. HttpServletResponse res = (HttpServletResponse) response;
14. res.setContentType("text/html;charset=UTF-8");
15. res.setHeader("Access-Control-Allow-Origin", "*");
16. res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
17. res.setHeader("Access-Control-Max-Age", "0");
18. res.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
19. res.setHeader("Access-Control-Allow-Credentials", "true");
20. res.setHeader("XDomainRequestAllowed","1");
21. chain.doFilter(request, response);
22. }
23.
24. @Override
25. public void destroy() {
26. // TODO Auto-generated method stub
27.
28. }
29. }
web.xml的配置
1. <filter>
2. <filter-name>cors</filter-name>
3. <filter-class>com.tianlong.common.base.CorsFilter</filter-class>
4. </filter>
5. <filter-mapping>
6. <filter-name>cors</filter-name>
7. <url-pattern>/*</url-pattern>
8. </filter-mapping>
接口校验传递的数据完整性
Java中使用 Md5+Key的方式对Url签名防篡改
最近系统接入支付宝,在demo中看到了如何对get请求的url签名防止篡改的方法,觉得不错特此记录一下。
实现方式是:Md5(url+key) 的方式进行的。
1、key可以是任意的字符串,然后“客户端”,“服务器端”各自保留一份,千万不能外泄。
2、请求的URL例如: name=jack&age=18
3、 URL+Key字符串拼接后的值用MD5加密生成签名,将签名发送到服务器端,同时服务器端已同样的方式计算出签名,然后比较俩个MD5的值是否相同,来确定URL是否被篡改。
4、别人拿不到key是无法正确计算出签名的。
其他
Spring boot
3、扎实的JAVA语言基础;熟悉Multi-Thread、Spring、MyBatis、Dubbo、ActiveMQ、WebX、分库分表等;
反射机制
序列化和反序列化
数据库,搜索引擎
10.20
遇到的技术难点
Spring注解的实现原理,AOP,AOP靠什么实现,AOP有哪几种实现方法
事务的传播机制
Redis抢锁,分布式锁,redis锁的实现细节
Nginx
分布式的框架dubbi
Springboot
消息中间件kafka
Spring的功能点
Spring的事务管理,工作的原理
Linux的top定位到指定SQL
接口和抽象类的区别:
Object对象有几个共用的方法?
各个都是什么时候进行使用?
Map
Put方法的实现
怎么让它的hashCode不冲突,和final的关系
Final不能再被继承
Spring事务,传播机制的意义,传播机制是为了解决什么问题?
Spring怎么完成事务,和jdbc交互commit的
事务和线程的关系?
事务的隔离级别
查询特别慢的情况
Top可以看到哪段代码执行慢的
数据量是多少?
持久化主键:mybatis
分库:按业务分
@Autowired
private StringRedisTemplate stringRedisTemplate;
stringRedisTemplate.boundValueOps(“所名称”).setIfAbsent(CompetConstants.TRUE)
SpringMVC处理异常:
<!-- service层入参校验器切面 -->
<bean id="validatorAspect" class="com.hundsun.ftenant.common.aspect.ValidatorAspect"></bean>
<aop:config>
<aop:pointcut
id="performance"
expression="execution(*com.hundsun.cloudtrade.*.service.*.*(..))" />
<aop:aspect ref="validatorAspect" >
<aop:before pointcut-ref="performance"method="doAccessCheck"/>
<aop:after-returning pointcut-ref="performance"method="doAfter"/>
<aop:after-throwing pointcut-ref="performance"method="doAfterThrow"/>
<aop:after method="after" pointcut-ref="performance"/>
<aop:around method="doBasicProfiling" pointcut-ref="performance"/>
</aop:aspect>
</aop:config>