1. 实现多线程方式
1、继承Thread类,重写run函数
2、实现Runnable接口,重写run函数
3、实现Callable接口,重写call函数
2.java面试题Arraylist和Hashset区别
1、Arraylist是有序集合,可以存放重复元素(数据结构数组)
2、Hashset是无序集合,不可以存放重复元素(数据结构哈希表)
3. 基本的排序有哪些
1、冒泡排序、快速排序、选择排序,他们的原理
4. 常见设计模式有哪些?使用场景
1、单例模式,适配器模式,工厂方法模式,观察者模式,装饰模式
Springmvc框架结构
1、前端控制器DispatcherServlet:功能就是拦截用户请求,负责整个处理流程的的调度。没有业务逻辑。可以降低各个组件之间的耦合度。
2、处理器映射器HandlerMapping:保存url和处理器Handler之间的映射关系。映射关系可以使用配置文件配置,可以是注解配置。
3、处理器适配器HandlerAdapter:根据不同的handler选择不同的适配器,执行handler,返回处理结果。
4、处理器Handler:处理用户请求,调用Service返回处理结果。
5、视图解析器ViewResolver:根据逻辑视图,转换成物理视图。逻辑视图就是一个字符串,物理视图就是jsp的全路径。
6、视图view:最常见的视图就是jsp。Freemaker、pdf、excel
8.事物的特性
1、原子性,一致性,隔离性,持久性
9.不使用java原生集合,怎么写一个集合类
1、参考链表的实现原理
10.自己实现AOP原理
1、实现InvocationHandler接口,重写invoke方法(细节百度)
11.java并发锁知识
1、使用同步锁(synchronized),使用lock锁
以下为他们的区别:
2、ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候
线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情
ReentrantLock获取锁定与三种方式:
a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
c)tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断
3、synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中
4、在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态;
12.怎么获取spring容器的方法
方法一:在初始化时保存ApplicationContext对象
代码:
12
ApplicationContext ac = new FileSystemXmlApplicationContext(“applicationContext.xml”);
ac.getBean(“beanId”);
说明:
这种方式适用于采用Spring框架的独立应用程序,需要程序通过配置文件手工初始化Spring的情况。
方法二:通过Spring提供的工具类获取ApplicationContext对象
代码:
import org.springframework.web.context.support.WebApplicationContextUtils;
ApplicationContext ac1 = WebApplicationContextUtils.getRequiredWebApplicationContext(ServletContext sc)
ApplicationContext ac2 = WebApplicationContextUtils.getWebApplicationContext(ServletContext sc)
ac1.getBean(“beanId”);
ac2.getBean(“beanId”);
说明:
这种方式适合于采用Spring框架的B/S系统,通过ServletContext对象获取ApplicationContext对象,然后在通过它获取需要的类实例。
上面两个工具方式的区别是,前者在获取失败时抛出异常,后者返回null。
方法三:继承自抽象类ApplicationObjectSupport
说明:
抽象类ApplicationObjectSupport提供getApplicationContext()方法,可以方便的获取到ApplicationContext。Spring初始化时,会通过该抽象类的setApplicationContext(ApplicationContext context)方法将ApplicationContext 对象注入。
方法四:继承自抽象类WebApplicationObjectSupport
说明:
类似上面方法,调用getWebApplicationContext()获取WebApplicationContext
方法五:实现接口ApplicationContextAware
说明:
实现该接口的setApplicationContext(ApplicationContext context)方法,并保存ApplicationContext 对象。Spring初始化时,会通过该方法将ApplicationContext 对象注入。
以上方法适合不同的情况,请根据具体情况选用相应的方法。
这里值得提一点的是,系统中用到上述方法的类实际上就于Spring框架紧密耦合在一起了,因为这些类是知道它们是运行在Spring框架上的,因此,系统中,应该尽量的减少这类应用,使系统尽可能的独立于当前运行环境,尽量通过DI的方式获取需要的服务提供者。
13.IO流中的数据结构是什么?
1、Byte
14.分布式系统事物管理的原理(保证分布式系统数据的一致性)
更详细的参考第33条
在 JAVA 中要想使用分布式事务处理,需要使用 JTA。但是 JTA 在 J2SE 环境中是没办法测试的,必须在 J2EE 应用服务器中(阿帕奇,weblogic等)。
分布式事务一般采用一种称为 2-PC(两阶段提交)的协议进行处理,2-PC 是分布式事务处理协议。
第一阶段:
协调者会问所有的参与者节点,是否可以执行提交操作。
各个参与者开始事务执行的准备工作:如:为资源上锁,预留资源,写undo/redo log……
参与者响应协调者,如果事务的准备工作成功,则回应“可以提交”,否则回应“拒绝提交”。
第二阶段:
如果所有的参与者都回应“可以提交”,那么,协调者向所有的参与者发送“正式提交”的命令。参与者完成正式提交,并释放所有资源,然后回应“完成”,协调者收集各节点的“完成”回应后结束这个Global Transaction。
如果有一个参与者回应“拒绝提交”,那么,协调者向所有的参与者发送“回滚操作”,并释放所有资源,然后回应“回滚完成”,协调者收集各节点的“回滚”回应后,取消这个Global Transaction。
15.sql语句优化
1、减少表之间的关联,特别对于批量数据处理,尽量单表查询数据,统一在内存中进行逻辑处理,减少数据库压力(视具体业务而定,不一定单表后java处理就是最优)。
2、对访问频繁的数据,充分利用数据库cache和应用的缓存。
3、数据量比较大的,在设计过程中,为了减少其他表的关联,增加一些冗余字段,提高查询性能。
4、对经常查询数据建立索引。
5、尽量要什么字段查什么字段
6、分库分表
7、减少子查询
8、开启mysql查询缓存
9、使用explain关键字来查看查询语句执行效率并根据结果进行查询语句优化
16.Redis和memcached区别
1.、Memcached基本只支持简单的key-value存储,不支持枚举,不支持持久化和复制等功能
Redis除key/value之外,还支持list,set,sorted set,hash等众多数据结构,提供了KEYS
2、redis是单线程的IO复用模型;Memcached是多线程,非阻塞IO复用的网络模型
3.、Memcached使用预分配的内存池的方;Redis使用现场申请内存的方式来存储数据
17.消息管理开源技术
RabbitMQ :
18.JVM知识
Jvm分为三个代,年轻代,年老代,持久代
其中
年轻代:所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个Survivor区(一般而言)。
年老代:在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
持久代:用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,
两种jc方式:
Scavenge GC
一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。
Full GC
对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:
· 年老代(Tenured)被写满
· 持久代(Perm)被写满
· System.gc()被显示调用
·上一次GC之后Heap的各域分配策略动态变化
19.Spring bean的生命周期
这Spring框架中,一旦把一个bean纳入到Spring IoC容器之中,这个bean的生命周期就会交由容器进行管理,一般担当管理者角色的是BeanFactory或ApplicationContext。认识一下Bean的生命周期活动,对更好的利用它有很大的帮助。
下面以BeanFactory为例,说明一个Bean的生命周期活动:
• Bean的建立
由BeanFactory读取Bean定义文件,并生成各个实例。
• Setter注入
执行Bean的属性依赖注入。
• BeanNameAware的setBeanName()
如果Bean类实现了org.springframework.beans.factory.BeanNameAware接口,则执行其setBeanName()方法。
• BeanFactoryAware的setBeanFactory()
如果Bean类实现了org.springframework.beans.factory.BeanFactoryAware接口,则执行其setBeanFactory()方法。
• BeanPostProcessors的processBeforeInitialization()
容器中如果有实现org.springframework.beans.factory.BeanPostProcessors接口的实例,则任何Bean在初始化之前都会执行这个实例的processBeforeInitialization()方法。
• InitializingBean的afterPropertiesSet()
如果Bean类实现了org.springframework.beans.factory.InitializingBean接口,则执行其afterPropertiesSet()方法。
• Bean定义文件中定义init-method
在Bean定义文件中使用“init-method”属性设定方法名称,如下:
…….
这时会执行initMethod()方法,注意,这个方法是不带参数的。
• BeanPostProcessors的processAfterInitialization()
容器中如果有实现org.springframework.beans.factory.BeanPostProcessors接口的实例,则任何Bean在初始化之前都会执行这个实例的processAfterInitialization()方法。
• DisposableBean的destroy()
在容器关闭时,如果Bean类实现了org.springframework.beans.factory.DisposableBean接口,则执行它的destroy()方法。
• Bean定义文件中定义destroy-method
在容器关闭时,可以在Bean定义文件中使用“destory-method”定义的方法
…….
这时会执行destroyMethod()方法,注意,这个方法是不带参数的。
以上就是BeanFactory维护的一个Bean的生命周期。下面这个图可能更直观一些:
如果使用ApplicationContext来维护一个Bean的生命周期,则基本上与上边的流程相同,只不过在执行BeanNameAware的setBeanName()后,若有Bean类实现了org.springframework.context.ApplicationContextAware接口,则执行其setApplicationContext()方法,然后再进行BeanPostProcessors的processBeforeInitialization()
实际上,ApplicationContext除了向BeanFactory那样维护容器外,还提供了更加丰富的框架功能,如Bean的消息,事件处理机制等。
21.Mysql存储引擎
MySQL5.5以后默认使用InnoDB存储引擎,其中InnoDB和BDB提供事务安全表,其它存储引擎都是非事务安全表。
若要修改默认引擎,可以修改配置文件中的default-storage-engine。可以通过:show variables like ‘default_storage_engine’;查看当前数据库到默认引擎。命令:show engines和show variables like ‘have%’可以列出当前数据库所支持到引擎。其中Value显示为disabled的记录表示数据库支持此引擎,而在数据库启动时被禁用。在MySQL5.1以后,INFORMATION_SCHEMA数据库中存在一个ENGINES的表,它提供的信息与show engines;语句完全一样,可以使用下面语句来查询哪些存储引擎支持事物处理:select engine from information_chema.engines where transactions = ‘yes’;
可以通过engine关键字在创建或修改数据库时指定所使用到引擎。
主要存储引擎:MyISAM、InnoDB、MEMORY和MERGE介绍:
在创建表到时候通过engine=…或type=…来指定所要使用到引擎。show table status from DBname来查看指定表到引擎。
(一)MyISAM
它不支持事务,也不支持外键,尤其是访问速度快,对事务完整性没有要求或者以SELECT、INSERT为主的应用基本都可以使用这个引擎来创建表。
每个MyISAM在磁盘上存储成3个文件,其中文件名和表名都相同,但是扩展名分别为:
• .frm(存储表定义)
• MYD(MYData,存储数据)
• MYI(MYIndex,存储索引)
数据文件和索引文件可以放置在不同的目录,平均分配IO,获取更快的速度。要指定数据文件和索引文件的路径,需要在创建表的时候通过DATA DIRECTORY和INDEX DIRECTORY语句指定,文件路径需要使用绝对路径。
每个MyISAM表都有一个标志,服务器或myisamchk程序在检查MyISAM数据表时会对这个标志进行设置。MyISAM表还有一个标志用来表明该数据表在上次使用后是不是被正常的关闭了。如果服务器以为当机或崩溃,这个标志可以用来判断数据表是否需要检查和修复。如果想让这种检查自动进行,可以在启动服务器时使用–myisam-recover现象。这会让服务器在每次打开一个MyISAM数据表是自动检查数据表的标志并进行必要的修复处理。MyISAM类型的表可能会损坏,可以使用CHECK TABLE语句来检查MyISAM表的健康,并用REPAIR TABLE语句修复一个损坏到MyISAM表。
MyISAM的表还支持3种不同的存储格式:
• 静态(固定长度)表
• 动态表
• 压缩表
其中静态表是默认的存储格式。静态表中的字段都是非变长字段,这样每个记录都是固定长度的,这种存储方式的优点是存储非常迅速,容易缓存,出现故障容易恢复;缺点是占用的空间通常比动态表多。静态表在数据存储时会根据列定义的宽度定义补足空格,但是在访问的时候并不会得到这些空格,这些空格在返回给应用之前已经去掉。同时需要注意:在某些情况下可能需要返回字段后的空格,而使用这种格式时后面到空格会被自动处理掉。
动态表包含变长字段,记录不是固定长度的,这样存储的优点是占用空间较少,但是频繁到更新删除记录会产生碎片,需要定期执行OPTIMIZE TABLE语句或myisamchk -r命令来改善性能,并且出现故障的时候恢复相对比较困难。
压缩表由myisamchk工具创建,占据非常小的空间,因为每条记录都是被单独压缩的,所以只有非常小的访问开支。
(二)InnoDB
InnoDB存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是对比MyISAM的存储引擎,InnoDB写的处理效率差一些并且会占用更多的磁盘空间以保留数据和索引。
1)自动增长列:
InnoDB表的自动增长列可以手工插入,但是插入的如果是空或0,则实际插入到则是自动增长后到值。可以通过”ALTER TABLE…AUTO_INCREMENT=n;”语句强制设置自动增长值的起始值,默认为1,但是该强制到默认值是保存在内存中,数据库重启后该值将会丢失。可以使用LAST_INSERT_ID()查询当前线程最后插入记录使用的值。如果一次插入多条记录,那么返回的是第一条记录使用的自动增长值。
对于InnoDB表,自动增长列必须是索引。如果是组合索引,也必须是组合索引的第一列,但是对于MyISAM表,自动增长列可以是组合索引的其他列,这样插入记录后,自动增长列是按照组合索引到前面几列排序后递增的。
2)外键约束:
MySQL支持外键的存储引擎只有InnoDB,在创建外键的时候,父表必须有对应的索引,子表在创建外键的时候也会自动创建对应的索引。
在创建索引的时候,可以指定在删除、更新父表时,对子表进行的相应操作,包括restrict、cascade、set null和no action。其中restrict和no action相同,是指限制在子表有关联的情况下,父表不能更新;casecade表示父表在更新或删除时,更新或者删除子表对应的记录;set null 则表示父表在更新或者删除的时候,子表对应的字段被set null。
当某个表被其它表创建了外键参照,那么该表对应的索引或主键被禁止删除。
可以使用set foreign_key_checks=0;临时关闭外键约束,set foreign_key_checks=1;打开约束。
(三)MEMORY
memory使用存在内存中的内容来创建表。每个MEMORY表实际对应一个磁盘文件,格式是.frm。MEMORY类型的表访问非常快,因为它到数据是放在内存中的,并且默认使用HASH索引,但是一旦服务器关闭,表中的数据就会丢失,但表还会继续存在。
默认情况下,memory数据表使用散列索引,利用这种索引进行“相等比较”非常快,但是对“范围比较”的速度就慢多了。因此,散列索引值适合使用在”=”和”<=>”的操作符中,不适合使用在””操作符中,也同样不适合用在order by字句里。如果确实要使用””或betwen操作符,可以使用btree索引来加快速度。
存储在MEMORY数据表里的数据行使用的是长度不变的格式,因此加快处理速度,这意味着不能使用BLOB和TEXT这样的长度可变的数据类型。VARCHAR是一种长度可变的类型,但因为它在MySQL内部当作长度固定不变的CHAR类型,所以可以使用。
create table tab_memory engine=memory select id,name,age,addr from man order by id;
使用USING HASH/BTREE来指定特定到索引。
create index mem_hash using hash on tab_memory(city_id);
在启动MySQL服务的时候使用–init-file选项,把insert into…select或load data infile 这样的语句放入到这个文件中,就可以在服务启动时从持久稳固的数据源中装载表。
服务器需要足够的内存来维持所在的在同一时间使用的MEMORY表,当不再使用MEMORY表时,要释放MEMORY表所占用的内存,应该执行DELETE FROM或truncate table或者删除整个表。
每个MEMORY表中放置到数据量的大小,受到max_heap_table_size系统变量的约束,这个系统变量的初始值是16M,同时在创建MEMORY表时可以使用MAX_ROWS子句来指定表中的最大行数。
(四)MERGE
merge存储引擎是一组MyISAM表的组合,这些MyISAM表结构必须完全相同,MERGE表中并没有数据,对MERGE类型的表可以进行查询、更新、删除的操作,这些操作实际上是对内部的MyISAM表进行操作。对于对MERGE表进行的插入操作,是根据INSERT_METHOD子句定义的插入的表,可以有3个不同的值,first和last值使得插入操作被相应的作用在第一个或最后一个表上,不定义这个子句或者为NO,表示不能对这个MERGE表进行插入操作。可以对MERGE表进行drop操作,这个操作只是删除MERGE表的定义,对内部的表没有任何影响。MERGE在磁盘上保留2个以MERGE表名开头文件:.frm文件存储表的定义;.MRG文件包含组合表的信息,包括MERGE表由哪些表组成,插入数据时的依据。可以通过修改.MRG文件来修改MERGE表,但是修改后要通过flush table刷新。
create table man_all(id int,name varchar(20))engine=merge union=(man1,man2) insert_methos=last;
23.Mysql索引原理
对需要查询的列进行排序,再二分查找,没有索引时候是无序的,顺序查找
24.classLoader加载顺序
1、首先bootstrap classloader(核心类加载器)加载,其次extension classloader(扩展类加载器),最后system classloader(系统类加载器)
每个ClassLoader加载Class的过程是:
1、检测此Class是否载入过(即在cache中是否有此Class),如果有到8,如果没有到2
2、如果parent classloader不存在(没有parent,那parent一定是bootstrap classloader了),到4
3、请求parent classloader载入,如果成功到8,不成功到5
4、请求jvm从bootstrap classloader中载入,如果成功到8
5、寻找Class文件(从与此classloader相关的类路径中寻找)。如果找不到则到7.
6、从文件中载入Class,到8.
7、抛出ClassNotFoundException.
8、返回Class.
25.hashmap的数据结构
HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。
1、当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。
2、当程序试图将一个key-value对放入HashMap中时,程序首先根据该 key 的 hashCode() 返回值决定该 Entry 的存储位置:如果两个 Entry 的 key 的 hashCode() 返回值相同,那它们的存储位置相同。如果这两个 Entry 的 key 通过 equals 比较返回 true,新添加 Entry 的 value 将覆盖集合中原有 Entry 的 value,但key不会覆盖。如果这两个 Entry 的 key 通过 equals 比较返回 false,新添加的 Entry 将与集合中原有 Entry 形成 Entry 链,而且新添加的 Entry 位于 Entry 链的头部——具体说明继续看 addEntry() 方法的说明。
总结:
归纳起来简单地说,HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。
当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的。所以为了提高查询的效率,就要对HashMap的数组进行扩容,数组扩容这个操作也会出现在ArrayList中,这是一个常用的操作,而在HashMap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize。
那么HashMap什么时候进行扩容呢?当HashMap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,这是一个折中的取值。也就是说,默认情况下,数组大小为16,那么当HashMap中元素个数超过16*0.75=12的时候,就把数组的大小扩展为 2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。
26. 线程安全map之ConcurrentHashMap
原理:一个ConcurrentHashMap 由多个segment 组成,每个segment 包含一个Entity 的数组。这里比HashMap 多了一个segment 类。该类继承了ReentrantLock 类,所以本身是一个锁。当多线程对ConcurrentHashMap 操作时,不是完全锁住map, 而是锁住相应的segment 。这样提高了并发效率。
27. 怎么在循环集合时,安全的删除元素
使用集合迭代器循环,再删除元素
28. spring框架的核心是什么?怎么理解的
IOC和AOP,理解请自行总结(个人理解见下)
1、spring是一个很广泛的东西,包含Spring Framework, Spring Data, Spring Security,Spring Boot等,我们工作中讲的主要是Framework,而它的核心则是IOC和AOP。
2、spring通过IOC将创建对象和管理对象交由spring处理,spring会管理类之间的依赖关系,同时将对象创建过程从编译时延期到运行时,即通过配置进行加载,而我们程序员可以更专注于业务开发;另外我们在使用其他框架时,spring可以通过配置,将其他框架的对象等资源交由spring来管理生命周期,即整合各种框架达到解耦;另外spring的AOP面向切面编程将我们之前要操心的事务管理、日志管理统统交给spring自己来处理,这样大大了提高了我们的开发效率,也是面向对象编程思想的另一个体现,综合这些所以spring现在是非常流行的。
PS:上帝创造了男人和女人,并将男人和女人整合起来和管理他们的生老病死,男人和女人只关注他们的业务-生孩子即可。
29.java面试题SpringMVC是单例的,高并发情况下,如何保证性能的?
首先在大家的思考中,肯定有影响的,你想想,单例顾名思义:一个个排队过… 高访问量的时候,你能想象服务器的压力了… 而且用户体验也不怎么好,等待太久~
实质上这种理解是错误的,Java里有个API叫做ThreadLocal,spring单例模式下用它来切换不同线程之间的参数。用ThreadLocal是为了保证线程安全,实际上ThreadLoacal的key就是当前线程的Thread实例。单例模式下,spring把每个线程可能存在线程安全问题的参数值放进了ThreadLocal。这样虽然是一个实例在操作,但是不同线程下的数据互相之间都是隔离的,因为运行时创建和销毁的bean大大减少了,所以大多数场景下这种方式对内存资源的消耗较少,而且并发越高优势越明显。
总的来说就是,单利模式因为大大节省了实例的创建和销毁,有利于提高性能,而ThreadLocal用来保证线程安全性。
另外补充说一句,单例模式是spring推荐的配置,它在高并发下能极大的节省资源,提高服务抗压能力。spring IOC的bean管理器是“绝对的线程安全”。
30. java反射实现方式
1、通过Class.forName()方法加载字符串,就可以得到该字符串做代表的Class对象。
例如:Class clazz = Class.forName(“java.lang.String”)就可以得到String类的Class对象。值得注意的是,字符串必须是类的全名,即包名+类名。
2、通过类名调用class属性得到该类的Class对象。
例如:Class clazz = String.class也可以得到String类的Class对象。
3、调用实例的getClass()方法。
例如:Date date = new Date(); Class clazz = date.getClass();
4、如果是基本类型的包装类,则可以通过调用包装类的Type属性来获得该包装类的Class对象。
例如:Class clazz = Integer.TYPE
48.zookeeper是干嘛的,简单说明下
zookeeper = 分布式系统协调服务,主要可以管理配置文件同步,集群投票机制,服务发现,分布式锁
49.docker是干嘛的,简单说明下
docker = 主要是存放应用的一个容器,它解决了应用需要的环境等问题,可以自动部署应用到容器,而不需要用户去配置一大堆应用需要的环境配置
50.对称加密和非对称加密的举例描述
虽然非对称加密很安全,但是和对称加密比起来,它非常的慢,所以我们还是要用对称加密来传送消息,但对称加密所使用的密钥我们可以通过非对称加密的方式发送出去。为了解释这个过程,请看下面的例子:
(1) Alice需要在银行的网站做一笔交易,她的浏览器首先生成了一个随机数作为对称密钥。
(2) Alice的浏览器向银行的网站请求公钥。
(3) 银行将公钥发送给Alice。
(4) Alice的浏览器使用银行的公钥将自己的对称密钥加密。
(5) Alice的浏览器将加密后的对称密钥发送给银行。
(6) 银行使用私钥解密得到Alice浏览器的对称密钥。
(7) Alice与银行可以使用对称密钥来对沟通的内容进行加密与解密了。