java中.java文件在执行编译后,产生.class文件,.class文件是在JVM虚拟机中运行的,而JVM虚拟机可以安装在各个主流系统上,所以编译后的.class文件可以运行在各个系统。
在java中int是中基本类型的其中一种,占4字节,32位,定义后初始化数据为0。
Integer为int类型的包装类,里面定义了许多关于int类型的方法,同时也可以达到存储int类型数据的目的,拥有自动装箱,自动拆箱功能,定义后初始化数据为null,当存储数据在-128到127时,Integer对象是在IntegerCache.cache产生,会复用已有对象,此时使用==进行判断,两个是同一个对象。当不在这个区间内,则会重新new一个Integer对象,使用==判断后结果为false。
实际上equals方法的底层是==去实现的,当比较的数据类型为基本类型时则不能使用基本类型.equals,需要使用==才能进行比较。
使用equals或==对类型为String的数据进行判断时比较的是字符串内容,当使用equals或==对引用数据进行判断时,比较的是地址在内存中的地址值,当重写equals后可以自己指定比较内容,同时也可以使用hashcode作为比较值。当需要比较大量数据时,使用重写equals方法并结合hashcode进行查找效率最高。
抽象: 将一类对象的共同特征总结出来构造类的过程,抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
封装: 把一个对象的属性私有化,但是同时也提供一些可以被外界访问的属性的方法,如果不希望外界访问,则也不必提供方法给外界。
继承: 使用已存在的类的定义作为基础,建立新类的技术,继承父类的子类可以增加自己独有的属性和方法,同时也可以使用父类的属性和功能,但不能选择性的继承父类,通过继承可以减少代码冗余,增加代码复用。要注意的是java是单继承多实现。
子类拥有父类非private的属性和方法。
子类可以拥有自己的属性和方法,可以在父类的基础上进行扩展。
子类可以用自己的方式实现父类的方法,也就是方法重写。
多态: 指程序中定义的引用变量所指向的具体类型在编程时不确定,而是在运行期间才确定,即一个引用变量到底指向哪个实例对象,该引用变量发出的方法调用到底是那个类中的实现方法,必须由程序运行期间才能决定。
抽象类是定义要被继承的子类的通用特性的,接口只能定义方法。
抽象类是对类的抽象,是一种模板,接口是对行为的抽象,是对行为的规范。
相同点:
接口和抽象类都不能实例化,只能被继承或实现。
都包含抽象方法,并且子类必须重写这些方法。(1.8以后接口引入默认方法和静态方法,默认方法不用被强制实现)
不同点:
声明关键字不同。
实现关键字不同。
抽象类可以有构造器,接口没有。
抽象类的方法可以有各种访问修饰符,接口访问修饰符必须是public
一个类最多继承一个抽象类,但是可以实现多个接口
字符串常量池位于堆内存,专门存放字符串常量,可以提高内存使用率,避免多块空间存储相同字符串。在初始化字符串时会在内存中检查字符串常量池,如果常量池已存在该字符串,则返回它的引用,如果不存在,则实例化一个字符串放入池中,并返回其引用。
字符串是不可变的,java中String类是用final修饰的一个常量char数组,所以不可修改。表面看上去的可以修改实际上是在字符串常量池中新建了一个字符串,并返回了该字符串的引用。
String底层是被final修饰过的所以是不可变的,StringBuffer和StringBuilder虽然也是用char数组实现,但是没有用final修饰,所以是可变的,并且拥有一些独有的字符串处理方法
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以它是线程安全的,而StringBuilder没有加锁,所以它不是安全的。
StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并引用,相同情况下使用StringBuilder要比StringBuffer效率要高。
修饰符 | 当前类 | 同包 | 子类 | 其他包 |
---|---|---|---|---|
public | ✔ | ✔ | ✔ | ✔ |
producted | ✔ | ✔ | ✔ | ✖ |
default | ✔ | ✔ | ✖ | ✖ |
private | ✔ | ✖ | ✖ | ✖ |
&和&&都是逻辑运算符,从某种意义上来讲,两个效果是相同的,但是&&拥有短路特性,当有多个逻辑表达式进行运算时,如果第一个逻辑表达式就是false,那么不管后面是否正确,都不在执行。同理 | 和 || 也是一样的,只要第一个为true那么后面都不在执行了。
不可以,String类是被final修饰过的,并且继承String本身就是一种错误的行为。
是值传递,因为java中不存在引用传递,当一个对象实例被传入方法中,参数的值是该对象的引用,对象的属性可能会被改变,但是对象引用的改变是不会影响到调用者的。
重载是相同的方法名,参数列表的类型、数量、顺序不同,并且与返回值无关,可以有不同修饰符。
重写是将继承的父类方法重写覆盖掉父类方法,所以要求,方法名,参数列表都与父类方法名和参数列表相同
注:构造方法不能被重写,被final修饰的方法不能被重写,声明为static的方法不能被重写,但是可以被再次声明
可以,因为java使用的默认编码为Unicode,直接使用字符在字符集的编号,一个charl类型占两字节,所以放一个中文汉字是没问题的。
都不可以
首先抽象方法是要被子类重写的,而static方法无法被重写,这是相互矛盾的。
native是有本地方法实现的,而抽象方法是没有实现的,所以也不可以。
synchronized和实现的细节有关,而抽象方法不设计实现细节,因此也是矛盾的。
多态是父类或接口定义的引用变量可以指向子类实现,而程序调用的方法在执行期间才会动态绑定,就是引用变量所指向的具体实例对象的方法,也就是对象内存中正在运行的那个对象,而不是引用变量的类型中定义的那个方法。
java异常分为运行时异常和编译时异常
编译时异常是可以被手动处理掉的,大部分是可以预见性的异常,如果是=提前知道怎么处理异常,则可以使用try…cache捕获并处理异常,如果不知道如何处理,则定义该方法是声明时抛出该异常。
运行时异常则是只有在代码运行时才发出的异常,比如类转换异常,数组下标越界异常、除数为0数学异常等,这种异常在出现时会被系统自动捕获,可以手动try…cache进行处理,或者直接交给程序自动处理。
error和exception都继承于throwable类
error一般是虚拟机相关的问题,如系统崩溃,内存空间不足,方法调用栈溢出等,这种问题一旦出现,就代表这无法修复的错误,是非常严重的。
exception表示程序可处理的异常,遇见这种问题,应该尽可能的解决异常,使程序恢复运行。
exception又分为运行时异常和编译时异常,编译时异常表示语法都没用办法通过,运行时异常表示的是只有在程序运行时,才会出现的异常,比如数组下标越界,没找到类异常等。遇见异常时,尽量使用try…cache进行异常捕获并处理,保证程序的正常运行。
在程序运行状态时,都能够知道任何一个类的所有属性和方法,对于任意一个对象,都能调用它的任意一个方法和属性,这种动态获取对象类或对象信息,并动态调用对象的方法被称为反射。
优点:可以在运行期间就对类或对象进行判断,动态加载,提高代码灵活度。
缺点:相当于手动操作JVM进行操作,不如JVM自动操作效率高。
在平时的项目开发过程中,很少直接使用反射,但是在框架设计、模块化开发等都是通过反射调用相对应的字节码,在spring的动态代理中就使用到了反射机制,spring就是通过读取配置文件后,通过全限定类名和反射获得对象实例。
.class
方法.getClass()
方法获取clazz.newInstance()
获取实例clazz.getConstructor()
,通过constroctor.newInstance()
创建实例对象首先通过反射获取class对象
通过class对象获取属性 getDeclaredField(filedName)
给属性设置可以访问 field.setAccessible(true);
Collection接口 和 Map接口继承于Iterator接口
set接口 和 List接口继承于Collection接口
set接口下有HashSet、TreeSet和LinkedHashSet
- HashSet:无序,集合内元素不可重复,线程不安全
- TreeSet:指定排序算法后可以进行排序,线程不安全
- LinkedHashSet:有序并且保证排序,线程不安全
List接口下有ArrayList、LinkedList
- ArrayList:查、改快,线程不安全,初始长度没有指定默认长度的时候,长度为0,并在第一次添加元素时初始化,初始长度为10,再次扩容会先copy数组,并扩容为原数组的1.5倍。
- LinkedList:增、删快,线程不安全,插入元素是将元素置于链表末尾,属于尾插法。
Map接口下有HashMap、TreeMap、LinkedHashMap和HashTable
- HashMap:无序双列集合,线程不安全
- TreeMap:可排序双列集合,线程不安全
- LinkedHashMap:有序并且保证排序的双列集合,线程不安全
- HashTable:无序双列集合,线程安全
HashMap和HashTable都继承于Map接口,都是双列集合,两者的区别在于:
两个类都继承了List接口,都属于单列有序集合。
Vector是Java出现的时候就存在的类,而ArrayList是后面更新后才出现的。java也推荐我们优先使用ArrayList,但是在使用前要考虑线程安全问题,因为ArrayList是线程不安全的,而Vector是线程安全的,如果没有多线程问题则选择ArrayList,这样效率高于Vector。
ArrayList和Vector在创建时都有一个初始容量大小,当存储的数据超过初始容量时,会自动对集合进行扩容,Vector默认每次增长为原来的2倍,ArrayList是增长为原来的1.5倍,但是Vector可以手动设置增长的空间大小,ArrayList不能手动设置增长空间大小。
Array大小是固定的,ArrayList大小是动态变化的。
ArrayList处理固定大小的基本数据类型时,这种方式效率较慢。
在实际应用场景中,如果提前知道需要存储的数量,并且后期不会在改变大小的时候可以使用数组,如果后期对存储容量有动态变化的时候则使用ArrayList。
在jdk1.7之前HashMap的底层原理是由数组+链表实现的,当创建出来HashMap时,是一个数组,数组中的每一个元素是一个单向链表的头指针,指向一个entry键值对,当一个键值对放入hashMap时会根据键的hashcode选择放入哪一个数组元素,也就是选择放入哪一个单向链表中,如果出现两个entry的hash值一样,这样就产生了hash冲突,那么新放入的entry键值对则使用头插法,插入在表头。
jdk1.8之后采用数组+链表+红黑树实现,在基础思想上添加了红黑树进行优化,当链表长度大于等于阈值(8)时,链表转化为红黑树,利用红黑树的自平衡在查找性能上得到提升。当链表长度小于于等于阈值(6)时,红黑树转化为链表。HashMap的初始长度是16,每次自动扩展或手动扩展时,长度必须是2的幂,1.8之后遇到hash冲突后是尾插法。
set底层先使用hashcode值和将要加入的hashcode值进行比较,如果相同则继续使用equals方法进行比较。
HashSet底层是有HashMap实现的,在HashSet的构造方法中初始化了一个HashMap,利用HashMap的键值不唯一,使用HashMap的键来存储值,因此当存储的值重复时会返回false。
TreeSet是有树形结构,基于TreeMap实现的,所以存储是有序的,但是同样是线程不安全的。
LinkedHashMap也是基于HashMap实现的,只是它额外定义了一个Entry header,这个header是独立出来的一个链表头指针。每个entry添加了两个属性,before和after和header结合起来形成了一个双向链表,由此就实现了插入顺序或访问顺序排序。默认排序即插入的顺序。
两者都是线程安全的,但是底层对于线程安全实现方式不同,Hashtable是对表结构进行上锁,其他操作需要等待执行完毕后才能访问,1.8之前ConcurrentHashMap是采用分离锁形式,没有对整表进行锁定,而是对局部进行锁定,不影响其他线程对表的其他地方操作。1.8之后ConcurrentHashMap采用CAS算法进行安全实现线程安全。
进程是包含多个线程的集合,每一个程序在执行时都是一个进程。
线程是进程中最小的数据单位,每个指令都是一个线程。
进程可以没有线程,但是线程必须要存在于进程,即线程依赖于进程。
守护线程是为了服务用户线程的存在,比如jvm虚拟机会等待用户线程执行完成后关闭,但是不会等待GC线程执行完再关闭。
start()方法用来创建线程,底层也是调用了run()方法,这和直接调用run()方法不一样,当你调用run()方法时,是在当前线程调用,使用start()方法是启动一个新线程。
导致线程阻塞的原因大体上来说是因为需要的资源没有就绪,所以会陷入阻塞状态,需要等待资源就绪后才会继续执行。常见的阻塞原因有以下几种
线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享,是一种实现线程安全的方式
被volatile修饰的共享变量保证了不同线程对该变量操作的内存可见性,禁止指令重排序。
就是当你写一个 volatile 变量之前,会插入一个写屏障,读一个 volatile 变量之前,会插入一个读屏障。在你写一个 volatile 变量时,能保证任何线程都能看到你写的值,在写之前,也能保证任何数值的更新对所有线程是可见的
http是超文本传输协议,它规定通过浏览器访问服务器时要遵循请求发送的规则,要求请求的格式要有请求行,请求头,请求体。get方法没有请求体
get和post则是发送请求的两种不同的方法
get请求可以被缓存,可以保存在历史浏览记录中,可以被收藏为书签,响应速度比post快,但是缺乏安全性,请求的数据会在地址栏中显示,请求长度限制最大4k。
post请求不可以被缓存,不会保留在历史浏览记录中,不能被收藏为书签,请求长度没有限制,请求数据不会显示在地址栏上,请求头比get更大。
servlet包含四个生命周期
jsp是servlet的技术加强,所有的jsp文件都会被解析成一个继成HttpServlet的类,这个类可以对外访问,并且可以动态的生成HTML,XML,或其他格式的web文档返回给用户。
servlet是应用在java文件中,处理用户请求,以HTML,XML,或其他格式返回给用户。
jsp多侧重于页面展示,servlet侧重处理业务逻辑。
jsp拥有9个内置对象,4个作用域
内置对象
作用域
两者同是追踪回话的一种方式,最直观的区别就在于Session是在服务器端存储用户的登录信息,Cookie是在客户端浏览器存储用户的登录信息。
Seesion的机制决定了用户只能获取自己的session,其他用户的seesion不可见,各客户的session相互独立不可见。seesion的使用比cookie要方便,但是会对服务器产生一定的压力。
MVC是目前B/S架构中最常见的设计思想,利用分层思想将程序的整个运行流程分为三层,方便开发中的逻辑处理,实现每一层处理不同的业务。
M: 用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
V: view代表向用户展示的页面数据。
C: 是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
Java类的生命周期为:加载、验证、准备、解析、初始化、使用、卸载七个生命周期,其中,加载、验证、准备、解析、初始化可以称之为类的加载过程。
首先将class文件转为二进制流,接着验证是否为class文件和文件的正确性,验证完毕后在内存中生成该类对象,接着对类中的属性进行预定义,在局部变量表中进行分配存储。解析主要完成符号引用到直接引用的转换动作,有可能在初始化之后开始解析。初始化是对类中的变量进行初始化,加载构造方法,剩余的都又jvm进行初始,之后才开始执行类中定义的java程序代码。开始使用,最后卸载。
当运行指定程序时JVM会按照一定规则编译并加载class文件,组织成为一个完整的java应用程序,这个过程由类加载器完成。一般有隐式加载(使用new的方式创建对象)和显示加载(利用class.forName()创建对象)两种方式。
类的加载是动态的,它不会一次性将所有的类加载完后再执行,而是保证基础类的加载,至于其他类则在需要时加载。
GC是JVM虚拟机中的垃圾回收器,可以自动监测堆内存中是否存在垃圾对象,从而达到自动回收内存的目的,尽量避免内存溢出。Java 语言没有提供释放已分配内存的显示操作方法。
在 Java 中垃圾回收由虚拟机自行执行。JVM 有一个垃圾回收线程,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫描垃圾对象,将他们回收。
判断一个对象是否存活有两种方法:
引用计数法:给每一个对象设置一个引用计数器,当有一个地方引用这个对象时,计数器加一,引用失效时,计数器减一。当一个对象的引用计数器为零时,说明此对象没有被引用,也就是“死对象”,将会被垃圾回收。
引用计数法有一个缺陷就是无法解决循环引用问题,所以主流的虚拟机都没有采用这种算法。
可达性算法:从一个被称为 GC Roots 的对象开始向下搜索,如果一个对象到 GC Roots 没有任何引用链相连时,则说明此对象不可用。当一个对象不可达 GC Root 时,这个对象并不会立马被回收,若要被真正的回收需要经历两次标记。当经历两次标记后该对象将被移除” 即将回收”集合,等待回收。
在程序开发过程中,最让人头疼的就是内存回收,但是java引入了垃圾回收机制,使此类为题迎刃而解,使开发人员在开发中不用再考虑内存管理。
垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。不可预知的情况下对内存中已经死亡的或者长时间没有使用的对象进行清除和回收,开发者不能手动的调用垃圾回收器对某个对象进行垃圾回收。
回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。
当程序员创建对象时,GC 就开始监控这个对象的地址、大小以及使用情况。GC 采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式判断是不是不可引用的垃圾对象,当确定是垃圾对象后GC会回收这些内存空间。而程序员虽然可以手动执行System.gc()但是java不能保证一定会销毁对象。
内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中。Java 中有垃圾回收机制,它可以保证一个对象不再被引用的时候,对象将被垃圾回收器从内存中清除。
而java中使用有向图进行垃圾回收管理,可以消除引用循环的问题。
当然java也存在内存泄漏的可能,比如创建了一个对象,以后一直不再使用这个对象,这个对象却一直被引用,即这个对象无用但是却无法被垃圾回收器回收的,这就是 java中可能出现内存泄露的情况。
深拷贝就是更改copy后的对象数据,原对象数据不变。浅拷贝就是更改copy后的对象数据原对象数据也跟着改变。简单来说就是深拷贝复制对象的值,浅拷贝是复制对象的地址。
如果对象的引用被置为null,只是断开了当前线程栈帧中对该对象的引用关系,而垃圾收集器是运行在后台的线程,只有当用户线程运行到安全点或者安全区域才会扫描对象引用关系,扫描到对象没有被引用则会标记对象,这时候仍然不会立即释放该对象内存,因为有些对象是可恢复的(在 finalize方法中恢复引用 )。只有确定了对象无法恢复引用的时候才会清除对象内存。
当对象没有变量引用的时候,这个对象就可以被回收了。
• 对象优先在堆的 Eden 区分配
• 大对象直接进入老年代
• 长期存活的对象将直接进入老年代
当 Eden 区没有足够的空间进行分配时,虚拟机会执行一次Minor GC 。Eden 区的对象生存期短,所以可能会频繁触发MinorGC,触发MinorGC后会将未被释放掉的对象放入S0或S1内存,如果要放入对象大于S0或S1内存的50%,则跳过S1或S0直接放入老年代。
垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发Full GC
注:Java 8 中已经移除了永久代,新加了一个叫做元数据区的native内存区。
类加载器是一个用来加载类文件的类。Java源代码通过javac编译器编译成类文件。然后JVM执行加载后的字节码。类加载器负责加载文件系统、网络或其他来源的类文件。
加载器 | 说明 |
---|---|
启动类加载器(BootstrapClassLoader) | 用来加载 Java 核心类库,无法被 Java 程序直接引用。 |
扩展类加载器(ExtensionsClassLoader) | 用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。 |
系统类加载器(SystemClassLoader) | 根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java应用的类都是由它来完成加载的。 |
用户自定义类加载器 | 通过继承 java.lang.ClassLoader 类的方式实现。 |
spring框架是一个轻量级,一站式的开源java框架,它提供了综合广泛的基础性支持平台。拥有简单性,可测试性,松耦合性,任何java程序都能从中收益。
spring中已经集成了20多个模块,比如常用的web,jdbc,aop,tx等。
spring框架提供了IoC(控制翻转)和DI(依赖注入)实现了松耦合的依赖关系
spring框架提供了AOP切面编程,对指定功能进行增强,减少冗余代码,提高代码复用性。
spring框架提供了简单的声明式事务,使开发中能够专注逻辑代码而不用花费过多心思管理事务。
spring框架是按照模块的形式来进行组装的,使用的时候一目了然,直接导入使用的模块即可。
IoC(控制翻转)将对象交给spring进行创建、初始化、管理、销毁,并存放在IoC容器中。
DI(依赖注入)在程序使用IoC容器中的某个对象时,从容器中获取对象。有三种注入方式:set方法注入,构造方法注入,工厂方法注入,静态方法注入,接口注入。
spring框架没有对单例Bean进行任何的线程封装处理,关于单例Bean的线程安全问题需要开发者自己解决。但是在实际开发中,大部分bean都没有可变状态,所以从某种情况上考虑,spring的单例是安全的,如果bean有多种状态的话,就需要自己保证线程安全。最常用的办法就是将bean的作用域由singleton变更为prototype。
spring中用到了大量的设计模式具体如下:
作用域 | 说明 |
---|---|
singleton | bean在每个spring ioc容器中只有一个实例,配置文件配置后默认是单例 |
prototype | 一个bean的定义可以有多个实例 |
request | 每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效 |
session | 在一个HTTP Session中,一个bean定义对应一个实例,该作用域仅在基于web的Spring ApplicationContext情形下有效 |
global-session | 在一个HTTP Session中,一个bean定义对应一个实例,该作用域仅在基于web的Spring ApplicationContext情形下有效,缺省的Spring bean的作用域是Singleton |
AOP是面向切面编程,是一种编程技术,允许对功能做横向切割,对目标方法做增强,可以有效的增加代码复用性,减少冗余代码。
AOP底层是以jdk动态代理和cglib动态代理技术实现的,jdk的动态代理是对接口方法进行增强,所以要求切入点至少要实现一个接口,否则spring会使用另一个cglib动态代理进行增强,而cglib是在程序运行期间找到继承的父类,并生成代理后的子类。
切面是我们要织入的功能,可以是一个类中的一个方法,也可以是一个类中的多个方法。
切入点是我们要对哪个功能进行增强,在开发中,我们通常理解为一个切入点就是一个方法。
增强的方式也有前置增强,返回前增强,返回后增强,异常增强,环绕增强,总共5中方式,具体使用哪种方法还要参考具体业务逻辑需求。
spring有自己的一套事务管理机制,一般使用TransactionMananger进行管理,可以通过配置文件进行注入来完成此功能,通过配置可以实现对事务级别和传播性的控制。而底层也是使用到了spring的AOP思想。
ApplicationContext是BeanFactory的子类,两者都有getBean
这个核心方法
Beanfactory只用基本只有跟Bean相关的功能,而ApplicationContext则在此基础上有增加了很多bean的细节,且进行了一定程度的封装。
用户请求发送给DispatcherServlet,DispatcherServlet调用HandlerMapping处理器映射器;
HandlerMapping根据xml或注解找到对应的处理器,生成处理器对象返回给DispatcherServlet;
DispatcherServlet会调用相应的HandlerAdapter;
HandlerAdapter经过适配调用具体的处理器去处理请求,生成ModelAndView返回给DispatcherServlet
DispatcherServlet将ModelAndView传给ViewReslover解析生成View返回给DispatcherServlet;
DispatcherServlet根据View进行渲染视图;
Spring的MVC框架是围绕DispatcherServlet来设计的,它用来处理所有的HTTP请求和响应。
是单例模式,所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能的,解决方案是在控制器里面不能写字段。
通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象。
这样就可以直接返回一个json字符串形式的json对象,但是要注意方法上要加上@ResponseBody注解,或者类上加@RestController注解。
是对象关系映射,是一种为了解决关系型数据库与java对象的映射技术,ORM是通过使用描述对象和数据库之间的映射关系,将程序中的对象自动持久化到关系型数据库中。
mybatis支持一对一、一对多的延迟加载
当调用方法进行查询时,进入拦截器方法,当判断到关联查询的值为null时,会去查找相对应的sql语句进行二次查询,然后在赋予值。
'%${question}%'
可能引起SQL注入,不推荐"%"#{question}"%"
CONCAT(’%’,#{question},’%’)
dao接口工作原理是JDK动态代理,在运行时会通过动态代理产生代理对象,代理对象会拦截接口方法,执行所对应的sql语句,随后返回执行结果。
dao接口方法是不能重载的,因为要靠全限定名和方法名去进行拦截处理
首先判断返回结果是resultType还是resultMap,如果是ResultType会根据全限定类名查找实体类通过反射创建对象,调用set方法给对象赋值。如果是ResultMap则会直接根据配置的名称和数据库查询后的别名进行映射处理。
Mybatis动态sql可以让我们在Xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能
比如
mybatis拥有一级和二级缓存
快速开发,快速整合,配置简化、内嵌服务容器
启动类上面的注解是@SpringBootApplication,它也是核心注解,主要包含以下三个注解
首先使用注解EnableTransactionManagement开启事物之后,然后在Service方法上添加注解Transactional便可。
RabbitMQ 使用信道的方式来传输数据。信道是建立在真实的 TCP 连接内的虚拟连接,且每条 TCP 连接上的信道数量没有限制
数据库分为关系型数据库和非关系型数据库
关系型数据库:
Mysql,Oracle,sqlServer,DB2等
因为关系型数据库的原因,各表之间可以有关联,方便对用户请求数据的分模块、分功能管理,可以使用简单的sql语句就可以做非常复杂的数据查询。
非关系型数据库:
NoSql,Redis,MongoDB
非关系型数据库利用键值对的方式存储数据,不便于存储用户的大量数据,也正是因为这个机制,非关系型数据库经常被用做关系型数据库的缓存,以此减少关系型数据库的压力和资源消耗。
第一范式: 数据表中的每个字段都具有不可拆分性。也是数据设计中最基本的要求。
第二范式: 在满足第一范式的基础上,消除了部分依赖,解决了删除异常,插入异常和修改异常。
第三范式: 在满足第二范式的基础上,消除了传递依赖。
mysql的最大值是16384,默认值是100,最大连接数同时也根据当前系统的内存、tcp连接数相关,但是一般不建议设置为最大值,这样很容易导致数据库崩溃,一般情况下我们会设置一个最大连接数,用来限制数据库的最大连接,防止访问量过大导致数据库崩溃。
select * from table limit 0,5;
0代表开始查找的索引值,5代表查找数量。
jdbc是java执行访问数据库的桥梁,它提供了多种主流关系数据库的统一接口。
连接池极大的提高了数据库的性能,极大的节省了数据库资源。
提高获取数据库连接的速度,避免了频繁创建和销毁连接。
方便对数据库连接的管理,方便对数据库连接的调优。
首先B树上,内容节点可以存放键和值,但是B+树的内容节点都是键,所有的值都在叶子节点上
其次B树的每个叶子节点相互独立,B+树的叶子节点有一条链相连
B树可以将经常查询的热点数据放入内容节点中,有利于缩小查找范围,方便快速查找。
B+树因为在叶子节点上有一条链相连,所以查找时会根据链进行顺序查找,也更适合于范围查找。
当写操作影响数据库查询效率时,这时候就要用到读写分离。
读写分离的基本的原理是让主数据库处理事务性增、改、删操作,而从数据库处理查询操作。我们可以通过主从复制实现数据同步,通过读写分离提升并发负载能力。
首先在主数据库执行完事务操作后,将执行操作写入binlog日志文件,随后通知从数据库进行同步。从数据库会建立一个I/O线程,随后从主数据库中读取事件放入中继日志,如果已经将事件读取完毕,则陷入阻塞状态,等待新的事件到来。随后从数据库从中继日志读取事件进行数据的同步更新。
当读操作频繁时,我们可以增加从数据库进行读写分离来降低数据库压力,但是当从数据库也变多的时候,我们就要考虑分库。同样当写操作压力很大时,我们也要进行分库操作。
垂直分库:
我们可以按照业务进行拆分,将不同业务的表放在不同的库中,这样就可以减轻单库的操作压力。但是随着数据的增大同样会让数据库的能力达到瓶颈,服务器磁盘、内存也会受到影响,这时候就要将其拆分到多个服务器上。
垂直分表:
一般就是将大表拆成小表,将不常用的字段拆分出来放到扩展表中,避免查询的时候数据量太大造成跨页问题。
水平分表
类似于垂直分库,只是将表的内容做了一定限制,可以减少单表数据量,达到查询效率的提升。
水平分库分表
在水平分表的基础上将数据切片部署在多个服务器中,每个服务器具有相对应的库和表,但是表的数据不同,这样做可以有效缓解数据库的大部分压力。
策略 | 介绍 |
---|---|
哈希取模 | 根据用户的id进行取模,按照取模后的结果放入不同的数据库 |
范围分片 | 比如根据id进行划分,1-10000划入表1,10001-20000划入表2 |
地理位置分片 | 按照地理位置进行分片,华东区一个表,华北区一个表 |
时间分片 | 按照时间进行分片,比如按年或者月进行分片 |
mysql总共有4种日志类型
日志类型 | 介绍 |
---|---|
Error Log(错误日志) | 用于几率sql错误信息 |
General Query Log(一般查询日志) | 记录目前sql正在做的事情 |
Binary Log(二进制日志) | 记录数据库的建表,改动等,可以通过该日志进行数据回滚和主从复制的数据同步 |
Slow Query Log(慢查询日志) | 记录查询时间超过阈值的sql语句,常用于数据库调优 |
mysql拥有4种事务隔离界别从低到高分别是
读未提交、读已提交(解决脏读)、可重复读(解决不可重复读)、序列化(解决幻读)
游标是系统为用户开设的一个数据缓冲区,存放SQL语句的执行结果,每个游标区都有一个名字。用户可以通过游标逐一获取记录并赋给主变量,交由主语言进一步处理
存储过程是一个预编译的SQL语句,允许模块化的设计,只需要创建一次,以后在该程序中就可以调用多次。如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语句执行要快。
触发器是用户定义在关系表上的一类由事件驱动的特殊的存储过程。触发器是指一段代码,当触发某个事件时,自动执行这些代码。
Hash索引是通过hash函数计算出hash值在表中查找对应的数据,所以在精确查找上hash索引要快
BTree索引底层使用B+树实现,要多次使用折半查找来找到对应的数据,所有数据都存放在叶子节点上,并且每个叶子节点之间都有链相连,所以BTree索引支持范围查找,模糊查询。
全量同步
增量同步
4. 如果出现网络问题导致的数据丢失情况,从节点自身已经保存了已复制的偏移量和主节点id
5. 主节点根据偏移量将缓存区的数据发送给从节点,保证主从复制状态的正常。
有RDB和AOF两种方式做持久化,RDB做快照持久,AOF做增量持久
RDB根据快照保存当前数据库中的信息,随后开启一个线程创建RDB文件,但是RDB机制依旧会存在一定问题,因为它是每隔一段时间进行快照,这种方式会造成上次快照时间到这一秒中的数据丢失。
AOF是遵循redis协议对执行的命令进行持久化,redis会记录下所有变更数据库状态的命令,redis在载入AOF文件的时候,把AOF中每一条命令都执行一遍,最终还原回数据库的状态,它的载入也是自动的。在RDB和AOF文件都有的情况下,redis会优先载入AOF文件
全局的键空间选择删除策略:
设置过期时间的键空间选择性的删除: