面试题

最近在准备找工作,所以自己总结了一下面试题,有些来自于网上的,自己做了下整理。
1、JDK 和 JRE 有什么区别?

JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境。

JRE:Java Runtime Environment 的简称,java 运行环境,为 java 的运行提供了所需环境。

具体来说 JDK 其实包含了 JRE,同时还包含了编译 java 源码的编译器 javac,还包含了很多 java 程序调试和分析的工具。简单来说:如果你需要运行 java 程序,只需安装 JRE 就可以了,如果你需要编写 java 程序,需要安装 JDK。

2、包装类和基本数据类型区别?

声明方式不同:基本类型不使用new关键字,而包装类型需要使用new关键字来在堆中分配存储空间;

存储方式及位置不同:基本类型是直接将变量值存储在<u>栈中</u>,而包装类型是将对象放在堆中,然后通过引用来使用;

初始值不同:基本类型的初始值如int为0,boolean为false,而包装类型的初始值为null;

使用方式不同:基本类型直接赋值直接使用就好,而包装类型在集合如Collection、Map时会使用到。

3、Java 中堆和栈有什么区别?

JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。

栈:存储的都是局部变量,凡是定义在方法中的都是<u>局部变量</u>(方法外的是全局变量),for循环内部定义的也是局部变量,在函数中定义的一些<u>基本类型的变量和对象的引用变量</u>都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。栈内存的更新速度很快,因为局部变量的生命周期都很短。

堆:堆内存用来存放由 new 创建的<u>对象和数组</u>,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。

4、== 和 equals 的区别是什么?

== 解读 ==较的是两个引用在内存中指向的是不是同一对象(即同一内存空间),也就是说在内存空间中的存储位置是否一致

对于基本类型和引

用类型 == 的作用效果是不同的,如下所示:

基本类型:比较的是值是否相同;

引用类型:比较的是引用是否相同;

代码示例:

image.png

equals 解读

equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。看下面的代码就明白了

首先来看默认情况下 equals 比较一个有相同值的对象,代码如下:[图片上传失败...(image-829f54-1585306775165)]

原来是 String 重写了 Object 的 equals 方法,把引用比较改成了值比较。

总结:== 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重新了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。

5、string stringbuffer stringbulider

String底层使用一个不可变的字符数组private final char value[];所以它内容不可变。

StringBuffer和StringBuilder都继承了AbstractStringBuilder底层使用的是可变字符数组:char[] value;

6、ConcurentModificationException异常出现的原因

7、java中的switch选择结构可以使用数据类型的数据(JDK1.8)?

byte short int char

Byte Short Integer Character

String enum

更好的记忆方法:

基本类型中,没有boolean和浮点类型+长类型long 相应的包装类型也没有。外加String和enum。

枚举类是一种特殊类,它和普通类一样可以使用构造器、定义成员变量和方法,也能实现一个或多个接口,但枚举类不能继承其他类。

image.png

8、final关键字

final 修饰的类叫最终类,该类不能被继承。

final 修饰的方法不能被重写。

final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。

使用final关键字修饰一个变量时,<u>是指引用变量不能变</u>,引用变量所指向的对象中的内容还是可以改变的。例如,对于如下语句:

Final StringBuffer a=new StringBuffer("immutable"); 执行如下语句将报告编译期错误:a=new StringBuffer(""); 但是,执行如下语句则可以通过编译:append(" broken!");

9、下面这条语句一共创建了多少个对象:String s="a"+"b"+"c"+"d";

对于如下代码:

String s1 = "a";

String s2 = s1 + "b";

String s3 = "a" + "b";

System.out.println(s2 == "ab");

System.out.println(s3 == "ab");

第一条语句打印的结果为false,第二条语句打印的结果为true,这说明<u>javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期再去进行加法运算处理</u>,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。

题目中的第一行代码被编译器在编译时优化后,相当于直接定义了一个”abcd”的字符串,所以,上面的代码应该只创建了一个String对象。写如下两行代码,

      String s ="a" + "b" +"c" + "d";

      System.out.println(s== "abcd");

最终打印的结果应该为true。

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

Math 类中提供了三个与取整有关的方法:ceil、floor、round,这些方法的作用与它们的英文名称的含义相对应,例如,ceil 的英文意义是天花板,该方法就表示向上取整,Math.ceil(11.3)的结果为 12,Math.ceil(-11.3)的结果是-11;floor 的英文意义是地板,该方法就表示向下取整,Math.ceil(11.6)的结果为 11,Math.ceil(-11.6)的结果是-12;最难掌握的是 round 方法,它表示“四舍五入”,算法为 Math.floor(x+0.5),即将<u>原来的数字加上</u> <u>0.5</u> <u>后再向下取整</u>,所以,Math.round(11.5)的结果为 12,Math.round(-11.5)的结果为-11。

