Java面试准备(一)

工作之后的学习都是在业务的驱动下进行的,对于以前的基础知识生疏了许多,也不知从何开始,借助别人面试分享的经验,接下去准备回顾一波。

Java基础篇

 

MAP集合类问题:

1.HashMap的源码,实现原理,JDK1.8中对HashMap做了怎么样的优化。

HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。   在jdk1.8里  加入了红黑树的实现,当链表的长度大于8时,转换为红黑树的结构。

2.HashMap是怎么样进行扩容的,为什么都是2的N次幂的大小。

首先,length为2的整数次幂的话,h&(length-1)就相当于对length取模,这样便保证了散列的均匀,同时也提升了效率;其次,length为2的整数次幂的话,为偶数,这样length-1为奇数,奇数的最后一位是1,这样便保证了h&(length-1)的最后一位可能为0,也可能为1(这取决于h的值),即与后的结果可能为偶数,也可能为奇数,这样便可以保证散列的均匀性,而如果length为奇数的话,很明显length-1为偶数,它的最后一位是0,这样h&(length-1)的最后一位肯定为0,即只能为偶数,这样任何hash值都只会被散列到数组的偶数下标位置上,这便浪费了近一半的空间,因此,length取2的整数次幂,是为了使不同hash值发生碰撞的概率较小,这样就能使元素在哈希表中均匀地散列。

3.HashMap,HashTable,ConcurrentHashMap区别。

首先讲讲HashMap和HashTable的区别,最重要的两个区别特性是:a.HashMap是非线程安全的,HashTable是线程安全的。b.HashMap的键和值都允许有null存在,而HashTable则都不行。c.一般线程安全没有太大的要求和考虑哈希码效率问题时,所以一般HashMap。在同等考虑线程安全的情况下,concurrentHashMap就是HashMap的一个比较好的替代了。HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。

4.极高并发下HashTable 和ConcurrentHashMap哪个性能好,为什么如何实现的。

区别:

HashTable使用一把锁处理并发问题,当有多个线程访问时,需要多个线程竞争一把锁,导致阻塞

ConcurrentHashMap则使用分段,相当于把一个HashMap分成多个,然后每个部分分配一把锁,这样就可以支持多线程访问

5.HashMap在高并发下如果没有处理线程安全会有怎么样的安全隐患,具体表现是什么。

a.HashMap在多线程put后可能导致get无限循环 。b.多线程put的时候可能导致元素丢失。

对于安全使用hash类的建议:使用Hashtable 类,Hashtable 是线程安全的;
使用并发包下的java.util.concurrent.ConcurrentHashMap,ConcurrentHashMap实现了更高级的线程安全;
或者使用synchronizedMap() 同步方法包装 HashMap object,得到线程安全的Map,并在此Map上进行操作。

java基础问题 

6.java中四种类型的修饰符的限制范围。

都可以用来修饰类,方法,字段。

                    类内     包内     子类     包外

public:       √           √           √           √

projected:  √           √           √

default:      √           √

private:      √

7.Object类中的方法

clone() ,hascode(),equals(),toString(),getClass(),wait(),wait(),wait(long,int),notify(),notifyAll(),finalize。

8.接口和抽象类的区别,注意jdk8的接口可以有实现。

从现实的角度去理解, 接口表示这个对象能做什么,抽象类表示这个对象是什么。比如男人和女人,是两种类别的东西,由男人女人可以,延伸一些带品质,带职业的人,这时候通常用抽象类去描述。如果描述一个人有多种动作,吃、喝、玩、乐,则就是用接口去实现,实现了就代表有这个行为。

a. 接口是抽象类的变体,接口中所有的方法都是抽象的。而抽象类是声明方法的存在而不去实现它的类。
b. 接口可以多继承,抽象类不行
c.接口定义方法,不能实现,而抽象类可以实现部分方法。
d. 接口中基本数据类型为static 而抽类象不是的。

一个类可以继承一个抽象类,但是可以实现多个接口。抽象类的功能比接口强大,但是代价比较高。

9.动态代理的两种方式,以及区别。

jdk动态代理和cglib动态代理。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。。

10.Java序列化的方式。

 Java常见的序列化的方式有Java原生序列化,json序列化,fastjson序列化,protobuff序列化。

Java原生序列化方法即通过Java原生流(InputStream和OutputStream之间的转化)的方式进行转化。需要注意的是JavaBean实体类必须实现Serializable接口,否则无法序列化。

Json序列化一般会使用jackson包,通过ObjectMapper类来进行一些操作,比如将对象转化为byte数组或者将json串转化为对象。现在的大多数公司都将json作为服务器端返回的数据格式。比如调用一个服务器接口,通常的请求为xxx.json?a=xxx&b=xxx的形式。

fastjson 是由阿里巴巴开发的一个性能很好的Java 语言实现的 Json解析器和生成器。特点:速度快,测试表明fastjson具有极快的性能,超越任其他的java json parser。功能强大,完全支持java bean、集合、Map、日期、Enum,支持范型和自省。无依赖,能够直接运行在Java SE 5.0以上版本

ProtoBuff序列化对象可以很大程度上将其压缩,可以大大减少数据传输大小,提高系统性能。对于大量数据的缓存,也可以提高缓存中数据存储量。原始的ProtoBuff需要自己写.proto文件,通过编译器将其转换为java文件,显得比较繁琐。百度研发的jprotobuf框架将Google原始的protobuf进行了封装,对其进行简化,仅提供序列化和反序列化方法。其实用上也比较简洁,通过对JavaBean中的字段进行注解就行,不需要撰写.proto文件和实用编译器将其生成.java文件,百度的jprotobuf都替我们做了这些事情了。

11.传值和传引用的区别,java是怎么样的,有没有传值的引用。

    一、“传值”传递的是一个值,而“传引用”传递的是指向一个另一块内存空间的地址;

    二、“传值”实际是将一个值的拷贝传递至方法内部,这个值的原始数据是不会改变的,无论你内部进行的是何种操作,都不会改变这个源数据的值;而“传引用”传递进去的则是指向一个对象的地址,那么在方法内部进行实际操作的时候,就很可能会改变该对象的属性值(当然具体是否改变,还需要结合具体的业务)。

12.一个ArrayList在循环过程中删除,会不会有问题,为什么。

 ArrayList在循环的过程中删除及易发生错误,主要原因1:如果删除的原素有相同的,则删除第一个以后,第二个不再做删除,看ArrayList的jdk源码知道:删除第一个以后,在遍历第一个字符串时因为符合删除条件,所以将该元素从数组中删除,并且将后一个元素移动至当前位置,导致下一次循环遍历时后一个字符串并没有遍历到,所以无法删除。针对这种情况可以倒序删除的方式来避免。原因2:用foreach遍历循环,写法是对实际的Iterable、hasNext、next方法的简写,问题同样处在上文的fastRemove方法中,这里会做迭代器内部修改次数检查,因为上面的remove(Object)方法修改了modCount的值,所以才会报出并发修改异常。要避免这种情况的出现则在使用迭代器迭代时(显示或for-each的隐式)不要使用ArrayList的remove,改为用Iterator的remove即可。

13@transactional注解在什么情况下回失效,为什么?

下面是@Transactional注解事务的4点特性:

1、service类标签(一般不建议在接口上)上添加@Transactional,可以将整个类纳入spring事务管理,在每个业务方法执行时都会开启一个事务,不过这些事务采用相同的管理方式。

2、@Transactional 注解只能应用到 public 可见度的方法上。 如果应用在protected、private或者 package可见度的方法上,也不会报错,不过事务设置不会起作用。

3、默认情况下,Spring会对unchecked异常进行事务回滚;如果是checked异常则不回滚。 

4、只读事务: 

@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) 
只读标志只在事务启动时应用,否则即使配置也会被忽略。 
启动事务会增加线程开销,数据库因共享读取而锁定(具体跟数据库类型和事务隔离级别有关)。通常情况下,仅是读取数据时,不必设置只读事务而增加额外的系统开销。

事物的七种传播模式:REQUIRED(默认模式)、NOT_SUPPORTED、REQUIRESNEW、MANDATORY、SUPPORTS、NEVER、NESTED。

通过了解特性后再去根据多种原因判断@transactional注解失效的理由:

1、检查你方法是不是public的

2、你的异常类型是不是unchecked异常 
如果我想check异常也想回滚怎么办,注解上面写明异常类型即可

@Transactional(rollbackFor=Exception.class) 

类似的还有norollbackFor,自定义不回滚的异常

3、数据库引擎要支持事务,如果是MySQL,注意表要使用支持事务的引擎,比如innodb,如果是myisam,事务是不起作用的

4、是否开启了对注解的解析

    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

5、spring是否扫描到你这个包,如下是扫描到org.test下面的包

<context:component-scan base-package="org.test" ></context:component-scan>

6、检查是不是同一个类中的方法调用(如a方法调用同一个类中的b方法) 
7、异常是不是被你catch住了

posted @ 2018-04-24 13:05  南往人6  阅读(1167)  评论(0编辑  收藏  举报