Java面试题

Java面试

Java、框架、JVM

Java

基础、容器、多线程、反射、对象拷贝、Java web模块、异常模块、网络模块、设计模块

一、基础

1.JDK 和 JRE 有什么区别?

JRE( Java Runtime Environment)是Java 运行时环境,包括Java虚拟机(JVM)、Java基础类库、Java 命令和其他基础设施。

JDK是Java 开发工具包,它拥有JRE所拥有的一切,还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:jconsole,jvisualvm等工具软件,还包含了java程序编写所需的文档和demo例子程序。它能够创建和编译程序,是提供给程序员使用的。

JDK包含的基本组件包括:

  • javac – 编译器,将源程序转成字节码
  • jar – 打包工具,将相关的类文件打包成一个文件
  • javadoc – 文档生成器,从源码注释中提取文档
  • jdb – debugger,查错工具
  • java – 运行编译后的java程序(.class后缀的)
  • appletviewer:小程序浏览器,一种执行HTML文件上的Java小程序的Java浏览器。
  • Javah:产生可以调用Java过程的C过程,或建立能被Java程序调用的C过程的头文件。
  • Javap:Java反汇编器,显示编译类文件中的可访问功能和数据,同时显示字节代码含义。
  • Jconsole: Java进行系统调试和监控的工具。

2.== 和 equals 的区别是什么?

  • equals():是超类Object中的方法。 2、==:是操作符。
  • equals():用来检测两个对象是否相等,即两个对象的内容是否相等。==:用于比较引用和比较基本数据类型时具有不同的功能。
  • equals():没有==运行速度快。==:运行速度比equals()快,因为==只是比较引用。

3、两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?

两个对象equals相等,则它们的hashcode必须相等,反之则不一定。
两个对象==相等,则其hashcode一定相等,反之不一定成立。

4.final 在 java 中有什么作用?