11、Java中实现多态的机制是什么?

靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。

12、普通类和抽象类有哪些区别?

普通类能被实例化,抽象类不能被实例化。

抽象类中可以有构造方法和普通方法

含有抽象方法的类必须声明为抽象类

抽象方法只需申明,而无需实现,抽象类中可以允许普通方法有主体

抽象类的子类必须实现抽象类中的所有抽象方法,除非它的子类也是抽象类

13、抽象类和接口的区别?

接口可以说成是抽象类的一种特例

抽下该类可以有普通成员变量,接口中的成员变量默认都是public static final类型

抽象类和接口中都可以定义静态成员变量,抽象类中的静态成员变量的访问类型可以是任意的,接口中的变量定义的类型只能是public static final

抽象类可以有构造方法,接口没有构造方法

抽象类中可以包含非抽象方法,接口中的方法都是抽象方法

抽象类中可以包含静态方法,接口中不能包含静态方法

接口是对动作的抽象,抽象类是对本质的抽象。抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么。比如,男人,女人,这两个类(如果是类的话……),他们的抽象类是人。说明,他们都是人。人可以吃东西,狗也可以吃东西,你可以把“吃东西”定义成一个接口,然后让这些类去实现它。当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。

14、BIO、NIO、AIO 有什么区别?

NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统IO基于字节流和字符流进行操作,而<u>NIO基于Channel和Buffer(缓冲区)进行操作</u>,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。

NIO和传统IO(一下简称IO)之间第一个最大的区别是,<u>IO是面向流的,NIO是面向缓冲区的。</u> <u>Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。</u>此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。<u>NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,</u>需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变得可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。

NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。

AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。

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

finally语句总会执行

如果try/catch中有return语句,finally中没有return语句,那么finally中修改数据(除集合类、静态变量、全局变量)对try/catch中返回的变量没有任何影响

在finally中使用return,会忽略try/catch中的返回语句,也会忽略try/catch中的异常

finally中如果发生异常,代码执行将会抛出finally中的异常信息

会执行,在 return 前执行。<u>finally是在return后面的表达式运算之后执行的,此时并没有返回运算之后的值,而是把值保存起来,不管finally对该值做任何的改变,返回的值都不会改变,依然返回保存起来的值。也就是说方法的返回值是在finally运算之前就确定了的。</u>


image.png

16、error和exception有什么区别?

Error:Error类对象由 Java虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。例如,Java虚拟机运行错误(Virtual Machine Error),当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止;还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError)、链接错误(LinkageError)。这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在Java中,错误通常是使用Error的子类描述。

Exception:在Exception分支中有一个重要的子类RuntimeException(运行时异常),该类型的异常自动为你所编写的程序定义ArrayIndexOutOfBoundsException(数组下标越界)、NullPointerException(空指针异常)、ArithmeticException(算术异常)、MissingResourceException(丢失资源)、ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生;而RuntimeException之外的异常我们统称为非运行时异常,类型上属于Exception类及其子类,从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

image.png

17、hashCode() 有什么用?与 a.equals(b) 有什么关系?

hashCode() 方法对应对象整型的 hash 值。它常用于基于 hash 的集合类,如 Hashtable、HashMap、LinkedHashMap等等。它与 equals() 方法关系特别紧密。根据 Java 规范,两个使用 equal() 方法来判断相等的对象,必须具有相同的 hash code。

set集合是无序不能重复的,那么怎么能保证不能被放入重复的元素呢,但靠equals方法一样比较的话,如果原来集合中以后又10000个元素了,那么放入10001个元素,难道要将前面的所有元素都进行比较,看看是否有重复,这个效率可想而知,因此hashcode就应遇而生了,java就采用了hash表,利用哈希算法(也叫散列算法),就是将对象数据根据该对象的特征使用特定的算法将其定义到一个地址上,那么在后面定义进来的数据只要看对应的hashcode地址上是否有值,那么就用equals比较,如果没有则直接插入,只要就大大减少了equals的使用次数,执行效率就大大提高了。

重写equals()必须重写hashCode()

equals()相等的两个对象,hashcode()一定相等

反过来:hashcode()不等,一定能推出equals()也不等

hashcode()相等,equals()可能相等,也可能不等。

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

不对,两个对象的 hashCode()相同,equals()不一定 true

image.png

代码解读:很显然“通话”和“重地”的 hashCode() 相同,然而 equals() 则为 false,因为在散列表中,hashCode()相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等。

19、怎么确保一个集合不能被修改?

可以使用 Collections. unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常。

image.png

20、LinkedList 的底层数据结构是双向循环链表

21、如何实现数组和 List 之间的转换?

List转换成为数组:调用ArrayList的toArray方法。

数组转换成为List:调用Arrays的asList方法。

22、Array 和 ArrayList 有何区别?

