Java面试考题集锦之Java基础

这篇文章记录在准备Java后端面试复习过程中网上常见的考题,同时也会标明题目出现频率,方便大家参考。有缺少、错误的部分欢迎大家补充纠正。–持续更新
推荐https://download.csdn.net/download/qq_41011723/12255299,基本涵盖了面试题的原理

也欢迎大家来我自己搭建的博客看看,之后博客也会同步更新
hofe’s blog

文章目录

    • 数据类型
          • Java中的八大类型及其包装类型(占用字节数)
          • String类为什么是final的?
          • string、stringbuilder、stringbuffer区别?
          • String 类的常用方法?
    • Java面向对象
          • Java 接口\抽象类区别?
          • 重载和重写?
          • Java对象的生命周期?
    • List、Set、Map
        • 高频
      • List
          • ArrayList和LinkedList、Vector的区别?
          • ArrayList和LinkedList的区别?分别用在什么场景?
      • Set
        • HashSet
          • 重写equals为何要重写hashCode?
        • TreeSet
        • LinkHashSet(HashSet+LinkHashMap)
      • Map
        • HashMap
        • Hashtable
        • CurrentHashMap
        • TreeMap
        • LinkedHashMap
          • hashtable和hashmap的区别及实现原理,
          • Hashtable,HashMap,ConcurrentHashMap 底层实现原理与线程安全问题
          • 请你说明HashMap和Hashtable的区别?
          • HashMap 和 ConcurrentHashMap区别?
          • 如何使HashMap变得安全?
          • ConcurrentHashMap如何保证线程安全的?
        • 常见
          • 请说明Collection 和 Collections的区别?
          • HashMap底层实现?
          • hash冲突了解哪些?
        • 少见
          • Collection集合接口和Map接口有什么关系?
          • Map的各种实现类,它们有什么区别?
          • Hash冲突怎么办?哪些解决散列冲突的方法?
          • HashMap冲突很厉害,最差性能,你会怎么解决?
          • rehash
          • hashCode() 与 equals() 生成算法、方法怎么重写
    • JVM
        • 高频
          • JVM 的内存结构?
          • 分代?
          • 垃圾清除算法?
          • 垃圾回收器?
          • 类的实例化顺序?
          • java的.class加载机制,java到class到二进制字节码文件的转化过程?
          • 描述一下JVM加载class文件的原理机制?
          • 双亲委派?
          • Minor GC和Full GC的触发时机
        • 常见
          • JVM 调优?
          • 垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
          • java中会存在内存泄漏吗,请简单描述。
          • 内存泄漏会导致什么?
          • 内存泄漏检查
        • 少见
          • CGLib
          • JVM的作用?
    • Java 并发编程
        • 高频
          • 什么是线程?
          • 线程和进程有什么区别?
          • Java中sleep方法和wait方法的区别?
          • Thread 类中的start() 和 run() 方法有什么区别?
          • 终止线程4种方式?
          • 什么是线程池? 为什么要使用它?
          • 线程池原理?
          • Java线程池工作过程 ?
          • 什么是ThreadLocal?
          • Java中Runnable和Callable有什么不同?
          • 提交任务时,线程池队列已满。会发生什么?(工作过程、拒绝策略)
          • 乐观锁与悲观锁?
          • 同步锁与死锁?
          • 共享锁与独占锁?
        • 常见
          • Java中如何停止一个线程?
          • 线程基本方法?
          • synchronized?
          • volatile?
          • 如何在Java中实现线程?
          • 线程池的组成?
          • 线程池,threadpool有哪些重要的参数?
          • 拒绝策略 ?
          • JAVA阻塞队列原理?
          • Java中的同步集合与并发集合有什么区别?
        • 少见
          • Java中CyclicBarrier 和 CountDownLatch有什么不同?
          • Java中如何停止一个线程?
          • 一个线程运行时发生异常会怎样?
          • 如何在两个线程间共享数据?
          • Java中notify 和 notifyAll有什么区别?
          • Java中interrupted 和 isInterruptedd方法的区别?
          • 为什么wait和notify方法要在同步块中调用?
          • 有哪些不同的线程生命周期?
          • 你对线程优先级的理解是什么?
          • Sleep()、suspend()和wait()之间有什么区别?
          • 什么是线程饿死,什么是活锁?
          • 什么是Java Timer类?如何创建一个有特定时间间隔的任务?
          • 同步方法和同步块,哪个是更好的选择?
          • Java中invokeAndWait 和 invokeLater有什么区别?
          • 多线程中的忙循环是什么?
    • Java IO
          • 讲讲IO里面的常见类,字节流、字符流、接口、实现类、方法阻塞。
          • 讲讲NIO
          • String 编码UTF-8 和GBK的区别?
          • 什么时候使用字节流、什么时候使用字符流?
          • 递归读取文件夹下的文件,代码怎么实现
    • 引用
    • 异常
        • 高频
          • Exception 与 Error?
        • 常见
          • Throw和throws的区别:
    • 反射
          • 什么是反射?
          • Java反射机制的作用?
          • 哪里用到反射机制?
          • 如何使用Java的反射?
          • 反射用到了哪些接口,哪些类?
          • 反射机制中可以获取private成员的值吗?
          • 反射机制的优缺点?
    • 注解
          • Java中有哪些注解?在SpringMVC中,requestmapping是自定义注解,问:如何实现自定义注解?
          • JSR-250注解
          • JSR-330注解
          • JPA注解
          • Spring自带依赖注入注解

图片来源:牛客网面经
Java面试考题集锦之Java基础_第1张图片

数据类型

Java中的八大类型及其包装类型(占用字节数)

Java面试考题集锦之Java基础_第2张图片

String类为什么是final的?

https://www.jianshu.com/p/9c7f5daac283

string、stringbuilder、stringbuffer区别?

https://www.cnblogs.com/liyy7520/p/11899260.html

String 类的常用方法?

equals:字符串是否相同
compareTo:根据字符串中每个字符的Unicode编码进行比较
indexOf:目标字符或字符串在源字符串中位置下标
valueOf:其他类型转字符串
concat:追加字符串到当前字符串
isEmpty:字符串长度是否为0
contains:是否包含目标字符串

Java面向对象

Java 接口\抽象类区别?

https://www.cnblogs.com/zzfpz/p/10990210.html

重载和重写?

https://blog.csdn.net/qunqunstyle99/article/details/81007712

Java对象的生命周期?

创建阶段 、 应用阶段 、不可见阶段 、不可达阶段 、收集阶段 、终结阶段、 对象空间重新分配阶段。

创建阶段系统通过下面的几个步骤来完成对象的创建过程

  1. 为对象分配存储空间
  2. 开始构造对象
  3. 从超类到子类对static成员进行初始化
  4. 超类成员变量按顺序初始化,递归调用超类的构造方法
  5. 子类成员变量按顺序初始化,子类构造方法调用
    一旦对象被创建,并被分派给某些变量赋值,这个对象的状态就切换到了应用阶段

应用阶段
对象至少被一个强引用持有着

当一个对象处于不可见阶段时,说明程序本身不再持有该对象的任何强引用,虽然该这些引用仍然是存在着的。

简单说就是程序的执行已经超出了该对象的作用域了

不可达阶段(Unreachable)
对象处于不可达阶段是指该对象不再被任何强引用所持有。
与“不可见阶段”相比,“不可见阶段”是指程序不再持有该对象的任何强引用,这种情况下,该对象仍可能被JVM等系统下的某些已装载的静态变量或线程或JNI等强引用持有着,这些特殊的强引用被称为”GC root”。存在着这些GC root会导致对象的内存泄露情况,无法被回收。

收集阶段(Collected)
当垃圾回收器发现该对象已经处于“不可达阶段”并且垃圾回收器已经对该对象的内存空间重新分配做好准备时,则对象进入了“收集阶段”。则对象进入了“收集阶段”。如果该对象已经重写了finalize()方法,则会去执行该方法的终端操作。

这里要特别说明一下:不要重载finazlie()方法!原因有两点:

  1. 会影响JVM的对象分配与回收速度
    在分配该对象时,JVM需要在垃圾回收器上注册该对象,以便在回收时能够执行该重载方法;在该方法的执行时需要消耗CPU时间且在执行完该方法后才会重新执行回收操作,即至少需要垃圾回收器对该对象执行两次GC。

  2. 可能造成该对象的再次“复活”
    在finalize()方法中,如果有其它的强引用再次持有该对象,则会导致对象的状态由“收集阶段”又重新变为“应用阶段”。这个已经破坏了Java对象的生命周期进程,且“复活”的对象不利用后续的代码管理。