final作为Java中的关键字可以用于三个地方。用于修饰类、类属性和类方法。

  • (1)修饰类:表示该类不能被继承;
  • (2)修饰方法:表示方法不能被重写;
  • (3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。

5.java 中的 Math.round(-1.5) 等于多少?

Math.round()计算方式为:+0.5取整

6.String 属于基础的数据类型吗?

Java8种基础的数据类型:byte、short、char、int、long、float、double、boolean。

7.String str="i"与 String str=new String(“i”)一样吗?

需要注意的是:String str="i"; 因为String 是final类型的,所以“i”应该是在常量池。而new String("i");则是新建对象放到堆内存中。

String str = "i" 是把"i"的内存地址赋值给str

String str = new String("i") 这里创建了一个新的String对象,然后把内存地址赋值给str

8.java 中操作字符串都有哪些类?它们之间有什么区别?

String、StringBuffer、StringBuilder

  • String:final修饰,String类的方法都是返回new String。即对String对象的任何改变都不影响到原对象,对字符串的修改操作都会生成新的对象
  • StringBuffer:对字符串的操作的方法都加了synchronized,保证线程安全。
  • StringBuilder:它代表可变字符串对象。不保证线程安全,在方法体内需要进行字符串的修改操作,可以new StringBuilder对象,调用StringBuilder对象的append、replace、delete等方法修改字符串。

9.如何将字符串反转?

  • 1.利用 StringBuffer 或 StringBuilder 的 reverse 成员方法:
  • 2.利用 String 的 toCharArray 方法先将字符串转化为 char 类型数组,然后将各个字符进行重新拼接

10.String常用方法。

  • equals:字符串是否相同
  • equalsIgnoreCase:忽略大小写后字符串是否相同
  • compareTo:根据字符串中每个字符的Unicode编码进行比较
  • compareToIgnoreCase:根据字符串中每个字符的Unicode编码进行忽略大小写比较
  • indexOf:目标字符或字符串在源字符串中位置下标
  • lastIndexOf:目标字符或字符串在源字符串中最后一次出现的位置下标
  • valueOf:其他类型转字符串
  • charAt:获取指定下标位置的字符
  • codePointAt:指定下标的字符的Unicode编码
  • concat:追加字符串到当前字符串
  • isEmpty:字符串长度是否为0
  • contains:是否包含目标字符串
  • startsWith:是否以目标字符串开头
  • endsWith:是否以目标字符串结束
  • format:格式化字符串
  • getBytes:获取字符串的字节数组
  • getChars:获取字符串的指定长度字符数组
  • toCharArray:获取字符串的字符数组
  • join:以某字符串,连接某字符串数组
  • length:字符串字符数
  • matches:字符串是否匹配正则表达式
  • replace:字符串替换
  • replaceAll:带正则字符串替换
  • replaceFirst:替换第一个出现的目标字符串
  • split:以某正则表达式分割字符串
  • substring:截取字符串
  • toLowerCase:字符串转小写
  • toUpperCase:字符串转大写
  • trim:去字符串首尾空格

11.抽象类必须要有抽象方法吗?

抽象类可以没有抽象方法,但是如果你的一个类已经声明成了抽象类,即使这个类中没有抽象方法,它也不能再实例化,即不能直接构造一个该类的对象。如果一个类中有了一个抽象方法,那么这个类必须声明为抽象类,否则编译通不过。

12.普通类和抽象类区别。

  • 抽象类不能被实例化
  • 抽象类可以有抽象方法,抽象方法只需申明,无需实现
  • 含有抽象方法的类必须申明为抽象类
  • 抽象的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类
  • 抽象方法不能被声明为静态
  • 抽象方法不能用private修饰
  • 抽象方法不能用final修饰

13.抽象类能使用 final 修饰吗?

不行,抽象类是被用于继承的,final修饰代表不可修改、不可继承的。

14.接口和抽象类有什么区别?

  • 1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象
  • 2、抽象类要被子类继承,接口要被类实现
  • 3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
  • 4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量
  • 5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类
  • 6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
  • 7、抽象类里可以没有抽象方法
  • 8、如果一个类里有抽象方法,那么这个类只能是抽象类
  • 9、抽象方法要被实现,所以不能是静态的,也不能是私有的
  • 10、接口可继承接口,并可多继承接口,但类只能单根继承

15.java 中 IO 流分为几种?

Java中的流分为两种,一种是字节流,另一种是字符流。分别由四个抽象类来表示。

  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

16.BIO、NIO、AIO 有什么区别?

  • BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
  • NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
  • AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。

实现

  • BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
  • NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
  • AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

适用场景分析

  • BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
  • NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
  • AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

17.Files的常用方法都有哪些?

  • Files.exists() 检测文件路径是否存在
  • Files.createFile()创建文件
  • Files.createDirectory()创建文件夹
  • Files.delete() 删除文件或者目录
  • Files.copy() 复制文件
  • Files.move() 移动文件
  • Files.size()查看文件个数
  • Files.read() 读取文件
  • Files.write()写入文件

二、容器

1.Java容器有那些?

集合容器

  • Collection
  • Set
    • HashSet
      • LinkedHashSet
    • TreeSet
  • List
    • ArrayList
    • LinkedList
    • Vector
  • Queue
    • BlockingQueue
    • Deque
  • Map
  • HashMap
    • LinkedHashMap
  • HashTable
  • TreeMap

2.Collection 和 Collections 有什么区别?

Collection 是集合的接口,其继承类又List Set。Collections 是集合的工具类,定义了许多操作集合的静态方法。是帮助类。

3.List、Set、Map 之间的区别是什么?

  • List:有序集合,元素可重复
  • Set:不重复集合,LinkedHashSet按照插入排序,SortedSet可排序,HashSet无序
  • Map:键值对集合,存储键、值和之间的映射;Key无序,唯一;value 不要求有序,允许重复

4.HashMap 和 Hashtable 有什么区别?

  • 1.(同步性)HashTable的方法是同步的,HashMap不能同步。
  • 2.(继承的父类不同)HashTable是继承自Dictionary类,而HashMap是继承自AbstractMap类。不过它们都实现了同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口。
  • 3.(对null key和null value的支持不同).HashTable不允许null值(key和value都不可以),HashMap允许使用null值(key和value)都可以。这样的键只有一个,可以有一个或多个键所对应的值为null。
  • 4.(遍历方法不同)HashTable使用Enumeration遍历,HashMap使用Iterator进行遍历。
  • 5.(初始化和扩容方式不同)HashTable中hash数组初始化大小及扩容方式不同。
    Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。

5.如何决定使用 HashMap 还是 TreeMap?

TreeMap的Key值是要求实现java.lang.Comparable,所以迭代的时候TreeMap默认是按照Key值升序排序的;TreeMap的实现是基于红黑树结构。适用于按自然顺序或自定义顺序遍历键(key)。

HashMap的Key值实现散列hashCode(),分布是散列的、均匀的,不支持排序;数据结构主要是桶(数组),链表或红黑树。适用于在Map中插入、删除和定位元素。

如果你需要得到一个有序的结果时就应该使用TreeMap(因为HashMap中元素的排列顺序是不固定的)。除此之外,由于HashMap有更好的性能,所以大多不需要排序的时候我们会使用HashMap。

6.说一下 HashMap 的实现原理?

  • HashMap使用数组加链表实现。每个数组中储存着链表。
  • 当使用put方法储存key-value键值对时,会先调用key的hashCode方法,得到此key经特定哈希运算后的值,然后将此值通过其他运算(?)得到一个值,将这个值与(length-1)做或操作(&),相当于对数组长度做取余操作。最终得到一个值作为此key在数组中的索引值,然后将key-value键值对储存进去。通过这种方法将储存的不同key-value键值对“散列”到数组的不同位置。在储存的时候,如果索引位置尚无元素,那么直接储存。如果有元素,那么就调用此key的equals方法与原有的元素的Key进行比较。如果返回true,说明在这个equals定义的规则上,这两个Key相同,那么将原有的key保留,用新的value代替原来的value。如果返回false,那么就说明这两个key在equals定义的规则下是不同元素,那么就与此链表的下一个结点进行比较,知道最后一个结点都没有相同元素,再下一个是null的时候,就用头插法将此key-value添加到链表上。HashMap对重复元素的处理方法是:key不变,value覆盖。
  • 当使用get方法获取key对应的value时,会和储存key-value时用同样的方法,得到key在数组中的索引值,如果此索引值上没有元素,就返回null。如果此索引值上有元素,那么就拿此key的equals方法与此位置元素上的key进行比较,如果返回true。就返回此位置元素对应的value。如果返回false,就一直按链表往下比较,如果都是返回false,那么就返回null。

7.说一下 HashSet 的实现原理?

首先,我们需要知道它是Set的一个实现,所以保证了当中没有重复的元素。一方面Set中最重要的一个操作就是查找。而且通常我们会选择HashSet来实现,因为它专门对快速查找进行了优化。

  • 基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
  • 当我们试图把某个类的对象当成 HashMap的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的equals(Object obj)方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。通常来说,所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。
  • HashSet的其他操作都是基于HashMap的。

8.ArrayList 和 LinkedList 的区别是什么?

  • ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。
  • ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
  • 在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。

9.如何实现数组和 List 之间的转换?

List转数组:list.toArray(arraylist.size())

数组转List:new ArrayList(Arrays.asList(数组))

10.ArrayList 和 Vector 的区别是什么?

主要区别为线程是否安全,扩容规则

  • Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。
  • 如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度的50%.如过在集合中使用数据量比较大的数据,用vector有一定的优势。

11.Array 和 ArrayList 有何区别?

  • Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。
  • Array大小是固定的,ArrayList的大小是动态变化的。
  • ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。

12.在 Queue 中 poll()和 remove()有什么区别?

Queue 中 remove() 和 poll()都是用来从队列头部删除一个元素。在队列元素为空的情况下,remove() 方法会抛出NoSuchElementException异常,poll() 方法只会返回 null 。

13.哪些集合类是线程安全的?

Vector,HashTable,ConcurrentHashMap,Stack

  • Vector:就比Arraylist多了个同步化机制(线程安全)。
  • Hashtable:就比Hashmap多了个线程安全。
  • ConcurrentHashMap:是一种高效但是线程安全的集合。
  • Stack:栈,也是线程安全的,继承于Vector。

14.迭代器 Iterator 是什么?

专门处理集合中的元素的对象

15.Iterator 怎么使用?有什么特点?

使用

  • 使用next()获得序列中的下一个元素
  • 使用hasNext()检查序列中是否还有元素。
  • 使用remove()将迭代器新近返回的元素删除。

特点

  • Iterator遍历集合元素的过程中不允许线程对集合元素进行修改,否则会抛出ConcurrentModificationEception的异常。
  • Iterator遍历集合元素的过程中可以通过remove方法来移除集合中的元素,删除的是上一次Iterator.next()方法返回的对象。
  • Iterator必须依附于一个集合类对象而存在,Iterator本身不具有装载数据对象的功能。
  • next()方法,该方法通过游标指向的形式返回Iterator下一个元素。

16.Iterator 和 ListIterator 有什么区别?

  • 所属关系,ListIterator是一个Iterator的子类型。
  • 局限:只能应用于各种List类的访问。
  • 优势:Iterator只能向前移动,而ListIterator可以相对双向移动。使用nextIndex()、previousIndex()方法获取双向索引。还可以通过set()方法替换它访问过的最后一个元素。通过调用listIterator()方法产生一个指向List开始处的ListIterator。通过调用listIterator(index),即指向索引为参数处的ListIterator。ListIterator 有 add() 方法,可以向List中添加对象,而 Iterator 不能。

17.怎么确保一个集合不能被修改?

Collections.unmodifiable*()

三、多线程

1.并行和并发有什么区别?

并行是指两个或者多个事件在同一时刻发生;并发是指两个或多个事件在同一时间间隔发生。

2.线程和进程的区别?

进程:进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位。

线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位。

3.守护线程是什么?

1、守护线程,专门用于服务其他的线程,如果其他的线程(即用户自定义线程)都执行完毕,连main线程也执行完毕,那么jvm就会退出(即停止运行)——此时,连jvm都停止运行了,守护线程当然也就停止执行了。

2、再换一种说法,如果有用户自定义线程存在的话,jvm就不会退出——此时,守护线程也不能退出,也就是它还要运行,干嘛呢,就是为了执行垃圾回收的任务啊。

4.创建线程有哪几种方式?

  • new Thread(),或者继承Thread类并重写run方法
  • 实现Runnable接口并重写run方法
  • 实现Callable接口并重写call方法,然后使用FutureTask类来包装Callable对象,使用FutureTask对象作为Thread对象的target创建并启动新线程。

5.说一下 runnable 和 callable 有什么区别

Java多线程有两个重要的接口,Runnable和Callable,分别提供一个run方法和call方法,二者是有较大差异的。

  • Runnable提供run方法,无法通过throws抛出异常,所有CheckedException必须在run方法内部处理。Callable提供call方法,直接抛出Exception异常。
  • Runnable的run方法无返回值,Callable的call方法提供返回值用来表示任务运行的结果
  • Runnable可以作为Thread构造器的参数,通过开启新的线程来执行,也可以通过线程池来执行。而Callable只能通过线程池执行。

6.线程有哪些状态?

线程状态有 5 种,新建,就绪,运行,阻塞,死亡。

  • 线程 start 方法执行后,并不表示该线程运行了,而是进入就绪状态,意思是随时准备运行,但是真正何时运行,是由操作系统决定的,代码并不能控制,
  • 同样的,从运行状态的线程,也可能由于失去了 CPU 资源,回到就绪状态,也是由操作系统决定的。这一步中,也可以由程序主动失去 CPU 资源,只需调用 yield 方法。
  • 线程运行完毕,或者运行了一半异常了,或者主动调用线程的 stop 方法,那么就进入死亡。死亡的线程不可逆转。
  • 下面几个行为,会引起线程阻塞。
    • 主动调用 sleep 方法。时间到了会进入就绪状态
    • 主动调用 suspend 方法。主动调用 resume 方法,会进入就绪状态
    • 调用了阻塞式 IO 方法。调用完成后,会进入就绪状态。
    • 试图获取锁。成功的获取锁之后,会进入就绪状态。
    • 线程在等待某个通知。其它线程发出通知后,会进入就绪状态

7.sleep() 和 wait() 的区别

结合synchronized,会更好的理解sleep()和wait()这两个方法。sleep() 和 wait() 的区别就是 调用sleep方法的线程不会释放对象锁,而调用wait() 方法会释放对象锁。

  • sleep() 方法是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间。
    因为sleep() 是static静态的方法,他不能改变对象的机锁,当一个synchronized块中调用了sleep() 方法,线程虽然进入休眠,但是对象的机锁没有被释放,其他线程依然无法访问这个对象。
  • wait()是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池,同时释放对象的机锁,使得其他线程能够访问,可以通过notify,notifyAll方法来唤醒等待的线程

8.notify()和 notifyAll()有什么区别?

  • 等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池,等待池中的线程不会去竞争该对象的锁。
  • 锁池:只有获取了对象的锁,线程才能执行对象的 synchronized 代码,对象的锁每次只有一个线程可以获得,其他线程只能在锁池中等待

notify() 方法随机唤醒对象的等待池中的一个线程,进入锁池;notifyAll() 唤醒对象的等待池中的所有线程,进入锁池。

9.线程的 run()和 start()有什么区别?

一个线程对线的 start() 方法只能调用一次,多次调用会抛出 java.lang.IllegalThreadStateException 异常;run() 方法没有限制。

调用start()方法是用来启动线程的,该线程处于就绪状态,而非运行状态。在调度过程中,JVM会通过调用线程类的run()方法来完成试机的操作,当run()方法结束之后,此线程就会终止。

调用run()方法,无法达到启动多线程的目的,相当于主线程线性执行Thread对象的run()方法。

10.创建线程池有哪几种方式?

java中创建线程池的方式一般有两种:1.通过Executors工厂方法创建;2.通过new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)自定义创建