Array可以容纳基本类型和对象,而ArrayList只能容纳对象。

Array是指定大小的,而ArrayList大小不是固定的。

Array没有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。

23、ArrayList 和 Vector 的区别是什么?

线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的

性能:ArrayList 在性能方面要优于 Vector。

扩容:ArrayList 和Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而ArrayList 只会增加 50%。

24、在 Queue 中 poll()和 remove()有什么区别?

相同点:都是返回第一个元素,并在队列中删除返回的对象。

不同点:如果没有元素poll()会返回 null,而remove()会直接抛出 NoSuchElementException 异常。

25、HashMap和Hashtable的区别?

HashMap是非线程安全的,HashTable是线程安全的。

HashMap的键和值都允许有null值存在,而HashTable则不行。

因为线程安全的问题,HashMap效率比HashTable的要高。

Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。但是一般现在不建议用HashTable,

①是HashTable是遗留类,内部实现很多没优化和冗余。

②即使在多线程环境下,现在也有同步的ConcurrentHashMap替代,没有必要因为是多线程而用HashTable。

26、HaseSet(允许null)实现原理

每次存储对象的时候, 调用对象的hashCode()方法, 计算一个哈希值. 在集合中查找是否包含哈希值相同的元素.如果没有哈希值相同元素, 直接存入.

如果有哈希值相同的元素, 逐个使用equals()方法比较. 比较结果全为false就存入.

如果比较结果有true则不存.

27、HaseMap实现原理

当添加一个元素(key-value)时,就首先计算元素key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,但是形成了链表,同一各链表上的Hash值是相同的,所以说数组存放的是链表。而当链表长度太长时,链表就转换为<u>红黑树(自平衡的二叉查找树)</u>,这样大大提高了查找的效率。

28、如何决定使用 HashMap 还是 TreeMap?

对于在 Map 中插入、删除、定位一个元素这类操作,HashMap 是最好的选择,因为相对而言 HashMap 的插入会更快,但如果你要对一个 key 集合进行有序的遍历,那 TreeMap 是更好的选择。

image.png

29、HashSet与TreeSet的比较

1.TreeSet 是二叉树实现的,Treeset中的数据是自动排好序的,不允许放入null值 。

2.HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束 。

3.HashSet要求放入的对象必须实现HashCode()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的String对象,hashcode是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例。

HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。

30、同步异步?

<u>同步</u>:A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制存在,A线程请求不到,怎么办,A线程只能等待下去 <u>异步</u>:A线程要请求某个资源,但是此资源正在被B线程使用中,因为没有同步机制存在,A线程仍然请求的到,A线程无需等待

31、关于线程?

(1) Thread和Runnable的区别

实现Runnable接口比继承Thread类所具有的优势:

Runnable的实现方式是实现其接口即可

Thread的实现方式是继承其类

Thread实现了Runnable接口,提供了更多的可用方法和成员而已。

Thread和Runnable的实质是实现关系,没有可比性。无论使用Runnable还是Thread,都会new Thread,然后执行run方法。用法上,如果有复杂的线程操作需求,那就选择继承Thread,如果只是简单的执行一个任务,那就实现runnable。

  1. Runnable接口支持多继承,可以避免java中的单继承的局限性。但基本上用不到

Thread实现了Runnable接口并进行了扩展,而Thread和Runnable的实质是实现的关系,不是同类东西,所以Runnable或Thread本身没有可比性。

  1. 适合多个相同的程序代码的线程去共享同一个资源。(错误的结论!https://blog.csdn.net/zhaojianting/article/details/97664370

  2. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

(2) 线程安全

如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

引起的原因:

线程安全问题都是由<u>全局变量及静态变量</u>引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

(3) 线程同步

当我们使用多 线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。 要解决上述多线程并发访问一个资源的安全性问题:也就是解决重复票与不存在票问题,Java中提供了同步机制 (synchronized)来解决。

  1. 同步代码块。 2. 同步方法。 3. 锁机制。

(4) 同步代码块

synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。

image.png

对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.

锁对象 可以是任意类型。 2. 多个线程对象 要使用同一把锁。

image.png

(5) 同步方法

使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外 等着。

image.png

同步锁是谁? 对于非static方法,同步锁就是this。 对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。

(6) LOCK锁

java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作, 同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。

Lock锁也称同步锁,加锁与释放锁方法化了,如下:

public void lock() :加同步锁。

public void unlock() :释放同步锁。

(7) 线程状态

1、新建状态(New):新创建了一个线程对象。

2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取CPU的使用权。即在就绪状态的进程除CPU之外,其它的运行所需资源都已全部获得。

3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

阻塞的情况分三种:

(1)、等待阻塞:运行的线程执行<u>wait</u>()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒,

(2)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中。

(3)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者<u>I/O</u>处理完毕时,线程重新转入就绪状态。

5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

(8) notify()和notifyAll()方法区别?(wait和notify方法要在同步块中调用)

notify()方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地。而notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行。

(9) Volatile

是一个特殊的修饰符,只有成员变量才能使用它

· 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(实现可见性)

· 禁止进行指令重排序。(实现有序性)(volatile变量可以保证下一个读取操作会在前一个写操作之后发生。)

· volatile 只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性。

32、创建线程有三种方式

继承 Thread 重写 run 方法;实现 Runnable 接口;实现 Callable 接口。

33、说一下 runnable 和 callable 有什么区别?

Runnable从JDK1.0开始就有了,Callable是在JDK1.5增加的。

1)Runnable提供run方法,<u>不会抛出异常</u>,只能在run方法内部处理异常。Callable提供call方法,直接抛出Exception异常,也就是你不会因为call方法内部出现检查型异常而不知所措,完全可以抛出即可。