终结阶段
当对象执行完finalize()方法后仍然处于不可达状态时,则该对象进入终结阶段。在该阶段是等待垃圾回收器对该对象空间进行回收

对象空间重新分配阶段
垃圾回收器对该对象的所占用的内存空间进行回收或者再分配了,则该对象彻底消失了,称之为“对象空间重新分配阶段”



List、Set、Map

Java面试考题集锦之Java基础_第3张图片

高频

List

ArrayList和LinkedList、Vector的区别?

https://blog.csdn.net/renfufei/article/details/17077425
Java面试考题集锦之Java基础_第4张图片

ArrayList和LinkedList的区别?分别用在什么场景?

①ArrayList和LinkedList可想从名字分析,它们一个是Array(动态数组)的数据结构,一个是Link(链表)的数据结构,此外,它们两个都是对List接口的实现。
前者是数组队列,相当于动态数组;后者为双向链表结构,也可当作堆栈、队列、双端队列
②当随机访问List时(get和set操作),ArrayList比LinkedList的效率更高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找。
③当对数据进行增加和删除的操作时(add和remove操作),LinkedList比ArrayList的效率更高,因为ArrayList是数组,所以在其中进行增删操作时,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动。
④从利用效率来看,ArrayList自由性较低,因为它需要手动的设置固定大小的容量,但是它的使用比较方便,只需要创建,然后添加数据,通过调用下标进行使用;而LinkedList自由性较高,能够动态的随数据量的变化而变化,但是它不便于使用。
⑤ArrayList主要控件开销在于需要在lList列表预留一定空间;而LinkList主要控件开销在于需要存储结点信息以及结点指针信息。

场景:
链表,插入删除快,查找修改慢。 适用于频繁增删的场景。
数组,查找快,插入删除慢。 适用于频繁查找和修改的场景。

Set

Java面试考题集锦之Java基础_第5张图片
Set注重独一无二的性质,该体系集合用于存储无序(存入和取出的顺序不一定相同)元素,值不能重 复。对象的相等性本质是对象hashCode值(java是依据对象的内存地址计算出的此序号)判断 的,如果想要让两个不同的对象视为相等的,就必须覆盖Object的hashCode方法和equals方 法

HashSet

重写equals为何要重写hashCode?

答:HashSet首先判断两个元素的哈希值,如果哈希值一样,接着会比较 equals方法 如果 equls结果为true ,HashSet就视为同一个元素。如果equals 为false就不是 同一个元素。

判断两个对象是否相等,比较的就是其hashCode, 如果你重载了equals,比如说是基于对象的内容实现的,而保留hashCode的实现不变,那么很可能某两个对象明明是“相等”,而hashCode却不一样。 hashcode不一样,就无法认定两个对象相等了

哈希值相同equals为false的元素是怎么存储呢,就是在同样的哈希值下顺延(可以认为哈希值相 同的元素放在一个哈希桶中)。也就是哈希一样的存一列。如图1表示hashCode值不相同的情 况;图2表示hashCode值相同,但equals不相同的情况。
Java面试考题集锦之Java基础_第6张图片

TreeSet

  1. TreeSet()是使用二叉树的原理对新add()的对象按照指定的顺序排序(升序、降序),每增 加一个对象都会进行排序,将对象插入的二叉树指定的位置。
  2. Integer和String对象都可以进行默认的TreeSet排序,而自定义类的对象是不可以的,自 己定义的类必须实现Comparable接口,并且覆写相应的compareTo()函数,才可以正常使 用。
  3. 在覆写compare()函数时,要返回相应的值才能使TreeSet按照一定的规则来排序
  4. 比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整 数、零或正整数

LinkHashSet(HashSet+LinkHashMap)

对于 LinkedHashSet 而言,它继承与 HashSet、又基于 LinkedHashMap 来实现的。 LinkedHashSet 底层使用 LinkedHashMap 来保存所有元素,它继承与 HashSet,其所有的方法 操作上又与HashSet相同,因此LinkedHashSet 的实现上非常简单,只提供了四个构造方法,并 通过传递一个标识参数,调用父类的构造器,底层构造一个 LinkedHashMap 来实现,在相关操 作上与父类HashSet的操作相同,直接调用父类HashSet的方法即可

Map

HashMap

Java面试考题集锦之Java基础_第7张图片
HashMap根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,因而具有很快 的访问速度,但遍历顺序却是不确定的。 HashMap多只允许一条记录的键为null,允许多条记 录的值为 null。HashMap 非线程安全,即任一时刻可以有多个线程同时写 HashMap,可能会导 致数据的不一致。如果需要满足线程安全,可以用 Collections 的 synchronizedMap 方法使 HashMap 具有线程安全的能力,或者使用 ConcurrentHashMap。我们用下面这张图来介绍 HashMap 的结构

Java7实现
数组+链表
在这里插入图片描述
大方向上,HashMap 里面是一个数组,然后数组中每个元素是一个单向链表。上图中,每个绿色 的实体是嵌套类 Entry 的实例,Entry 包含四个属性:key, value, hash 值和用于单向链表的 next。

  1. capacity:当前数组容量,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2 倍。
  2. loadFactor:负载因子,默认为 0.75。
  3. threshold:扩容的阈值,等于 capacity * loadFactor

Java8实现
数组+链表+红黑树
Java8 对 HashMap 进行了一些修改,大的不同就是利用了红黑树,所以其由 数组+链表+红黑 树 组成。
根据 Java7 HashMap 的介绍,我们知道,查找的时候,根据 hash 值我们能够快速定位到数组的 具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决 于链表的长度,为 O(n)。为了降低这部分的开销,在 Java8 中,当链表中的元素超过了 8 个以后, 会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度为 O(logN)。
在这里插入图片描述

Hashtable

Hashtable 是遗留类,很多映射的常用功能与 HashMap 类似,不同的是它承自 Dictionary 类, 并且是线程安全的,任一时间只有一个线程能写 Hashtable,并发性不如 ConcurrentHashMap, 因为 ConcurrentHashMap 引入了分段锁。Hashtable 不建议在新代码中使用,不需要线程安全 的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换

CurrentHashMap

支持并发操作。ConcurrentHashMap 由一个个 Segment 数组+数组+红黑树组成。Segment 通过继承 ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每 个 Segment 是线程安全的,也就实现了全局的线程安全。

concurrencyLevel:并行级别、并发数、Segment 数,怎么翻译不重要,理解它。默认是 16, 也就是说 ConcurrentHashMap 有 16 个 Segments,所以理论上,这个时候,多可以同时支 持 16 个线程并发写,只要它们的操作分别分布在不同的 Segment 上。这个值可以在初始化的时 候设置为其他值,但是一旦初始化以后,它是不可以扩容的。再具体到每个 Segment 内部,其实 每个 Segment 很像之前介绍的 HashMap,不过它要保证线程安全,所以处理起来要麻烦些。

jdk1.7:segment数组+数组+链表
Java面试考题集锦之Java基础_第8张图片
jdk1.8:segment数组+数组+红黑树
Java面试考题集锦之Java基础_第9张图片

TreeMap

TreeMap 实现 SortedMap 接口,能够把它保存的记录根据键排序,默认是按键值的升序排序, 也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。
如果使用排序的映射,建议使用TreeMap。
在使用 TreeMap 时,key 必须实现 Comparable 接口或者在构造 TreeMap 传入自定义的 Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异常。
参考:https://www.ibm.com/developerworks/cn/java/j-lo-tree/index.html

LinkedHashMap

LinkedHashMap 是 HashMap 的一个子类,保存了记录的插入顺序,在用 Iterator 遍历 LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。 参考1:http://www.importnew.com/28263.html
参考2:http://www.importnew.com/20386.html#comment-648123

hashtable和hashmap的区别及实现原理,

hashmap会问到数组索引,hash碰撞怎么解决?

Hashtable,HashMap,ConcurrentHashMap 底层实现原理与线程安全问题

建议熟悉 jdk 源码,才能从容应答

请你说明HashMap和Hashtable的区别?

HashMap和Hashtable都实现了Map接口,因此很多特性非常相似。但是,他们有以下不同点:
HashMap允许键和值是null,而Hashtable不允许键或者值是null。
Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
HashMap提供了可供应用迭代的键的集合,因此,HashMap是快速失败的。另一方面,Hashtable提供了对键的列举(Enumeration)。
一般认为Hashtable是一个遗留的类。

