Java面试题总结(二)
1.java的类加载器有哪些
类加载器工作图
AppClassloader
应用类加载器,又称系统类加载器。它负责在 JVM 启动时,加载来自在命令 java 中的-classpath 或者 java.class.path 系统属性或者 CLASSPATH 操作系统属性所指定的 JAR 类包和类路径。
ExtClassloader
扩展类加载器,主要负责加载 Java 的扩展类库,默认加载 JAVA_HOME/jre/lib/ext/ 目录下的所有 Jar 包或者由 java.ext.dirs 系统属性指定的 Jar 包。
BootstrapClassloader
引导类加载器,又称启动类加载器,是最顶层的类加载器,主要用来加载 Java 核心类,如 rt.jar、resources.jar、charsets.jar 等。
custom class loader
自定义类加载器,除了系统提供的类加载器,开发人员可以继承java.lang…ClassLoader 来满足一些特殊的需求.
Ps:AppClassloader是ExtClassloader的子类,而bootstrapClassloader是用c++来写的类加载器
2.java的类加载机制,说一下双亲委派
JVM在加载类时默认采用的是双亲委派机制。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
3.jvm的内存分布模型
- (1)jvm是一种用于计算设备的规范,它是一个虚构出来的机器,是通过在实际的计算机上仿真模拟各种功能实现的。
- (2)jvm包含一套字节码指令集,一组寄存器,一个栈,一个垃圾回收堆和一个存储方法域。
- (3)JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。
java语言到机器语言的转换图
Jvm内存结构图
4.堆和栈的区别,堆和栈是线程共享的吗?
java的内存分为两个类,一类是栈内存,一类是堆内存.栈内存是指程序进入一个方法的时候,会为这个方法单独分配一块私属存储空间,用于存储这个方法内部的局部变量,当这个方法结束的时候,分配给这个方法的栈会释放,这个栈的变量也将随之释放.
堆是与栈作用不用的内存,一般用于存放不放在当前方法栈中的数据,例如,使用new创建的对象都放在堆中,所以,他不会随方法的消失而消失,方法中的局部变量使用final修饰,放在堆中,而不是栈中.
栈中的变量指向堆内存中的变量,这就是 Java 中的指针!
通过上述测试,栈是私有的,而堆是共享
因为在多线程环境下,每个线程拥有一个栈和一个程序计数器。栈和程序计数器用来保存线程的执行历史和线程的执行状态,是线程私有的资源。其他的资源(比如堆、地址空间、全局变量)是由同一个进程内的多个线程共享。
5.如何创建线程
-
继承Thread类创建线程,重写run方法
-
实现Runnable接口创建线程,实例化thread类
-
使用Callable和Future创建线程
-
使用线程池例如用Executor框架
6.集成thread类和事项runable接口的区别
- 继承Thread: 线程代码存放Thread子类run方法中。
- 实现Runnable,线程代码存在接口的子类的run方法。
- 实现Runnable接口相对于继承Thread类来说,有如下的显著优势:
- 适合多个相同代码的线程去处理同一个资源的情况
- 可以避免由于java的单继承特性带来的局限
- 增强了程序的健壮性,代码能够被多个线程共享,代码与数据时独立的
7.线程之间如何进行数据共享
-
1.如果线程执行的代码相同,多个线程共享同一个runnable对象时,将共享数据放在runnable对象
-
2.如果多个线程执行的代码不同,将共享数据封装到一个对象中,将这个对象逐一传递给各个runnable对象
-
3.如果多个线程执行的代码不同,将共享数据作为外部类的final成员变量,将不同的runnable对象作为内部类主动取数据
-
4.将数据声明为static的方式()
8.Volatile关键字有啥子作用,被它修饰的变量有什么特点。
volatile关键字的作用是告诉编译器,凡是被该关键字申明的变量都是易变的、不稳定的,所以不要试图对该变量使用缓存等优化机制,而应当每次都从他的内存地址中去读取值,但volatile并不是每次更改完就要立刻将他写回内存,volatile只提供了内存的可见性,并不提供原子性(与锁机制的区别:锁机制即提供了内存的可见性又提供了原子性)。
使用volatile标记的变量在读取或写入时不需要使用锁,这将减少产生死锁的概率。
9.如何创建线程池
这里给大家列出了4种创建线程池的方式具体怎么创,大家自己去了解 因为把代码般上来篇幅可能太大了
- newSingleThreadExecutor
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
- newFixedThreadPool
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
- newCachedThreadPool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,
那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
- newScheduledThreadPool
创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
10.Hashmap、CcurrentHashMap HashTable 三者之间的区别。
主要区别Hashmap是线程不安全的,HashTable 是线程安全的,而currentHashMap 是jdk1.5用来弥补Hashmap的线程不安全
currentHashMap 是将hashmap分为很多个片然后使用分布式锁才对每一片进行加锁
大家都知道线程安全安全往往是需要牺牲性能的,所以它们的性能如下: HashMap>CurrentHashMap>HashTable;
11.推荐使用线程安全的集合(vector,hashtale除外)
StringBuffer ConcurrentHashMap Collections.synchronizedList CopyOnWriteArrayList
12.MySQL数据库的搜索引擎有哪些,并说出他们之间的优缺点
MYISAM:支持3中存储方式:静态型,动态型,压缩型
优点:占用的空间小,存储的速度快
缺点:不支持事务和并发
innoDB:
优点:提供事务的支持,回滚,支持崩溃修复能力和并发控制,多版本事务并发控制
缺点:读写效率较差,占用的数据库空间较大
Memory:内存中对数据创建表,数据全部存储在内存
缺点:生命周期短
优点:读写速度非常快,对数据的安全性要求比较低的时候可以选择memory
13.数据库的优化
-
1、选取最适用的字段属性
MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。 -
2、使用连接(JOIN)来代替子查询(Sub-Queries)
MySQL从4.1开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。 -
3、使用联合(UNION)来代替手动创建的临时表
MySQL从4.0的版本开始支持union查询,它可以把需要使用临时表的两条或更多的select查询合并的一个查询中。在客户端的查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用union来创建查询的时候,我们只需要用UNION作为关键字把多个select语句连接起来就可以了,要注意的是所有select语句中的字段数目要相同。 -
4、使用事务来避免数据污染
它的作用是:要么语句块中每条语句都操作成功,要么都失败。换句话说,就是可以保持数据库中数据的一致性和完整性。事物以BEGIN关键字开始,COMMIT关键字结束。在这之间的一条SQL操作失败,那么,ROLLBACK命令就可以把数据库恢复到BEGIN开始之前的状态。
事务的另一个重要作用是当多个用户同时使用相同的数据源时,它可以利用锁定数据库的方法来为用户提供一种安全的访问方式,这样可以保证用户的操作不被其它的用户所干扰。 -
5、锁定表
尽管事务是维护数据库完整性的一个非常好的方法,但却因为它的独占性,有时会影响数据库的性能,尤其是在很大的应用系统中。由于在事务执行的过程中,数据库将会被锁定,因此其它的用户请求只能暂时等待直到该事务结束。 -
6、使用外键
锁定表的方法可以维护数据的完整性,但是它却不能保证数据的关联性。这个时候我们就可以使用外键。 -
7、使用索引
索引是提高数据库性能的常用方法,它可以令数据库服务器以比没有索引快得多的速度检索特定的行,尤其是在查询语句当中包含有MAX(),MIN()和ORDERBY这些命令的时候,性能提高更为明显。 -
8、优化的查询语句
绝大多数情况下,使用索引可以提高查询的速度,但如果SQL语句使用不恰当的话,索引将无法发挥它应有的作用。
14.数据库去重的方法
数据库中的去重操作(删除数据库中重复记录的SQL语句)主要有三种方法
-
1、rowid方法
根据Oracle带的rowid属性,可以进行判断是否存在重复语句; -
2、用group by方法
主要用于分组统计,一般都是使用在聚合函数中使用; -
3、用distinct方法
一般用于比较小的表进行去重,会过滤掉多余的重复记录,返回不重复的记录或字段;
15.数据库in 和exist 的区别,in是全表搜索吗
in 是把外表和内表作HASH JOIN,而exists是对外表作LOOP,每次LOOP再对内表进行查询。
哈希连接(HASH JOIN)是一种两个表在做表连接时主要依靠哈希运算来得到连接结果集的表连接方法。
一直以来认为exists比in效率高的说法是不准确的。如果查询的两个表大小相当,那么用in和exists差别不大。
如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in。
16.谈谈你对索引的理解。索引的数据结构是什么
-
1、首先要明白无名无实莫要用索引:因为数据中的记录很多,为了方便我们查找,提高查询的效率;
-
2、索引的原理:对要查询的字段建立索引其实就是把该字段按照一定的方式排序;建立的索引只对该字段有用,如果查询的字段改变,那么这个索引也就无效了,比如图书馆的书是按照书名的第一个字母排序的,那么你想要找作者叫张三的就不能用改索引了;还有就是如果索引太多会降低查询的速度。
-
3、索引是优缺点: 首先明白为什么索引会增加速度,DB在执行一条Sql语句的时候,默认的方式是根据搜索条件进行全表扫描,遇到匹配条件的就加入搜索结果集合。如果我们对某一字段增加索引,查询时就会先去索引列表中一次定位到特定值的行数,大大减少遍历匹配的行数,所以能明显增加查询的速度。那么在任何时候都应该加索引么?这里有几个反例:1、如果每次都需要取到所有表记录,无论如何都必须进行全表扫描了,那么是否加索引也没有意义了。2、对非唯一的字段,例如“性别”这种大量重复值的字段,增加索引也没有什么意义。3、对于记录比较少的表,增加索引不会带来速度的优化反而浪费了存储空间,因为索引是需要存储空间的,而且有个致命缺点是对于update/insert/delete的每次执行,字段的索引都必须重新计算更新。所以并不是任何情况下都改建立索引的。
索引的实现通常使用B树及其变种B+树。
B树中每个节点包含了键值和键值对于的数据对象存放地址指针,所以成功搜索一个对象可以不用到达树的叶节点。成功搜索包括节点内搜索和沿某一路径的搜索,成功搜索时间取决于关键码所在的层次以及节点内关键码的数量。
B+树非叶节点中存放的关键码并不指示数据对象的地址指针,非也节点只是索引部分。所有的叶节点在同一层上,包含了全部关键码和相应数据对象的存放地址指针,且叶节点按关键码从小到大顺序链接。如果实际数据对象按加入的顺序存储而不是按关键码次数存储的话,叶节点的索引必须是稠密索引,若实际数据存储按关键码次序存放的话,叶节点索引时稀疏索引
17.高并发下,多个服务对同一条数据进行访问,你们是如何解决的。
最先想的是在交易业务代码中用lock对修改方法加锁,运行时注入单例,并且对方法加同步锁,保证了业务数据的正确性, 但是效率低下,用户在使用中不方便,背离了系统可以并发操作的原则。
然后考虑使用悲观锁,这次运行时注入原型,并发操作效率提高, 但是,在同步处理数据库表时又造成锁表,这样并发效率依旧很低。蔡磊老师说悲观锁在开发中很少用到,因为用悲观锁就等于自闭了,一次只能一个人来操作一条数据,比较憨憨。
最后考虑乐观锁, 只是一种数据版本校验机制,它不做数据库层次上的锁定,需要在要并发操作的主表中加入一个"VERSION"字段或者“LASTUPDATETIME”,如果研究过oracle的SCN应该有异曲同工之妙,它的原理是这样:运行时注入原型,这时数据表并不锁住,只是每次update或insert时将VERSION更新, 当下次update,insert时,会校验VERSION,如果不一致,此时提示信息已经修改过了,并且在事务控制下rollback,这样,保证了数据的正确性,并且也不影响并发操作的效率
总结:使用乐观锁来解决多用户对同一个数据的修改和访问。也就是波波老师说的为数据加一个version版本标识,在我们读取数据的时候把版本号也读取出来,在数据发生改变的时候给版本号加一个1,读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
18.Integer 为什么在-127到128之间不创建对象,这样做有什么意图
没看懂这题是啥意思,感觉题目都不对,做了一个小测试,结果如下
官方给-128到127的解释
19.谈谈你对hashmap的理解
HashMap 是计算机数据结构哈希表 ( hash table ) 的一种实现。
Hashtable 是一个 java 早期版本的哈希表实现
不支持 null 键和值
线程安全
同步开销较大, 现已很少被推荐使用
HashMap 是应用更加广发的哈希表实现
支持 null 键和值
线程不安全
20.Spring 中 调用@Controller @Service @Mapper等注解,会创建对少个对象
这题不懂是什么意思
21.Spring中的默认传播特性是什么
@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY)
必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER)
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS)
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
22.Spring在启动的时候,如果要先加载一些代码,这些代码的加载策略是啥
23.Hibernate中get 和 load的区别
get()返回的是查询出来的实体对象,而load()查询出来的是一个目标实体的代理对象。
get与list最本质的区别就是get会立即产生一条SQL语句,而load会实现懒加载。
数据库中没有数据时:get不会抛出异常,load会抛出异常
24.BeanFactory 和applicationContext的区别。
BeanFactory实际上是实例化,配置和管理众多bean的容器。 这些bean通常会彼此合作,因而它们之间会产生依赖。 BeanFactory使用的配置数据可以反映这些依赖关系中 (一些依赖可能不像配置数据一样可见,而是在运行期作为bean之间程序交互的函数)。
ApplicationContext,它以一种更加面向框架的方式增强了BeanFactory的功能。多数用户可以以一种完全的声明式方式来使用ApplicationContext,甚至不用去手工创建它,但是却去依赖像ContextLoader的支持类,在J2EE的Web应用的启动进程中用它启动ApplicationContext。
- 点赞
- 收藏
- 关注作者
评论(0)