2)Runnable的run方法<u>无返回值</u>,Callable的call方法提供返回值用来表示任务运行的结果

3)Runnable可以作为<u>Thread构造器的参数</u>,通过开启新的线程来执行,也可以通过线程池来执行。而Callable只能通过线程池执行。

34、sleep() 和 wait() 有什么区别?

每个对象都有一个锁来控制同步访问,Synchronized关键字可以和对象的锁交互,来实现同步方法或同步块。sleep()方法正在执行的线程主动让出CPU,在sleep指定时间后CPU再回到该线程继续往下执行(注意:sleep方法只让出了CPU,而并不会释放同步资源锁!!!);wait()方法则是指当前线程让自己暂时退让出同步资源锁,以便其他正在等待该资源的线程得到该资源进而运行,只有调用了notify()方法,之前调用wait()的线程才会解除wait状态,可以去参与竞争同步资源锁,进而得到执行。(注意:notify的作用相当于叫醒睡着的人,而并不会给他分配任务,就是说notify只是让之前调用wait的线程有权利重新参与线程的调度);

sleep()方法可以在任何地方使用;wait()方法则只能在同步方法或同步块中使用;sleep()是线程线程类(Thread)的方法,调用会暂停此线程指定的时间,但监控依然保持,不会释放对象锁,到时间自动恢复;wait()是Object的方法,调用会放弃对象锁,进入等待队列,待调用notify()/notifyAll()唤醒指定的线程或者所有线程,才会进入锁池,再次获得对象锁才会进入运行状态;

sleep()方法必须捕获异常,而wait()、notify()、notifyAll()不需要捕获异常。

35、线程的 run() 和 start() 有什么区别?

start() 方法用于启动线程,run() 方法用于执行线程的运行时代码。run() 可以重复调用,而 start() 只能调用一次。

36、线程池都有哪些状态?

1、RUNNING (1) 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。 (2) 状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0!

2、 SHUTDOWN (1) 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。 (2) 状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。

3、STOP (1) 状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。 (2) 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。

4、TIDYING(tidy :整理,收拾,使整齐) (1) 状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。 (2) 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。 当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。

5、 TERMINATED (1) 状态说明:线程池彻底终止,就变成TERMINATED状态。 (2) 状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

image.png

37、ThreadLocal 是什么?有哪些使用场景?

ThreadLocal</u> 是线程本地存储,在每个线程中都创建了一个 ThreadLocalMap 对象,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。经典的使用场景是为每个线程分配一个 JDBC 连接 Connection。这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作,不会出现 A 线程关了 B线程正在使用的 Connection; 还有 Session 管理 等问题。

ThreadLocal和Synchronized都是为了解决多线程中相同变量的访问冲突问题,不同的点是

Synchronized是通过线程等待,牺牲时间来解决访问冲突

ThreadLocal是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于Synchronized,ThreadLocal具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。

38、线程池的组成?

一个线程池包括四个基本部分

1.线程管理池(ThreadPool):用于创建并管理线程池,有创建,销毁,添加新任务;

2.工作线程(PoolWorker):线程池中的线程在没有任务的时候处于等待状态,可以循

环的执行任务;

3.任务接口(Task):每个任务必须实现接口,用来提供工作线程调度任务的执行,规定了任务的入口以及执行结束的收尾工作和任务的执行状态等;

4.任务队列:用于存放没有处理的任务,提供一种缓存机制。

39、使用线程池的好处?

·减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

·运用线程池能有效的控制线程的最大并发数,可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存。

·对线程进行一些简单的管理,比如:延时执行、定时循环执行的策略。

40、常见线程池的种类

image.png

核心线程:

线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程,如果超过corePoolSize,则新建的是非核心线程

核心线程默认情况下会一直存活在线程池中,即使这个核心线程啥也不干(闲置状态)。

如果指定ThreadPoolExecutor的allowCoreThreadTimeOut这个属性为true,那么核心线程如果不干活(闲置状态)的话,超过一定时间(时长下面参数决定),就会被销毁掉