HashMap 和 ConcurrentHashMap区别?
  1. HashMap不支持并发操作,没有同步方法,ConcurrentHashMap支持并发操作,通过继承 ReentrantLock(JDK1.7重入锁)/CAS和synchronized(JDK1.8内置锁)来进行加锁(分段锁),每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。
  2. JDK1.8之前HashMap的结构为数组+链表,JDK1.8之后HashMap的结构为数组+链表+红黑树;JDK1.8之前ConcurrentHashMap的结构为segment数组+数组+链表,JDK1.8之后ConcurrentHashMap的结构为数组+链表+红黑树。
如何使HashMap变得安全?
ConcurrentHashMap如何保证线程安全的?

常见

请说明Collection 和 Collections的区别?

Collection是集合类的上级接口,继承与他的接口主要有Set 和List.
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作

HashMap底层实现?
hash冲突了解哪些?

少见

Collection集合接口和Map接口有什么关系?

没有直接关系,但是一些子类会有依赖,Collection是最基本的集合接口,声明了适用于JAVA集合(只包括Set和List)的通用方法。Map接口并不是Collection接口的子接口,但是它仍然被看作是Collection框架的一部分。

Map的各种实现类,它们有什么区别?
Hash冲突怎么办?哪些解决散列冲突的方法?
HashMap冲突很厉害,最差性能,你会怎么解决?

从O(n)提升到log(n)咯,用二叉排序树的思路说了一通

rehash
hashCode() 与 equals() 生成算法、方法怎么重写


JVM

高频

JVM 的内存结构?

Java面试考题集锦之Java基础_第10张图片
Java面试考题集锦之Java基础_第11张图片

  1. 程序计数器:程序计数器(Program Counter Register)是一块较小的内存空间,可以看作是当前线程所执行字节码的行号指示器,指向下一个将要执行的指令代码,由执行引擎来读取下一条指令。更确切的说,一个线程的执行,是通过字节码解释器改变当前线程的计数器的值,来获取下一条需要执行的字节码指令,从而确保线程的正确执行。每个线程都有一个独立的程序计数器,是线程私有的内存

  2. 虚拟机栈:描述java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame) 用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成 的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

  3. 本地方法栈:本地方法栈则为 JVM 使用到的 Native 方法服务。Native 方法不是以 Java 语言实现的,而是以本地语言实现的(比如 C 或 C++)。个人理解Native 方法是与操作系统直接交互的。比如通知垃圾收集器进行垃圾回收的代码 System.gc(),就是使用 native 修饰的

  4. 堆:堆是Java虚拟机所管理的内存中最大的一块存储区域。堆内存被所有线程共享。主要存放使用new关键字创建的对象。所有对象实例以及数组都要在堆上分配。垃圾收集器就是根据GC算法,收集堆上对象所占用的内存空间(收集的是对象占用的空间而不是对象本身)
    Java堆分为年轻代(Young Generation)和老年代(Old Generation);年轻代又分为伊甸园(Eden)和幸存区(Survivor区);幸存区又分为From Survivor空间和 To Survivor空间。

年轻代存储“新生对象”,我们新创建的对象存储在年轻代中。当年轻内存占满后,会触发Minor GC,清理年轻代内存空间。

老年代存储长期存活的对象和大对象。年轻代中存储的对象,经过多次GC后仍然存活的对象会移动到老年代中进行存储。老年代空间占满后,会触发Full GC。

注:Full GC是清理整个堆空间,包括年轻代和老年代。如果Full GC之后,堆中仍然无法存储对象,就会抛出OutOfMemoryError异常。

  1. 方法区:方法区同 Java 堆一样是被所有线程共享的区间,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码。更具体的说,静态变量+常量+类信息(版本、方法、字段等)+运行时常量池存在方法区中。常量池是方法区的一部分
    注:JDK1.8 使用元空间 MetaSpace 替代方法区,元空间并不在 JVM中,而是使用本地内存。元空间两个参数:
    MetaSpaceSize:初始化元空间大小,控制发生GC阈值
    MaxMetaspaceSize : 限制元空间大小上限,防止异常占用过多物理内存

常量池中存储编译器生成的各种字面量和符号引用。字面量就是Java中常量的意思。比如文本字符串,final修饰的常量等。方法引用则包括类和接口的全限定名,方法名和描述符,字段名和描述符等。

分代?

Java堆从GC的角度还可以细分为: 新生代( Eden 区 、 From Survivor 区 和To Survivor 区 )和老年代

新生代:是用来存放新生的对象。一般占据堆的1/3空间。由于频繁创建对象,所以新生代会频繁触发 MinorGC进行垃圾回收。新生代又分为 Eden区、ServivorFrom、ServivorTo三个区
Java面试考题集锦之Java基础_第12张图片
MinorGC的过程
采用复制算法
Java面试考题集锦之Java基础_第13张图片

老年代
主要存放应用程序中生命周期长的内存对象。
老年代的对象比较稳定,所以 MajorGC 不会频繁执行。在进行 MajorGC 前一般都先进行 了一次 MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足 够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间。
MajorGC 采用标记清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没 有标记的对象。MajorGC的耗时比较长,因为要扫描再回收。MajorGC 会产生内存碎片,为了减 少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。当老年代也满了装不下的 时候,就会抛出OOM(Out of Memory)异常。

永久代
指内存的永久保存区域,主要存放 Class 和 Meta(元数据)的信息,Class 在被加载的时候被 放入永久区域,它和存放实例的区域不同,GC 不会在主程序运行期对永久区域进行清理。所以这 也导致了永久代的区域会随着加载的Class的增多而胀满,终抛出OOM异常

垃圾清除算法?

Java面试考题集锦之Java基础_第14张图片
确定垃圾算法

  • 引用计数法:在 Java 中,引用和对象是有关联的。如果要操作对象则必须用引用进行。因此,很显然一个简单 的办法是通过引用计数来判断一个对象是否可以回收。简单说,即一个对象如果没有任何与之关 联的引用,即他们的引用计数都不为 0,则说明对象不太可能再被用到,那么这个对象就是可回收 对象

  • 可达性分析:为了解决引用计数法的循环引用问题,Java 使用了可达性分析的方法。通过一系列的“GC roots” 对象作为起点搜索。如果在“GC roots”和一个对象之间没有可达路径,则称该对象是不可达的。要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记 过程。两次标记后仍然是可回收对象,则将面临回收。

标记清除算法(mark-sweep)
最基础的垃圾回收算法,分为两个阶段,标注和清除。标记阶段标记出所有需要回收的对象,清 除阶段回收被标记的对象所占用的空间
缺陷:大的问题是内存碎片化严重,后续可能发生大对象不能找到可 利用空间的问题
Java面试考题集锦之Java基础_第15张图片

复制算法(coping)
为了解决Mark-Sweep算法内存碎片化的缺陷而被提出的算法。按内存容量将内存划分为等大小 的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去,把已使用 的内存清掉。
缺陷:这种算法虽然实现简单,内存效率高,不易产生碎片,但是大的问题是可用内存被压缩到了原 本的一半。且存活对象增多的话,Copying算法的效率会大大降低
Java面试考题集锦之Java基础_第16张图片

标记整理算法(mark-compact)
结合了以上两个算法,为了避免缺陷而提出。标记阶段和Mark-Sweep算法相同,标记后不是清 理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象

Java面试考题集锦之Java基础_第17张图片

