1. 什么是多态?
多态:一种是编译时多态,另外一种是运行时多态,编译时多态是通过方法的重载来实现的,运行时多态是通过方法的重写来实现的。
2. Hashmap和hashset的区别
set是线性结构,set中的值不能重复,hashset是set的hash实现,hashset中值不能重复是用hashmap的key来实现的。
map是键值对映射,可以空键空值。HashMap是Map接口的hash实现,key的唯一性是通过key值hash值的唯一来确定,value值是则是链表结构。
他们的共同点都是hash算法实现的唯一性,他们都不能持有基本类型,只能持有对象
3. List中的哪个集合线程安全?安全原理是怎么实现的?
ArrayList 是线程不安全的, Vector 是线程安全的,这两个类底层都是由数组实现的,Vector 是用同步方法来实现线程安全的, 而和它相似的ArrayList不是线程安全的。
Vector有四个构造方法:
1 public Vector()//使用指定的初始容量和等于零的容量增量构造一个空向量。 2 3 public Vector(int initialCapacity)//构造一个空向量,使其内部数据数组的大小,其标准容量增量为零。 4 5 public Vector(Collection<? extends E> c)//构造一个包含指定 collection 中的元素的向量 6 7 public Vector(int initialCapacity,int capacityIncrement)//使用指定的初始容量和容量增量构造一个空的向量
ArrayList和Vector都是用数组实现的,主要有这么三个区别:
Vector是多线程安全的,线程安全就是说多线程访问同一代码,不会产生不确定的结果。而ArrayList不是,这个可以从源码中看出,Vector类中的方法很多有synchronized进行修饰,这样就导致了Vector在效率上无法与ArrayList相比;
两个都是采用的线性连续空间存储元素,但是当空间不足的时候,两个类的增加方式是不同。
Vector可以设置增长因子,而ArrayList不可以。
Vector是一种老的动态数组,是线程同步的,效率很低,一般不赞成使用。
适用场景分析:
Vector是线程同步的,所以它也是线程安全的,而ArrayList是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用ArrayList效率比较高。
如果集合中的元素的数目大于目前集合数组的长度时,在集合中使用数据量比较大的数据,用Vector有一定的优势。
4. ArrayList和linkedList的区别
ArrayList 是线程不安全的, Vector 是线程安全的,这两个类底层都是由数组实现的
LinkedList 是线程不安全的,底层是由链表实现的
Vector、ArrayList都是以类似数组的形式存储在内存中,LinkedList则以链表的形式进行存储。
Vector线程同步,ArrayList、LinkedList线程不同步。
LinkedList适合指定位置插入、删除操作,不适合查找;ArrayList、Vector适合查找,不适合指定位置的插入、删除操作。
ArrayList在元素填满容器时会自动扩充容器大小的50%,而Vector则是100%,因此ArrayList更节省。
Arraylist:
优点:ArrayList是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的)。
缺点:因为地址连续, ArrayList要移动数据,所以插入和删除操作效率比较低。
LinkedList:
优点:LinkedList基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址,对于新增和删除操作add和remove,LinedList比较占优势。LinkedList 适用于要头尾操作或插入指定位置的场景
缺点:因为LinkedList要移动指针,所以查询操作性能比较低。
5. 多线程的状态有哪些,多线程的上下文切换?
线程状态:
新建
new语句创建的线程对象处于新建状态,此时它和其他java对象一样,仅被分配了内存。
等待
当线程在new之后,并且在调用start方法前,线程处于等待状态。
就绪
当一个线程对象创建后,其他线程调用它的start()方法,该线程就进入就绪状态。处于这个状态的线程位于Java虚拟机的可运行池中,等待cpu的使用权。
运行状态
处于这个状态的线程占用CPU,执行程序代码。在并发运行环境中,如果计算机只有一个CPU,那么任何时刻只会有一个线程处于这个状态。
只有处于就绪状态的线程才有机会转到运行状态。
阻塞状态
阻塞状态是指线程因为某些原因放弃CPU,暂时停止运行。当线程处于阻塞状态时,Java虚拟机不会给线程分配CPU,直到线程重新进入就绪状态,它才会有机会获得运行状态。
阻塞状态分为三种:
等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
同步阻塞:运行的线程在获取对象同步锁时,若该同步锁被别的线程占用,则JVM会把线程放入锁池中。
其他阻塞:运行的线程执行Sleep()方法,或者发出I/O请求时,JVM会把线程设为阻塞状态。当Sleep()状态超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态
当线程执行完run()方法中的代码,或者遇到了未捕获的异常,就会退出run()方法,此时就进入死亡状态,该线程结束生命周期。
cpu通过时间片分配算法来循环执行任务,当前任务执行一个时间片后切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换。
多线程环境中,当一个线程的状态由Runnable转换为非Runnable(Blocked、Waiting、Timed_Waiting)时,相应线程的上下文信息(包括cpu的寄存器和程序计数器在某一时间点的内容等)需要被保存,以便相应线程稍后再次进入Runnable状态时能够在之前的执行进度的基础上继续前进。而一个线程从非Runnable状态进入Runnable状态可能涉及恢复之前保存的上下文信息。这个对线程的上下文进行保存和恢复的过程就被称为上下文切换。
6. 如何保证线程的安全?
线程安全:
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。
如何保证呢:
1、使用线程安全的类;
2、使用synchronized同步代码块,或者用Lock锁;
由于线程安全问题,使用synchronized同步代码块
原理:当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。
另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
3、多线程并发情况下,线程共享的变量改为方法局部级变量;
7. 什么是同步锁,什么是异步锁,两者的用法和区别是什么?
异步锁:同一进程内的,多个线程间有互斥关系。只有等一个线程运行结束才能运行运行另一个进程。
同步锁:多个线程运行一个方法,因为方法上加了同了同步,一次只有一个线程运行,其他线程进入竞争进制。同步是一种更为复杂的互斥,而互斥是一种特殊的同步。
8. 值传递和引用传递的区别是什么?
按值传递:值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。简单来说就是直接复制了一份数据过去,因为是直接复制,所以这种方式在传递时如果数据量非常大的话,运行效率自然就变低了,所以java在传递数据量很小的数据是值传递
按引用传递:引用传递其实就弥补了上面说的不足,如果每次传参数的时候都复制一份的话,如果这个参数占用的内存空间太大的话,运行效率会很底下,所以引用传递就是直接把内存地址传过去,也就是说引用传递时,操作的其实都是源数据,这样的话修改有时候会冲突,记得用逻辑弥补下就好了,具体的数据类型就比较多了,比如Object,二维数组,List,Map等除了基本类型的参数都是引用传递。
9. Hashmap和linkedhashmap的底层代码,怎么实现的,实现原理是什么?
LinkedHashMap可以保证HashMap集合有序。存入的顺序和取出的顺序一致。
TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。
HashMap不保证顺序,即为无序的,具有很快的访问速度。HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;HashMap不支持线程的同步。
LinkedHashMap原理:
LinkedHashMap是HashMap的子类,实现结构和HashMap类似,只是HashMap中的链表是单向链表,而LinkedHashMap是双向链表,只是在在HashMap的基础上加上了排序实现。
HashMap原理:
JDK1.8之前:HashMap内部是由数组+单向链表实现的,如图HashMap Internal Structure before JDK1.8。
JDK1.8以后: HashMap内部是由数组+单向链表+红黑树实现的,如图HashMap Internal Structure after JDK1.8。
数组和链表存放的都是HashMap.Node对象,Node是一个单链结构,具有hash, key, value, next四个field。
具有相同hash值的Node放在同一个链表中,该链表的头放在在table中。
10.项目用到的前端技术有哪些?Ajax原理是什么,怎么触发实现?
Ajax 的全称是Asynchronous JavaScript and XML(异步的JavaScript 和 XML),其中,Asynchronous 是 异步 的意思,它有别于传统web开发中采用的同步的方式。
Ajax相当于在用户和服务器之间加了—个中间层,使用户操作与服务器响应异步化。并不是所有的用户请求都提交给服务器,像—些数据验证和数据处理等都交给Ajax引擎自己来做, 只有确定需要从服务器读取新数据时再由Ajax引擎代为向服务器提交请求。
Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发送异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。这其中最关键的一步就是从服务器获得请求数据。要清楚这个过程和原理,我们必须对 XMLHttpRequest有所了解。
XMLHttpRequest是ajax的核心机制,它是在IE5中首先引入的,是一种支持异步请求的技术。简单的说,也就是javascript可以及时向服务器提出请求和处理响应,而不阻塞用户。达到无刷新的效果。
11.Spring中的两大对象是什么?
IOC和AOP,也就是依赖注入和面向切面编程
12.AOP是什么,怎么使用?
AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。
(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
(5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
13. 笔试题:单利模式(普通单例,懒汉模式,饿汉模式)
1 public class SingleObject { 2 3 4 5 //创建 SingleObject 的一个对象 6 7 private static SingleObject instance = new SingleObject(); 8 9 10 11 //让构造函数为 private,这样该类就不会被实例化 12 13 private SingleObject(){} 14 15 16 17 //获取唯一可用的对象 18 19 public static SingleObject getInstance(){ 20 21 return instance; 22 23 } 24 25 26 27 }
懒汉式,线程不安全
1 public class Singleton { 2 3 private static Singleton instance; 4 5 private Singleton (){} 6 7 8 9 public static Singleton getInstance() { 10 11 if (instance == null) { 12 13 instance = new Singleton(); 14 15 } 16 17 return instance; 18 19 } 20 21 }
懒汉式,线程安全
public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
饿汉式
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }
14. Map集合如何遍历效率最高?
遍历 map 集合时,应该同时遍历出 键值对,这样效率更高。
1 public static void main(String[] args) 2 3 { 4 5 HashMap<String, String> hm = new HashMap<String, String>(); 6 7 hm.put("111", "222"); 8 9 10 11 Set<Map.Entry<String, String>> entrySet = hm.entrySet(); 12 13 Iterator<Map.Entry<String, String>> iter = entrySet.iterator(); 14 15 while (iter.hasNext()) 16 17 { 18 19 Map.Entry<String, String> entry = iter.next(); 20 21 System.out.println(entry.getKey() + "\t" + entry.getValue()); 22 23 } 24 25 }
15.Foreach怎么遍历map集合?
1 public class TestMap { 2 3 4 5 public static void main(String[] args) { 6 7 Map<String, Object> map = new HashMap<String, Object>(); 8 9 map.put("aaa", 111); 10 11 map.put("bbb", 222); 12 13 map.put("ccc", 333); 14 15 map.put("ddd", 444); 16 17 //Map集合循环遍历方式一 18 19 System.out.println("第一种:通过Map.keySet()遍历key和value:"); 20 21 for(String key:map.keySet()){//keySet获取map集合key的集合 然后在遍历key即可 22 23 String value = map.get(key).toString();// 24 25 System.out.println("key:"+key+" vlaue:"+value); 26 27 } 28 29 30 31 //Map集合循环遍历二 通过迭代器的方式 32 33 System.out.println("第二种:通过Map.entrySet使用iterator遍历key和value:"); 34 35 Iterator<Entry<String, Object>> it = map.entrySet().iterator(); 36 37 while(it.hasNext()){ 38 39 Entry<String, Object> entry = it.next(); 40 41 System.out.println("key:"+entry.getKey()+" key:"+entry.getValue()); 42 43 } 44 45 46 47 // Map集合循环遍历方式三 推荐,尤其是容量大时 48 49 System.out.println("第三种:通过Map.entrySet遍历key和value"); 50 51 for (Map.Entry<String, Object> m : map.entrySet()) { 52 53 System.out.println("key:" + m.getKey() + " value:" + m.getValue()); 54 55 } 56 57 58 59 // 第四种: 60 61 System.out.println("第四种:通过Map.values()遍历所有的value,但不能遍历key"); 62 63 for(Object m:map.values()){ 64 65 System.out.println(m); 66 67 } 68 69 } 70 71 }
16.For循环的方式有哪些,谁的效率最高?运用增强for循环时,循环体中的元素是否可以删除,如果能,怎么删除?
For循环效率低于foreach循环效率
增强for循环是无法在循环的时候删除元素的。
17.转发和重定向的区别?
Forward和Redirect代表了两种请求转发方式:直接转发和间接转发。
直接转发方式(Forward),客户端和浏览器只发出一次请求,Servlet、HTML、JSP或其它信息资源,由第二个信息资源响应该请求,在请求对象request中,保存的对象对于每个信息资源是共享的。
间接转发方式(Redirect)实际是两次HTTP请求,服务器端在响应第一次请求的时候,让浏览器再向另外一个URL发出请求,从而达到转发的目的。