Executors

  • newSingleThreadExecutor
    创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

  • newFixedThreadPool
    创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

  • newCachedThreadPool
    创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

  • newScheduledThreadPool
    创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

11.线程池都有哪些状态?

  • 1.RUNNING:这是最正常的状态,接受新的任务,处理等待队列中的任务。线程池的初始化状态是RUNNING。线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0。
  • 2.SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务。调用线程池的shutdown()方法时,线程池由RUNNING -> SHUTDOWN。
  • 3.STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。调用线程池的shutdownNow()方法时,线程池由(RUNNING or SHUTDOWN ) -> STOP。
  • 4.TIDYING:所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()。因为terminated()在ThreadPoolExecutor类中是空的,所以用户想在线程池变为TIDYING时进行相应的处理;可以通过重载terminated()函数来实现。 当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。
  • 5.TERMINATED:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

12.线程池中 submit()和 execute()方法有什么区别?

  • execute() 参数 Runnable ;submit() 参数 (Runnable) 或 (Runnable 和 结果 T) 或 (Callable)
  • execute() 没有返回值;而 submit() 有返回值
  • submit() 的返回值 Future 调用get方法时,可以捕获处理异常

13.在 java 程序中怎么保证多线程的运行安全?

线程的安全性问题体现在:

  • 原子性:一个或者多个操作在 CPU 执行的过程中不被中断的特性
  • 可见性:一个线程对共享变量的修改,另外一个线程能够立刻看到
  • 有序性:程序执行的顺序按照代码的先后顺序执行