分代收集算法
分代收集法是目前大部分JVM所采用的方法,其核心思想是根据对象存活的不同生命周期将内存 划分为不同的域,一般情况下将GC堆划分为老生代(Tenured/Old Generation)和新生代(Young Generation)。老生代的特点是每次垃圾回收时只有少量对象需要被回收,新生代的特点是每次垃 圾回收时都有大量垃圾需要被回收,因此可以根据不同区域选择不同的算法

  • 新生代-复制算法:目前大部分JVM的GC 对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要 回收大部分对象,即要复制的操作比较少,但通常并不是按照1:1来划分新生代。一般将新生代 划分为一块较大的Eden空间和两个较小的Survivor空间(From Space, To Space),每次使用 Eden空间和其中的一块Survivor空间,当进行回收时,将该两块空间中还存活的对象复制到另 一块Survivor空间中
    Java面试考题集锦之Java基础_第18张图片
  • 老年代-标记整理算法:因为对象存活率高、没有额外空间对它进行分配担保, 就必须采用“标记—清理”或“标 记—整理”算法来进行回收, 不必进行内存复制, 且直接腾出空闲内存。
  1. JAVA虚拟机提到过的处于方法区的永生代(Permanet Generation),它用来存储class类, 常量,方法描述等。对永生代的回收主要包括废弃常量和无用的类。
  2. 对象的内存分配主要在新生代的Eden Space和Survivor Space的From Space(Survivor目 前存放对象的那一块),少数情况会直接分配到老生代。
  3. 当新生代的Eden Space和From Space空间不足时就会发生一次GC,进行GC后,Eden Space和From Space区的存活对象会被挪到To Space,然后将Eden Space和From Space进行清理。
  4. 如果To Space无法足够存储某个对象,则将这个对象存储到老生代。
  5. 在进行GC后,使用的便是Eden Space和To Space了,如此反复循环。
  6. 当对象在Survivor区躲过一次GC 后,其年龄就会+1。默认情况下年龄到达15 的对象会被 移到老生代中

分区收集算法:分区算法则将整个堆空间划分为连续的不同小区间, 每个小区间独立使用, 独立回收. 这样做的 好处是可以控制一次回收多少个小区间 , 根据目标停顿时间, 每次合理地回收若干个小区间(而不是 整个堆), 从而减少一次GC 所产生的停顿。

垃圾回收器?

Java 堆内存被划分为新生代和年老代两部分,新生代主要使用复制和标记-清除垃圾回收算法; 年老代主要使用标记-整理垃圾回收算法,因此 java 虚拟中针对新生代和年老代分别提供了多种不 同的垃圾收集器,JDK1.6中Sun HotSpot虚拟机的垃圾收集器如下
Java面试考题集锦之Java基础_第19张图片

  • 新生代垃圾收集器
  1. Serial(英文连续)是基本垃圾收集器,使用复制算法,曾经是JDK1.3.1之前新生代唯一的垃圾 收集器。Serial 是一个单线程的收集器,它不但只会使用一个 CPU 或一条线程去完成垃圾收集工 作,并且在进行垃圾收集的同时,必须暂停其他所有的工作线程,直到垃圾收集结束。 Serial 垃圾收集器虽然在收集垃圾过程中需要暂停所有其他的工作线程,但是它简单高效,对于限 定单个 CPU 环境来说,没有线程交互的开销,可以获得高的单线程垃圾收集效率,因此 Serial 垃圾收集器依然是java虚拟机运行在Client模式下默认的新生代垃圾收集器
  2. ParNew垃圾收集器其实是Serial收集器的多线程版本,也使用复制算法,除了使用多线程进行垃 圾收集之外,其余的行为和Serial收集器完全一样,ParNew垃圾收集器在垃圾收集过程中同样也 要暂停所有其他的工作线程
    ParNew 收集器默认开启和 CPU 数目相同的线程数,可以通过-XX:ParallelGCThreads 参数来限 制垃圾收集器的线程数。【Parallel:平行的】 ParNew虽然是除了多线程外和Serial收集器几乎完全一样,但是ParNew垃圾收集器是很多java 虚拟机运行在Server模式下新生代的默认垃圾收集器
  3. Parallel Scavenge 收集器也是一个新生代垃圾收集器,同样使用复制算法,也是一个多线程的垃 圾收集器,它重点关注的是程序达到一个可控制的吞吐量(Thoughput,CPU 用于运行用户代码 的时间/CPU 总消耗时间,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)), 高吞吐量可以高效率地利用 CPU 时间,尽快地完成程序的运算任务,主要适用于在后台运算而 不需要太多交互的任务。自适应调节策略也是 ParallelScavenge 收集器与 ParNew 收集器的一个 重要区别
  • 老年代
  1. Serial Old 是 Serial 垃圾收集器年老代版本,它同样是个单线程的收集器,使用标记-整理算法, 这个收集器也主要是运行在Client默认的java虚拟机默认的年老代垃圾收集器
    在Server模式下,主要有两个用途:
    (1)在JDK1.5之前版本中与新生代的Parallel Scavenge收集器搭配使用。
    (2)作为年老代中使用CMS收集器的后备垃圾收集方案。 新生代Serial与年老代Serial Old搭配垃圾收集过程图:
    Java面试考题集锦之Java基础_第20张图片
    (3)新生代Parallel Scavenge收集器与ParNew收集器工作原理类似,都是多线程的收集器,都使 用的是复制算法,在垃圾收集过程中都需要暂停所有的工作线程。新生代Parallel Scavenge/ParNew与年老代Serial Old搭配垃圾收集过程图:
    Java面试考题集锦之Java基础_第21张图片

  2. Parallel Old收集器是Parallel Scavenge的年老代版本,使用多线程标记-整理算法,在JDK1.6 才开始提供。 在 JDK1.6 之前,新生代使用 ParallelScavenge 收集器只能搭配年老代的 Serial Old 收集器,只 能保证新生代的吞吐量优先,无法保证整体的吞吐量,Parallel Old 正是为了在年老代同样提供吞 吐量优先的垃圾收集器,如果系统对吞吐量要求比较高,可以优先考虑新生代 Parallel Scavenge 和年老代Parallel Old收集器的搭配策略。 新生代Parallel Scavenge和年老代Parallel Old收集器搭配运行过程图:
    Java面试考题集锦之Java基础_第22张图片

  3. Concurrent mark sweep(CMS)收集器是一种年老代垃圾收集器,其主要目标是获取短垃圾 回收停顿时间,和其他年老代使用标记-整理算法不同,它使用多线程的标记-清除算法。 短的垃圾收集停顿时间可以为交互比较高的程序提高用户体验。 CMS工作机制相比其他的垃圾收集器来说更复杂,整个过程分为以下4个阶段:
    初始标记:只是标记一下GC Roots能直接关联的对象,速度很快,仍然需要暂停所有的工作线程。
    并发标记:进行GC Roots跟踪的过程,和用户线程一起工作,不需要暂停工作线程。
    重新标记:为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记 记录,仍然需要暂停所有的工作线程。
    并发清除:清除GC Roots不可达对象,和用户线程一起工作,不需要暂停工作线程。由于耗时长的并 发标记和并发清除过程中,垃圾收集线程可以和用户现在一起并发工作,所以总体上来看 CMS收集器的内存回收和用户线程是一起并发地执行。
    Java面试考题集锦之Java基础_第23张图片
    7.G1收集器:Garbage first 垃圾收集器是目前垃圾收集器理论发展的前沿成果,相比与CMS 收集器,G1 收 集器两个突出的改进是:
    1.基于标记-整理算法,不产生内存碎片
    2.可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。 G1 收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域 的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间优先回收垃圾 多的区域。区域划分和优先级区域回收机制,确保 G1 收集器可以在有限时间获得高的垃圾收 集效率。

类的实例化顺序?

比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,他们的执行顺序?
父类静态成员和静态代码块->
子类静态成员和静态代码块->
父类非静态成员和非静态代码块->
父类构造方法->
子类非静态成员和非静态代码块->
子类构造方法

java的.class加载机制,java到class到二进制字节码文件的转化过程?
描述一下JVM加载class文件的原理机制?

这块引用链接:https://www.jianshu.com/p/7423f01bf77f

JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。
由于Java的跨平台性,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。
当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。
类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。
加载完成后,Class对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。
最后JVM对类进行初始化,包括:
1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;
2)如果类中存在初始化语句,就依次执行这些初始化语句。

类的加载是由类加载器完成的,类加载器包括:
根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader的子类)。
从Java 2(JDK 1.2)开始,类加载过程采取了父亲委托机制(PDM)。PDM更好的保证了Java平台的安全性,在该机制中,JVM自带的Bootstrap是根加载器,其他的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载。JVM不会向Java程序提供对Bootstrap的引用。下面是关于几个类加载器的说明:

Bootstrap:一般用本地代码实现,负责加载JVM基础核心类库(rt.jar);负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被 虚拟机认可(按文件名识别,如rt.jar)

Extension:从java.ext.dirs系统属性所指定的目录中加载类库,它的父加载器是Bootstrap; 负责加载 JAVA_HOME\lib\ext 目录中的,或通过java.ext.dirs系统变量指定路径中的类 库。

System:又叫应用类加载器,其父类是Extension。它是应用最广泛的类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中记载类,是用户自定义加载器的默认父加载器。负责加载用户路径(classpath)上的类库。
Java面试考题集锦之Java基础_第24张图片