很好理解吧,正常情况下你不干活我也养你,因为我总有用到你的时候,但有时候特殊情况(比如我自己都养不起了),那你不干活我就要把你干掉了

线程总数 = 核心线程数 + 非核心线程数。核心线程在上面解释过了,这里说下非核心线程:

不是核心线程的线程

41、java四种常用的线程池实现

1、Executors.newCachedThreadPool():可缓存线程池。

一种线程数量不定的线程池,并且其最大线程数为Integer.MAX_VALUE,这个数是很大的,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。但是线程池中的空闲线程都有超时限制,这个超时时长是60秒,超过60秒闲置线程就会被回收。调用execute将重用以前构造的线程(如果线程可用)。这类线程池比较适合执行大量的耗时较少的任务,当整个线程池都处于闲置状态时,线程池中的线程都会超时被停止。

2、Executors.newFixedThreadPool(int n):创建一个可重用固定个数的线程池。

当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列(没有大小限制)中。由于newFixedThreadPool只有核心线程并且这些核心线程不会被回收,这样它更加快速底相应外界的请求。

·3、Executors.newScheduledThreadPool(int n):创建一个定长线程池,支持定时及周期性任务执行。

它的线程数量是固定的,它可安排给定延迟后运行命令或者定期地执行,这类线程池主要用于执行定时任务和具有固定周期的重复任务。

4、Executors.newSingleThreadExecutor():创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

42、** 什么是ORM?**

对象关系映射(Object-Relational Mapping,简称ORM)是一种为了解决程序的面向对象模型与数据库的关系模型互不匹配问题的技术;

简单的说,ORM是通过使用描述对象和数据库之间映射的元数据(在Java中可以用XML或者是注解),将程序中的对象自动持久化到关系数据库中或者将关系数据库表中的行转换成Java对象,其本质上就是将数据从一种形式转换到另外一种形式。

43、mybatis 中 #{}和 ${}的区别是什么?

{}是预编译处理,${}是字符串替换;

Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理{}时,就是把{}替换成变量的值;
使用#{}可以有效的防止SQL注入,提高系统安全性。
{}在什么情况下使用呢?有时候可能需要直接插入一个不做任何修改的字符串到SQL语句中。这时候应该使用{}语法。比如,动态SQL中的字段名,如:ORDER BY ${columnName}

将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。

如:where username=#{username},如果传入的值是111,那么解析成sql时的值为where username="111", 如果传入的值是id,则解析成的sql为where username="id". 
将传入的数据直接显示生成在sql中。 如:where username={username},如果传入的值是111,那么解析成sql时的值为where username=111;
针对上面的sql,如果传入的值是;drop table user;,
那么第一条用#{}的sql解析为:select id, username, password, role from user where username=";drop table user;"
那么第二条用{}的sql解析为:select id, username, password, role from user where username=;drop table user;这时候已经sql注入了。方式一般用于传入数据库对象,例如传入表名和列名,还有排序时使用order by动态参数时需要使用,ORDER BY{columnName}

image.png

44、RowBounds 是一次性查询全部结果吗?为什么?

RowBounds 表面是在“所有”数据中检索数据,其实并非是一次性查询出所有数据,因为 MyBatis 是对 jdbc 的封装,在 jdbc 驱动中有一个 Fetch Size 的配置,它规定了每次最多从数据库查询多少条数据,假如你要查询更多数据,它会在你执行 next()的时候,去查询更多的数据。就好比你去自动取款机取 10000 元,但取款机每次最多能取 2500 元,所以你要取 4 次才能把钱取完。只是对于 jdbc 来说,当你调用 next()的时候会自动帮你完成查询工作。这样做的好处可以有效的防止内存溢出。

45、redis什么是缓存穿透?怎么解决?

缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。

解决方案:最简单粗暴的方法如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们就把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

缓存雪崩,是指在某一个时间段,缓存集中过期失效。

产生雪崩的原因之一,比如在写本文的时候,马上就要到双十二零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。

小编在做电商项目的时候,一般是采取不同分类商品,缓存不同周期。在同一分类中的商品,加上一个随机因子。这样能尽可能分散缓存过期时间,而且,热门类目的商品缓存时间长一些,冷门类目的商品缓存时间短一些,也能节省缓存服务的资源。

缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

小编在做电商项目的时候,把这货就成为“爆款”。

其实,大多数情况下这种爆款很难对数据库服务器造成压垮性的压力。达到这个级别的公司没有几家的。所以,务实主义的小编,对主打商品都是早早的做好了准备,让缓存永不过期。即便某些商品自己发酵成了爆款,也是直接设为永不过期就好了。

46、redis 支持的 java 客户端都有哪些?

Redisson、Jedis、lettuce等等,官方推荐使用Redisson。

47、怎么保证缓存和数据库数据的一致性?

合理设置缓存的过期时间。

新增、更改、删除数据库操作时同步更新 Redis,可以使用事物机制来保证数据的一致性

48、redis持久化有几种方式?

Redis 的持久化有两种方式,或者说有两种策略:

RDB(Redis Database):指定的时间间隔能对你的数据进行快照存储。

AOF(Append Only File):每一个收到的写命令都通过write函数追加到文件中。

49、Jedis操作各种redis中的数据结构

  1. 字符串类型 string (Set Get)

//存储 jedis.set("username","zhangsan");

//获取 String username = jedis.get("username");

//可以使用setex()方法存储可以指定过期时间的 key value

jedis.setex("activecode",20,"hehe");//将activecode:hehe键值对存入redis,并且20秒后自动删除该键值对

  1. 哈希类型 hash : map格式 (hset hget hgetAll)

// 存储hash jedis.hset("user","name","lisi");

// 获取hash String name = jedis.hget("user", "name");

// 获取hash的所有map中的数据 Map<String, String> user = jedis.hgetAll("user");

// keyset Set<String> keySet = user.keySet();

    for (String key : keySet) {

        //获取value

        String value = user.get(key);

        System.out.println(key + ":" + value);

    }

3)列表类型 list : linkedlist格式。支持重复元素

lpush / rpush

lpop / rpop 弹出

lrange start end : 范围获取

// list 存储

    jedis.lpush("mylist","a","b","c");//从左边存

    jedis.rpush("mylist","a","b","c");//从右边存

    // list 范围获取

    List<String> mylist = jedis.lrange("mylist", 0, -1);

    System.out.println(mylist);

    // list 弹出

    String element1 = jedis.lpop("mylist");//c

    System.out.println(element1);

    String element2 = jedis.rpop("mylist");//c

    System.out.println(element2);

    // list 范围获取

    List<String> mylist2 = jedis.lrange("mylist", 0, -1);
  1. 集合类型 set : 不允许重复元素

sadd

smembers:获取所有元素

// set 存储

    jedis.sadd("myset","java","php","c++");

    // set 获取

    Set<String> myset = jedis.smembers("myset");

  jedis.close();
  1. 有序集合类型 sortedset:不允许重复元素,且元素有顺序

zadd

Zrange

50、RabbitMQ(微秒级延迟)

(1)消息队列概述:

消息队列是应用程序之间的通信方法;无需即时返回的且耗时的操作进行异步处理从而提高系统的吞吐量;可以实现程序之间的解耦合

  • 实现方式:AMQP(协议),JMS(Java消息服务(JavaMessage Service)应用程序接口java平台中关于面向消息中间件(MOM)的API)

  • 常见产品:activeMQ,zeroMQ,RabbitMQ(AMQP,基于erlang语言开发的),RocketMQ,kafka

(2)消息队列的优缺点:

优点:常见使用场景有很多但是核心的有三个:解耦、异步、削峰

缺点:

系统可用性降低:系统引入的外部依赖越多,越容易挂掉,本来你就是A系统调用BCD三个系统的接口就好了,人ABCD四个系统好好的没什么问题,你偏加个MQ进来,万一MQ挂了怎么办,整套系统崩溃了,就完蛋了

系统复杂性提高:硬生生加个MQ进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?

一致性问题:系统A处理完了直接返回成功了,人家都认为你这个请求成功了;但问题是,要是BCD三个系统哪里BD系统成功了,结果C系统写库失败了,咋整?数据就不一致了?

(3)解决RabbitMQ数据丢失问题

① 生产者弄丢数据

一般来说,如果你要确保说写rabbitmq的消息别丢,可以开启confirm模式,在生产者那里<u>设置开启confirm模式</u>(<u>异步机制</u>)之后,你每次写的消息都会分配一个唯一的id,然后如果写入了rabbitmq中,rabbitmq会给你回传一个ack消息,告诉你说这个消息ok了。如果超过一定时间还没接收到这个消息的回调,那么你可以重发。

还可以选择用rabbitmq提供的事务功能(<u>同步机制</u>),就是生产者发送数据之前开启rabbitmq事务(channel.txSelect),然后发送消息,如果消息没有成功被rabbitmq接收到,那么生产者会收到异常报错,此时就可以回滚事务(channel.txRollback),然后重试发送消息;如果收到了消息,那么可以提交事务(channel.txCommit)。但是问题是,rabbitmq事务机制一搞,基本上吞吐量会下来,因为太耗性能。

② RabbitMQ弄丢数据