导致原因:缓存导致的可见性问题;线程切换带来的原子性问题;编译优化带来的有序性问题

解决办法:

  • JDK Atomic开头的原子类、synchronized、LOCK,可以解决原子性问题
  • synchronized、volatile、LOCK,可以解决可见性问题
  • Happens-Before 规则可以解决有序性问题

14.多线程锁的升级原理是什么?

没有优化以前,sychronized是重量级锁(悲观锁),使用 wait 和 notify、notifyAll 来切换线程状态非常消耗系统资源;线程的挂起和唤醒间隔很短暂,这样很浪费资源,影响性能。所以 JVM 对 sychronized 关键字进行了优化,把锁分为 无锁、偏向锁、轻量级锁、重量级锁 状态。

  • 无锁:没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功,其他修改失败的线程会不断重试直到修改成功。
  • 偏向锁:对象的代码一直被同一线程执行,不存在多个线程竞争,该线程在后续的执行中自动获取锁,降低获取锁带来的性能开销。偏向锁,指的就是偏向第一个加锁线程,该线程是不会主动释放偏向锁的,只有当其他线程尝试竞争偏向锁才会被释放。偏向锁的撤销,需要在某个时间点上没有字节码正在执行时,先暂停拥有偏向锁的线程,然后判断锁对象是否处于被锁定状态。如果线程不处于活动状态,则将对象头设置成无锁状态,并撤销偏向锁;如果线程处于活动状态,升级为轻量级锁的状态。
  • 轻量级锁:轻量级锁是指当锁是偏向锁的时候,被第二个线程 B 所访问,此时偏向锁就会升级为轻量级锁,线程 B 会通过自旋的形式尝试获取锁,线程不会阻塞,从而提高性能。当前只有一个等待线程,则该线程将通过自旋进行等待。但是当自旋超过一定的次数时,轻量级锁便会升级为重量级锁;当一个线程已持有锁,另一个线程在自旋,而此时又有第三个线程来访时,轻量级锁也会升级为重量级锁。
  • 重量级锁:指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。重量级锁通过对象内部的监视器(monitor)实现,而其中 monitor 的本质是依赖于底层操作系统的 Mutex Lock 实现,操作系统实现线程之间的切换需要从用户态切换到内核态,切换成本非常高。