双亲委派?

当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父 类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中, 只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的 Class),子类加载器才会尝试自己去加载。
采用双亲委派的一个好处是比如加载位于 rt.jar 包中的类 java.lang.Object,不管是哪个加载 器加载这个类,终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载 器终得到的都是同样一个Object对象。
Java面试考题集锦之Java基础_第25张图片

Minor GC和Full GC的触发时机

年轻代存储“新生对象”,我们新创建的对象存储在年轻代中。当年轻内存占满后,会触发Minor GC,清理年轻代内存空间。

老年代存储长期存活的对象和大对象。年轻代中存储的对象,经过多次GC后仍然存活的对象会移动到老年代中进行存储。老年代空间占满后,会触发Full GC。

注:Full GC是清理整个堆空间,包括年轻代和老年代。如果Full GC之后,堆中仍然无法存储对象,就会抛出OutOfMemoryError异常。

  • Minor GC触发条件:当Eden区满时,触发Minor GC。
  • Full GC触发条件:
    (1)调用System.gc时,系统建议执行Full GC,但是不必然执行
    (2)老年代空间不足
    (3)方法去空间不足
    (4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存
    (5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

常见

JVM 调优?

调整年轻代、年老代的内存空间大小及使用GC发生器的类型
工具:
Jconsole,jProfile,VisualVM
Jconsole : jdk自带,功能简单,但是可以在系统有一定负荷的情况下使用。对垃圾回收算法有很详细的跟踪。
JProfiler:商业软件,需要付费。功能强大。
VisualVM:JDK自带,功能强大,与JProfiler类似。推荐

垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
  1. 基本原理(对象引用遍历方式):
    对于GC(垃圾收集)来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。
    通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象。
    通过这种方式确定哪些对象是"可达的",哪些对象是"不可达的"。
    当GC确定一些对象为"不可达"时,GC就有责任回收这些内存空间。

  2. 垃圾回收器不可以马上回收内存。
    垃圾收集器不可以被强制执行,但程序员可以通过调研System.gc方法来建议执行垃圾收集。
    记住,只是建议。一般不建议自己写System.gc,因为会加大垃圾收集工作量

  3. 程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行

java中会存在内存泄漏吗,请简单描述。

这部分引用https://blog.csdn.net/qq_41033290/article/details/85265449

论上来说,Java是有GC垃圾回收机制的,也就是说,不再被使用的对象,会被GC自动回收掉,自动从内存中清除。

但是,即使这样,Java也还是存在着内存泄漏的情况,
1、长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露。

尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是Java中内存泄露的发生场景,通俗地说,就是程序员可能创建了一个对象,以后一直不再使用这个对象,这个对象却一直被引用,即这个对象无用但是却无法被垃圾回收器回收的,这就是Java中可能出现内存泄露的情况,例如,缓存系统,我们加载了一个对象放在缓存中(例如放在一个全局map对象中),然后一直不再使用它,这个对象一直被缓存引用,但却不再被使用。

如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持久外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露。

2、当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,造成内存泄露

内存泄漏会导致什么?

没有及时的把不用的内存释放或根本没有释放该内存,造成资源的浪费,表现为运行速度变慢,甚至系统的崩溃

内存泄漏检查

参考https://www.cnblogs.com/andy-zhou/p/5327288.html#_caption_24

年老代堆空间被占满
异常: java.lang.OutOfMemoryError: Java heap space

持久代被占满
异常:java.lang.OutOfMemoryError: PermGen space
Perm空间被占满。无法为新的class分配存储空间而引发的异常。这个异常以前是没有的,但是在Java反射大量使用的今天这个异常比较常见了。主要原因就是大量动态反射生成的类不断被加载,最终导致Perm区被占满。

更可怕的是,不同的classLoader即便使用了相同的类,但是都会对其进行加载,相当于同一个东西,如果有N个classLoader那么他将会被加载N次。因此,某些情况下,这个问题基本视为无解。当然,存在大量classLoader和大量反射类的情况其实也不多。
解决:

-XX:MaxPermSize=16m
换用JDK。比如JRocket

堆栈溢出
异常:java.lang.StackOverflowError
说明:这个就不多说了,一般就是递归没返回,或者循环调用造成

线程堆栈满
异常:Fatal: Stack size too small
说明:java中一个线程的空间大小是有限制的。JDK5.0以后这个值是1M。与这个线程相关的数据将会保存在其中。但是当线程空间满了以后,将会出现上面异常。
解决:增加线程栈大小。-Xss2m。但这个配置无法解决根本问题,还要看代码部分是否有造成泄漏的部分。

系统内存被占满
异常:java.lang.OutOfMemoryError: unable to create new native thread
说明:
这个异常是由于操作系统没有足够的资源来产生这个线程造成的。系统创建线程时,除了要在Java堆中分配内存外,操作系统本身也需要分配资源来创建线程。因此,当线程数量大到一定程度以后,堆中或许还有空间,但是操作系统分配不出资源来了,就出现这个异常了。

分配给Java虚拟机的内存愈多,系统剩余的资源就越少,因此,当系统内存固定时,分配给Java虚拟机的内存越多,那么,系统总共能够产生的线程也就越少,两者成反比的关系。同时,可以通过修改-Xss来减少分配给单个线程的空间,也可以增加系统总共内生产的线程数。

解决:
重新设计系统减少线程数量。
线程数量不能减少的情况下,通过-Xss减小单个线程大小。以便能生产更多的线程

少见

CGLib
JVM的作用?

JVM是Java字节码执行的引擎,为Java程序的执行提供必要的支持,它还能优化Java字节码,使之转换成效率更高的机器指令。程序员编写的程序最终都要在 JVM 上执行,JVM 中类的装载是由类加载器(ClassLoader)和它的子类来实现的。ClassLoader是Java运行时一个重要的系统组件,负责在运行时查找和装入类文件的类。
JVM屏蔽了与具体操作系统平台相关的信息,从而实现了Java程序只需生成在JVM上运行的字节码文件(class 文件),就可以在多种平台上不加修改地运行。不同平台对应着不同的JVM,在执行字节码时,JVM负责将每一条要执行的字节码送给解释器,解释器再将其翻译成特定平台环境的机器指令并执行。Java语言最重要的特点就是跨平台运行,使用JVM就是为了支持与操作系统无关,实现跨平台运行



Java 并发编程

高频

什么是线程?

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成一个任务只需10毫秒。Java在语言层面对多线程提供了卓越的支持,它也是一个很好的卖点。

线程和进程有什么区别?

线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。

Java中sleep方法和wait方法的区别?

两个方法区别在于:
sleep()是Thread类的,而wait()是Object类的;
sleep是睡眠,指定时间后线程会继续执行,不放弃对cpu资源的占用(即不放弃对象锁),相当于暂停指定t;
wait()是等待,需要唤醒,它会释放对cpu资源的占用(即会放弃对象锁),进入等待此对象的等待锁定池,调用notify()和notifyAll()唤醒(本线程才进入对象锁定池准备获取对象锁进入运行状态)

Thread 类中的start() 和 run() 方法有什么区别?

这个问题经常被问到,但还是能从此区分出面试者对Java线程模型的理解程度。start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用执行,没有新的线程启动,start()方法才会启动新线程。

  1. start()方法来启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕, 可以直接继续执行下面的代码。
  2. 通过调用 Thread 类的 start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运 行。
  3. 方法 run()称为线程体,它包含了要执行的这个线程的内容,线程就进入了运行状态,开始运 行run函数当中的代码。 Run方法运行结束, 此线程终止。然后CPU再调度其它线程
终止线程4种方式?
  1. 使用标志退出
    一般 run()方法执行完,线程就会正常结束,然而,常常有些线程是伺服线程。它们需要长时间的 运行,只有在外部某些条件满足的情况下,才能关闭这些线程。使用一个变量来控制循环,例如: 直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while 循环是否退出,代码示例:
    Java面试考题集锦之Java基础_第26张图片
    定义了一个退出标志exit,当exit为true时,while循环退出,exit的默认值为false.在定义exit 时,使用了一个 Java 关键字 volatile,这个关键字的目的是使 exit 同步,也就是说在同一时刻只 能由一个线程来修改exit的值。

2.使用interrupt()方法来中断线程有两种情况:

  • 线程处于阻塞状态:如使用了 sleep,同步锁的 wait,socket 中的 receiver,accept 等方法时, 会使线程处于阻塞状态。当调用线程的 interrupt()方法时,会抛出 InterruptException 异常。 阻塞中的那个方法抛出这个异常,通过代码捕获该异常,然后 break 跳出循环状态,从而让 我们有机会结束这个线程的执行。通常很多人认为只要调用 interrupt 方法线程就会结束,实 际上是错的, 一定要先捕获InterruptedException异常之后通过break来跳出循环,才能正 常结束run方法。
  • 线程未处于阻塞状态:使用isInterrupted()判断线程的中断标志来退出循环。当使用 interrupt()方法时,中断标志就会置true,和使用自定义的标志来控制循环是一样的道理。
    Java面试考题集锦之Java基础_第27张图片
  1. stop方法
    程序中可以直接使用thread.stop()来强行终止线程,但是stop方法是很危险的,就象突然关 闭计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果,不安全主要是: thread.stop()调用之后,创建子线程的线程就会抛出 ThreadDeatherror 的错误,并且会释放子 线程所持有的所有锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用 thread.stop()后导致了该线程所持有的所有锁的突然释放(不可控制),那么被保护数据就有可能呈 现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。因 此,并不推荐使用stop方法来终止线程。
什么是线程池? 为什么要使用它?

创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。

种类:缓存线程池(可变尺寸的线程池)、固定大小的线程池、调度线程池、单例线程池、自定义线程池

  1. newCachedThreadPool
  2. newFixedThreadPool
  3. newScheduledThreadPool
  4. newSingleThreadExecutor
线程池原理?

线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后 启动这些任务,如果线程数量超过了大数量超出数量的线程排队等候,等其它线程执行完毕, 再从队列中取出任务来执行。
主要特点为:线程复用;控制大并发数;管理线程。

  • 线程复用:每一个 Thread 的类都有一个 start 方法。 当调用start启动线程时Java虚拟机会调用该类的 run 方法。 那么该类的 run() 方法中就是调用了 Runnable 对象的 run() 方法。 我们可以继承重写 Thread 类,在其 start 方法中添加不断循环调用传递过来的 Runnable 对象。 这就是线程池的实 现原理。循环方法中不断获取 Runnable 是用 Queue 实现的,在获取下一个 Runnable 之前可以 是阻塞的。
Java线程池工作过程 ?
  1. 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面 有任务,线程池也不会马上执行它们。

  2. 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
    a) 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
    b) 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
    c) 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要 创建非核心线程立刻运行这个任务;
    d) 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池 会抛出异常RejectExecutionException。

  3. 当一个线程完成任务时,它会从队列中取下一个任务来执行。

  4. 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运 行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它 终会收缩到 corePoolSize 的大小

