JAVA核心,200例,查缺补漏

适用于想要查缺补漏的人;本已经掌握的技术,但是不知道如何表达的人;不断完善技自己,顺带梳理下答案。
主要包括以下模块:Java基础、容器、多线程、反射、对象拷贝、Java Web模块,异常、网络、设计模式、Spring/Spring MVC 、Spring Boot/Spring Cloud、Hibernate、Mybaits、RabbitMQ、Kafka、Zookeeper、Mysql、Redis、JVM

一、JAVA基础

1、JDK和JRE有什么区别?
解析:首先,JDK和JRE是Java开发和运行工具,其中JDK包含了JRE,但是JRE是可以独立安装的,它们再开发和运行时起到了不同的作用。
1>JDK是Java Development kit的缩写,是Java开发工具包,主要包含了各种工具和类库,当然也包含了另外一个JRE。那么它,为什么要包含一个JRE呢?而且JDK安装目录中如下图:在这里插入图片描述
这说明JDK提供了一个虚拟机。
另外,JDK的bin目录下边有各种Java程序需要用到的命令,与JRE的bin目录最明显的区别就是JDK文件下才有javac,这个比较好理解,因为JRE只是一个运行环境而已,与开发没有关系。正因为如此,具备开发功能的JDK所包含的JRE下边才会同时有server的JVM,而仅仅只是作为运行环境的JRE下,只需要server的jvm.dll就足够了。
注:JDK所提供的运行环境和工具度需要进行环境变量的配置之后才能使用,其中最为主要的配置就是把JDK的安装目录/bin目录设置为环境变量path值的一部分。
2>JRE是Java Runtime Environment的缩写,是Java程序的运行环境。既然是运行,肯定就会包含JVM,也就是咱们经常所说的Java虚拟机,还有所有的Java类库的class文件,都在lib目录下,并且都打包成了jar包。
JDK是Java开发工具,它不仅提供了Java程序运行的JRE,还提供了一系列的编译,运行等工具,如Javac,Java,javaw等。JRE只是Java程序的运行环境,它最核心的内容就是JVM(Java虚拟机)以及核心类库。
3>Tomcat和JDK是什么关系?
解析:tomcat是Java的web项目运行的容器之一;JDK是Java程序的运行环境,也就是说Java没有JDK是没法编辑运行的;Java运行必须依赖于JDK环境,但是不一定要用tomcat容器,如WebLogic、WebSphere等都是可以的。

2、== 和 equals 的区别是什么?
解析:二者功能不同、定义不同、运算速度不同。
1>功能不同:“”是判断两个变量或者实例是不是指向一个内存空间;“equals”是判断两个变量或者实例所指向的内存空间的值是不是相同。
2>定义不同:“equals”在Java中是一个方法,而“
”在Java中是一个运算符号。
3>运算速度不同:“”比“equals”运算速度快,因为“”只是比较引用,“equals”比“==”运算速度慢。
知识扩展:equals()方法的特点,如下
1>自反性:x.equals(x)返回true;
2>对称性:若x.equals(y)为true,则y.equals(s)也为true;
3>传递性:x.equals(y)返回true且y.equals(z)也返回true,则x.equals(z)也为true;
4>一致性:x.equals(y)的第一次调用为true,那么x.equals(y)的第二次、第三次、第n次调用也均为true,前提条件是没有修改x也没有修改y;
5>对于非空引用x,x.equals(null)永远返回为false。
3. 两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?
解析:首先,答案肯定是不一定的。同时反过来,equals为true,hashCode也不一定相同。
hashCode()返回该对象的哈希码值;equals()返回两个对象是否相等。
关于hashCode和equals是方法是有一些常规协定:
1>两个对象用equals()比较返回true,那么两个对象的hashCode()值必须返回相同的结果。
2>两个对象用equals()比较返回false,不要求hashCode()方法也一定返回不同的值,但是最好返回不同值,以提高哈希表性能。
3>重写equals()方法,必须重写hashCode()方法,以保证equals()方法相等时两个对象hashCode返回相同的值。
4.final在Java中有什么作用?
解析:final作为Java中的关键字可以用于三个地方。用于修饰类、类属性和类方法。
1>修饰类:表示该类不能被继承;
2>修饰变量:表示变量只能一次赋值之后值不能被修改(常量);
3>修饰方法:标识方法不能被重写。
5.Java中的Math.round(-1.5)等于多少?
解析:Math.round(1.5)的返回值是2,Math.round(-1.5)的返回值是-1。四舍五入的原理是在参数上加0.5然后做向下取整。
6.String属于基础的数据类型吗?
解析:String类并不是基础数据类型,而是一个类(class),是C++、Java等编程语言中的字符串。
String类是不可变的,对String类的任何改变都会返回一个新的String类对象。String对象是System.Char对象的有序集合,用于表示字符串。String对象的值是该有序集合的内容,并且该值是不可变的。
而Java的8大基本数据类型是:
1>逻辑类:boolean
2>文本类:char
3>整数类:byte、short、int、long
4>浮点型:double、float
7.Java中操作字符串都有哪些类?它们之间有什么区别?
解析:主要有一下三种:String、StringBuffer、StringBuilder
String类是不可变的对象,每次对String改变的时候都会产生一个新的对象。
StringBuffer和StringBuilder是可以改变对象的。
对于操作效率来说的话:StringBuilder>StringBuffer>String。
对于线程安全:StringBuiler是线程安全的,可以用于多线程。
不频繁的操作字符串,使用String,反之,StringBuffer和StringBuilder都要优于String,所以在项目中需要拼接字符串最好采用StringBuffer。
8.String str="i"跟String str = new String(“i”)一样吗?
解析:不一样,因为内存得分配方式不一样。String str = "i"的方式,Java虚拟机会将其分配到常量池中;而String str = new String(“i”)则会被分配到堆内存中。
9.如何将字符串反转?
解析如下:在这里插入图片描述
10.String类的常用方法有哪些?
解析:如下图,在这里插入图片描述
11. 抽象类必须要有抽象方法吗?
解析:抽象类不一定要有抽象方法,但是抽象方法的类一定是抽象类。
12. 普通类和抽象类有哪些区别?
解析:

  1. 抽象类不能被实例化。
  2. 抽象类可以有构造函数,被继承时子类必须继承父类一个构造方法,抽象方法不能被声明为静态。
  3. 抽象方法只需要声明,而不需要实现,抽象类中可以允许普通方法有主体。
  4. 含有抽象方法的类必须声明为抽象类
  5. 抽象类的子类必须实现抽象类中的所有方法,否则这个子类也是抽象类

13. 抽象类能使用 final 修饰吗?
解析:final关键字不能用来抽象类和接口。
14. 接口和抽象类有什么区别?
解析:1.抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2.抽象类要被子类继承,接口要被类实现。
3.接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4.接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5.抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
6.抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
7.抽象类里可以没有抽象方法
8.如果一个类里有抽象方法,那么这个类只能是抽象类
8.抽象方法要被实现,所以不能是静态的,也不能是私有的。
10.接口可继承接口,并可多继承接口,但类只能单根继承
15. java 中 IO 流分为几种?
解析:Java中的流分为两种,一种是字节流,一种是字符流。分别用四个抽象类来表示(每种流包括输入和输出两种,所以一共有四个):InputStream、OutputStream、Reader、Writer。Java中其他多种多样的流都是由它们派生出来的。读文本的时候用的是字符流,例如txt文件,读非文本的时候使用的是字节流,例如MP3,理论上任何文件都能够用字节流的方式读取,但是当读取的文件是文本的时候,为了能还原成文本你必须在经过一个转换的程序,相对于来说字符流就省略了这个麻烦,可以有方法直接读取。

如下图:

16. BIO、NIO、AIO 有什么区别?
解析:BIO:Block IO 同步阻塞IO,就是我们平常使用的传统IO,它的特点是使用简单,模式方便,并发处理能力低。 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情就会造成不必要的线程开销,当然可以通过线程池的结构来进行改善;
NIO:Non IO 同步非阻塞IO,是传统的IO的升级,客户端和服务端通过Channel(通道)通讯,实现了多路复用。 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器l轮询到连接有I/O请求时才启动一个线程进行处理;
AIO:Asynchronous IO是NIO的升级,也叫NIO2,实现了异步非阻塞IO,异步IO的操作基于事件和回调机制。异步非阻塞,服务器实现模式为一个有效的请求一个线程,客户端的I/O请求都是由OS先完成了在通知服务器应用去启动线程进行处理。
BIO是一个连接一个线程;NIO是一个请求一个线程;AIO是一个有效请求一个线程。

使用场景分布:BIO方式适用于连接数比较小且固定的架构,这种方式对服务器的要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解;NIO方式适用于连接数目多且连接数比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持;AIO方式适用于连接数目较多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较负责,JDK1.7开始支持。
17. Files的常用方法都有哪些?
解析:1.判断文件是否存在 Files.exists()

public static void main(String[] args) {
		Path path = Paths.get("E:/ceshi.txt");
		boolean pathExists = Files.exists(path, new LinkOption[] { LinkOption.NOFOLLOW_LINKS });
		System.out.println(pathExists);
	}

2.创建文件夹:Files.createDirectory()

public static void main(String[] args) {
		Path path = Paths.get("E:/ceshi");
        try {
            Files.createDirectory(path) ;
        } catch (IOException e) {
            e.printStackTrace();
        }
	}

3.复制文件:Files.copy()

public static void main(String[] args) throws FileNotFoundException, IOException {
		Path path = Paths.get("E:/ceshi.txt");
		Files.copy(path, new FileOutputStream(new File("E:/nnn.txt")));
	}

4.移动文件:Files.move()

public static void main(String[] args) throws FileNotFoundException, IOException {
		Path path = Paths.get("E:/nnn.txt");
        Path path2 = Paths.get("E:/ceshi/nnn.txt");
        Files.move(path,path2) ;
	}

5.删除一个文件或者目录(空目录):Files.delete()

public static void main(String[] args) throws FileNotFoundException, IOException {
		Path path = Paths.get("E:/ccc");
        Files.delete(path);
	}

6.删除一个文件或者目录(空目录),如果存在就删除:Files.deleteIfExists()

public static void main(String[] args) throws FileNotFoundException, IOException {
		Path path = Paths.get("E:/ccc");
        Files.deleteIfExists(path);
	}

18. 泛型 中"?" 与“object” 的区别?
解析:泛型object的类型其实这个object就是指object,不能考虑它是所有类的父类,如下

public static void main(String[] args) {
		List<String> ls = new ArrayList<String>();//1
		List<Object> lo = ls; //2
	}

上面的语句2是不能编译通过的,也就是说List 不是List的子类型,原因是什么呢?假设语句2编译通过,那么lo就可以put不是Sting类型的对象到集合中,那么取出来的时候就会发生千奇百怪的问题,所以Java编译器对语句2报错。

泛型?类型其实表示它的类型是未知的,可以认为?是泛型中所有类型的父类型。

public static void main(String[] args) {
		List<String> ls = new ArrayList<String>();
		ls.add("我是String类型的数组");
		printCollection(ls);
	}
	
	public static void printCollection(Collection<?> c) {
	    for (Object e : c) {
	        System.out.println(e);
	    }
	}

如上,printCollection可以传入任何类型的Collection。

19. 值传递和引用传递的区别?
解析:值传递:只要是基本类型的传递都是值传递
引用传递:针对于基本类型进行封装,对封装进行传递,就是引用传递。如下:
值传递(int类型):

public static void main(String[] args) {
		int int1 = 10;
		int int2 = int1;
		System.out.println("int1==="+int1);
		System.out.println("int2==="+int2);
		
		int2 = 20;
		System.out.println("改变之后");
		System.out.println("int1==="+int1);
		System.out.println("int2==="+int2);
	 
	}

运行以上代码后得到如下结果:

int1===10
int2===10
改变之后
int1===10
int2===20

根据结果你会发现int类型的传递,当int2发生改变的时候,int1的值是没有任何变化的,所以基本类型都是根据它的值去发生传递的,传递之后,int2的值的改变已经和int1没有任何的关系了。

String类型的传递:

public static void main(String[] args) {
		String str1 = "str1";
		String str2 = str1;
		
		System.out.println("str1==="+str1);
		System.out.println("str2==="+str2);
		
		str2 = "str2";
		System.out.println("改变之后");
		System.out.println("str1==="+str1);
		System.out.println("str2==="+str2);
	 
	}

运行以上代码后得到如下结果:

str1===str1
str2===str1
改变之后
str1===str1
str2===str2

结果证明String类型和int类型结果一样,但是表达方式是不一样的,String类型的表达方式和Integer是一样的,都是重新new的方式去传递值的。
String str2 = str1;可以理解为String str2 = new String(str1);把str1当作是参数传递过去的,重新new了一个新的String,所以str2和str1的值虽然是一样的,但是str2和str1却不是同一个String.

类传递(引用传递):
代码:创建一个TestDemo的类,里面有一个String的变量txtProtected

public static void main(String[] args) {
		TestDemo testDemo1 = new TestDemo();
		testDemo1.setTxtProtected("potected");

		TestDemo testDemo2 = testDemo1;

		TestDemo testDemo3 = new TestDemo();
		testDemo3.setTxtProtected(testDemo1.getTxtProtected());
		System.out.println("testDemo1==" + testDemo1.getTxtProtected());
		System.out.println("testDemo2==" + testDemo2.getTxtProtected());
		System.out.println("testDemo3==" + testDemo3.getTxtProtected());

		testDemo2.setTxtProtected("testDemo2");
		System.out.println("改变之后");
		System.out.println("testDemo1==" + testDemo1.getTxtProtected());
		System.out.println("testDemo2==" + testDemo2.getTxtProtected());
		System.out.println("testDemo3==" + testDemo3.getTxtProtected());

	}

运行代码的结果:

testDemo1==potected
testDemo2==potected
testDemo3==potected
改变之后
testDemo1==testDemo2
testDemo2==testDemo2
testDemo3==potected

从结果可以看出testDemo1和testDemo2是一样的,但是testDemo3和testDemo1(testDemo2)结果是不一样的,那么testDemo2这个类的指针所指向的地方是和testDemo1所指向的地方是一个,所以testDemo2的txtProtected的值发生变化。
testDemo3和testDemo1的值确是不一样的,其实testDemo3的方式和上面的String的方式是一样的,是new出来的。
二、容器
18. java 容器都有哪些?
解析:首先什么是容器,容器是指容纳物料并以壳体为主的基本装置。那么Java中的容器类类库的用途是保存对象。Java集合类是一种特别的有用的工具类,可以用于存储数量不等的d对象,并可以实现常用的数据结构,如栈、队列等等,Java集合就像是一种容器,可以把多个对象放到该容器中。Java中的容器也叫集合,是专门用来管理对象的对象。
那么容器都有那些?如下:
List、ArrayList、Vector及map、HashTable、HashMap、Hashset
ArrayList和hashMap是异步的,Vector和HashTable是同步的,Vector和HashTable是线程安全的,而ArrayList和hashMap的线程不安全的,因为同步需要花时间,所以ArrayList和hashMap的执行效率要优于Vector和HashTable。

19. Collection 和 Collections 有什么区别?
解析:Collection是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,实现该接口的类主要有List和Set,该接口的设计目标是为各种具体的集合提供最大化的统一操作方式。
Collections是针对集合类的一个包装类,它提供了一系列的j静态方法以实现各种集合的搜索、排序、线程安全等操作,其中大多数方法都是用来处理线性表。Collections不能实例化,如同一个工具类,服务于Collection,若是使用collections类的方法的时候,对应的collection的对象则为null,则这些方法都会抛出NullPointerException。之前我们见过的包装类还有Arrays,它是为数组提供服务的。
以下为Collections的常用方法:

public static void main(String[] args) {
		List<String> list = new ArrayList<>();
        for (int i = 0 ;i<5;i++){
            list.add("a"+i);
        }
        System.out.println(list);  //输出 [a0, a1, a2, a3, a4]
 
        //Collections.shuffle(list);   //1、将list随机排列
        //System.out.println(list);   //输出[a2, a3, a0, a1, a4]
 
        Collections.reverse(list);  //2、将集合中的内容逆序
        System.out.println(list);    //输出 [a4, a3, a2, a1, a0]
 
        Collections.sort(list);  //3、将集合中的内容进行排序
        System.out.println(list);   //输出[a0, a1, a2, a3, a4]
 
 
        Collections.fill(list,"hello world");  //4、填充集合
        System.out.println(list);   //输出[hello world, hello world, hello world, hello world, hello world]
	}

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

解析:List:有序集合,元素可以重复
Set:不重复集合,LinkedHashSet按照插入排序,SortedSet可排序,HashSet无序
Map:键值对集合,存储键值之间的映射;key无序,唯一;value不允许有序,允许重复
详解:https://www.cnblogs.com/IvesHe/p/6108933.html
21. HashMap 和 Hashtable 有什么区别?
解析:1.线程是否安全:HashMap是非线程安全的,HashTable是线程安全的;hashTable内部的方法都是经过synchronized修饰;
2.效率:因为线程安全的问题,hashMap效率比hashTable效率高一点,另外,hashTable基本在代码中已经淘汰,不要在代码中使用它;
3.对 null key 和 null value的支持:hashMap中null可以作为键,这样的键只能有一个,可以有一个或者多个的键对应的值为null,但是在hashTable中put进的键值只要有一个是null,直接就会抛出NullPointerException;
4.初始容量大小和每次扩充容量大小的不同,创建时如果不指定容量初始值,hashTable的初始值为11,之后每次扩充,变为原来的2n+1。hashMap默认的初始大小为16,之后每次扩充变更为原来的2倍,。创建时给了初始大小,那么hashTable会直接使用你给定的大小,而hashMap会扩充为2的幂次方大小;
5.底层数据结构:JDK1.8之后,hashMap在解决哈希冲突时候有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。
22. 如何决定使用 HashMap 还是 TreeMap?
解析:TreeMap<keyt,value>的key值要求实现java.lang.Comparable,所以迭代的时候TreeMap默认是按照key值升序排列的;TreeMap的实现也是基于红黑树结构。
而HashMap<k,v>的key值实现散列hashCode(),分布是散列的均匀的,不支持排序,数据结构主要是数组,链表或者红黑树,所以查询的时候使用HashMap,增加、快速创建的时候使用TreeMap。
23. 说一下 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覆盖。

24. 说一下 HashSet 的实现原理?
25. ArrayList 和 LinkedList 的区别是什么?
解析:1>ArrayList的实现是基于数组,LinkedList的实现是基于链表;
2>对于随机访问,ArrayList要由于LinkedList;
3>对于删除和插入操作,LinkedList由于ArrayList;
26. 如何实现数组和 List 之间的转换?
27. ArrayList 和 Vector 的区别是什么?
28. Array 和 ArrayList 有何区别?
29. 在 Queue 中 poll()和 remove()有什么区别?
30. 哪些集合类是线程安全的?
31. 迭代器 Iterator 是什么?
32. Iterator 怎么使用?有什么特点?
33. Iterator 和 ListIterator 有什么区别?
34. 怎么确保一个集合不能被修改?
三、多线程
35. 并行和并发有什么区别?
解析:并发(concurrency) 并行(parallellism)
解释一:并行是指两个或者多个事件在同一时刻发生;而并发是指两个或者多个事件在同一时间间隔发生;
解释二:并行是指在不同实体上的多个事件;并发实在同一个实体上的多个事件;
解释三:在同一台处理器上“同时”处理多个任务,在多台处理器上同时处理多个任务,如hadoop分布式集群。
所以并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。
36. 线程和进程的区别?
37. 守护线程是什么?
38. 创建线程有哪几种方式?
39. 说一下 runnable 和 callable 有什么区别?
40. 线程有哪些状态?
41. sleep() 和 wait() 有什么区别?
42. notify()和 notifyAll()有什么区别?
43. 线程的 run()和 start()有什么区别?
44.创建线程池有哪几种方式?
45.线程池都有哪些状态?
46. 线程池中 submit()和 execute()方法有什么区别?
47. 在 java 程序中怎么保证多线程的运行安全?
48. 多线程锁的升级原理是什么?
49. 什么是死锁?
50. 怎么防止死锁?
51. ThreadLocal 是什么?有哪些使用场景?
52. 说一下 synchronized 底层实现原理?
53. synchronized 和 volatile 的区别是什么?
54. synchronized 和 Lock 有什么区别?
55. synchronized 和 ReentrantLock 区别是什么?
56. 说一下 atomic 的原理?
四、反射
57. 什么是反射?
58. 什么是 java 序列化?什么情况下需要序列化?
59. 动态代理是什么?有哪些应用?
60. 怎么实现动态代理?
五、对象拷贝
61. 为什么要使用克隆?
62. 如何实现对象克隆?
63. 深拷贝和浅拷贝区别是什么?
六、Java Web
64. jsp 和 servlet 有什么区别?
65. jsp 有哪些内置对象?作用分别是什么?
66. 说一下 jsp 的 4 种作用域?
67. session 和 cookie 有什么区别?
68. 说一下 session 的工作原理?
69. 如果客户端禁止 cookie 能实现 session 还能用吗?
70. spring mvc 和 struts 的区别是什么?
71. 如何避免 sql 注入?
72. 什么是 XSS 攻击,如何避免?
73. 什么是 CSRF 攻击,如何避免?
七、异常
74. throw 和 throws 的区别?
75. final、finally、finalize 有什么区别?
76. try-catch-finally 中哪个部分可以省略?
77. try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
78. 常见的异常类有哪些?
八、网络
79. http 响应码 301 和 302 代表的是什么?有什么区别?
80. forward 和 redirect 的区别?
81. 简述 tcp 和 udp的区别?
82. tcp 为什么要三次握手,两次不行吗?为什么?
83. 说一下 tcp 粘包是怎么产生的?
84. OSI 的七层模型都有哪些?
85. get 和 post 请求有哪些区别?
86. 如何实现跨域?
87. 说一下 JSONP 实现原理?
九、设计模式
88. 说一下你熟悉的设计模式?
89. 简单工厂和抽象工厂有什么区别?
十、Spring/Spring MVC
90. 为什么要使用 spring?
91. 解释一下什么是 aop?
92. 解释一下什么是 ioc?
93. spring 有哪些主要模块?
94. spring 常用的注入方式有哪些?
95. spring 中的 bean 是线程安全的吗?
96. spring 支持几种 bean 的作用域?
97. spring 自动装配 bean 有哪些方式?
98. spring 事务实现方式有哪些?
99. 说一下 spring 的事务隔离?
100. 说一下 spring mvc 运行流程?
101. spring mvc 有哪些组件?
102. @RequestMapping 的作用是什么?
103. @Autowired 的作用是什么?
十一、Spring Boot/Spring Cloud
104. 什么是 spring boot?
解析:spring boot就是spring开源框架下边的子项目,是Spring的一站式解决方案,主要简化了Spring的使用难度,降低了对配置文件的要求,使得开发人员更容易上手。
105. 为什么要用 spring boot?
解析:Spring boot有很多的优点:
1>简化了Spring的配置文件;
2>没有代码和XML文件的生成;
3>内置tomcat;
4>能够独立运行;
5>简化监控。
106. spring boot 核心配置文件是什么?
解析:Spring boot核心的两个配置文件:
1>bootstrap(.properties或者.yml):bootstrap由父ApplicationContext加载的,比application优先加载,且bootstrap里边的属性不能被覆盖;
2>application(.yml或者.properties):用于Spring boot项目的自动化配置。
107. spring boot 配置文件有哪几种类型?它们有什么区别?
解析:配置文件有.yml和.properties格式,它们的主要区别是书法风格不同。
.properties配置文件以键值对配置。
如下:
在这里插入图片描述
.yml配置文件:
在这里插入图片描述
注意:.yml格式不支持@PropertySource注解导入。
108. spring boot 有哪些方式可以实现热部署?
1>模板热部署
在Spring boot中,添加了对各种模板框架的支持,当然也就有对模板的配置,模板引擎的页面默认是开启缓存的,如果修改了页面的内容,则刷新页面是得不到修改后的页面的,因此我们可以在application.properties中关闭模板引擎的缓存。如下:
Thymeleaf的配置:spring.thymeleaf.cache=false
FreeMarker的配置:spring.freemarker.cache=false
Groovy的配置:spring.groovy.template.cache=false
Velocity的配置:spring.velocity.cache=false
2>使用IDE的调试模式Debug实现热部署
此种方式为最简单最快速的一种热部署方式,Idea、Eclipse都有,运行系统时使用Debug模式,无需装任何插件即可,但是无法对配置文件,方法名称改变,增加类及方法进行热部署,使用范围有限。
3>spring-boot-devtools
在 Spring Boot 项目中添加 spring-boot-devtools依赖即可实现页面和代码的热部署。
如下:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-devtools</artifactId>
</dependency>

此种方式的特点是作用范围广,系统的任何变动包括配置文件修改、方法名称变化都能覆盖。

但是缺点也非常明显,它是采用文件变化后重启的策略来实现了,主要是节省了我们手动点击重启的时间,提高了实效性,在体验上会稍差。

spring-boot-devtools 默认关闭了模版缓存,如果使用这种方式不用单独配置关闭模版缓存。
4>Spring Loaded
此种方式与Debug模式类似,适用范围有限,但是不依赖于Debug模式启动,通过Spring Loaded库文件启动,即可在正常模式下进行实时热部署。此种需要在 run confrgration 中进行配置。
5>JRebel
Jrebel是Java开发最好的热部署工具,对 Spring Boot 提供了极佳的支持,JRebel为收费软件,试用期14天,可直接通过插件安装。
109. jpa 和 hibernate 有什么区别?
110. 什么是 spring cloud?
111. spring cloud 断路器的作用是什么?
112. spring cloud 的核心组件有哪些?
十二、Hibernate
113. 为什么要使用 hibernate?
解析:hibernate对JDBC访问数据库的代码做了封装,大大简化了数据库访问层繁琐的代码;hibernate是一个基于jdbc的主流持久层框架,是一个优秀的orm实现,它很大程度的j简化了dao层的编码工作;hibernate使用Java的反射机制,而不是字节码增强程序类实现透明性;它的性能非常好,因为它是一个轻量级的框架,映射的灵活性很出色,它支持很多关系型数据库,从一对一到多对多各种复杂的数据关系;它本身的性能不是很好,存在很多的优化手段(一级缓存、二级缓存、查询缓存、抓取策略)。
114. 什么是 ORM 框架?
解析:1.对象-关系映射简称ORM。面向对象的开发是当今企业级应用开发环境中的主流框架,关系数据库是企业级应用开发环境中永久存放数据的主流存储数据系统,对象和关系数据是业务实现的两种表现形式,业务实体在内存中表现为对象,在数据中表现为关系数据,内存中的对象之间存在关联和继承的关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系,因此,对象-关系映射系统一般以中间件的形式存在,主要实现程序对象到关系数据库的映射。
2.为什么要使用ORM?当我们实现一个应用程序时,我们可能会写很多数据访问层的代码,从数据库保存、删除、读取对象信息,而这些代码都是重复的,而是用ORM会大大减少代码的重复性,对象关系映射主要实现程序对象到关系数据库数据的映射;
3.对象-关系映射解释:
A.简单:ORM以最简单的形式建模,比如ORM会将mysql中的一张表映射成一个Java类(模型),表的字段就是这个类的成员变量;
B.精确:ORM使Mysql数据表按照统一的方式都映射成Java类,使系统在代码层面保持准确统一;
C.易懂:ORM使数据库结构文档化,比如mysql数据库就被ORM转换成了程序员可以读懂的Java类,Java程序员只要把注意力放到他擅长的Java层面;
D.易用:ORM包含对持久类对象进行CRUD操作的API,例如create()、update()、save()、load()、find()、find_all()、where()等,也就是将sql查询全部封装成了编程语言中的函数,通过函数的链表形式组合成最终的sql语句,通过这种封装避免了不规范、冗余、风格不统一的sql语句,可以避免很多的认为bug,方便编码风格的统一和后期维护;
4.ORM的优缺点:优点1>提高开发效率,降低开发成本;2>使开发更加对象化;3>可移植;4>可以很方便的引入数据缓存之类的附加功能。缺点1>自动化进行关系数据库的映射需要消耗系统性能,但是影响并不大;2>在处理多表联查、where条件复杂的查询时,ORM语句变得复杂。
5.常用的框架:hibernate全自动,需要手写hql语句;ibatis半自动,自己写sql语句,可操作性强,小巧;mybatis;eclipseLink;jfinal。
115. hibernate 中如何在控制台查看打印的 sql 语句?
解析:只需要在配置文件中加如下语句:

<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.Oracle9Dialect
hibernate.show_sql=true
</value>
</property>

116. hibernate 有几种查询方式?
解析:常见的主要分为三种:HQL、QBC(命名查询)、以及使用原生sql查询
一、HQL:HQL(Hibernate Query Language)提供了丰富灵活的查询方式,使用HQL进行查询也是Hibernate官方推荐使用的查询方式。HQL在语法结构上和SQL语句十分的相同,所以可以很快的上手进行使用。使用HQL需要用到Hibernate中的Query对象,该对象专门执行HQL方式的操作。

// 查询所有示例:

session.beginTransaction();
String hql = "from User"; // from 后跟的是要查询的对象,而不是表
Query query = session.createQuery(hql);
List<User> userList = query.list();
for(User user:userList){
System.out.println(user.getUserName());
}
session.getTransaction().commit();

// 带where条件查询示例:

session.beginTransaction();
String hql = "from User where userName = 'James'";
Query query = session.createQuery(hql);
List<User> userList = query.list();
for(User user:userList){
System.out.println(user.getUserName());
}
session.getTransaction().commit();
/*
在HQL中where语句中使用的是持久化对象的属性名,如上面示例中的userName。当然在HQL中也可以使用别名
*/
String hql = "from User as u where u.userName = 'James'";
/*
过滤条件
在where语句中还可以使用各种过滤条件,如:=、<>、<、>、>=、<=、between、not between、
in、not in、is、like、and、or等
*/

统计和分组查询:

session.beginTransaction();
String hql = "select count(*),max(id) from User";
Query query = session.createQuery(hql);
List nameList = query.list();
for(Object obj:nameList){
Object[] array = (Object[]) obj;
System.out.println("count:" + array[0]);
System.out.println("max:" + array[1]);
}
session.getTransaction().commit();
/*
该条sql语句返回的是单条数据,所以还可以这样写
单列数据用Object,多列数据用Object[]
*/
Object[] object = (Object[]) query.uniqueResult();
System.out.println("count:" + object[0]);
System.out.println("max:" + object[1]);

HQL占位符:

session.beginTransaction();
String hql = "from User where userName = ?";
Query query = session.createQuery(hql);
// 索引从0开始
query.setString(0, "James");
List<User> userList = query.list();
for(User user:userList){
System.out.println(user.getUserName());
}
session.getTransaction().commit();

HQL分页:

session.beginTransaction();
String hql = "from User";
Query query = session.createQuery(hql);
query.setFirstResult(0);
query.setMaxResults(2);
List<User> userList = query.list();
for(User user:userList){
System.out.println(user.getUserName());
}
session.getTransaction().commit();

二、QBC(Query By Criteria)查询
Criteria对象提供了一种面向对象的方式查询数据库。Criteria对象需要使用Session对象来获得。
一个Criteria对象表示对一个持久化类的查询。
// 查询所有

session.beginTransaction();
Criteria c = session.createCriteria(User.class);
List<User> userList = c.list();
for(User user:userList){
System.out.println(user.getUserName());
}
session.getTransaction().commit();