15.什么是死锁?

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

16.怎么防止死锁?

  • 尽量使用 tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。
  • 尽量使用 Java. util. concurrent 并发类代替自己手写锁。
  • 尽量降低锁的使用粒度,尽量不要几个功能用同一把锁。
  • 尽量减少同步的代码块。

17.ThreadLocal 是什么?

ThreadLocal 是线程本地存储,在每个线程中都创建了一个 ThreadLocalMap 对象,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。

18.说一下 synchronized 底层实现原理?

  • 同步代码块是通过 monitorenter 和 monitorexit 指令获取线程的执行权
  • 同步方法通过加 ACC_SYNCHRONIZED 标识实现线程的执行权的控制

19.synchronized 和 volatile 的区别是什么?

作用:

  • synchronized 表示只有一个线程可以获取作用对象的锁,执行代码,阻塞其他线程。
  • volatile 表示变量在 CPU 的寄存器中是不确定的,必须从主存中读取。保证多线程环境下变量的可见性;禁止指令重排序。

区别:

  • synchronized 可以作用于变量、方法、对象;volatile 只能作用于变量。
  • synchronized 可以保证线程间的有序性(猜测是无法保证线程内的有序性,即线程内的代码可能被 CPU 指令重排序)、原子性和可见性;volatile 只保证了可见性和有序性,无法保证原子性。
  • synchronized 线程阻塞,volatile 线程不阻塞。

20.synchronized 和 Lock 有什么区别?

  • 可重入锁:如果锁具备可重入性,则称作为可重入锁。像synchronized和ReentrantLock都是可重入锁,可重入性在我看来实际上表明了锁的分配机制:基于线程的分配,而不是基于方法调用的分配。
  • 可中断锁:在Java中,synchronized就不是可中断锁,而Lock是可中断锁。如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。
  • 公平锁和非公平锁:公平锁以请求锁的顺序来获取锁,非公平锁则是无法保证按照请求的顺序执行。synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。

21.说一下 atomic 的原理?

Atomic原子类底层用的不是传统意义的锁机制,而是无锁化的CAS机制,通过CAS机制保证多线程修改一个数值的安全性

反射

1.什么是反射?

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

2.动态代理是什么?有哪些应用?

动态代理:当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。

动态代理的应用:Spring的AOP,加事务,加权限,加日志。

3.怎么实现动态代理?

JDKProxy

jdk动态代理由java.lang.reflect.*包提供,它必须借助一个接口才能实现代理。