什么是ThreadLocal?

ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,ThreadLocal 的作用 是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或 者组件之间一些公共变量的传递的复杂度。
线程范围内的共享变量,每个线程只能访问他自己的,不能访问别的线程。

Java中Runnable和Callable有什么不同?

Runnable和Callable代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增加的。它们的主要区别是Callable的 call() 方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象

提交任务时,线程池队列已满。会发生什么?(工作过程、拒绝策略)

这个问题问得很狡猾,许多程序员会认为该任务会阻塞直到线程池队列有空位。事实上如果一个任务不能被调度执行那么ThreadPoolExecutor’s submit()方法将会抛出一个RejectedExecutionException异常

乐观锁与悲观锁?
  • 乐观锁:认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数 据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新), 如果失败则要重复读-比较-写的操作。
    java 中的乐观锁基本都是通过 CAS 操作实现的,CAS 是一种更新的原子操作,比较当前值跟传入 值是否一样,一样则更新,否则失败。

  • 悲观锁:认为写多,遇到并发写的可能性高,每次去拿数据的时候都会上锁,这样别人想读写这个数据就会block直到拿到锁。 java中的悲观锁就是Synchronized,AQS框架下的锁则是先尝试cas乐观锁去获取锁,获取不到, 才会转换为悲观锁,如RetreenLock

同步锁与死锁?
  • 同步锁:当多个线程同时访问同一个数据时,很容易出现问题。为了避免这种情况出现,我们要保证线程 同步互斥,就是指并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。 Java 中可 以使用synchronized关键字来取得一个对象的同步锁。

  • 死锁:就是多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放

共享锁与独占锁?
  • 共享锁:共享锁则允许多个线程同时获取锁,并发访问 共享资源,如:ReadWriteLock。共享锁则是一种 乐观锁,它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。
  • 独占锁:独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock 就是以独占方式实现的互斥锁。 独占锁是一种悲观保守的加锁策略,它避免了读/读冲突,如果某个只读线程获取锁,则其他读线 程都只能等待,这种情况下就限制了不必要的并发性,因为读操作并不会影响数据的一致性

常见

Java中如何停止一个线程?
线程基本方法?

线程相关的基本方法有wait,notify,notifyAll,sleep,join,yield等。

wait():调用该方法的线程进入WAITING 状态,只有等待另外线程的通知或被中断才会返回,需要注意的 是调用wait()方法后,会释放对象的锁。因此,wait方法一般用在同步方法或同步代码块中。

sleep(): 导致当前线程休眠,与 wait 方法不同的是 sleep 不会释放当前占有的锁,sleep(long)会导致 线程进入TIMED-WATING状态,而wait()方法会导致当前线程进入WATING状态

yield(): 会使当前线程让出 CPU 执行时间片,与其他线程一起重新竞争 CPU 时间片。一般情况下, 优先级高的线程有更大的可能性成功竞争得到 CPU 时间片,但这又不是绝对的,有的操作系统对 线程优先级并不敏感

interrupt():中断一个线程,其本意是给这个线程一个通知信号,会影响这个线程内部的一个中断标识位。这 个线程本身并不会因此而改变状态(如阻塞,终止等)。

  1. 调用 interrupt()方法并不会中断一个正在运行的线程。也就是说处于 Running 状态的线 程并不会因为被中断而被终止,仅仅改变了内部维护的中断标识位而已。
  2. 若调用 sleep()而使线程处于 TIMED-WATING 状态,这时调用 interrupt()方法,会抛出 InterruptedException,从而使线程提前结束TIMED-WATING状态。
  3. 许多声明抛出InterruptedException的方法(如Thread.sleep(long mills方法)),抛出异 常前,都会清除中断标识位,所以抛出异常后,调用 isInterrupted()方法将会返回 false。
  4. 中断状态是线程固有的一个标识位,可以通过此标识位安全的终止线程。比如,你想终止 一个线程thread的时候,可以调用thread.interrupt()方法,在线程的run方法内部可以 根据thread.isInterrupted()的值来优雅的终止线程。

notify() :Object 类中的 notify() 方法,唤醒在此对象监视器上等待的单个线程,如果所有线程都在此对象 上等待,则会选择唤醒其中一个线程,选择是任意的,并在对实现做出决定时发生,线程通过调 用其中一个 wait() 方法,在对象的监视器上等待,直到当前的线程放弃此对象上的锁定,才能继 续执行被唤醒的线程,被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞 争。类似的方法还有 notifyAll() ,唤醒再次监视器上等待的所有线程

synchronized?

底层实现:
进入时,执行 monitorenter,将计数器 +1,释放锁monitorexit 时,计数器-1;
当一个线程判断到计数器为 0 时,则当前锁空闲,可以占用;反之,当前线程进入等待状态。

含义:(monitor 机制)
Synchronized 是在加锁,加对象锁。对象锁是一种重量锁(monitor),synchronized 的锁机制会根据线程竞争情况在运行时会有偏向锁(单一线程)、轻量锁(多个线程访问 synchronized 区域)、对象锁(重量锁,多个线程存在竞争的情况)、自旋锁等。
该关键字是一个几种锁的封装。

volatile?

概念:Java语言提供了一种稍弱的同步机制(比 sychronized更轻量级的同步锁),即volatile变量,用来确保将变量的更新操作通知到其他 线程。volatile 变量具备两种特性,volatile变量不会被缓存在寄存器或者对其他处理器不可见的 地方,因此在读取volatile类型的变量时总会返回新写入的值。

功能:

  1. 变量可见性:保证该变量对所有线程可见,这里的可见性指的是当一个线程修改了变量的值,那么新的 值对于其他线程是可以立即获取的。
  2. 禁止 JVM 进行的指令重排序。
  3. 访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一 种比sychronized关键字更轻量级的同步机制。volatile适合这种场景:一个变量被多个线程共 享,线程直接给这个变量赋值。