这个你必须<u>开启rabbitmq的持久化</u>,就是消息写入之后会持久化到磁盘,哪怕是rabbitmq自己挂了,恢复之后会自动读取之前存储的数据,一般数据不会丢。除非极其罕见的是,rabbitmq还没持久化,自己就挂了,可能导致少量数据会丢失的,但是这个概率较小。而且<u>持久化可以跟生产者那边的confirm机制配合起来,只有消息被持久化到磁盘之后,才会通知生产者ack了,所以哪怕是在持久化到磁盘之前,rabbitmq挂了,数据丢了,生产者收不到ack,你也是可以自己重发的。</u>

哪怕是你给rabbitmq开启了持久化机制,也有一种可能,就是这个消息写到了rabbitmq中,但是还没来得及持久化到磁盘上,结果不巧,此时rabbitmq挂了,就会导致内存里的一点点数据会丢失。

③ 消费者弄丢数据

rabbitmq如果丢失了数据,主要是因为你消费的时候,刚消费到,还没处理,结果进程挂了,比如重启了,那么就尴尬了,rabbitmq认为你都消费了,这数据就丢了。

这个时候得用rabbitmq提供的ack机制,简单来说,就是你关闭rabbitmq自动ack,可以通过一个api来调用就行,然后每次你自己代码里确保处理完的时候,再程序里ack一把。这样的话,如果你还没处理完,不就没有ack?那rabbitmq就认为你还没处理完,这个时候rabbitmq会把这个消费分配给别的consumer去处理,消息是不会丢的。

(4)如何保证消息的顺序性?

一个queue,多个consumer时:

拆分多个queue,每个queue一个consumer,就是多一些queue而已,确实是麻烦点。

(5)消息基于什么传输?

由于 TCP 连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ 使用信道的方式来传输数据。信道是建立在真实的 TCP 连接内的虚拟连接,且每条 TCP 连接上的信道数量没有限制。

(6)生产者实现发送消息的步骤:

1. 创建连接工厂(设置RabbitMQ的连接参数);

2. 创建连接;

3. 创建频道;

4. 声明队列;

5. 发送消息;

6. 关闭资源

(7)实现消费者步骤:

1. 创建连接工厂;

2. 创建连接;(抽取一个获取连接的工具类)

3. 创建频道;

4. 声明队列;

5. 创建消费者(接收消息并处理消息);

6. 监听队列

(8)5种模式特征

(1)不直接Exchange交换机(默认交换机)

simple简单模式:一个生产者生产一个消息到一个队列被一个消费者接收

work工作队列模式:生产者发送消息到一个队列中,然后可以被多个消费者监听该队列;一个消息只能被一个消费者接收,消费者之间是竞争关系

(2)使用Exchange交换机;订阅模式(交换机:广播fanout、定向direct、通配符topic)发布与订阅模式:使用了fanout广播类型的交换机,可以将一个消息发送到所有绑定了该交换机的队列

路由模式:使用了direct定向类型的交换机,消费会携带路由key,交换机根据消息的路由key与队列的路由key进行对比,一致的话那么该队列可以接收到消息