public class jdkProxyExample implements InvocationHandler{
    //真实对象
    private Object target = null;
    /**
     * 建立真实对象和代理对象的代理关系
     * @param target真实对象
     * @return 代理对象
     */
    public Object setTargetAndBind(Object target) {
        this.target = target;
        /**
         * 参数1:getClassLoader()提供类加载器;
        参数2:getInterfaces()要挂载动态代理对象的接口,就是target的接口;
         */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), this);  
    }
    /**
     * 代理方法逻辑
     * @param:代理对象
     * method:当前调度方法
     * args:当前方法参数
     * return 代理结果返回
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("进入代理对象方法,在此执行代理对象之前的一些操作");
        //执行目标对象中的某个方法
        Object object = method.invoke(target, args);
        System.out.println("调用代理对象方法之后的操作");
        return object;
    }
}

CGLib


    cglib
    cglib
    3.2.10

public class CglibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();

    public Object getProxy(Class clazz) {
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before");
        Object invoke = methodProxy.invokeSuper(o, objects);
        System.out.println("after");
        return invoke;
    }
}

public static void testCglibProxy() {
    Myfunction proxy = (Myfunction) new CglibProxy().getProxy(MyfunctionImpl.class);
    proxy.show();
}

对象拷贝

1.为什么要使用克隆?

想对一个对象进行处理,又想保留原有的数据进行接下来的操作,就需要克隆了。克隆分浅克隆和深克隆,浅克隆后的对象中非基本对象和原对象指向同一块内存,因此对这些非基本对象的修改会同时更改克隆前后的对象。深克隆可以实现完全的克隆,可以用反射的方式或序列化的方式实现。

2.如何实现对象克隆?

深克隆

实现Serializable接口,并添加一个方法deepClone()

public Object deepClone() throws IOException, ClassNotFoundException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(this);

    ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
    ObjectInputStream ois = new ObjectInputStream(bis);
    return ois.readObject();
}

浅克隆

直接赋值引用

3.深拷贝和浅拷贝区别是什么?

深拷贝:复制基本类型的属性;引用类型的属性复制,复制栈中的变量 和 变量指向堆内存中的对象的指针和堆内存中的对象。

浅拷贝:复制基本类型的属性;引用类型的属性复制,复制栈中的变量 和 变量指向堆内存中的对象的指针,不复制堆内存中的对象。

Java web模块

1.session 和 cookie 有什么区别?

Session

sessionid是一个会话的key,浏览器第一次访问服务器会在服务器端生成一个session,有一个sessionid和它对应。存储在服务器的内存中,tomcat的StandardManager类将session存储在内存中,也可以持久化到file,数据库,memcache,redis等。客户端只保存sessionid到cookie中,而不会保存session,session销毁只能通过invalidate或超时,关掉浏览器并不会关闭session)

Cookie

Cookie技术是客户端的解决方案,Cookie就是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。

2.说一下 session 的工作原理?

  • 浏览器第一次访问服务器,生成session,sessionId和它对应
  • 服务器:会将session存放在内存中,或者持久化到redis和数据库中
  • 客户端:只会将sessionId保存在cookie中
  • 浏览器:关闭不会销毁session,必须通过invalidate或超时才能销毁
  • 创建:httpServletRequest的getSession(true),sessionId由tomcat的ManagerBase类提创建sessionid的方法

3.客户端禁止 cookie,session 还能用吗?

一般默认情况下,在会话中,服务器存储 session 的 sessionid 是通过 cookie 存到浏览器里。如果浏览器禁用了 cookie,浏览器请求服务器无法携带 sessionid,服务器无法识别请求中的用户身份,session失效。但是可以通过其他方法在禁用 cookie 的情况下,可以继续使用session。

  • 通过url重写,把 sessionid 作为参数追加的原 url 中,后续的浏览器与服务器交互中携带 sessionid 参数。
  • 服务器的返回数据中包含 sessionid,浏览器发送请求时,携带 sessionid 参数。
  • 通过 Http 协议其他 header 字段,服务器每次返回时设置该 header 字段信息,浏览器中 js 读取该 header 字段,请求服务器时,js设置携带该 header 字段。

4.spring mvc 和 struts 的区别是什么?

拦截机制的不同

  • Struts2是类级别的拦截,每次请求就会创建一个Action,和Spring整合时Struts2的ActionBean注入作用域是原型模式prototype,然后通过setter,getter吧request数据注入到属性。Struts2中,一个Action对应一个request,response上下文,在接收参数时,可以通过属性接收,这说明属性参数是让多个方法共享的。Struts2中Action的一个方法可以对应一个url,而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了,只能设计为多例。
  • SpringMVC是方法级别的拦截,一个方法对应一个Request上下文,所以方法直接基本上是独立的,独享request,response数据。而每个方法同时又何一个url对应,参数的传递是直接注入到方法中的,是方法所独有的。处理结果通过ModeMap返回给框架。在Spring整合时,SpringMVC的Controller Bean默认单例模式Singleton,所以默认对所有的请求,只会创建一个Controller,有应为没有共享的属性,所以是线程安全的,如果要改变默认的作用域,需要添加@Scope注解修改。

底层框架的不同

Struts2采用Filter(StrutsPrepareAndExecuteFilter)实现,SpringMVC(DispatcherServlet)则采用Servlet实现。Filter在容器启动之后即初始化;服务停止以后坠毁,晚于Servlet。Servlet在是在调用时初始化,先于Filter调用,服务停止后销毁。

性能方面

Struts2是类级别的拦截,每次请求对应实例一个新的Action,需要加载所有的属性值注入,SpringMVC实现了零配置,由于SpringMVC基于方法的拦截,有加载一次单例模式bean注入。所以,SpringMVC开发效率和性能高于Struts2。

配置方面

spring MVC和Spring是无缝的。从这个项目的管理和安全上也比Struts2高。

5.如何避免 sql 注入?

  • 严格限制 Web 应用的数据库的操作权限,给连接数据库的用户提供满足需要的最低权限,最大限度的减少注入攻击对数据库的危害
  • 校验参数的数据格式是否合法(可以使用正则或特殊字符的判断)
  • 对进入数据库的特殊字符进行转义处理,或编码转换
  • 预编译 SQL(Java 中使用 PreparedStatement),参数化查询方式,避免 SQL 拼接
  • 发布前,利用工具进行 SQL 注入检测
  • 报错信息不要包含 SQL 信息输出到 Web 页面

6.什么是 XSS 攻击,如何避免?

即跨站脚本攻击,它是 Web 程序中常见的漏洞。原理是攻击者往 Web 页面里插入恶意的脚本代码(css 代码、Javascript 代码等),当用户浏览该页面时,嵌入其中的脚本代码会被执行,从而达到恶意攻击用户的目的,如盗取用户 cookie、破坏页面结构、重定向到其他网站等。

预防 XSS 的核心是必须对输入的数据做过滤处理。

7.什么是 CSRF 攻击,如何避免?

Cross Site Request Forgery(跨站点请求伪造)。
CSRF 攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。

异常模块

1.throw 和 throws 的区别?

throw

表示方法内抛出某种异常对象
如果异常对象是非RuntimeException则需要在方法申明时加上该异常的抛出,即需要加上throws语句或者在方法体内try catch处理该异常,否则编译报错
执行到 throw 语句则后面的语句块不再执行

throws

方法的定义上使用 throws 表示这个方法可能抛出某种异常。需要由方法的调用者进行异常处理

2.final、finally、finalize 有什么区别?

final

final修饰类,表示该类不可以被继承
final修饰变量,表示该变量不可以被修改,只允许赋值一次
final修饰方法,表示该方法不可以被重写

finally

finally是java保证代码一定要被执行的一种机制。比如try-finally或try-catch-finally,用来关闭JDBC连接资源,用来解锁等等

finalize

finalize是Object的一个方法,它的目的是保证对象在被垃圾收集前完成特定资源的回收。不过finalize已经不推荐使用,JDK9已经标记为过时。

3.try-catch-finally 中哪个部分可以省略?

catch 和 finally 语句块可以省略其中一个。

4.try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

会执行,在return 前执行

5.常见异常类有哪些?

  • (1)NullPointerException 当应用程序试图访问空对象时,则抛出该异常。
  • (2)SQLException 提供关于数据库访问错误或其他错误信息的异常。
  • (3)IndexOutOfBoundsException指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
  • (4)NumberFormatException当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
  • (5)FileNotFoundException当试图打开指定路径名表示的文件失败时,抛出此异常。
  • (6)IOException当发生某种I/O异常时,抛出此异常。此类是失败或中断的I/O操作生成的异常的通用类。
  • (7)ClassCastException当试图将对象强制转换为不是实例的子类时,抛出该异常。
  • (8)ArrayStoreException试图将错误类型的对象存储到一个对象数组时抛出的异常。
  • (9)IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数。
  • (10)ArithmeticException当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。
  • (11)NegativeArraySizeException如果应用程序试图创建大小为负的数组,则抛出该异常。
  • (12)NoSuchMethodException无法找到某一特定方法时,抛出该异常。
  • (13)SecurityException由安全管理器抛出的异常,指示存在安全侵犯。
  • (14)UnsupportedOperationException当不支持请求的操作时,抛出该异常。
  • (15)RuntimeException 是那些可能在Java虚拟机正常运行期间抛出的异常的超类。

网络模块

1.http 响应码 301 和 302 代表的是什么?有什么区别?

301 表示被请求 url 永久转移到新的 url;302 表示被请求 url 临时转移到新的 url。

2.forward和redirect是什么?

是servlet种的两种主要的跳转方式。forward又叫转发,redirect叫做重定向。

从地址栏显示来说:

forword是服务器内部的重定向,服务器直接访问目标地址的 url网址,把里面的东西读取出来,但是客户端并不知道,因此用forward的话,客户端浏览器的网址是不会发生变化的。redirect是服务器根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,所以地址栏显示的是新的地址。

从数据共享来说:

由于在整个定向的过程中用的是同一个request,因此forward会将request的信息带到被重定向的jsp或者servlet中使用。即可以共享数据;redirect不能共享

从运用的地方来说

forword 一般用于用户登录的时候,根据角色转发到相应的模块;redirect一般用于用户注销登录时返回主页面或者跳转到其他网站

从效率来说:

forword效率高,而redirect效率低

从本质来说:

forword转发是服务器上的行为,而redirect重定向是客户端的行为

3.简述 tcp 和 udp的区别?

UDP

是面向无连接的通讯协议,UDP 数据包括目的端口号和源端信息。
优点:UDP 速度快、操作简单、要求系统资源较少,由于通讯不需要连接,可以实现广播发送
缺点:UDP 传送数据前并不与对方建立连接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收,也不重复发送,不可靠。

TCP

是面向连接的通讯协议,通过三次握手建立连接,通讯完成时四次挥手
优点:TCP 在数据传递时,有确认、窗口、重传、阻塞等控制机制,能保证数据正确性,较为可靠。
缺点:TCP 相对于 UDP 速度慢一点,要求系统资源较多。

4.TCP 为什么三次握手而不是两次握手

为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤。如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认

5.说一下 tcp 粘包是怎么产生的?

发送方发送的多个数据包,到接收方缓冲区首尾相连,粘成一包,被接收。

TCP 协议默认使用 Nagle 算法可能会把多个数据包一次发送到接收方。应用程读取缓存中的数据包的速度小于接收数据包的速度,缓存中的多个数据包会被应用程序当成一个包一次读取。

6.OSI 的七层模型都有哪些?

OSI模型分为七层,自下而上为 物理层(Physical Layer)、数据链路层(Data Link Layer)、网络层(Network Layer)、传输层(Transport Layer)、会话层(Session Layer)、表达层(Presentation Layer)、应用层(Application Layer)。

7.get 和 post 请求有哪些区别?

  • 在浏览器进行回退操作时,get请求是无害的,而post请求则会重新请求一次
  • get请求参数是连接在url后面的,而post请求参数是存放在requestbody内的
  • get请求因为浏览器对url长度有限制(不同浏览器长度限制不一样)对传参数量有限制,而post请求因为参数存放在requestbody内所以参数数量没有限制(事实上get请求也能在requestbody内携带参数,只不过不符合规定,有的浏览器能够获取到数据,而有的不能)
  • 因为get请求参数暴露在url上,所以安全方面post比get更加安全
  • get请求浏览器会主动cache,post并不会,除非主动设置
  • get请求参数会保存在浏览器历史记录内,post请求并不会
  • get请求只能进行url编码,而post请求可以支持多种编码方式
  • get请求产生1个tcp数据包,post请求产生2个tcp数据包
  • 浏览器在发送get请求时会将header和data一起发送给服务器,服务器返回200状态码,而在发送post请求时,会先将header发送给服务器,服务器返回100,之后再将data发送给服务器,服务器返回200 OK

8.如何实现跨域?

Jsonp

利用了 script 不受同源策略的限制。缺点:只能 get 方式,易受到 XSS攻击

CORS(Cross-Origin Resource Sharing),跨域资源共享

当使用XMLHttpRequest发送请求时,如果浏览器发现违反了同源策略就会自动加上一个请求头 origin;
后端在接受到请求后确定响应后会在后端在接受到请求后确定响应后会在 Response Headers 中加入一个属性 Access-Control-Allow-Origin;
浏览器判断响应中的 Access-Control-Allow-Origin 值是否和当前的地址相同,匹配成功后才继续响应处理,否则报错
缺点:忽略 cookie,浏览器版本有一定要求

代理跨域请求

前端向发送请求,经过代理,请求需要的服务器资源
缺点:需要额外的代理服务器

Html5 postMessage 方法

允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本、多窗口、跨域消息传递
缺点:浏览器版本要求,部分浏览器要配置放开跨域限制

修改 document.domain 跨子域

相同主域名下的不同子域名资源,设置 document.domain 为 相同的一级域名。缺点:同一一级域名;相同协议;相同端口

基于 Html5 websocket 协议

websocket 是 Html5 一种新的协议,基于该协议可以做到浏览器与服务器全双工通信,允许跨域请求。缺点:浏览器一定版本要求,服务器需要支持 websocket 协议

document.xxx + iframe

通过 iframe 是浏览器非同源标签,加载内容中转,传到当前页面的属性中
缺点:页面的属性值有大小限制

9.说一下 JSONP 实现原理?

利用了 script 不受同源策略的限制,调用不同源的接口。访问服务器时提供callback回调函数名或者提供一些请求参数并且本地提供回调函数的实现。服务器根据参数进行逻辑处理,并使用回调函数名拼接返回参数的做法调用本地回调函数的实现方法。

设计模式

单例模式:某个类只能有一个实例,提供一个全局的访问点。
简单工厂:一个工厂类根据传入的参量决定创建出那一种产品类的实例。
工厂方法:定义一个创建对象的接口,让子类决定实例化那个类。
抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体类。
建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造。
原型模式:通过复制现有的实例来创建新的实例。
适配器模式:将一个类的方法接口转换成客户希望的另外一个接口。
组合模式:将对象组合成树形结构以表示“”部分-整体“”的层次结构。
装饰模式:动态的给对象添加新的功能。
代理模式:为其他对象提供一个代理以便控制这个对象的访问。
亨元(蝇量)模式:通过共享技术来有效的支持大量细粒度的对象。
外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。
模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
观察者模式:对象间的一对多的依赖关系。
备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
中介者模式:用一个中介对象来封装一系列的对象交互。
命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。
责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。

1.说一下你熟悉的设计模式?

单例模式

它的定义就是确保某一个类只有一个实例,并且提供一个全局访问点。
单例模式具备典型的3个特点:1、只有一个实例。 2、自我实例化。 3、提供全局访问点。
因此当系统中只需要一个实例对象或者系统中只允许一个公共访问点,除了这个公共访问点外,不能通过其他访问点访问该实例时,可以使用单例模式。
单例模式的主要优点就是节约系统资源、提高了系统效率,同时也能够严格控制客户对它的访问。也许就是因为系统中只有一个实例,这样就导致了单例类的职责过重,违背了“单一职责原则”,同时也没有抽象类,所以扩展起来有一定的困难。

2.简单工厂和抽象工厂有什么区别?

简单工厂模式

是由一个工厂对象创建产品实例,简单工厂模式的工厂类一般是使用静态方法,通过不同的参数的创建不同的对象实例。可以生产结构中的任意产品,不能增加新的产品

抽象工厂模式

提供一个创建一系列相关或相互依赖对象的接口,而无需制定他们具体的类,生产多个系列产品。生产不同产品族的全部产品,不能新增产品,可以新增产品族

2、框架

Spring/Spring MVC

1.为什么要使用 spring?

spring 是一个开源的轻量级 JavaBean 容器框架。使用 JavaBean 代替 EJB ,并提供了丰富的企业应用功能,降低应用开发的复杂性。

  • 轻量:非入侵性的、所依赖的东西少、资源占用少、部署简单,不同功能选择不同的 jar 组合
  • 容器:工厂模式实现对 JavaBean 进行管理,通过控制反转(IOC)将应用程序的配置和依赖性与应用代码分开
  • 松耦合:通过 xml 配置或注解即可完成 bean 的依赖注入
  • AOP:通过 xml 配置 或注解即可加入面向切面编程的能力,完成切面功能,如:日志,事务...的统一处理
  • 方便集成:通过配置和简单的对象注入即可集成其他框架,如 Mybatis、Hibernate、Shiro...
  • 丰富的功能:JDBC 层抽象、事务管理、MVC、Java Mail、任务调度、JMX、JMS、JNDI、EJB、动态语言、远程访问、Web Service...

2.AOP

AOP分为静态AOP和动态AOP

静态AOP是指AspectJ实现的AOP,他是将切面代码直接编译到Java类文件中。
动态AOP是指将切面代码进行动态织入实现的AOP。

Spring的AOP为动态AOP,实现的技术为: JDK提供的动态代理技术 和 CGLIB(动态字节码增强技术) 。尽管实现技术不一样,但 都是基于代理模式 , 都是生成一个代理对象 。

3.IOC

Spring Boot/Spring Cloud

Hibernate

Mybatis

RabbitMQ

3、JVM

你可能感兴趣的:(Java面试题)