原理:

  • 当对非 volatile 变量进行读写的时候,每个线程先从内存拷贝变量到CPU缓存中。如果计算机有 多个CPU,每个线程可能在不同的CPU上被处理,这意味着每个线程可以拷贝到不同的 CPU cache 中。而声明变量是 volatile 的,JVM 保证了每次读变量都从内存中读,跳过 CPU cache 这一步。

适用场景:
对volatile变量的单次读/写操作可以保证原子性的,如long和double类型变量, 但是并不能保证i++这种操作的原子性,因为本质上i++是读、写两次操作。在某些场景下可以代替Synchronized。但是, volatile的不能完全取代Synchronized的位置,只有在一些特殊的场景下,才能适用volatile。
总的来说,必须同时满足下面两个条件才能保证在并发环境的线程安 全:

  1. 对变量的写操作不依赖于当前值(比如 i++),或者说是单纯的变量赋值(boolean flag = true)。
  2. 该变量没有包含在具有其他变量的不变式中,也就是说,不同的volatile变量之间,不 能互相依赖。只有在状态真正独立于程序内其他内容时才能使用 volatile。
如何在Java中实现线程?
  1. 继承Thread类:Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方 法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线 程,并执行run()方法。
    Java面试考题集锦之Java基础_第28张图片
  2. 实现Runnable:如果类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个 Runnable接口。
    Java面试考题集锦之Java基础_第29张图片

以下两种可能不算,因为本质也是实现接口

  1. ExecutorService、Callable、Future有返回值线程:有返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。执行 Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务 返回的Object了,再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了
  2. 基于线程池的方式
    Java面试考题集锦之Java基础_第30张图片
线程池的组成?
  1. 线程池管理器:用于创建并管理线程池
  2. 工作线程:线程池中的线程
  3. 任务接口:每个任务必须实现的接口,用于工作线程调度其运行
  4. 任务队列:用于存放待处理的任务,提供一种缓冲机制
线程池,threadpool有哪些重要的参数?

Java 中的线程池是通过 Executor 框架实现的,该框架中用到了 Executor,Executors, ExecutorService,ThreadPoolExecutor ,Callable和Future、FutureTask这几个类。
ThreadPoolExecutor的构造方法如下:
Java面试考题集锦之Java基础_第31张图片

  1. corePoolSize:指定了线程池中的线程数量。
  2. maximumPoolSize:指定了线程池中的大线程数量。
  3. keepAliveTime:当前线程池数量超过corePoolSize时,多余的空闲线程的存活时间,即多 次时间内会被销毁。
  4. unit:keepAliveTime的单位。
  5. workQueue:任务队列,被提交但尚未被执行的任务。
  6. threadFactory:线程工厂,用于创建线程,一般用默认的即可。
  7. handler:拒绝策略,当任务太多来不及处理,如何拒绝任务。
拒绝策略 ?

线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列也已经排满了,再也 塞不下新任务了。这时候我们就需要拒绝策略机制合理的处理这个问题。 JDK内置的拒绝策略如下:

  1. AbortPolicy : 直接抛出异常,阻止系统正常运行。
  2. CallerRunsPolicy : 只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的 任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。
  3. DiscardOldestPolicy : 丢弃老的一个请求,也就是即将被执行的一个任务,并尝试再 次提交当前任务。
  4. DiscardPolicy : 该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢 失,这是好的一种方案。

以上内置拒绝策略均实现了RejectedExecutionHandler接口,若以上策略仍无法满足实际 需要,完全可以自己扩展RejectedExecutionHandler接口。

JAVA阻塞队列原理?

阻塞队列,关键字是阻塞,先理解阻塞的含义,在阻塞队列中,线程阻塞有这样的两种情况:
Java面试考题集锦之Java基础_第32张图片
Java面试考题集锦之Java基础_第33张图片

Java中的同步集合与并发集合有什么区别?

同步集合与并发集合都为多线程和并发提供了合适的线程安全的集合,不过并发集合的可扩展性更高。在Java1.5之前程序员们只有同步集合来用且在多线程并发的时候会导致争用,阻碍了系统的扩展性。Java5介绍了并发集合像HashMap,不仅提供线程安全还用锁分离和内部分区等现代技术提高了可扩展性

少见

Java中CyclicBarrier 和 CountDownLatch有什么不同?

CyclicBarrier 和 CountDownLatch 都可以用来让一组线程等待其它线程。与 CyclicBarrier 不同的是,CountdownLatch 不能重新使用。

Java中如何停止一个线程?

Java提供了很丰富的API但没有为停止线程提供API。JDK 1.0本来有一些像stop(), suspend() 和 resume()的控制方法但是由于潜在的死锁威胁因此在后续的JDK版本中他们被弃用了,之后Java API的设计者就没有提供一个兼容且线程安全的方法来停止一个线程。当run() 或者 call() 方法执行完的时候线程会自动结束,如果要手动结束一个线程,你可以用volatile 布尔变量来退出run()方法的循环或者是取消任务来中断线程

一个线程运行时发生异常会怎样?

这是我在一次面试中遇到的一个很刁钻的Java面试题, 简单的说,如果异常没有被捕获该线程将会停止执行。Thread.UncaughtExceptionHandler是用于处理未捕获异常造成线程突然中断情况的一个内嵌接口。当一个未捕获异常将造成线程中断的时候JVM会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler并将线程和异常作为参数传递给handler的uncaughtException()方法进行处理

如何在两个线程间共享数据?
Java中notify 和 notifyAll有什么区别?

这又是一个刁钻的问题,因为多线程可以等待单监控锁,Java API 的设计人员提供了一些方法当等待条件改变的时候通知它们,但是这些方法没有完全实现。notify()方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地。而notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行。

Java中interrupted 和 isInterruptedd方法的区别?

interrupted() 和 isInterrupted()的主要区别是前者会将中断状态清除而后者不会。Java多线程的中断机制是用内部标识来实现的,调用Thread.interrupt()来中断一个线程就会设置中断标识为true。当中断线程调用静态方法Thread.interrupted()来检查中断状态时,中断状态会被清零。而非静态方法isInterrupted()用来查询其它线程的中断状态且不会改变中断状态标识。简单的说就是任何抛出InterruptedException异常的方法都会将中断状态清零。无论如何,一个线程的中断状态有有可能被其它线程调用中断来改变。

为什么wait和notify方法要在同步块中调用?

主要是因为Java API强制要求这样做,如果你不这么做,你的代码会抛出IllegalMonitorStateException异常。还有一个原因是为了避免wait和notify之间产生竞态条件。
Java线程池中submit() 和 execute()方法有什么区别?

两个方法都可以向线程池提交任务,execute()方法的返回类型是void,它定义在Executor接口中, 而submit()方法可以返回持有计算结果的Future对象,它定义在ExecutorService接口中,它扩展了Executor接口,其它线程池类像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有这些方法

有哪些不同的线程生命周期?
你对线程优先级的理解是什么?
Sleep()、suspend()和wait()之间有什么区别?
什么是线程饿死,什么是活锁?
什么是Java Timer类?如何创建一个有特定时间间隔的任务?
同步方法和同步块,哪个是更好的选择?
Java中invokeAndWait 和 invokeLater有什么区别?
多线程中的忙循环是什么?


Java IO

Java面试考题集锦之Java基础_第34张图片
Java面试考题集锦之Java基础_第35张图片

讲讲IO里面的常见类,字节流、字符流、接口、实现类、方法阻塞。
讲讲NIO

NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统IO基于字节流和字 符流进行操作,而NIO基于 Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区 中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开, 数据到达)。因此,单个线程可以监听多个数据通道。
Java面试考题集锦之Java基础_第36张图片
Channel和IO中的Stream(流)是差不多一个 等级的。只不过Stream是单向的,譬如:InputStream, OutputStream,而Channel是双向 的,既可以用来进行读操作,又可以用来进行写操作。 NIO中的Channel的主要实现有:

  1. FileChannel
  2. DatagramChannel
  3. SocketChannel
  4. ServerSocketChannel 这里看名字就可以猜出个所以然来:分别可以对应文件IO、UDP和TCP(Server和Client

Buffer,故名思意,缓冲区,实际上是一个容器,是一个连续数组。Channel提供从文件、 网络读取数据的渠道,但是读取或写入的数据都必须经由Buffer

Java面试考题集锦之Java基础_第37张图片
在NIO中,Buffer是一个顶层父类,它是一个抽象类,常用的Buffer的子类有: ByteBuffer、IntBuffer、 CharBuffer、 LongBuffer、 DoubleBuffer、FloatBuffer、 ShortBuffer

Selector类是NIO的核心类,Selector能够检测多个注册的通道上是否有事件发生,如果有事 件发生,便获取事件然后针对每个事件进行相应的响应处理。这样一来,只是用一个单线程就可 以管理多个通道,也就是管理多个连接。这样使得只有在连接真正有读写事件发生时,才会调用 函数来进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护 多个线程,并且避免了多线程之间的上下文切换导致的开销。

String 编码UTF-8 和GBK的区别?

Unicode是国际通用的一种编码标准,它包含着世界上各个国家的所有符号且每一个符号都对应着一个全球唯一的数字。每一个符号在计算机物理层存储的都是二进制,但是有些字符像英文字母只需要用一个字节就能表示,有的符号可能要用很多字节表示,计算机无法知道哪个符号用几个字节表示,要是全部统一用固定字节就会造成大量的浪费,如何解决这个问题?
西方人提出了一种编码方式就是utf-8(utf16/utf32),是变长编码,英文只用一个字节即可,汉字要用三个字节;
针对汉字编码的问题,专门提出了gbk,每个符号用两个字节表示,比utf8的方式占用资源少。
因此,处理的文本主要为中文时最好用gbk编码,英文较多时用utf8编码。
gbk和utf8两种编码之间转换要通过unicode来间接实现。
一个符号可通过unicode码表查到二进制数表示,再将其转化成utf8(gbk)编码的形式存贮;解码时将utf8(gbk)转化成unicode,就可还原符号。

什么时候使用字节流、什么时候使用字符流?
递归读取文件夹下的文件,代码怎么实现

通过file.listFiles()方法获取目录下的所有文件(包含子目录下的所有文件),得到files[]数组,然后遍历得到的所有文件,通过isFile(文件)和isDirectory(文件夹)方法来判断读取的是文件还是文件夹,如果得到的是文件夹,就递归调用test()方法,如果得到的是文件,就将其加入fileList中,最后测试的时候遍历fileList下的所有文件,来验证读取数据的准确性



引用

强引用:在 Java 中常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引 用。当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的,即 使该对象以后永远都不会被用到JVM也不会回收。因此强引用是造成Java内存泄漏的主要原因之 一

软引用需要用 SoftReference 类来实现,对于只有软引用的对象来说,当系统内存足够时它 不会被回收,当系统内存空间不足时它会被回收。软引用通常用在对内存敏感的程序中。

弱引用需要用WeakReference 类来实现,它比软引用的生存期更短,对于只有弱引用的对象 来说,只要垃圾回收机制一运行,不管JVM的内存空间是否足够,总会回收该对象占用的内存

虚引用需要PhantomReference类来实现,它不能单独使用,必须和引用队列联合使用。虚 引用的主要作用是跟踪对象被垃圾回收的状态。



异常

这部分常考的就这两题,欢迎补充;这部分已全部更新完毕。
概念:如果某个方法不能按照正常的途径完成任务,就可以通过另一种路径退出方法。在这种情况下 会抛出一个封装了错误信息的对象。此时,这个方法会立刻退出同时不返回任何值。另外,调用 这个方法的其他代码也无法继续执行,异常处理机制会将代码执行交给异常处理器。
Java面试考题集锦之Java基础_第38张图片

高频

Exception 与 Error?

Error:指java运行时系统的内部错误和资源耗尽错误。应用程序不会抛出该类对象。如果 出现了这样的错误,除了告知用户,剩下的就是尽力使程序安全的终止
Exception: Exception 又有两个分支,一个是 (受检异常)CheckedException,一个是(非受检)运行时异常 RuntimeException 。

  • 受检异常:一般是外部错误,这种异常都发生在编译阶段,Java 编译器会强 制程序去捕获此类异常,即会出现要求你把这段可能出现异常的程序进行 try catch。如: IOException、SQLException
  • 非受检异常:所谓的运行时异常,程序中可以选择捕获处理,也可以不处理。如:NullPointerException、ClassCastException,程序逻辑错误引起

常见

Throw和throws的区别:

Java面试考题集锦之Java基础_第39张图片

  1. throws 用在函数上,后面跟的是异常类,可以跟多个;而 throw 用在函数内,后面跟的 是异常对象。
  2. throws 用来声明异常,让调用者只知道该功能可能出现的问题,可以给出预先的处理方 式;
    throw抛出具体的问题对象,执行到throw,功能就已经结束了,跳转到调用者,并将具体的问题对象抛给调用者。也就是说 throw 语句独立存在时,下面不要定义其他语 句,因为执行不到。
  3. throws 表示出现异常的一种可能性,并不一定会发生这些异常;
    throw 则是抛出了异常, 执行throw则一定抛出了某种异常对象。
  4. 两者都是消极处理异常的方式,只是抛出或者可能抛出异常,但是不会由函数去处理异 常,真正的处理异常由函数的上层调用处理。


反射

这部分其实都不太常考,但还是需要了解一下,这部分已全部更新完毕。

什么是反射?

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

Java反射机制的作用?

应用场合:在Java程序中许多对象在运行是都会出现两种类型:编译时类型和运行时类型。 编译时的类型由 声明对象时实用的类型来决定,运行时的类型由实际赋值给对象的类型决定 。如Person p=new Student();
其中编译时类型为Person,运行时类型为Student

程序在运行时还可能接收到外部传入的对象,该对象的编译时类型为 Object,但是程序有需要调用 该对象的运行时类型的方法。为了解决这些问题,程序需要在运行时发现对象和类的真实信息。 然而,如果编译时根本无法预知该对象和类属于哪些类,程序只能依靠运行时信息来发现该对象 和类的真实信息,此时就必须使用到反射了 。
作用

  1. 在运行时能够判断任意一个对象所属的类
  2. 在运行时构造任意一个类的对象
  3. 在运行时判断任意一个类所具有的成员变量和方法
  4. 在运行时调用任一对象的方法
  5. 在运行时创建新类对象
哪里用到反射机制?
  1. JDBC中,利用反射动态加载了数据库驱动程序。
  2. Web服务器中利用反射调用了Sevlet的服务方法。
  3. Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。
  4. 很多框架都用到反射机制,注入属性,调用方法,如Spring。
  5. 读取配置文件
如何使用Java的反射?

获得Class对象的三种方法
Java面试考题集锦之Java基础_第40张图片
创建对象的两种方法
Java面试考题集锦之Java基础_第41张图片
Java面试考题集锦之Java基础_第42张图片
在这里插入图片描述

反射用到了哪些接口,哪些类?
反射机制中可以获取private成员的值吗?
反射机制的优缺点?
  • 优点:可以动态执行,在运行期间根据业务功能动态执行方法、访问属性,最大限度发挥了java的灵活性。
  • 缺点:对性能有影响,这类操作总是慢于直接执行java代码。

注解

Java的注解没什么考点,考的都是Spring基于java的,这点详细放在Spring中解释

Java中有哪些注解?在SpringMVC中,requestmapping是自定义注解,问:如何实现自定义注解?
JSR-250注解

1 @Resource:自动装配,默认根据类型装配,如果指定name属性将根据名字装配,可以使用如下方式来指定
@Resource(name = “标识符”)
字段或setter方法

2 @PostConstruct和PreDestroy:通过注解指定初始化和销毁方法定义

JSR-330注解

1 @Inject:等价于默认的@Autowired,只是没有required属性
2 @Named:指定Bean名字,对应于Spring自带@Qualifier中的缺省的根据Bean名字注入情况
3 @Qualifier:只对应于Spring自带@Qualifier中的扩展@Qualifier限定描述符注解,即只能扩展使用,没有value属性

JPA注解

@PersistenceContext
@PersistenceUnit用于注入EntityManagerFactory和EntityManager

Spring自带依赖注入注解

1 @Required:依赖检查;

2 @Autowired:自动装配2 自动装配,用于替代基于XML配置的自动装配 基于@Autowired的自动装配,默认是根据类型注入,可以用于构造器、字段、方法注入

3 @Value:注入SpEL表达式 用于注入SpEL表达式,可以放置在字段方法或参数上
@Value(value = “SpEL表达式”)
@Value(value = “#{message}”)
4 @Qualifier:限定描述符,用于细粒度选择候选者
@Qualifier限定描述符除了能根据名字进行注入,但能进行更细粒度的控制如何选择候选者
@Qualifier(value = “限定标识符”)

你可能感兴趣的:(Java)