一、JAVA基础
1、面向对象的三个基本特征?
面向对象的三个基本特征是:封装、继承和多态。
封装:隐藏部分对象的属性和实现细节,对数据的访问只能通过外公开的接口。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。
继承:让某个类型的对象获得另一个类型的对象的属性的方法。继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
多态:对于同一个行为,不同的子类对象具有不同的表现形式。多态存在的3个条件:1)继承;2)重写;3)父类引用指向子类对象。
2、&(按位与)和&&(逻辑与)的区别?
&&:逻辑与运算符。当运算符左右两边的表达式都为 true,才返回 true。同时具有短路性,如果第一个表达式为 false,则直接返回 false。
3、JAVA 8大数据类型
4、String和StringBuilder、StringBuffer的区别?
String:String 的值被创建后不能修改,任何对 String 的修改都会引发新的 String 对象的生成。
StringBuffer:跟 String 类似,但是值可以被修改,使用 synchronized 来保证线程安全。
StringBuilder:StringBuffer 的非线程安全版本,没有使用 synchronized,具有更高的性能,推荐优先使用。
5、== 和 equals 的区别是什么?
==:运算符,用于比较基础类型变量和引用类型变量。对于基础类型变量,比较的变量保存的值是否相同,类型不一定要相同。对于引用类型变量,比较的是两个对象的地址是否相同。
equals:Object 类中定义的方法,通常用于比较两个对象的值是否相等。equals 在 Object 方法中其实等同于 ==,但是在实际的使用中,equals 通常被重写用于比较两个对象的值是否相同。
6、两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?
不对。hashCode() 和 equals() 之间的关系如下:
当有 a.equals(b) == true 时,则 a.hashCode() == b.hashCode() 必然成立,反过来,当 a.hashCode() == b.hashCode() 时,a.equals(b) 不一定为 true。
7、什么是反射?反射的实现方式是哪几种?
反射:反射是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能称为反射机制。反射的原理是通过类加载器将字节码文件加载至内存中,并在堆内存中生成对应的class对象实现方式:Class.forName(String className)className.class实例对象.getClass()
8、重载(Overload)和重写(Override)的区别?
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载:一个类中有多个同名的方法,但是具有有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)。重写:发生在子类与父类之间,子类对父类的方法进行重写,参数都不能改变,返回值类型可以不相同,但是必须是父类返回值的派生类。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。
9、抽象类(abstract class)和接口(interface)有什么区别?
抽象类只能单继承,接口可以多实现。抽象类可以有构造方法,接口中不能有构造方法。抽象类中可以有成员变量,接口中没有成员变量,只能有常量(默认就是 public static final)抽象类中可以包含非抽象的方法,在 Java 7 之前接口中的所有方法都是抽象的,在 Java 8 之后,接口支持非抽象方法:default 方法、静态方法等。Java 9 支持私有方法、私有静态方法。抽象类中的抽象方法类型可以是任意修饰符,Java 8 之前接口中的方法只能是 public 类型,Java 9 支持 private 类型。
10、Error 和 Exception 有什么区别?
Error 和 Exception 都是 Throwable 的子类,用于表示程序出现了不正常的情况。区别在于:Error 表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难的情况下的一种严重问题,比如内存溢出,不可能指望程序能处理这样的情况。Exception 表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题,也就是说,它表示如果程序运行正常,从不会发生的情况。
11、JDK1.8新特性
接口默认方法:Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可
Lambda 表达式和函数式接口:Lambda 表达式本质上是一段匿名内部类,也可以是一段可以传递的代码。Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中),使用 Lambda 表达式使代码更加简洁,但是也不要滥用,否则会有可读性等问题,《Effective Java》作者 Josh Bloch 建议使用 Lambda 表达式最好不要超过3行。
Stream API:用函数式编程方式在集合类上进行复杂操作的工具,配合Lambda表达式可以方便的对集合进行处理。Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
方法引用:方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
日期时间API:Java 8 引入了新的日期时间API改进了日期时间的管理。
Optional 类:著名的 NullPointerException 是引起系统失败最常见的原因。很久以前 Google Guava 项目引入了 Optional 作为解决空指针异常的一种方式,不赞成代码被 null 检查的代码污染,期望程序员写整洁的代码。受Google Guava的鼓励,Optional 现在是Java 8库的一部分。
新工具:新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器 jdeps。
12、java创建对象有哪几种方式
java中提供了以下四种创建对象的方式:
new创建新对象
通过反射机制
采用clone机制
通过序列化机制
13、final有哪些用法?
被final修饰的类不可以被继承
被final修饰的方法不可以被重写
被final修饰的变量不可以被改变.如果修饰引用,那么表示引用不可变,引用指向的内容可变.
被final修饰的方法,JVM会尝试将其内联,以提高运行效率
被final修饰的常量,在编译阶段会存入常量池中
14、java中的IO流
按流向分为:输入(InputStream、Writer)和输出流(OutputStream、Reader)
按操作单元分为:字节流(InputStream、OutputStream)和字符流(Writer、Reader)
15、为什么已经有了数组了还要搞个 ArrayList 呢?
因为数组的长度是固定的,而ArrayList可以自动扩容。可以把ArrayList理解为动态数组。
16、JDK 和 JRE 有什么区别?
JDK可以理解为java开发工具包工具包,JDK包含JRE。而JRE为java运行时的环境
17、String str="i"与 String str=new String("i")一样吗?
不一样String str="i",java虚拟机会将i放入常量池中,如果i已经存在,则将str指向i的地址,若不存在,则在常量池中新增。String str=new String("i")就是一个标准的对象创建方式,创建后,java虚拟机会直接放入堆内存中。
二、集合
1、List,Set,Map三者的区别?
List(对付顺序的好帮手): List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象
Set(注重独一无二的性质): 不允许重复的集合。不会有多个元素引用相同的对象。
Map(用Key来搜索的专家): 使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。
2、ArrayList 和 LinkedList 的区别?
ArrayList 底层基于动态数组实现(基于索引,查询快增删慢)LinkedList 底层基于链表实现(基于列表,增删快查询慢)
3、介绍下 HashMap 的底层数据结构
我们现在用的都是 JDK 1.8,底层是由“数组+链表+红黑树”组成,如下图,而在 JDK 1.8 之前是由“数组+链表”组成。
4、为什么要改成“数组+链表+红黑树”?
主要是为了提升在 hash 冲突严重时(链表过长)的查找性能
5、HashMap和HashTable的区别
1)两者父类不同HashMap是继承自AbstractMapHashtable是继承自Dictionary类
2)对外提供的接口不同Hashtable比HashMap多提供了elments() 和contains() 两个方法。
3)对null的支持不同Hashtable:key和value都不能为null。HashMap:key可以为null,但只能有一个,value可以为null。
4)安全性不同HashMap是线程不安全的,在多线程并发的环境下,可能会产生死锁等问题,因此需要开发人员自己处理多线程的安全问题。Hashtable是线程安全的,它的每个方法上都有synchronized 关键字。
5)初始容量大小和每次扩充容量大小不同6)计算hash值的方法不同
6、Collection包结构,与Collections的区别
Collection是所有集合的上层接口类,而Collections是集合的工具类
三、JVM
1、Java 内存结构
程序计数器:线程私有。一块较小的内存空间,可以看作当前线程所执行的字节码的行号指示器。如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器值则为空。
Java虚拟机栈:线程私有。它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
本地方法栈:线程私有。本地方法栈与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。
Java堆:线程共享。对大多数应用来说,Java堆是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
方法区:与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息(构造方法、接口定义)、常量、静态变量、即时编译器编译后的代码(字节码)等数据。方法区是JVM规范中定义的一个概念,具体放在哪里,不同的实现可以放在不同的地方。
运行时常量池:运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
2、我们平常NEW的对象是放在哪个区域中的?
堆内存
3、本地方法栈什么是本地方法栈?
本地方法栈 (Native Method Stacks),是指 Java 虚拟机用于处理本地方法执行的内存模型。本地方法指的是用 Java 之外的语言(如 C、C++)编写的方法,这些方法不在 Java 虚拟机栈中执行,而是在本地方法栈中执行。本地方法栈也是线程私有的,生命周期与线程相同。
4、为什么需要本地方法栈?
有时候,Java 的操作并不能满足我们的需求,或者说执行效率较低,这时候我们往往需要调用系统级别的库来执行一些任务。这些系统级别的库通常是基于 C 或者 C++实现的,这时候就需要通过 JNI(Java Native Interface,Java 本地接口)来调用这些本地方法。这种情况下,就需要本地方法栈来管理这些本地方法的执行状态。
5.本地方法栈的实现原理?
本地方法栈与虚拟机栈类似,它们之间的区别不过是虚拟机栈为虚拟机执行 Java 方法(即字节码)服务,而本地方法栈则为虚拟机使用到的本地方法服务。在 HotSpot 虚拟机中,并不区分虚拟机栈和本地方法栈这两个内存区域,统一用虚拟机栈实现。具体实现细节会因为虚拟机设计的不同而有所变化。本地方法栈的使用示例因为本地方法栈涉及到本地方法的调用,所以代码示例通常包含 JNI 的使用。
6. 本地方法栈的优点
本地方法栈为 Java 提供了更广泛的操作系统 API 调用,增强了 Java 的功能。同时,本地方法频繁执行的情况下,也可以提高性能。本地方法栈的缺点虽然本地方法栈为 Java 提供了扩展性,但也增加了开发的复杂性和潜在的风险。例如,必须手动管理本地代码里面的内存分配和释放,而且可能引入与平台相关的代码,降低 Java 的跨平台性。本地方法栈的使用注意事项合理使用本地方法: 本地方法并不能沉痛解决所有问题,而且本地方法的代码通常比 Java 代码更难维护。因此,只有在必要的时候才考虑使用本地方法。确保线程安全: 本地方法的执行不受 Java 内存模型的限制,要注意在多线程情况下共享数据的安全性。小心内存泄漏: 本地方法中应用动态内存分配要自己管理,不当的操作可能会导致内存泄漏。总结本地方法栈是 Java 虚拟机用于处理本地方法执行的内存模型。它扩展了 Java 的能力,但是和虚拟机栈相比,其使用和管理更为复杂。因此在使用本地方法和本地方法栈时,应当多加注意,合理使用,确保线程安全,并注意可能的内存泄漏问题。
7、Java虚拟机中有哪些类加载器?
启动类加载器
扩展类加载器
应用程序类加载器
8、什么是双亲委派模型?
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
意义:通过委派的方式,可以避免类的重复加载,当父加载器已经加载过某一个类时,子加载器就不会再重新加载这个类。
9、类加载的过程
类加载的过程包括:加载、验证、准备、解析、初始化,其中验证、准备、解析统称为连接。
加载:通过一个类的全限定名来获取定义此类的二进制字节流,在内存中生成一个代表这个类的java.lang.Class对象。
验证:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
准备:为静态变量分配内存并设置静态变量初始值,这里所说的初始值“通常情况”下是数据类型的零值。
解析:将常量池内的符号引用替换为直接引用。
初始化:到了初始化阶段,才真正开始执行类中定义的 Java 初始化程序代码。主要是静态变量赋值动作和静态语句块(static{})中的语句。
10、深拷贝和浅拷贝的区别
浅拷贝:浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝:深拷贝会另外创建一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
11、什么是垃圾?
在 JVM 进行垃圾回收之前,首先就是判断哪些对象是垃圾,也就是说,要判断哪些对象是可以被销毁的,其占有的空间是可以被回收的。根据 JVM 的架构划分,我们知道, 在 Java 世界中,几乎所有的对象实例都在堆中存放,所以垃圾回收也主要是针对堆来进行的。在 JVM 的眼中,垃圾就是指那些在堆中存在的,已经“死亡”的对象。而对于“死亡”的定义,我们可以简单的将其理解为“不可能再被任何途径使用的对象”。那怎样才能确定一个对象是存活还是死亡呢?这就涉及到了垃圾判断算法,其主要包括引用计数法和可达性分析法。
12、怎么判断是不是垃圾?
引用计数法:在这种算法中,假设堆中每个对象(不是引用)都有一个引用计数器。当一个对象被创建并且初始化赋值后,该对象的计数器的值就设置为 1,每当有一个地方引用它时,计数器的值就加 1,例如将对象 b 赋值给对象 a,那么 b 被引用,则将 b 引用对象的计数器累加 1。反之,当引用失效时,例如一个对象的某个引用超过了生命周期(出作用域后)或者被设置为一个新值时,则之前被引用的对象的计数器的值就减 1。而那些引用计数为 0 的对象,就可以称之为垃圾,可以被收集。特别地,当一个对象被当做垃圾收集时,它引用的任何对象的计数器的值都减 1。
优点:引用计数法实现起来比较简单,对程序不被长时间打断的实时环境比较有利。
缺点:需要额外的空间来存储计数器,难以检测出对象之间的循环引用。
可达性分析法:可达性分析法也被称之为根搜索法,可达性是指,如果一个对象会被至少一个在程序中的变量通过直接或间接的方式被其他可达的对象引用,则称该对象就是可达的。更准确的说,一个对象只有满足下述两个条件之一,就会被判断为可达的:对象是属于根集中的对象对象被一个可达的对象引用可达性分析法的基本思路是:将一系列的根对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,如果一个对象到根对象没有任何引用链相连,那么这个对象就不是可达的,也称之为不可达对象。
优点:可以解决循环引用的问题,不需要占用额外的空间
缺点:多线程场景下,其他线程可能会更新已经访问过的对象的引用
13、Java 中有四种引用类型
强引用(Strong Reference):如Object obj = new Object(),这类引用是 Java 程序中最普遍的。只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象。
软引用(Soft Reference):它用来描述一些可能还有用,但并非必须的对象。在系统内存不够用时,这类引用关联的对象将被垃圾收集器回收。JDK1.2 之后提供了SoftReference类来实现软引用。
弱引用(Weak Reference):它也是用来描述非必须对象的,但它的强度比软引用更弱些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在 JDK1.2 之后,提供了WeakReference类来实现弱引用。
虚引用(Phantom Reference):也称为幻引用,最弱的一种引用关系,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的是希望能在这个对象被收集器回收时收到一个系统通知。JDK1.2 之后提供了PhantomReference类来实现虚引用。
14、介绍下垃圾回收机制
垃圾回收算法,主要分为标记清除算法、标记整理算法、分代算法、复制算法,而分代算法中主要分为新生代和老年代,新生代中又分为Eden和Survivor区,Survivor区又分为from和to区,Eden和from、to的比例为8:1:1。大部分对象都在Eden区生成,当产生一个新对象时,如果因为内存空间不足导致申请Eden区的空间失败,则会触发一次GC,回收时会先将Eden区中所存活的对象运用复制算法将其复制到from区,然后再清空Eden区,当from区也满了以后,则将Eden区和from区中存活的对象复制到to区,随后再清空Eden和from区,此时from区是空的,然后将to和from区进行交换,即保持to区为空,如此反复,当to区不足以存放Eden和from区的存活对象时,就将存活对象直接放入老年代。当对象在Survivor区躲过一次GC时,其对象的年龄会+1,默认情况下,当对象的年龄达到15岁时,就会移至老年代中。若是老年代也满了,则会触发一次FULL GC,运用标记清除和标记整理算法同时回收新生代和老年代。在新生代中经历了 N 次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。内存比新生代也大很多(大概比例是 1:2),当老年代内存满时触发 Major GC 即 Full GC,Full GC 发生频率比较低,老年代对象存活时间比较长,存活率高。一般来说,大对象会被直接分配到老年代。所谓的大对象是指需要大量连续存储空间的对象,最常见的一种大对象就是大数组。当然分配的规则并不是百分之百固定的,这要取决于当前使用的是哪种垃圾收集器组合和 JVM 的相关参数。永久代(Permanent Generation):用于存放静态文件(class类、方法)和常量等。对永久代的回收主要回收两部分内容:废弃常量和无用的类。,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。永久代在 Java SE8 特性中已经被移除了,取而代之的是元空间(MetaSpace),因此也不会再出现java.lang.OutOfMemoryError: PermGen error的错误了。
四、多线程
1、编写多线程有哪几种实现方式?
通常来说,可以认为有三种方式:
1)继承 Thread 类;
2)实现 Runnable 接口;
3)实现 Callable 接口。
其中,Thread 其实也是实现了 Runable 接口。Runnable 和 Callable 的主要区别在于是否有返回值,Runnable是没有返回值的,而Callable有返回值。
2、线程的生命周期
新建状态:使用new关键字创建一个线程后,该线程就处于新建状态
就绪状态:当线程对象调用了start()方法之后,该线程就进入了就绪状态,等待获取cpu的执行片
运行状态:当线程对象获取了cpu执行权以后,开始运行run()方法,则该线程就进入了运行状态
阻塞状态:阻塞状态又分为同步阻塞、等待阻塞和其他阻塞
等待阻塞:执行wait()方法,JVM会把该线程放入等待队列
同步阻塞:在获取对象的同步锁时,若该同步锁已被别的线程获取,则JVM会把当前线程放入锁池中
其他阻塞:执行sleep()或join()方法,jvm会把该线程置为阻塞状态,
死亡状态:正常run()方法结束、程序抛出excption或error、调用stop方法来结束线程(该方法容易导致死锁,不推荐使用),就会进入死亡状态
3、线程的基本方法
sleep:当调用 Thread.sleep(long millis) 睡眠方法时,就会使当前线程进入阻塞状态。millis参数指定了线程睡眠的时间,单位是毫秒。当时间结束之后,线程会重新进入就绪状态。
wait、notify和notifyAll:调用线程的wait方法会使当前线程等待,直到其它线程调用此对象的notify/notifyAll方法。如果,当前对象锁有N个线程在等待,则notify方法会随机唤醒其中一个线程,而notifyAll会唤醒对象锁中所有的线程。需要注意,唤醒时,不会立马释放锁,只有当前线程执行完之后,才会把锁释放。
Join:当线程调用另外一个线程的join方法时,当前线程就会进入阻塞状态。直到另外一个线程执行完毕,当前线程才会由阻塞状态转为就绪状态。
Yield:Thread.yield 方法会使当前线程放弃CPU时间片,把执行机会让给相同或更高优先级的线程(yield英文意思就是屈服,放弃的意思嘛,可以理解为当前线程暂时屈服于别人了)。注意,此时当前线程不会阻塞,只是进入了就绪状态,随时可以再次获得CPU时间片,从而进入运行状态。也就是说,其实yield方法,并不能保证,其它相同或更高优先级的线程一定会获得执行权,也有可能,再次被当前线程拿到执行权。
Interrupt:线程中断
isAlive:判断线程是否存活
4、Thread 调用 start() 方法和调用 run() 方法的区别
run():普通的方法调用,在主线程中执行,不会新建一个线程来执行。
start()新启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到 CPU 时间片,就开始执行 run() 方法。
5、wait() 和 sleep() 方法的区别
来源不同:sleep() 来自 Thread 类,wait() 来自 Object 类。
对于同步锁的影响不同:sleep() 不会该表同步锁的行为,如果当前线程持有同步锁,那么 sleep 是不会让线程释放同步锁的。wait() 会释放同步锁,让其他线程进入 synchronized 代码块执行。
使用范围不同:sleep() 可以在任何地方使用。wait() 只能在同步控制方法或者同步控制块里面使用,否则会抛 IllegalMonitorStateException。
恢复方式不同:两者会暂停当前线程,但是在恢复上不太一样。sleep() 在时间到了之后会重新恢复;wait() 则需要其他线程调用同一对象的 notify()/nofityAll() 才能重新恢复。
6、如何停止一个正在运行的线程
(1)使用stop()来停止线程:stop()方法让线程立即停止运行, 这种暴力停止可能会破坏线程业务的原子性,不推荐使用
(2)使用interrupt产生打断标志位来停止线程而stop和方法的区别就是,打个比方,把线程比作一个word文档,当我们需要关闭文档时,stop的方式就相当于还没有保存文档就直接强行关闭。而如果是interrupt则会先将我们刚刚添加的内容进行保存再来关闭。
7、synchronized 和 Lock 的区别
synchronized是关键字,而lock是接口
synchronized是非公平锁,而lock是可以通过设置成公平或非公平锁
synchronized会自动释放锁,而lock只能手动释放
synchronized没法知道有没有成功获取锁,而lock可以知道synchronized不能让等待的线程响应中断,会让后面的线程一直等下去,而lock可以让等待的线程响应中断
8、synchronized 各种加锁场景的作用范围
1.作用于普通方法时,锁住的是对象实例(this),每一个对象实例有一个锁。public synchronized void method() {}
2.作用于静态方法时,锁住的是类的Class对象,因为Class的相关数据存储在永久代元空间,元空间是全局共享的,因此静态方法锁相当于类的一个全局锁,会锁所有调用该方法的线程。public static synchronized void method() {}
3. synchronized作用于一个代码块时,锁住的是所有代码块中配置的对象synchronized (lock) {}
9、什么是可重入锁?
可重入锁是可以重复进入的锁,比如说当一个线程获取锁对象之后,这个线程可以再次获取本对象上的锁,而其他线程是不可以的。synchronized和lock锁都是可重入锁可重入锁的意思之一在于防止死锁
10、可重入锁的实现原理是怎么样的?
加锁时,需要判断锁是否已经被获取。如果已经被获取,则判断获取锁的线程是否是当前线程。如果是当前线程,则给获取次数加1。如果不是当前线程,则需要等待。释放锁时,需要给锁的获取次数减1,然后判断,次数是否为0了。如果次数为0了,则需要调用锁的唤醒方法,让锁上阻塞的其他线程得到执行的机会
11、volatile 关键字
Java 语言提供了一种稍弱的同步机制,即 volatile 变量,用来确保将变量的更新操作通知到其他线程。volatile 变量具备两种特性,volatile 变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取 volatile 类型的变量时总会返回最新写入的值。
12、ThreadLocal 作用
(线程本地存储)ThreaLocal是线程局部变量,用于保存线程中的数据,threadlocal中保存的数据仅属于当前线程,对于其他线程来说是隔离的,不同的线程之间不会相互干扰。
13、什么是CAS?
CAS是Compare-And-Swap(比较并交换)的缩写,是一种轻量级的同步机制。其中CAS包括内存值、期望值和新值,只有当内存之等于期望值的时候,才会将内存之改为新值。反之如果内存值不等于期望值的话,说明已经有其它线程修改过内存值了,此时CAS操作失败,需要重新尝试。
14、什么是线程池?
线程池的作用是什么?简单看名字的意思就是装满线程的池子,我们可以把要执行的多线程交给线程池来管理,通过维护一定数量的线程池来达到多个线程的复用。如果我们平常一直通过用new Thread()的方式来创建并运行线程的话,线程少的情况下还不会是问题,但是线程多了并达到一定数量以后就会耗尽系统的CPU和内存资源,也会造成GC频繁收集和停顿,因为每次创建和销毁一个线程都是要消耗系统资源的,所以线程池中的线程复用极大的节省了系统资源。
15、线程池7个参数
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
1, // 核心线程数(也就是最小线程数)
2, // 最大线程数
5, // 线程闲置等待时间
TimeUnit.SECONDS, // 线程闲置等待时间单位
new ArrayBlockingQueue<>(200), // 消息队列容量
Executors.defaultThreadFactory(), // threadFactory 线程工厂new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略
16、线程启动线程submit()和execute()的区别
execute()方法主要用于启动任务的执行,任务执行后不会有返回值。
submit()方法也用于启动任务的执行,但是启动之后会返回Future对象,我们可以通过调用Future对象的.get()方法,来判断执行是否成功(结果为null时代表成功,而失败可以catch来捕获异常,从而分析失败原因)
17、interrupted和isInterruptedd方法的区别
Interrupted会清楚中断标记,而isInterruptedd不会
18、如果保证接口的幂等性幂等性
用在接口上可以理解为:同一个接口,多次请求,必须保证只执行一次操作。产生幂等性问题的原因:网络波动可能会造成重复请求、用户重复操作等等
解决方案:前台设置按钮置灰功能,当用户点击一次后短时间内进行置灰,不让其点击token机制,进入页面前获取一次token,后续每次操作都携带token去访问,后台根据token来判断是否为重复请求
唯一索引,利用数据库唯一索引机制,当数据重复时,插入数据库会抛出异常,保证不会出现脏数据。
分布式锁,利用分布式锁来实现判断是否为重复请求
悲观锁,利用sql语句for update来实现悲观锁的功能
五、MYSQL
1、InnoDB和MylSAM主要有什么区别?
存储结构:每个MyISAM在磁盘上存储成三个文件;InnoDB所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB.
事务支持:MyISAM不提供事务支持;InnoDB提供事务支持事务,具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全特性。
最小锁粒度:MyISAM只支持表级锁,更新时会锁住整张表,导致其它查询和更新都会被阻塞,InnoDB支持行级锁。
索引类型:MyISAM的索引为非聚簇索引,数据结构是B+树;InnoDB的索引是聚簇索引,数据结构是B+树。
主键必需:MyISAM 允许没有任何索引和主键的表存在;InnoDB 如果没有设定主键或者非空唯一索引,就会自动生成一个6字节的主键(用户不可见),数据是主索引的一部分,附加索引保存的是主索引的值。
表的具体行数:MyISAM保存了具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全特性。
最小锁粒度:MyISAM只支持表级锁,更新时会锁住整张表,导致其它查询和更新都会被阻塞,InnoDB支持行级锁。
索引类型:MyISAM的索引为非聚簇索引,数据结构是B+树;InnoDB的索引是聚簇索引,数据结构是B+树。
主键必需:MyISAM 允许没有任何索引和主键的表存在;InnoDB 如果没有设定主键或者非空唯一索引,就会自动生成一个6字节的主键(用户不可见),数据是主索引的一部分,附加索引保存的是主索引的值。
表的具体行数:MyISAM保存了表的总行数,如果select count(*) from table;会直接取出该值;InnoDB没有保存表的总行数,如果使用 select count(*) from table,就会遍历整个表;但是在加了wehre 条件后,MylSAM 和InnoDB 处理的方式都一样。
外键支持:MyISAM不支持外键;InnoDB支持外键。
2、事物特性有哪几种?
原子性:事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用
一致性:事务执行前后,数据保持一致,多个事务对同一个数据读取的结果是相同的
隔离性:并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的
持久性:一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
3、索引是什么
是一种高效获取数据的数据结构,相当于目录,更快的找到数据,是一个文件,占用物理空间。
4、索引的分类
普通索引:仅加速查询
唯一索引:加速查询 + 列值唯一(可以有null)主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个
组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
全文索引:对文本的内容进行分词,进行搜索
5、数据库三大范式是什么
第一范式(1NF): 保证字段不可再分,保证原子性。
第二范式(2NF): 满足1NF前提下,表的每一列都必须和主键有关系。消除部分依赖关系。
第三范式(3NF): 满足2NF前提下,表的每一列比必须和主键有直接关系,不能是间接关系。消除传递依赖
4、什么是脏读、幻读和不可重复度?
脏读:一个事务读取到另一个事务尚未提交的数据。 事务 A 读取事务 B 更新的数据,然后 B 回滚操作,那么 A 读取到的数据是脏数据。
不可重复读:一个事务中两次读取的数据的内容不一致。 事务 A 多次读取同一数据,事务 B 在事务 A 多次读取的过程中,对数据作了更新并提交,导致事务 A 多次读取同一数据时,结果 不一致。
幻读:一个事务中两次读取的数据量不一致。 系统管理员 A 将数据库中所有学生的成绩从具体分数改为 ABCDE 等级,但是系统管理员 B 就在这个时候插入了一条具体分数的记录,当系统管理员 A 改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读
5、事务的隔离级别有哪些
① read uncommited(读取未提交内容): 在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。读取未提交的数据,也被称之为脏读(Dirty Read)
② read committed(读取提交内容): 这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。一个事务只能看见已经提交事务所做的改变。可解决脏读
③ repeatable read(可重读): 这是MySQL的默认事务隔离级别,同一事务的多个实例在并发读取数据时,会看到同样的数据。不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。可解决脏读、不可重复读
④ serializable(可串行化) : 这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。可解决脏读、不可重复读、幻读。
6、什么情况下会使索引失效?
1、如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引
2、对于多列索引,不是使用的第一部分,则不会使用索引
3、like查询是以%开头,索引失效;以%结尾,索引有效
4、如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引
5、如果mysql估计使用全表扫描要比使用索引快,则不使用索引
7、什么是悲观锁和乐观锁?
8、哪些是悲观锁?
行锁,表锁,读锁,写锁
9、SQL优化
10、MySQL主从复制工作原理
(1) Master的更新事件(update、insert、delete)会按照顺序写入bin-log中。当Slave连接到Master的后,Master机器会为Slave开启binlog dump线程,该线程会去读取bin-log日志
(2) Slave连接到Master后,Slave库有一个I/O线程 通过请求binlog dump thread读取bin-log日志,然后写入从库的relay log日志中。
(3) Slave还有一个 SQL线程,实时监控 relay-log日志内容是否有更新,解析文件中的SQL语句,在Slave数据库中去执行。
11、MySQL主从复制解决的问题
数据分布:随意开始或停止复制,并在不同地理位置分布数据备份负载均衡:降低单个服务器的压力高可用和故障切换:帮助应用程序避免单点失败升级测试:可以用更高版本的MySQL作为从库
12、一条查询语句一直很慢,如何排查?
使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。
通过EXPLAIN,我们可以分析出以下结果:
表的读取顺序数据读取操作的操作类型哪些
索引可以使用哪些索引
被实际使用表之间的引用每张表有多少行被优化器查询
13、mysql索引失效场景
联合索引时不满足最左匹配原则(例:select name,age,sex where age = 1,此时name,age,sex是建立了联合索引的,而name是使用最多的查询条件,只有name放在最左边的查询条件时,索引才会生效)
使用select * 时,没有走覆盖索引(覆盖索引就是select 和where 条件里面的字段都是索引,检索索引就可以读取想要的数据,那就不需要再到数据表中读取行了)
索引列参与计算
索引列参使用了函数(原因是因为数据库要先进行全表扫描,获得数据之后再进行截取、计算,导致索引索引失效)
使用模糊查询时(like语句)进行了左模糊匹配类型的隐式转换(例:id_no字段类型为varchar,但在SQL语句中使用了int类型,导致全表扫描)
使用OR操作(例:explain select * from t_user where id = 2 or name = 'Tom'; id是有索引的,但是name是没有索引,如果单独使用name字段作为条件很显然是全表扫描,既然已经进行了全表扫描了,前面id的条件再走一次索引反而是浪费了)
两列做比较(例:explain select * from t_user where id > age; id是有索引的,而age是没有的。但是当这两列去比较时,id的索引的就会失效)
不等于比较(使用”<>“或”!=“作为条件查询,有可能不走索引,但也不全是。当查询结果集占比比较小时,会走索引,占比比较大时不会走索引。此处与结果集与总体的占比有关。比如同一个条件能查到多个数据的场景)
is not null(查询条件使用is null时正常走索引,使用is not null时,不走索引)
14、什么是MVCC
MVCC,全称 Multi-Version Concurrency Control ,即多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。MVCC 在 MySQL InnoDB 中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。说白了 MVCC 就是为了实现读-写冲突不加锁。
六、框架
1、Mybatis什么是Mybatis框架?
mybatis 是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。mybatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作
2、Mybaits的优缺点?
(1)优点:
① 基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
② 与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;
③ 很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。
④ 能够与Spring很好的集成;
⑤ 提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
(2)缺点:
① SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。
② SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。#{}和的区别是什么?的区别是什么?{}是字符串替换,#{}是预处理;使用#{}可以有效的防止SQL注入,提高系统安全性。Mybatis在处理时,就是把时,就是把{}直接替换成变量的值。而Mybatis在处理#{}时,会对sql语句进行预处理,将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
3、在Mybatis中resultType和resutMap的区别是什么?
resultType:使用resultType进行输出映射,只有查询出来的列名与pojo中的属性一致,该列才可以映射成功。
resultMap:如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作为一个映射关系,也能完成与pojo的关系映射
4、谈一下你对mybatis缓存机制的了解
(1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。
(2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置 ;
(3)对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear 掉并重新更新,如果开启了二级缓存,则只根据配置判断是否刷新。
5、SpringSpring的两大核心是什么?
IOC(控制反转)和AOP(面向切面编程)
6、谈一下你对IOC的理解
IOC的意思是控制反转,简单点说,就是创建对象的控制权,被反转到了Spring框架上。
通常,我们实例化一个对象时,都是使用类的构造方法来new一个对象,这个过程是由我们自己来控制的,而控制反转就把new对象的工交给了Spring容器。可以由Spring根据我们提供的配置文件自动生产,当我们需要对象的时候,直接从Spring容器中获取即可。
7、谈一下你对DI的理解
DI,英文全称,Dependency Injection,意为依赖注入。IoC容器动态地将某个对象所需要的外部资源(包括对象、资源、常量数据)注入到组件(Controller, Service等)之中。简单点说,就是IoC容器会把当前对象所需要的外部资源动态的注入给我们。
Spring依赖注入的方式主要有四个,基于注解注入方式、set注入方式、构造器注入方式、静态工厂注入方式。推荐使用基于注解注入方式,配置较少,比较方便。
8、谈一下你对AOP的理解
面向切面编程:AOP通过预编译方式和运行期动态代理实现,在不修改源代码的情况下,给程序动态统一添加功能的一种技术,简称AOP。是spring框架的一个重要内容,是OOP(面向对象编程)的衍生模范。
AOP的作用:利用AOP对业务逻辑的各个部分进行隔离,降低业务逻辑的耦合性,提高程序的可重用型和开发效率。
9.什么是DI
依赖注入(DI)是一种软件设计模式,旨在降低模块之间的耦合度。其核心思想是将对象之间的依赖关系外部化,使得代码更加灵活、可维护和可测试。在DI模式中,对象不再负责创建它所依赖的对象,而是由外部的容器(通常是框架或容器)负责将依赖的对象注入到目标对象中。框架如Spring提供了DI功能,通过注解、XML配置文件或Java配置类等方式,开发者可以在需要注入的地方指定相应的注解或配置,框架即可自动完成依赖对象的注入,无需手动创建和管理。依赖注入的三种方式:构造器方法注入、set注入、属性注入(自动装配@Autowired)
3、SpringMVC
什么是Spring MVC ?
Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。
SpringMVC的流程?
(1)用户发送请求至前端控制器DispatcherServlet;
(2)DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handler;
(3)处理器映射器根据请求url找到具体的处理器Handler,生成处理器对象及处理器拦截器(如果有则生成),一并返回给DispatcherServlet;
(4)DispatcherServlet 调用 HandlerAdapter处理器适配器,请求执行Handler;
(5)HandlerAdapter 经过适配调用 具体处理器进行处理业务逻辑;
(6)Handler执行完成返回ModelAndView;
(7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
(8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
(9)ViewResolver解析后返回具体View;
(10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
(11)DispatcherServlet响应用户。SpringBoot的常用注解@Derek.li@DY@RequestMapping@PathVariable@RequestParam@kaikai4、Dubbo5、Zookeeper