diff --git a/Java相关/Java基础知识面试题总结/超详细的Java面试题总结(一)之Java基础知识篇.md b/Java相关/Java基础知识面试题总结/超详细的Java面试题总结(一)之Java基础知识篇.md index 34a4d7e2..7966fda3 100644 --- a/Java相关/Java基础知识面试题总结/超详细的Java面试题总结(一)之Java基础知识篇.md +++ b/Java相关/Java基础知识面试题总结/超详细的Java面试题总结(一)之Java基础知识篇.md @@ -135,5 +135,3 @@ Java使用自动装箱和拆箱机制,节省了常用数值的内存开销和 术语来讲的区别:1.==是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同 2.==是指对内存地址进行比较 equals()是对字符串的内容进行比较3.==指引用是否相同 equals()指的是值是否相同 -**欢迎关注我的微信公众号(分享各种Java学习资源,面试题,以及企业级Java实战项目回复关键字免费领取):** -![微信公众号](https://user-gold-cdn.xitu.io/2018/3/19/1623c870135a3609?w=215&h=215&f=jpeg&s=29172) diff --git a/Java相关/Java基础知识面试题总结/超详细的Java面试题总结(三)之Java集合篇常见问题.md b/Java相关/Java基础知识面试题总结/超详细的Java面试题总结(三)之Java集合篇常见问题.md new file mode 100644 index 00000000..de357cdd --- /dev/null +++ b/Java相关/Java基础知识面试题总结/超详细的Java面试题总结(三)之Java集合篇常见问题.md @@ -0,0 +1,327 @@ + +## List,Set,Map三者的区别及总结 +- **List:对付顺序的好帮手** + + List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象 +- **Set:注重独一无二的性质** + + 不允许重复的集合。不会有多个元素引用相同的对象。 + +- **Map:用Key来搜索的专家** + + 使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。 + + +## Arraylist 与 LinkedList 区别 +Arraylist底层使用的是数组(存读数据效率高,插入删除特定位置效率低),LinkedList底层使用的是双向循环链表数据结构(插入,删除效率特别高)。学过数据结构这门课后我们就知道采用链表存储,插入,删除元素时间复杂度不受元素位置的影响,都是近似O(1)而数组为近似O(n),因此当数据特别多,而且经常需要插入删除元素时建议选用LinkedList.一般程序只用Arraylist就够用了,因为一般数据量都不会蛮大,Arraylist是使用最多的集合类。 + +## ArrayList 与 Vector 区别(为什么要用Arraylist取代Vector呢?) +Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector +,代码要在同步操作上耗费大量的时间。Arraylist不是同步的,所以在不需要同步时建议使用Arraylist。 + +## HashMap 和 Hashtable 的区别 +1. HashMap是非线程安全的,HashTable是线程安全的;HashTable内部的方法基本都经过synchronized修饰。 + +2. 因为线程安全的问题,HashMap要比HashTable效率高一点,HashTable基本被淘汰。 +3. HashMap允许有null值的存在,而在HashTable中put进的键值只要有一个null,直接抛出NullPointerException。 + +Hashtable和HashMap有几个主要的不同:线程安全以及速度。仅在你需要完全的线程安全的时候使用Hashtable,而如果你使用Java5或以上的话,请使用ConcurrentHashMap吧 + +## HashSet 和 HashMap 区别 +![HashSet 和 HashMap 区别](https://user-gold-cdn.xitu.io/2018/3/2/161e717d734f3b23?w=896&h=363&f=jpeg&s=205536) + +## HashMap 和 ConcurrentHashMap 的区别 +[HashMap与ConcurrentHashMap的区别](https://blog.csdn.net/xuefeng0707/article/details/40834595) + +1. ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁进行保护,相对于HashTable的synchronized锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。(JDK1.8之后ConcurrentHashMap启用了一种全新的方式实现,利用CAS算法。) +2. HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。 + +## HashSet如何检查重复 +当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让加入操作成功。(摘自我的Java启蒙书《Head fist java》第二版) + +**hashCode()与equals()的相关规定:** +1. 如果两个对象相等,则hashcode一定也是相同的 +2. 两个对象相等,对两个equals方法返回true +3. 两个对象有相同的hashcode值,它们也不一定是相等的 +4. 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖 +5. hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。 + +**==与equals的区别** + +1. ==是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同 +2. ==是指对内存地址进行比较 equals()是对字符串的内容进行比较3.==指引用是否相同 equals()指的是值是否相同 + +## comparable 和 comparator的区别? +- comparable接口实际上是出自java.lang包 它有一个 compareTo(Object obj)方法用来排序 +- comparator接口实际上是出自 java.util 包它有一个compare(Object obj1, Object obj2)方法用来排序 + +一般我们需要对一个集合使用自定义排序时,我们就要重写compareTo方法或compare方法,当我们需要对某一个集合实现两种排序方式,比如一个song对象中的歌名和歌手名分别采用一种排序方法的话,我们可以重写compareTo方法和使用自制的Comparator方法或者以两个Comparator来实现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的Collections.sort(). + +### Comparator定制排序 +```java +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +/** + * TODO Collections类方法测试之排序 + * @author 寇爽 + * @date 2017年11月20日 + * @version 1.8 + */ +public class CollectionsSort { + + public static void main(String[] args) { + + ArrayList arrayList = new ArrayList(); + arrayList.add(-1); + arrayList.add(3); + arrayList.add(3); + arrayList.add(-5); + arrayList.add(7); + arrayList.add(4); + arrayList.add(-9); + arrayList.add(-7); + System.out.println("原始数组:"); + System.out.println(arrayList); + // void reverse(List list):反转 + Collections.reverse(arrayList); + System.out.println("Collections.reverse(arrayList):"); + System.out.println(arrayList); +/* + * void rotate(List list, int distance),旋转。 + * 当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 + * list的前distance个元素整体移到后面。 + + Collections.rotate(arrayList, 4); + System.out.println("Collections.rotate(arrayList, 4):"); + System.out.println(arrayList);*/ + + // void sort(List list),按自然排序的升序排序 + Collections.sort(arrayList); + System.out.println("Collections.sort(arrayList):"); + System.out.println(arrayList); + + // void shuffle(List list),随机排序 + Collections.shuffle(arrayList); + System.out.println("Collections.shuffle(arrayList):"); + System.out.println(arrayList); + + // 定制排序的用法 + Collections.sort(arrayList, new Comparator() { + + @Override + public int compare(Integer o1, Integer o2) { + return o2.compareTo(o1); + } + }); + System.out.println("定制排序后:"); + System.out.println(arrayList); + } + +} + +``` +### 重写compareTo方法实现按年龄来排序 +```java +package map; + +import java.util.Set; +import java.util.TreeMap; + +public class TreeMap2 { + + public static void main(String[] args) { + // TODO Auto-generated method stub + TreeMap pdata = new TreeMap(); + pdata.put(new Person("张三", 30), "zhangsan"); + pdata.put(new Person("李四", 20), "lisi"); + pdata.put(new Person("王五", 10), "wangwu"); + pdata.put(new Person("小红", 5), "xiaohong"); + // 得到key的值的同时得到key所对应的值 + Set keys = pdata.keySet(); + for (Person key : keys) { + System.out.println(key.getAge() + "-" + key.getName()); + + } + } +} + +// person对象没有实现Comparable接口,所以必须实现,这样才不会出错,才可以使treemap中的数据按顺序排列 +// 前面一个例子的String类已经默认实现了Comparable接口,详细可以查看String类的API文档,另外其他 +// 像Integer类等都已经实现了Comparable接口,所以不需要另外实现了 + +class Person implements Comparable { + private String name; + private int age; + + public Person(String name, int age) { + super(); + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + /** + * TODO重写compareTo方法实现按年龄来排序 + */ + @Override + public int compareTo(Person o) { + // TODO Auto-generated method stub + if (this.age > o.getAge()) { + return 1; + } else if (this.age < o.getAge()) { + return -1; + } + return age; + } +} +``` + +## 如何对Object的list排序? +- 对objects数组进行排序,我们可以用Arrays.sort()方法 +- 对objects的集合进行排序,需要使用Collections.sort()方法 + + +## 如何实现数组与List的相互转换? +List转数组:toArray(arraylist.size()方法;数组转List:Arrays的asList(a)方法 +```java +List arrayList = new ArrayList(); + arrayList.add("s"); + arrayList.add("e"); + arrayList.add("n"); + /** + * ArrayList转数组 + */ + int size=arrayList.size(); + String[] a = arrayList.toArray(new String[size]); + //输出第二个元素 + System.out.println(a[1]);//结果:e + //输出整个数组 + System.out.println(Arrays.toString(a));//结果:[s, e, n] + /** + * 数组转list + */ + List list=Arrays.asList(a); + /** + * list转Arraylist + */ + List arrayList2 = new ArrayList(); + arrayList2.addAll(list); + System.out.println(list); +``` +## 如何求ArrayList集合的交集 并集 差集 去重复并集 +需要用到List接口中定义的几个方法: + +- addAll(Collection c) :按指定集合的Iterator返回的顺序将指定集合中的所有元素追加到此列表的末尾 +实例代码: +- retainAll(Collection c): 仅保留此列表中包含在指定集合中的元素。 +- removeAll(Collection c) :从此列表中删除指定集合中包含的所有元素。 +```java +package list; + +import java.util.ArrayList; +import java.util.List; + +/** + *TODO 两个集合之间求交集 并集 差集 去重复并集 + * @author 寇爽 + * @date 2017年11月21日 + * @version 1.8 + */ +public class MethodDemo { + + public static void main(String[] args) { + // TODO Auto-generated method stub + List list1 = new ArrayList(); + list1.add(1); + list1.add(2); + list1.add(3); + list1.add(4); + + List list2 = new ArrayList(); + list2.add(2); + list2.add(3); + list2.add(4); + list2.add(5); + // 并集 + // list1.addAll(list2); + // 交集 + //list1.retainAll(list2); + // 差集 + // list1.removeAll(list2); + // 无重复并集 + list2.removeAll(list1); + list1.addAll(list2); + for (Integer i : list1) { + System.out.println(i); + } + } + +} + +``` + +## HashMap 的工作原理及代码实现 + +[集合框架源码学习之HashMap(JDK1.8)](https://juejin.im/post/5ab0568b5188255580020e56) + +## ConcurrentHashMap 的工作原理及代码实现 + +[ConcurrentHashMap实现原理及源码分析](http://www.cnblogs.com/chengxiao/p/6842045.html) + + +## 集合框架底层数据结构总结 +### - Collection + +#### 1. List + - Arraylist:数组(查询快,增删慢 线程不安全,效率高 ) + - Vector:数组(查询快,增删慢 线程安全,效率低 ) + - LinkedList:链表(查询慢,增删快 线程不安全,效率高 ) + +#### 2. Set + - HashSet(无序,唯一):哈希表或者叫散列集(hash table) + - LinkedHashSet:链表和哈希表组成 。 由链表保证元素的排序 , 由哈希表证元素的唯一性 + - TreeSet(有序,唯一):红黑树(自平衡的排序二叉树。) + +### - Map + - HashMap:基于哈希表的Map接口实现(哈希表对键进行散列,Map结构即映射表存放键值对) + - LinkedHashMap:HashMap 的基础上加上了链表数据结构 + - HashTable:哈希表 + - TreeMap:红黑树(自平衡的排序二叉树) + + +## 集合的选用 +主要根据集合的特点来选用,比如我们需要根据键值获取到元素值时就选用Map接口下的集合,需要排序时选择TreeMap,不需要排序时就选择HashMap,需要保证线程安全就选用ConcurrentHashMap.当我们只需要存放元素值时,就选择实现Collection接口的集合,需要保证元素唯一时选择实现Set接口的集合比如TreeSet或HashSet,不需要就选择实现List接口的比如ArrayList或LinkedList,然后再根据实现这些接口的集合的特点来选用。 + +2018/3/11更新 +## 集合的常用方法 +今天下午无意看见一道某大厂的面试题,面试题的内容就是问你某一个集合常见的方法有哪些。虽然平时也经常见到这些集合,但是猛一下让我想某一个集合的常用的方法难免会有遗漏或者与其他集合搞混,所以建议大家还是照着API文档把常见的那几个集合的常用方法看一看。 + +会持续更新。。。 + +**参考书籍:** + +《Head first java 》第二版 推荐阅读真心不错 (适合基础较差的) + + 《Java核心技术卷1》推荐阅读真心不错 (适合基础较好的) + + 《算法》第四版 (适合想对数据结构的Java实现感兴趣的) + + + diff --git a/Java相关/Java基础知识面试题总结/超详细的Java面试题总结(二)之Java基础知识篇.md b/Java相关/Java基础知识面试题总结/超详细的Java面试题总结(二)之Java基础知识篇.md new file mode 100644 index 00000000..4efe9e67 --- /dev/null +++ b/Java相关/Java基础知识面试题总结/超详细的Java面试题总结(二)之Java基础知识篇.md @@ -0,0 +1,141 @@ + +# 多线程和Java虚拟机 +## 创建线程有几种不同的方式?你喜欢哪一种?为什么? +1. 继承Thread类 +2. 实现Runnable接口 +3. 应用程序可以使用Executor框架来创建线程池 +4. 实现Callable接口。 + +我更喜欢实现Runnable接口这种方法,当然这也是现在大多程序员会选用的方法。因为一个类只能继承一个父类而可以实现多个接口。同时,线程池也是非常高效的,很容易实现和使用。 + +## 简述线程,程序、进程的基本概念。以及他们之间关系是什么?(参考书籍:《Java程序设计基础》第五版) +**线程**与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。 + +**程序**是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,也就是说程序是静态的代码。 + +**进程**是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 +线程是进程划分成的更小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。从另一角度来说,进程属于操作系统的范畴,主要是同一段时间内,可以同时执行一个以上的程序,而线程则是在同一程序内几乎同时执行一个以上的程序段。 + +## 什么是多线程?为什么程序的多线程功能是必要的? + 多线程就是几乎同时执行多个线程(一个处理器在某一个时间点上永远都只能是一个线程!即使这个处理器是多核的,除非有多个处理器才能实现多个线程同时运行。)。几乎同时是因为实际上多线程程序中的多个线程实际上是一个线程执行一会然后其他的线程再执行,并不是很多书籍所谓的同时执行。这样可以带来以下的好处: +1. 使用线程可以把占据长时间的程序中的任务放到后台去处理 +2. 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度 +3. 程序的运行速度可能加快 +4. 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下可以释放一些珍贵的资源如内存占用等等。 +还有其他很多使用多线程的好处,这里就不一一说明了。 + +## 多线程与多任务的差异是什么?(参考书籍:《Java程序设计基础》第五版) +多任务与多线程是两个不同的概念, +多任务是针对操作系统而言的,表示操作系统可以同时运行多个应用程序。 +而多线程是针对一个进程而言的,表示在一个进程内部可以几乎同时执行多个线程 + +## 线程有哪些基本状态?这些状态是如何定义的? +1. **新建(new)**:新创建了一个线程对象。 +2. **可运行(runnable)**:线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获 取cpu的使用权。 +3. **运行(running)**:可运行状态(runnable)的线程获得了cpu时间片(timeslice),执行程序代码。 +4. **阻塞(block)**:阻塞状态是指线程因为某种原因放弃了cpu使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有 机会再次获得cpu timeslice转到运行(running)状态。阻塞的情况分三种: +(一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放 入等待队列(waitting queue)中。 +(二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁 被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。 +(三). 其他阻塞: 运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。 +5. **死亡(dead)**:线程run()、main()方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。 +![线程的基本状态](https://user-gold-cdn.xitu.io/2017/12/15/16059cc91ee8efb3?w=876&h=492&f=png&s=-1) +备注: +可以用早起坐地铁来比喻这个过程: +还没起床:sleeping +起床收拾好了,随时可以坐地铁出发:Runnable +等地铁来:Waiting +地铁来了,但要排队上地铁:I/O阻塞 +上了地铁,发现暂时没座位:synchronized阻塞 +地铁上找到座位:Running +到达目的地:Dead + +## 什么是线程的同步?程序中为什么要实现线程的同步?是如何实现同步的? +当一个线程对共享的数据进行操作时,应使之成为一个”原子操作“,即在没有完成相关操作之前,不允许其他线程打断它,否则,就会破坏数据的完整性,必然会得到错误的处理结果,这就是线程的同步。 +在多线程应用中,考虑不同线程之间的数据同步和防止死锁。当两个或多个线程之间同时等待对方释放资源的时候就会形成线程之间的死锁。为了防止死锁的发生,需要通过同步来实现线程安全。 + +## 在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步? +在 java 虚拟机中, 每个对象( Object 和 class )通过某种逻辑关联监视器,每个监视器和一个对象引用相关联, 为了实现监视器的互斥功能, 每个对象都关联着一把锁. +一旦方法或者代码块被 **synchronized** 修饰, 那么这个部分就放入了监视器的监视区域, **确保一次只能有一个线程执行该部分的代码**, 线程在获取锁之前不允许执行该部分的代码 +另外 java 还提供了显式监视器( Lock )和隐式监视器( synchronized )两种锁方案 + +## 什么是死锁(deadlock)? +[360百科](https://baike.so.com/doc/6950313-7172714.html) + +死锁 :是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去 。 +产生原因: +- 因为系统资源不足。 +- 进程运行推进顺序不合适。 +- 资源分配不当等。 +- 占用资源的程序崩溃等。 + +如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。 + +下面四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要下列条件之一不满足,就不会发生死锁。 +- 互斥条件:一个资源每次只能被一个进程使用。 +- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 +- 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。 +- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 +死锁的解除与预防: + +理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和 解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确 定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态的情况下占用资源。因此,对资源的分配要给予合理的规划。 + +## 如何确保N个线程可以访问N个资源同时又不导致死锁? +上面一题我们知道了发生死锁的四个必要条件。我们只要使其中一个不成立就行了。一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。这也就是破坏了第四个条件循环等待条件。 + +## Java中垃圾回收有什么目的?什么时候进行垃圾回收? +垃圾回收是在内存中存在没有引用的对象或超过作用域的对象时进行。 + +垃圾回收的目的是识别并且丢弃应用不再使用的对象来释放和重用资源。 + +## finalize()方法什么时候被调用?析构函数(finalization)的目的是什么? +1)垃圾回收器(garbage colector)决定回收某对象时,就会运行该对象的finalize()方法; +finalize是Object类的一个方法,该方法在Object类中的声明protected void finalize() throws Throwable { } +在垃圾回收器执行时会调用被回收对象的finalize()方法,可以覆盖此方法来实现对其资源的回收。注意:一旦垃圾回收器准备释放对象占用的内存,将首先调用该对象的finalize()方法,并且下一次垃圾回收动作发生时,才真正回收对象占用的内存空间 + +2)GC本来就是内存回收了,应用还需要在finalization做什么呢? 答案是大部分时候,什么都不用做(也就是不需要重载)。只有在某些很特殊的情况下,比如你调用了一些native的方法(一般是C写的),可以要在finaliztion里去调用C的释放函数。 + +## 如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存? +不会,在下一个垃圾回收周期中,这个对象将是可被回收的。 + +## Java中堆和栈的区别 +[Java中堆和栈的区别](http://www.cnblogs.com/perfy/p/3820594.html) + +**堆内存**用来存放由new创建的对象和数组。 在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。 +**栈**中一般存放的是局部变量(方法中的变量或某代码段里(比如for循环)) + +## Java堆的结构是什么样子的?什么是堆中的永久代(Perm Gen space)? +[Java堆的结构是什么样子的?什么是堆中的永久代](https://www.nowcoder.com/ta/review-java/review?page=39) + +# Java集合框架 +List,Set,Map三者的区别及总结 + +ArrayList 与 Vector 区别(为什么要用Arraylist取代Vector呢?) + +List,Set,Map三者的区别及总结 + +HashMap 和 Hashtable 的区别 + +HashSet 和 HashMap 区别 + +HashMap 和 ConcurrentHashMap 的区别 + +HashSet如何检查重复 + +comparable 和 comparator的区别? + +如何对Object的list排序? + +如何实现数组与List的相互转换? + +如何求ArrayList集合的交集 并集 差集 去重复并集? + +HashMap 的工作原理及代码实现 + +ConcurrentHashMap 的工作原理及代码实现 + +集合框架底层数据结构总结 + +集合的选用 + + + diff --git a/Java相关/Java基础知识面试题总结/超详细的Java面试题总结(四 )之JavaWeb基础知识总结.md b/Java相关/Java基础知识面试题总结/超详细的Java面试题总结(四 )之JavaWeb基础知识总结.md new file mode 100644 index 00000000..763238dd --- /dev/null +++ b/Java相关/Java基础知识面试题总结/超详细的Java面试题总结(四 )之JavaWeb基础知识总结.md @@ -0,0 +1,270 @@ + + +下面的都是自己之前在学习JavaWeb的时候总结的,对于巩固JavaWeb的基础知识应该有很大帮助。后面还会把框架的面试题总结一下。 +## Servlet总结: + +在Java Web程序中,**Servlet**主要负责接收用户请求**HttpServletRequest**,在**doGet()**,**doPost()**中做相应的处理,并将回应**HttpServletResponse**反馈给用户。Servlet可以设置初始化参数,供Servlet内部使用。一个Servlet类只会有一个实例,在它初始化时调用**init()方法**,销毁时调用**destroy()方法**。**Servlet需要在web.xml中配置**(MyEclipse中创建Servlet会自动配置),**一个Servlet可以设置多个URL访问**。**Servlet不是线程安全**,因此要谨慎使用类变量。 +## 阐述Servlet和CGI的区别? + +**CGI的不足之处:** + +1,需要为每个请求启动一个操作CGI程序的系统进程。如果请求频繁,这将会带来很大的开销。 + +2,需要为每个请求加载和运行一个CGI程序,这将带来很大的开销 + +3,需要重复编写处理网络协议的代码以及编码,这些工作都是非常耗时的。 + +**Servlet的优点:** + +1,只需要启动一个操作系统进程以及加载一个JVM,大大降低了系统的开销 + +2,如果多个请求需要做同样处理的时候,这时候只需要加载一个类,这也大大降低了开销 + +3,所有动态加载的类可以实现对网络协议以及请求解码的共享,大大降低了工作量。 + +4,Servlet能直接和Web服务器交互,而普通的CGI程序不能。Servlet还能在各个程序之间共享数据,使数据库连接池之类的功能很容易实现。 + +补充:Sun Microsystems公司在1996年发布Servlet技术就是为了和CGI进行竞争,Servlet是一个特殊的Java程序,一个基于Java的Web应用通常包含一个或多个Servlet类。Servlet不能够自行创建并执行,它是在Servlet容器中运行的,容器将用户的请求传递给Servlet程序,并将Servlet的响应回传给用户。通常一个Servlet会关联一个或多个JSP页面。以前CGI经常因为性能开销上的问题被诟病,然而Fast CGI早就已经解决了CGI效率上的问题,所以面试的时候大可不必信口开河的诟病CGI,事实上有很多你熟悉的网站都使用了CGI技术。 + +参考:《javaweb整合开发王者归来》P7 + +## Servlet接口中有哪些方法及Servlet生命周期探秘 +Servlet接口定义了5个方法,其中**前三个方法与Servlet生命周期相关**: +- **void init(ServletConfig config) throws ServletException** +- **void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException** +- **void destory()** +- java.lang.String getServletInfo() +- ServletConfig getServletConfig() + +**生命周期:** **Web容器加载Servlet并将其实例化后,Servlet生命周期开始**,容器运行其**init()方法**进行Servlet的初始化;请求到达时调用Servlet的**service()方法**,service()方法会根据需要调用与请求对应的**doGet或doPost**等方法;当服务器关闭或项目被卸载时服务器会将Servlet实例销毁,此时会调用Servlet的**destroy()方法**。**init方法和destory方法只会执行一次,service方法客户端每次请求Servlet都会执行**。Servlet中有时会用到一些需要初始化与销毁的资源,因此可以把初始化资源的代码放入init方法中,销毁资源的代码放入destroy方法中,这样就不需要每次处理客户端的请求都要初始化与销毁资源。 + +参考:《javaweb整合开发王者归来》P81 + +## get和post请求的区别? +①get请求用来从服务器上获得资源,而post是用来向服务器提交数据; + +②get将表单中数据按照name=value的形式,添加到action 所指向的URL 后面,并且两者使用"?"连接,而各个变量之间使用"&"连接;post是将表单中的数据放在HTTP协议的请求头或消息体中,传递到action所指向URL; + +③get传输的数据要受到URL长度限制(1024字节即256个字符);而post可以传输大量的数据,上传文件通常要使用post方式; + +④使用get时参数会显示在地址栏上,如果这些数据不是敏感数据,那么可以使用get;对于敏感数据还是应用使用post; + +⑤get使用MIME类型application/x-www-form-urlencoded的URL编码(也叫百分号编码)文本的格式传递参数,保证被传送的参数由遵循规范的文本组成,例如一个空格的编码是"%20"。 + +补充:GET方式提交表单的典型应用是搜索引擎。GET方式就是被设计为查询用的。 + + +## 什么情况下调用doGet()和doPost()? +Form标签里的method的属性为get时调用doGet(),为post时调用doPost()。 + +## 转发(Forward)和重定向(Redirect)的区别? +**转发是服务器行为,重定向是客户端行为。** + +**转发(Forword)** +通过RequestDispatcher对象的forward(HttpServletRequest request,HttpServletResponse response)方法实现的。RequestDispatcher可以通过HttpServletRequest 的getRequestDispatcher()方法获得。例如下面的代码就是跳转到login_success.jsp页面。 +```java + request.getRequestDispatcher("login_success.jsp").forward(request, response); +``` +**重定向(Redirect)** 是利用服务器返回的状态吗来实现的。客户端浏览器请求服务器的时候,服务器会返回一个状态码。服务器通过HttpServletRequestResponse的setStatus(int status)方法设置状态码。如果服务器返回301或者302,则浏览器会到新的网址重新请求该资源。 + +1. **从地址栏显示来说** + +forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址. +redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL. + +2. **从数据共享来说** + +forward:转发页面和转发到的页面可以共享request里面的数据. +redirect:不能共享数据. + +3. **从运用地方来说** + +forward:一般用于用户登陆的时候,根据角色转发到相应的模块. +redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等 + +4. 从效率来说 + +forward:高. +redirect:低. + +## 自动刷新(Refresh) +自动刷新不仅可以实现一段时间之后自动跳转到另一个页面,还可以实现一段时间之后自动刷新本页面。Servlet中通过HttpServletResponse对象设置Header属性实现自动刷新例如: +```java +Response.setHeader("Refresh","1000;URL=http://localhost:8080/servlet/example.htm"); +``` +其中1000为时间,单位为毫秒。URL指定就是要跳转的页面(如果设置自己的路径,就会实现没过一秒自动刷新本页面一次) + + +## Servlet与线程安全 +**Servlet不是线程安全的,多线程并发的读写会导致数据不同步的问题。** 解决的办法是尽量不要定义name属性,而是要把name变量分别定义在doGet()和doPost()方法内。虽然使用synchronized(name){}语句块可以解决问题,但是会造成线程的等待,不是很科学的办法。 +注意:多线程的并发的读写Servlet类属性会导致数据不同步。但是如果只是并发地读取属性而不写入,则不存在数据不同步的问题。因此Servlet里的只读属性最好定义为final类型的。 + +参考:《javaweb整合开发王者归来》P92 + + + +## JSP和Servlet是什么关系? +其实这个问题在上面已经阐述过了,Servlet是一个特殊的Java程序,它运行于服务器的JVM中,能够依靠服务器的支持向浏览器提供显示内容。JSP本质上是Servlet的一种简易形式,JSP会被服务器处理成一个类似于Servlet的Java程序,可以简化页面内容的生成。Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。有人说,Servlet就是在Java中写HTML,而JSP就是在HTML中写Java代码,当然这个说法是很片面且不够准确的。JSP侧重于视图,Servlet更侧重于控制逻辑,在MVC架构模式中,JSP适合充当视图(view)而Servlet适合充当控制器(controller)。 + +## JSP工作原理: +JSP是一种Servlet,但是与HttpServlet的工作方式不太一样。HttpServlet是先由源代码编译为class文件后部署到服务器下,为先编译后部署。而JSP则是先部署后编译。JSP会在客户端第一次请求JSP文件时被编译为HttpJspPage类(接口Servlet的一个子类)。该类会被服务器临时存放在服务器工作目录里面。下面通过实例给大家介绍。 +工程JspLoginDemo下有一个名为login.jsp的Jsp文件,把工程第一次部署到服务器上后访问这个Jsp文件,我们发现这个目录下多了下图这两个东东。 +.class文件便是JSP对应的Servlet。编译完毕后再运行class文件来响应客户端请求。以后客户端访问login.jsp的时候,Tomcat将不再重新编译JSP文件,而是直接调用class文件来响应客户端请求。 +![JSP工作原理](https://user-gold-cdn.xitu.io/2018/3/31/1627bee073079a28?w=675&h=292&f=jpeg&s=133553) +由于JSP只会在客户端第一次请求的时候被编译 ,因此第一次请求JSP时会感觉比较慢,之后就会感觉快很多。如果把服务器保存的class文件删除,服务器也会重新编译JSP。 + +开发Web程序时经常需要修改JSP。Tomcat能够自动检测到JSP程序的改动。如果检测到JSP源代码发生了改动。Tomcat会在下次客户端请求JSP时重新编译JSP,而不需要重启Tomcat。这种自动检测功能是默认开启的,检测改动会消耗少量的时间,在部署Web应用的时候可以在web.xml中将它关掉。 + + + +参考:《javaweb整合开发王者归来》P97 + +## JSP有哪些内置对象?作用分别是什么? +[JSP内置对象 - CSDN博客 ](http://blog.csdn.net/qq_34337272/article/details/64310849 ) + +JSP有9个内置对象: +- request:封装客户端的请求,其中包含来自GET或POST请求的参数; +- response:封装服务器对客户端的响应; +- pageContext:通过该对象可以获取其他对象; +- session:封装用户会话的对象; +- application:封装服务器运行环境的对象; +- out:输出服务器响应的输出流对象; +- config:Web应用的配置对象; +- page:JSP页面本身(相当于Java程序中的this); +- exception:封装页面抛出异常的对象。 + + +## Request对象的主要方法有哪些? +- setAttribute(String name,Object):设置名字为name的request 的参数值 +- getAttribute(String name):返回由name指定的属性值 +- getAttributeNames():返回request 对象所有属性的名字集合,结果是一个枚举的实例 +- getCookies():返回客户端的所有 Cookie 对象,结果是一个Cookie 数组 +- getCharacterEncoding() :返回请求中的字符编码方式 = getContentLength() :返回请求的 Body的长度 +- getHeader(String name) :获得HTTP协议定义的文件头信息 +- getHeaders(String name) :返回指定名字的request Header 的所有值,结果是一个枚举的实例 +- getHeaderNames() :返回所以request Header 的名字,结果是一个枚举的实例 +- getInputStream() :返回请求的输入流,用于获得请求中的数据 +- getMethod() :获得客户端向服务器端传送数据的方法 +- getParameter(String name) :获得客户端传送给服务器端的有 name指定的参数值 +- getParameterNames() :获得客户端传送给服务器端的所有参数的名字,结果是一个枚举的实例 +- getParameterValues(String name):获得有name指定的参数的所有值 +- getProtocol():获取客户端向服务器端传送数据所依据的协议名称 +- getQueryString() :获得查询字符串 +- getRequestURI() :获取发出请求字符串的客户端地址 +- getRemoteAddr():获取客户端的 IP 地址 +- getRemoteHost() :获取客户端的名字 +- getSession([Boolean create]) :返回和请求相关 Session +- getServerName() :获取服务器的名字 +- getServletPath():获取客户端所请求的脚本文件的路径 +- getServerPort():获取服务器的端口号 +- removeAttribute(String name):删除请求中的一个属性 + +## request.getAttribute()和 request.getParameter()有何区别? +**从获取方向来看:** + +getParameter()是获取 POST/GET 传递的参数值; + +getAttribute()是获取对象容器中的数据值; + +**从用途来看:** + +getParameter用于客户端重定向时,即点击了链接或提交按扭时传值用,即用于在用表单或url重定向传值时接收数据用。 + +getAttribute用于服务器端重定向时,即在 sevlet 中使用了 forward 函数,或 struts 中使用了 +mapping.findForward。 getAttribute 只能收到程序用 setAttribute 传过来的值。 + +另外,可以用 setAttribute,getAttribute 发送接收对象.而 getParameter 显然只能传字符串。 +setAttribute 是应用服务器把这个对象放在该页面所对应的一块内存中去,当你的页面服务器重定向到另一个页面时,应用服务器会把这块内存拷贝另一个页面所对应的内存中。这样getAttribute就能取得你所设下的值,当然这种方法可以传对象。session也一样,只是对象在内存中的生命周期不一样而已。getParameter只是应用服务器在分析你送上来的 request页面的文本时,取得你设在表单或 url 重定向时的值。 + +**总结:** + +getParameter 返回的是String,用于读取提交的表单中的值;(获取之后会根据实际需要转换为自己需要的相应类型,比如整型,日期类型啊等等) + +getAttribute 返回的是Object,需进行转换,可用setAttribute 设置成任意对象,使用很灵活,可随时用 + +## include指令include的行为的区别 +**include指令:** JSP可以通过include指令来包含其他文件。被包含的文件可以是JSP文件、HTML文件或文本文件。包含的文件就好像是该JSP文件的一部分,会被同时编译执行。 语法格式如下: +<%@ include file="文件相对 url 地址" %> + +i**nclude动作:** 动作元素用来包含静态和动态的文件。该动作把指定文件插入正在生成的页面。语法格式如下: + + +## JSP九大内置对象,七大动作,三大指令- +[JSP九大内置对象,七大动作,三大指令总结](http://blog.csdn.net/qq_34337272/article/details/64310849) + +## 讲解JSP中的四种作用域 +JSP中的四种作用域包括page、request、session和application,具体来说: +- **page**代表与一个页面相关的对象和属性。 +- **request**代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件;需要在页面显示的临时数据可以置于此作用域。 +- **session**代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中。 +- **application**代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。 + + + +## 如何实现JSP或Servlet的单线程模式? +对于JSP页面,可以通过page指令进行设置。 +<%@page isThreadSafe=”false”%> + +对于Servlet,可以让自定义的Servlet实现SingleThreadModel标识接口。 + +说明:如果将JSP或Servlet设置成单线程工作模式,会导致每个请求创建一个Servlet实例,这种实践将导致严重的性能问题(服务器的内存压力很大,还会导致频繁的垃圾回收),所以通常情况下并不会这么做。 + +## 实现会话跟踪的技术有哪些? +1. **使用Cookie** + +向客户端发送Cookie +```java +Cookie c =new Cookie("name","value"); //创建Cookie +c.setMaxAge(60*60*24); //设置最大时效,此处设置的最大时效为一天 +response.addCookie(c); //把Cookie放入到HTTP响应中 +``` +从客户端读取Cookie +```java +String name ="name"; +Cookie[]cookies =request.getCookies(); +if(cookies !=null){ + for(int i= 0;i +``` + +**优点:** Cookie被禁时可以使用 + +**缺点:** 所有页面必须是表单提交之后的结果。 + +4. HttpSession + + + 在所有会话跟踪技术中,HttpSession对象是最强大也是功能最多的。当一个用户第一次访问某个网站时会自动创建 HttpSession,每个用户可以访问他自己的HttpSession。可以通过HttpServletRequest对象的getSession方 法获得HttpSession,通过HttpSession的setAttribute方法可以将一个值放在HttpSession中,通过调用 HttpSession对象的getAttribute方法,同时传入属性名就可以获取保存在HttpSession中的对象。与上面三种方式不同的 是,HttpSession放在服务器的内存中,因此不要将过大的对象放在里面,即使目前的Servlet容器可以在内存将满时将HttpSession 中的对象移到其他存储设备中,但是这样势必影响性能。添加到HttpSession中的值可以是任意Java对象,这个对象最好实现了 Serializable接口,这样Servlet容器在必要的时候可以将其序列化到文件中,否则在序列化时就会出现异常。 +## Cookie和Session的的区别? + +1. 由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session.典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的Session,用用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几本书。这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件都有。集群的时候也要考虑Session的转移,在大型的网站,一般会有专门的Session服务器集群,用来保存用户会话,这个时候 Session 信息都是放在内存的,使用一些缓存服务比如Memcached之类的来放 Session。 +2. 思考一下服务端如何识别特定的客户?这个时候Cookie就登场了。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。实际上大多数的应用都是用 Cookie 来实现Session跟踪的,第一次创建Session的时候,服务端会在HTTP协议中告诉客户端,需要在 Cookie 里面记录一个Session ID,以后每次请求把这个会话ID发送到服务器,我就知道你是谁了。有人问,如果客户端的浏览器禁用了 Cookie 怎么办?一般这种情况下,会使用一种叫做URL重写的技术来进行会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。 +3. Cookie其实还可以用在一些方便用户的场景下,设想你某次登陆过一个网站,下次登录的时候不想再次输入账号了,怎么办?这个信息可以写到Cookie里面,访问网站的时候,网站页面的脚本可以读取这个信息,就自动帮你把用户名给填了,能够方便一下用户。这也是Cookie名称的由来,给用户的一点甜头。所以,总结一下:Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。 + + + +