// where

session.beginTransaction();
Criteria c = session.createCriteria(User.class);
c.add(Restrictions.eq("userName", "James"));
List<User> userList = c.list();
for(User user:userList){
System.out.println(user.getUserName());
}
session.getTransaction().commit();

等等查询方式;

三、原生SQL查询:
示例:

session.beginTransaction();
String sql = "select id,username,userpwd from t_user";
List list = session.createSQLQuery(sql).list();
for(Object item : list){
Object[] rows = (Object[]) item;
System.out.println("id:" + rows[0] + "username:"
+ rows[1] + "userpwd:" + rows[2]);
}
session.getTransaction().commit();

117. hibernate 实体类可以被定义为 final 吗?
解析:可以,但是不推荐这么使用,因为hibernate会使用代理模式在延迟关联的情况下提高性能,如果你把实体类定义为final之后,因为Java不允许对final类进行扩展,所以hibernate就无法使用代理了,如此就限制了使用可以提高性能的手段。
118. 在 hibernate 中使用 Integer 和 int 做映射有什么区别?
解析:hibernate的PO类中 经常会用到int 型得变量 ,这个时候如果使用基本类型的变量(int ), 如果数据库中对应的存储数据是 null 时使用PO类进行获取数据, 会出现类型转换异常,如果使用的是对象类型(Integer)这个时候则不会报错。
119. hibernate 是如何工作的?
解析:1.读取并解析配置;
2.读取并解析映射信息,创建Session Factory;
3.打开session;
4.持久化操作;
5.提交事务;
6.关闭session;
7.关闭session factory。
为什么要用:
1.对jdbc访问数据库的代码做了封装,大大简化了数据访问层的繁琐重复性代码;
2.Hibernate是一个基于jdbc的持久层框架,是一个优秀的orm实现,它很大程度上简化了dao层的编码工作;
3.hibernate使用Java的反射机制,而不是字节码增强程序来实现透明性;
4.hibernate的性能非常好,因为它是一个轻量级的框架,映射的灵活性很出色,它支持各种关系型数据库,从一对一到多对多各种复杂关系。
120. get()和 load()的区别?
解析:hibernate中根据id单条获取对象的方式有两种,分别是get()和load()。
1.get(),使用get()来根据ID获取单条查询,User user=session.get(User.class, “1”); 当get()方法被调用的时候会立即发送sql语句,并且返回的对象也是实际的对象,使用get()查询和普通的对象查询没有什么区别;
2.load(),当使用load()查询时情况就会变得很是不一样,User user=session.load(User.class, “1”); 当调用此方法时会返回一个目标对象的代理对象,在这个代理对象中只存储了目标对象的ID值,只有当调用除ID值以外的属性的时候才发出sql语句查询的。
所以:get返回的是查询出来的实体对象,而load查询出来的是一个目标实体的代理对象;get在调用的时候就立即发送sql,而load在访问非id属性的时候才会发出查询语句并且将代理对象target填充上,但是如果这个动作发生在session关闭之后的话就会抛出LazyInitializationException;查询结果为空时get抛出NullPointerException,而load抛出ObjectNotFoundException。
121. 说一下 hibernate 的缓存机制?
解析:hibernate的缓存分为一级缓存和二级缓存
一级缓存就是session级别的缓存,在事务范围内有效,内置的不能被卸载。二级缓存是sessionFactor级别的缓存,从应用启动到应用结束有效。是可选的,默认是没有二级缓存的,需要手动开启,保存数据库后,缓存在内存中保留了一份,如果更新了数据库就要同步更新。
什么样的数据适合放在二级缓存中:1>很少被修改的数据 帖子最后的回复时间;2>经常被查询的数据 电商的地点;3>不是很重要的数据 偶尔会出现在并发中的数据;4>不会被并发访问的数据 ;5>常量数据。

为什么要使用hibernate缓存:hibernate是一个持久层框架,经常访问物理数据库,为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能。缓存的数据是对物理数据的复制,应用程序在运行时从缓存中读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据。为了提供访问速度,把磁盘或数据库访问变成了从内存中访问。

hibernate的原理是怎样的?hibernate缓存包括了两大类,hibernate一级缓存和hibernate二级缓存。hibernate一级缓存被称为session缓存。session缓存内置不能被卸载,session的缓存是事务范围的缓存(session对象的生命周期通常对应一个数据库事务或者应用事务)。一级缓存中,持久化类的每个实例都具有唯一的OID;
hibernate的二级缓存又被称为sessionFactory的缓存,由于sessionfactory对象的生命周期和应用程序的整个过程对应,因此hibernate二级缓存是进程范围h或者集群范围的缓存,有可能出现并发的问题,因此需要采用适当的访问策略,该策略被缓存的数据提供了事务隔离级别,第二级缓存是可选的,是一个可配置的插件,默认下sessionFactory不会启用这个插件。hibernate提供了org.hibernate.cache.CacheProvider接口,它充当缓存插件与hibernate之间的适配器。
122. hibernate 对象有哪些状态?
解析:临时状态:刚用new语句创建,还没有被持久化,并且不处在sessIon的缓存中,处于临时状态的Java对象被称为临时对象;
持久化状态:已经被持久化,并且加入到session的缓存中,处于持久化状态的Java对象被称为持久化对象;
删除状态:不在处于session缓存中,并且sessoion已经计划将其从数据库中删除,处于删除状态的Java对象被称为删除对象;
游离状态:已经被持久化,但不在处于session的缓存中,处于游离状态的Java对象被称为游离对象。
123. 在 hibernate 中 getCurrentSession 和 openSession 的区别是什么?
解析:在进行配置信息管理时,我们一般进行一下简单步骤:
Configuration cfg = new Configuration(); // 获得配置信息对象
SessionFactory sf = cfg.configure().buildSessionFactory(); //解析并建立Session工厂

  1. Session session = sf.getCurrentSession(); // 获得Session
  2. Session session = sf.openSession(); // 打开Session
    对于上述的两个方法,有以下区别:
  3. openSession 从字面上可以看得出来,是打开一个新的session对象,而且每次使用都是打开一个新的session,假如连续使用多次,则获得的session不是同一个对象,并且使用完需要调用close方法关闭session。
  4. getCurrentSession ,从字面上可以看得出来,是获取当前上下文一个session对象,当第一次使用此方法时,会自动产生一个session对象,并且连续使用多次时,得到的session都是同一个对象,这就是与openSession的区别之一,简单而言,getCurrentSession 就是:如果有已经使用的,用旧的,如果没有,建新的。

注意:在实际开发中,往往使用getCurrentSession多,因为一般是处理同一个事务(即是使用一个数据库的情况),所以在一般情况下比较少使用openSession或者说openSession是比较老旧的一套接口了;

对于getCurrentSession 来说,有以下一些特点:

1.用途,界定事务边界

2.事务提交会自动close,不需要像openSession一样自己调用close方法关闭session

3.上下文配置(即在hibernate.cfg.xml)中,需要配置:

thread

(需要注意,这里的current_session_context_class属性有几个属性值:jta 、 thread 常用 , custom、managed 少用 )

a).thread使用connection 单数据库连接管理事务

b).jta (Java transaction api) Java 分布式事务管理 (多数据库访问),jta 由中间件提供(JBoss WebLogic 等, 但是tomcat 不支持)

下面是openSession 和 getCurrentSession 简单实例的区别 :

1.openSession方式 :

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import com.hibernate.model.Student; // 注意包路径

public class StudentTest {
public static void main(String[] args) {

Student s = new Student();
s.setId(1);
s.setName("s1");
s.setAge(1);

Configuration cfg = new Configuration(); // 获得配置信息对象
SessionFactory sf = cfg.configure().buildSessionFactory(); //解析并建立Session工厂
Session session = sessionFactory.openSession(); // 打开Session

session.beginTransaction(); // 看成一个事务,进行操作
session.save(s); // 会找到 Student 这个类,寻找set方法
session.getTransaction().commit(); // 提交对数据的操作
session.close();

sf.close();

}

}

2.getCurrentSession方式 :

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import com.hibernate.model.Student; // 注意包路径

public class StudentTest {
public static void main(String[] args) {

Student s = new Student();
s.setId(1);
s.setName("s1");
s.setAge(1);

Configuration cfg = new Configuration(); // 获得配置信息对象
SessionFactory sf = cfg.configure().buildSessionFactory(); //解析并建立Session工厂
Session session = sessionFactory.getCurrentSession(); // 打开Session

session.beginTransaction(); // 看成一个事务,进行操作
session.save(s); // 会找到 Student 这个类,寻找set方法
session.getTransaction().commit(); // 提交对数据的操作

sf.close();

}

}

Student 类代码 :

package com.hibernate.model;

public class Student {
private int id;
private String name;
private int age;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

124. hibernate 实体类必须要有无参构造函数吗?为什么?
解析:首先答案是肯定的。
原因:hibernate框架会调用这个默认的构造方法来构造实例对象,即class类的newInstance方法,这个方法就是通过调用默认构造方法来创建实例对象的。
当查询的时候返回一个实体类是一个对象实例,是hibernate动态通过反射生成的反射的Class.forName(“className”).newInstance()需要对应的类提供一个无参构造方法,必须有个无参的构造方法将对象创建出来,单从Hibernate的角度讲 他是通过反射创建实体对象的 所以没有默认构造方法是不行的,另外Hibernate也可以通过有参的构造方法创建对象。
如果你没有提供任何构造方法,虚拟机会自动提供默认构造方法(无参构造器),但是如果你提供了其他有参数的构造方法的话,虚拟机就不再为你提供默认构造方法,这时必须手动把无参构造器写在代码里,否则new Xxxx()是会报错的,所以默认的构造方法不是必须的,只在有多个构造方法时才是必须的,这里“必须”指的是“必须手动写出来”。

十三、Mybatis
125. mybatis 中 #{}和 ${}的区别是什么?
126. mybatis 有几种分页方式?
127. RowBounds 是一次性查询全部结果吗?为什么?
128. mybatis 逻辑分页和物理分页的区别是什么?
129. mybatis 是否支持延迟加载?延迟加载的原理是什么?
130. 说一下 mybatis 的一级缓存和二级缓存?
131. mybatis 和 hibernate 的区别有哪些?
132. mybatis 有哪些执行器(Executor)?
133. mybatis 分页插件的实现原理是什么?
134. mybatis 如何编写一个自定义插件?
十四、RabbitMQ
135. rabbitmq 的使用场景有哪些?
136. rabbitmq 有哪些重要的角色?
137. rabbitmq 有哪些重要的组件?
138. rabbitmq 中 vhost 的作用是什么?
139. rabbitmq 的消息是怎么发送的?
140. rabbitmq 怎么保证消息的稳定性?
141.rabbitmq 怎么避免消息丢失?
142. 要保证消息持久化成功的条件有哪些?
143. rabbitmq 持久化有什么缺点?
144. rabbitmq 有几种广播类型?
145. rabbitmq 怎么实现延迟消息队列?
146. rabbitmq 集群有什么用?
147. rabbitmq 节点的类型有哪些?
148. rabbitmq 集群搭建需要注意哪些问题?
149. rabbitmq 每个节点是其他节点的完整拷贝吗?为什么?
150. rabbitmq 集群中唯一一个磁盘节点崩溃了会发生什么情况?
151. rabbitmq 对集群节点停止顺序有要求吗?
十五、Kafka
152. kafka 可以脱离 zookeeper 单独使用吗?为什么?
153. kafka 有几种数据保留的策略?
154. kafka 同时设置了 7 天和 10G 清除数据,到第五天的时候消息达到了 10G,这个时候 kafka 将如何处理?
155. 什么情况会导致 kafka 运行变慢?
156. 使用 kafka 集群需要注意什么?
十六、Zookeeper
157. zookeeper 是什么?
158. zookeeper 都有哪些功能?
159. zookeeper 有几种部署模式?
160. zookeeper 怎么保证主从节点的状态同步?
161. 集群中为什么要有主节点?
162. 集群中有 3 台服务器,其中一个节点宕机,这个时候 zookeeper 还可以使用吗?
163. 说一下 zookeeper 的通知机制?
十七、MySql
164. 数据库的三范式是什么?
解析:1.第一范式(1NF)表示字段不可分,每个字段是原子级别的,如果一个字段为ID,那它就是一个ID,不能在把它分成了两个字段,不能说要把一个人的ID、姓名、班级都塞到一个字段中,这样是不合适的,在以后的应用场景中会造成很大的影响;2.第二范式(2NF)有主键,非主键字段依赖主键,ID字段就是主键,它能表示这条数据是唯一的,“unique”表示唯一的、不允许重复的,确实它经常会修饰某个字段,保证该字段的唯一性,然后在设置该字段喂主键;3.第三范式(3NF)非主键字段不能相互依赖,比如,在student表中,班级编号受人员编号的影响,如果在这个表中在插入班级的班主任、数学老师等信息,这样肯定是不合适的,这样就会造成班级有多个,那么每个班级的班主任和数学老师都会出现多条数据,而我们理想中是一个班级对应一个班主任和数学老师,这样的话就行成了class表,那么student表和class靠那个字段来关联呢,肯定通过“classNo”,这个字段也叫做两个表的外键。
165. 一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?
解析:id为6。
166. 如何获取当前数据库版本?
解析:在navicat界面输入select version();如下:
在这里插入图片描述
167. 说一下 ACID 是什么?
解析:此问题为mysql的事务基本要素:
1.原子性(Atomicity):事务开始所有的操作后,要么全部做完,要么全部不做,不可能停滞在中间的环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像是没有发生过一样,也就是说事务是一个不可分割的整体,就像化学中的原子,是物质构成的基本单位;
2.一致性(Consistency):事务开始前和结束后,数据库的完整性没有被破坏,比如A向B转账,不可能A扣掉了钱,B却没有收到,其实一致性也是原子性的一种表现;
3.隔离性(IsoIation):同一时间只允许一个事务请求一条数据,不同的事务之间没有任何的干扰,比如A正在从一张银行卡中取钱,在A取钱的过程中B不可以向这张卡转账。
4.持久性(Durability):事务完成之后,事务对数据库的所有的更新数据将被保存,不能回滚。
168. char 和 varchar 的区别是什么?
解析:首先char和varchar都是用来存储字符串的,区别在于char有固定的长度,varchar没有固定的长度,属于可变长的字符类型。
char(M)类型的数据列中,每个值都占用M个字节,如果某个长度小于M,mysql会在它的右边用空格字符来补足,char长度固定,所有在处理速度上要比varchar快很多,但是费存储空间,所以存储不大,对于在速度上有要求的可以使用char类型。
总的来说,char定长,存储效率不如varchar,对于短数据的存储优于varchar,因为在大数据量没有索引的情况下,mysql走的是主键创建的索引,通过主键查询,数据量大效率的瓶颈是磁盘的io,当数据量大时,char是固定长度,占用的磁盘空间比较大,查询的效率就会降低,在不同的场景下,瓶颈是不同的。
169. float 和 double 的区别是什么?
解析:float类型表示单精度浮点数值,double类型表示双精度浮点类型,float和double都是浮点型,而decimal是定点型。
170. mysql 的内连接、左连接、右连接有什么区别?
解析:首先创建一张学生表,一张分数表,并且添加几条数据进去,如下
在这里插入图片描述
用通俗得话来说得话,内连接是只有符合符合条件才会显示,左连接 左边得表是全部得数据,右边只有符合条件得才会有数据;右连接 右边的表是全部数据,左边得只有符合条件得才会有数据;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Mysql不支持全连接FULL JOIN,不过可以通过UNION来模拟
在这里插入图片描述
171. mysql 索引是怎么实现的?
解析:mysql索引采用的是B+tree,InnoDB 的数据文件本身就是索引文件。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶结点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。
172. 怎么验证 mysql 的索引是否满足需求?
解析:explain关键字,加在select的前面,possible_keys会显示索引名;
173. 说一下数据库的事务隔离?
解析:数据库隔离级别有四种:
1.未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据;
2.提交读(Read committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别(不重复读);
3.可重复读(Repeated Read ):可重复读,在同一个事务内的查询都是事务开始时刻是一致的,InnOB默认级别。在sql标准中,该隔离级别消除不了不可重复度,但是还存在幻象读;
4.串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会堵塞。
174. 说一下 mysql 常用的引擎?
解析:一、ISAM:在设计ISAM之前就要考虑到查询的次数要大于数据更新的次数,因此ISAM的读取速度是非常快的,不占用大量的内存和存储空间,但是不支持事务的处理,下面我们分析ISAM的优缺点。
ISAM的优点:读取速度快,不占用大量的内存和存储资源;
ISAM的缺点:不支持事务,不容错;
使用ISAM引擎的注意点:由于它不支持事务的处理,并且不容错,所以必要数据备份;
二、MyISAM:MyISAM是mysql对于ISAM的扩展引擎,在mysql5.6版本之后出现简单来说ISAM有的东西,它基本上都有,MyISAM提供了索引和字段管理的大量功能,MyISAM还提供了表格锁定的机制来优化多个并发的读写操作、但是有代价的,要经常运行OPTIMIZE TABLE命令来恢复优化过机制所浪费的空间、MyISAM知道自己的缺点,所以提供了优化的工具、比如MyISAMCHK工具用来回复浪费的空间。
MyISAM优点:增强了ISAM引擎的功能,增加了索引、表格锁的机制优化并发读写;
MyISAM缺点:因为有了表格锁的机制,最大的缺陷就是在表损坏后恢复数据,和ISAM一样不支持事务,数据量越大,写入效率越低;
使用MyISAM引擎的注意事项:数据要备份,虽有有索引增加了效率,但是要正确的使用索引,如果索引的字段越多,维护索引的信息就会越多,随着数据量的增加,相对的效率也会降低。
使用MyISAM生成的文件:如果使用MyISAM数据库引擎,会生成三个文件:.frm:表结构信息;.MYD:数据文件;.MYI:表的索引信息。
三、InnoDB引擎:InnoDB数据库引擎是直接造就MySQL辉煌的引擎,他能弥补ISAM、MyISAM的不足之处,他能支持事物的处理、也能支持外键、尽管比ISAM、MyISAM的查询速度慢一点,但是自身‘全能’的优点完全可以胜出,现在MySQL5.6以上的版本默认的数据库引擎是InnoDB引擎MySQL,官方对InnoDB是这样解释的:InnoDB给MySQL提供了具有提交、回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎。InnoDB锁定在行级并且也在SELECT语句提供一个Oracle风格一致的非锁定读,这些特色增加了多用户部署的性能。没有在InnoDB中扩大锁定的需要,因为在InnoDB中行级锁定适合非常小的空间。InnoDB也支持FOREIGN KEY强制。在SQL查询中,你可以自由地将InnoDB类型的表与其它MySQL的表的类型混合起来,甚至在同一个查询中也可以混合。
InnoDB优点:遵循ACID模式设计,具有事务,回滚和保护用户数据崩溃恢复能力,InnoDB为大数据量发挥更好的性能而设计的,针对提升cpu的效率而设计,其它任何基于磁盘的关系型数据库都不能跟它作比较。
InnoDB缺点:没有ISAM、MyISAM速度来的快;
InnoDB的特点:支持事务、数据多版本读取、锁定机制的改进、实现外键
InnoDB和MyISAM的区别:1.InnoDB支持事务,MyISAM不支持事务,对于InnoDB每一条sql都封装成事务,自动提交,这样的话会影响速度,所以最好把多条sql语言放在begin transaction和commit之间,组成一个事务;
2.InnoDB支持外键,而MyISAM不支持,对一个包含外键的InnoDB表转为MyISAM会失败;
3.InnoDB是聚锁引擎,数据文件和索引是绑在一起的,必须要有主键,通过主键索引效率就会很高,但是辅助索引需要两次查询,先查询到主键,然后在通过主键查询到数据,因此,主键不应该过大,因为主键过大,其他的索引也都会很大,而MyISAM是非聚集性索引,数据文件是分离的,索引保存的是数据文件的指针,主键索引和辅助索引是独立的。
4.InnoDB不保存表的具体行数,执行select count(*) from table时候需要全表扫描,而MyISAM用一个变量保存了整个表的行数,执行上述语句时候只需要读出该变量即可,速度很快;
5.InnoDB不支持全文索引,而支持全文索引,查询效率上要优于MyISAM。

175. 说一下 mysql 的行锁和表锁?
176. 说一下乐观锁和悲观锁?
177. mysql 问题排查都有哪些手段?
178. 如何做 mysql 的性能优化?
解析:1.表设计优化:
a.字段类型优化:一般以整数型为主得表在千万级以下,以字符串为主得表在五百万一下是没有太大得问题得,而事实上mysql单表得性能优化依然有不少得空间,甚至能支撑千万级以上得数据。1.尽量使用数字类型字段,若只含数值信息得字段尽量不要设计为字符串,这将会降低查询和连接性能,增加开销,因为如果是字符串的话,数据库会将每个字符串的字逐个都进行比较,而数字类型只需要进行比较一次就好;2.尽量使用tinyint、smallint、mediumint做为整数类型而非int;3.尽可能的使用varchar/nvarchar代替char/nchar,且长度分配到真正需要的空间;4.使用枚举或者整数代替字符串类型;5.尽量使用TIMESTAMP而非DATETIME;6.单表不要太多的字段,建议在20以内;7.避免使用null字段,很难查询优化且占用额外的空间。
b.索引优化:1.考虑在where及order by和join涉及的列上建立索引,能g够对查询进行优化,进而避免全表扫描;2.不要在where条件中对字段进行null的判断,否则会导致放弃使用索引而进行全表扫描(例如:select * from a where id !=null),所以在设计表的时候尽量避免插入null值,而是用0或者其他值来表示;3.当某一列有大量的重复数据,如sex性别字段中男女各一半,即使给这个字段建立索引也并不有效,因为sql是根据表中数据进行查询优化的;4.索引太多的话虽然能够提高查询效率,但是insert和update时会重新建立索引,插入和更新效率会降低,一般是一个表的索引最好不要超过6个;5.尽量避免更新索引列的数据,因为索引数据列的顺序是表记录的物理存储顺序,一旦改变数据值将会导致整个表记录的顺序调整,耗费大量资源,所以当某列需要频繁更新并且频繁查询,那么需要全面衡量是否将该列设置为索引。
c.临时表优化:1.尽量使用表变量来代替临时表,如果表变量包含大量数据,请注意索引非常有限(只包含主键索引);2.避免频繁创建和删除临时表,以减少系统表资源的消耗;3.临时表并不是不可以使用,适当的使用可以使某些例程更快,例如当需要重复引用大型表或者常用表中的某个数据集时,但是对于一次性事件,最好使用导出表;4.在新建临时表时,如果一次性插入数据量很大,那么可以使用select into 代替create table,避免造成大量log,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table再insert ;5.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先truncate table,然后drop table,这样可以避免系统表的较长时间锁定;6.尽量避免使用游标,因为游标的效率教差,如果游标操作的数据超过一万行,那么需要考虑进行改写;7.使用基于游标的方法或临时表方法之前,应先寻找基于集的方案来解决问题,基于集的方法通常更有效。

2.sql优化:
a.where语句优化:
1.以下为放弃索引而导致的全表扫描条件查询,应该避免
1.1 在where子句中使用!=或者<>操作符;
1.2 在where子句中使用or来连接条件,我们可以用union来代替or;
1.3 在where子句中使用in 和not in ,对于连续的数据,我们可以使用between;
1.4 在where子句中“=”的左边函数,算数运算或其他表达式运算,如select id from t where num/2 =1;select id from t where sum(num,1)=2;
1.5 在where子句中的模糊查询like之后,使用左模糊匹配或者全模糊匹配,如“%XXX”;
1.6 在where字句中使用参数。
2.效率优化:
2.1 在where子句中,or可以写成in,or的效率是n级别,in的效率是log(n)级别,in的个数建议控制在200以内 ;
2.2 在如 select num from a where num in (select num from b )这样的查询语句,如果b表比a表大,则可以使用exists代替in。
b.其他查询优化:
1.永远不要使用select * from t ,应该用具体的字段代替 *
2.sql语句要尽可能简单:一条sql只能在一个cpu运算,所以大语句要拆成小语句,减少锁时间(一条大sql可以堵死整个库);
3.少用函数和触发器,关于数据的操作尽量在应用程序实现;
4.尽量不要进行全表搜索,而是使用limit来分页,且每页的条数不大;
5.只有一条查询结果时,推荐使用limit 1 ,limt 1 可以避免全表扫描,找到对应结果就不会在继续扫描了;
6.排序尽量使用升序;
7.Innodb上用select count(*) ,因为Innodb会存储统计信息;
8.复合索引高选择性的字段排在前面;
9.避免返回大量数据,如果数据量过大,应该考虑需求是否合理;
10.对于相同结果集的多次查询,保持sql语句前后一致,这样可以充分利用查询缓存。

c.事务sql优化:
1.尽量规避大事务的sql,大事务的sql会影响数据库的并发性能及主从同步;
2.删除表中所有记录请使用truncate 不要使用delete;
3.尽量不要使用存储过程。
十八、Redis
179. redis 是什么?都有哪些使用场景?
180. redis 有哪些功能?
181. redis 和 memecache 有什么区别?
182. redis 为什么是单线程的?
183. 什么是缓存穿透?怎么解决?
184. redis 支持的数据类型有哪些?
185. redis 支持的 java 客户端都有哪些?
186. jedis 和 redisson 有哪些区别?
187. 怎么保证缓存和数据库数据的一致性?
188. redis 持久化有几种方式?
189.redis 怎么实现分布式锁?
190. redis 分布式锁有什么缺陷?
191. redis 如何做内存优化?
192. redis 淘汰策略有哪些?
193. redis 常见的性能问题有哪些?该如何解决?
十九、JVM
194. 说一下 jvm 的主要组成部分?及其作用?
195. 说一下 jvm 运行时数据区?
196. 说一下堆栈的区别?
197. 队列和栈是什么?有什么区别?
198. 什么是双亲委派模型?
199. 说一下类加载的执行过程?
200. 怎么判断对象是否可以被回收?
201. java 中都有哪些引用类型?
202. 说一下 jvm 有哪些垃圾回收算法?
203. 说一下 jvm 有哪些垃圾回收器?
204. 详细介绍一下 CMS 垃圾回收器?
205. 新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?
206. 简述分代垃圾回收器是怎么工作的?
解析:说到垃圾回收(GC)很多人自然而然的就会把它跟Java联系起来,在Java中,程序员不用关心内存动态分配和垃圾回收的问题,这一切都是交给JVM来处理。
顾名思义,垃圾回收就是释放垃圾占用的空间。
1.垃圾回收器对于提高对象的创建速度,有明显的效果。
那么问题来了,垃圾回收是释放对象,关创建对象什么事儿?
首先了解一下Java是如何在堆上分配内存的,Java使用“堆指针”,每分配一个对象,指针就往后移一位,类似于堆栈,这样就达到了高速创建对象的效果,但这样的方法带来了弊端,在堆栈中还可以出栈来释放,在堆里你没有出栈啊,不一会堆(内存)就满了,就要放到磁盘上去,非常的浪费时间。那么这个时候垃圾回收的装置就介入了,他帮助我们回收空间,并处理因为对象产生的碎片(因为有的对象可能比较小,释放了之后就空了一块内存,比如大小为5,但是其他对象的大小都在10以上,那么这块内存就是碎片了,别的对象根本就放不进去)。
有了垃圾回收,所以Java才能使出“堆指针”来创建对象,所以说垃圾回收器对创建速度有明显的效果。
2.Java回收器的具体机制
2.1、引用计数法:就是说每个对象都有一个标志,每次被引用一次就加1,如果引用为0,那么就会去回收,但会有特殊的情况,如果两个对象在堆里,互相引用,那么使用这个方法就失效了,所以Java中不会使用这种办法;
2.2、停止-复制(stop-and-copy)和标记-清扫(mark-and-sweep):这两个思想是相同的,不同具体实现略有不同。这两种方式需要程序暂停才能工作。
首先从堆栈或静态存储开始,遍历所有的引用,根据引用在找对象,在遍历查看找到对象中的引用,依次下去,这样就避免互相引用的问题, 因为如果互相引用,那么在堆栈中根本不会出现。
停止-复制就是开辟了一个堆,然后把活的引用复制到堆中,不过开辟堆实在太浪费,感觉就在把一个堆划分成不同的块,在块中复制。
标记-清扫,就是活的对象给个标记,先找出打好印记,然后在回收。
Java虚拟机可以自适应切换两种方法,因为如果在很少需要回收的对象时,如果还使用停止-复制未免也太浪费了,如果一直在用标记-清扫,那么就会产生很多的碎片,有很多碎片的时候就用停止-复制, 因为复制到另一个块中,程序自然会去把对象排列好。
207. 说一下 jvm 调优的工具?
解析:一、JDK自带的调优工具jconsole,它是从Java5开始,在jdk中自带的Java监控和管理控制台,用于对于JVM中的内存,和线程和类等的监控,是基于JMX的GUI性能监测工具。jconsole使用jvm的扩展机制获取并展示虚拟机中运行的应用程序的性能和资源的消耗等信息。

直接在jdk中bin目录文件下边点击jconsole.exe运行即可,界面如下:
在这里插入图片描述
在弹出的窗口中可以选择本机的监控本机的Java应用,也可以选择远程的Java应用来进行监控,如果监控选择服务需要在tomcat启动脚本中添加如下代码:

 -Dcom.sun.management.jmxremote.port=6969  
 -Dcom.sun.management.jmxremote.ssl=false  
 -Dcom.sun.management.jmxremote.authenticate=false

连接进去之后可以看到jjconsole概览图和主要的功能:概述、内存、线程、类、VM、MBeans

  1. 概述,以图标的方式显示出堆内存的使用量,活动线程数,已加载的类,cpu占用率的折线图,可以非常清晰的观察在程序执行过程中的变动情况。如下图:

在这里插入图片描述
2.内存:主要显示了内存的使用情况,同时可以查看堆和非堆内存的变化量值得对比,也可以点击执行GC来处罚GC的执行。如下图:
在这里插入图片描述
3.线程:主要展示线程数的活动数的峰值,同时点击左下方的连接可以查看线程的详细信息,比如线程的状态是什么,堆栈内容等信息,同时也可以点击“检测死“来检测线程之间是否有死锁的情况。如下图:
在这里插入图片描述
4.类:主要展示已经加载的类的信息;
5.VM概要:展示JVM所有信息的总览,包括基本信息、线程相关、堆相关、操作系统、VM参数等;
6.Mbean:查看Mbean的属性、方法等。

二、VisualVM
https://blog.csdn.net/ityouknow/article/details/56844837

208. 常用的 jvm 调优的参数都有哪些?
解析:例如:-Xms20m -Xmx20m -Xss256k
1.-Xms:s为starting,表示推内存的起始大小;
2.-Xmx:x为max,表示最大的堆内存;(一般来说-Xms和-Xmx设置为相同的大小,因为heap自动扩容时,会发生内存抖动,影响程序的稳定性);
3.-Xmn:n为new,表示新生代的大小(-Xss:规定了每个线程虚拟机栈(堆栈)的大小);
4.-XX:SurvivorRator=8 :表示推内存中新生代、老年代和永久代的比为8:1:1;
5.-XX:PertenureSizeThreshold=3145728:表示创建(new)的对象大于3M的时候直接进入老年代;
6.-XX:MaxTenuringThreshold=15:表示当对象的存活年龄大于多少时,进入老年代;
7.-XX:-DisableExplicirGc:表示是否(+表示是,-表示否)打开GC日志。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值