通配符模式:使用了topic通配符类型的交换机,消费会携带路由key(*, #),交换机根据消息的路由key与队列的路由key进行对比,匹配的话那么该队列可以接收到消息。

(9)RabbitMQ 怎么避免消息丢失?

把消息持久化磁盘,保证服务器重启消息不丢失。

每个集群中至少有一个物理磁盘,保证消息落入磁盘。

(10)RabbitMQ 有哪些重要的组件?

ConnectionFactory(连接管理器):应用程序与Rabbit之间建立连接的管理器,程序代码中使用。

Channel(信道):消息推送使用的通道。

Exchange(交换器):用于接受、分配消息。

Queue(队列):用于存储生产者的消息。

RoutingKey(路由键):用于把生成者的数据分配到交换器上。

BindingKey(绑定键):用于把交换器的消息绑定到队列上。

51、Dubbo和Zookeeper

(1) SOA(Service Oriented Architecture)“面向服务的架构”:他是一种设计方法其中包含多个服务, 服务之间通过相互依赖最终提供一系列的功能。一个服务通常以独立的形式存在与操作系统进程中。各个服务之间通过网络调用。

(2) Dubbo简介

Apache Dubbo是一款高性能的Java RPC框架,RPC全称为remote procedure call,即远程过程调用,Dubbo提供了三大核心能力:<u>面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。</u>

(3) Dubbo架构

image.png

image.png

虚线都是异步访问,实线都是同步访问

蓝色虚线:在启动时完成的功能

红色虚线(实线)都是程序运行过程中执行的功能

调用关系说明:

0. 服务容器负责启动,加载,运行服务提供者。

1. 服务提供者在启动时,向注册中心注册自己提供的服务。

2. 服务消费者在启动时,向注册中心订阅自己所需的服务。

3. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。

4. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

5. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

(4) 服务注册中心Zookeeper(树型的目录服务)

通过前面的Dubbo架构图可以看到,Registry(服务注册中心)在其中起着至关重要的作用。Dubbo官方推荐使用Zookeeper作为服务注册中心。

(5) 如何防止Zookeeper单点故障?

Zookeeper其实是支持集群模式的,可以配置Zookeeper集群来达到Zookeeper服务的高可用,防止出现单点故障。

(6) Dubbo管理控制台

我们在开发时,需要知道Zookeeper注册中心都注册了哪些服务,有哪些消费者来消费这些服务。我们可以通过部署一个管理中心来实现。其实管理中心就是一个web应用,部署到tomcat即可。

(7) 解决Dubbo无法发布被事务代理的Service问题

① 修改applicationContext-service.xml配置文件,开启事务控制注解支持时指定proxy-target-class属性,值为true。其作用是使用cglib代理方式为Service类创建代理对象

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

② 修改HelloServiceImpl类,在Service注解中加入interfaceClass属性,值为HelloService.class,作用是指定服务的接口类型

52、Mysql及其优化

(1)where子句的作用是在对查询结果进行分组前,将不符合where条件的行去掉,即在<u>分组之前过滤数据</u>,条件中不能包含聚组函数,使用where条件显示特定的行。

(2)having 子句的作用是筛选满足条件的组,即在<u>分组之后过滤数据</u>,条件中经常包含聚组函数,使用having 条件显示特定的组,也可以使用多个分组标准进行分组。

(3)当distinct应用到多个字段的时候,其应用的范围是其后面的所有字段,而不只是紧挨着它的一个字段,而且distinct只能放到所有字段的前面,如下语句是错误的:SELECT country, distinct province from person; // 该语句是错误的,distinct对NULL是不进行过滤的,即返回的结果中是包含NULL值的代表整列,使用distinct对操作select DISTINCT * from person相当于select DISTINCT id, name, country, province, city from person;

(4)isnull(exper) 判断exper是否为空,是则返回1,否则返回0

(5)ifnull(exper1,exper2)判断exper1是否为空,是则用exper2代替;

(6)nullif(exper1,exper2)如果expr1=expr2 成立,那么返回值为NULL,否则返回值为expr1

(7)if(expr1,expr2,expr3),如果expr1的值为true,则返回expr2的值,如果expr1的值为false,则返回expr3的值。

(8)<u>SQL语句中IN包含的值不应过多</u>:MySQL对于IN做了相应的优化,即将IN中的常量全部存储在一个数组里面,而且这个数组是排好序的。但是如果数值较多,产生的消耗也是比较大的。再例如:select id from t where num in(1,2,3) 对于连续的数值,能用between就不要用in了;再或者使用连接来替换。

(9)应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:select id from t where num=0

(10)SELECT语句务必指明字段名称:SELECT*增加很多不必要的消耗。

(11)使用连接(JOIN)来代替子查询(Sub-Queries)

(12)当只需要一条数据的时候,使用limit 1

(13)如果排序字段没有用到索引,就尽量少排序

(14)区分in和exist、not in 和not exists

(15)select * from 表A where id in (select id from 表B)上面SQL语句相当于select * from 表A where exists(select * from 表B where 表B.id=表A.id)区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询。所以IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况。

(16)关于not in和not exists,推荐使用not exists,不仅仅是效率问题,not in可能存在逻辑问题。如何高效的写出一个替代not exists的SQL语句?原SQL语句:select colname … from A表 where a.id not in (select b.id from B表)高效的SQL语句:select colname … from A表 Left join B表 on where a.id = b.id where b.id is null取出的结果集如下图表示,A表不在B表中的数据


image.png

(17)避免在where子句中对字段进行表达式操作

(18)比如:select user_id,user_project from user_base where age*2=36;中对字段就行了算术运算,这会造成引擎放弃使用索引,建议改成:select user_id,user_project from user_base where age=36/2;

(19)事务

① 四大特性

原子性(A):事务是最小单位,不可再分

一致性(C):事务要求所有的DML语句操作的时候,必须保证同时成功或者同时失败

隔离性(I):事务A和事务B之间具有隔离性

持久性(D):是事务的保证,事务终结的标志(内存的数据持久到硬盘文件中)

② 隔离性有隔离级别(4个)

读未提交:read uncommitted

读已提交:read committed

可重复读:repeatable read

串行化:serializable

(20)范式

1NF的关系中的每个属性都不可再分。

2NF在1NF的基础之上,消除了非主属性对于码的部分函数依赖。

3NF在2NF的基础之上,消除了非主属性对于码的传递函数依赖。

BCNF在3NF的基础之上,消除主属性对于码的部分与传递函数依赖。

(21)索引

索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。

举例说明索引:如果把数据库中的某一张看成一本书,那么索引就像是书的目录,可以通过目录快速查找书中指定内容的位置,对于数据库表来说,可以通过索引快速查找表中的数据。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容