万物皆对象,把现实中有共同特性行为的对象抽象成类,类是程序中最基本的单位。html
面向对象的思想是如何在java展示的呢? 就是经过类和对象前端
*类是 一组相关的属性和行为的集合。是一个抽象的概念。java
*对象 是该类事物的具体表现形式。具体存在的个体。mysql
类是对象的抽象,对象是类的实例。linux
*成员变量 事物的属性程序员
*成员方法 事物的行为web
Java的跨平台是经过Java虚拟机JVM来实现的。不一样的平台须要安装不一样的虚拟机,java程序编译以后的代码不是能被硬件系统直接运行的代码,而是一种“中间码”——字节码。而后不一样的硬件平台上安装有不一样的Java虚拟机(JVM),由JVM来把字节码再“翻译”成所对应的硬件平台可以执行的代码。ajax
JDK包含JRE,JRE包含JVMredis
JRE(JavaRuntimeEnvironment,Java运行环境),也就是Java平台。全部的Java 程序都要在JRE下才能运行。普通用户只须要运行已开发好的java程序,安装JRE便可。算法
JDK(Java Development Kit)是程序开发者用来来编译、调试java程序用的开发工具包。JDK的工具也是Java程序,也须要JRE才能运行。jre是jdk的一部分。为了保持JDK的独立性和完整性,在JDK的安装过程当中,JRE也是 安装的一部分。因此,在JDK的安装目录下有一个名为jre的目录,用于存放JRE文件。
JVM(JavaVirtualMachine,Java虚拟机)是JRE的一部分。它是一个虚构出来的计算机,是经过在实际的计算机上仿真模拟各类计算机功能来实现的。JVM有本身完善的硬件架构,如处理器、堆栈、寄存器等,还具备相应的指令系统。Java语言最重要的特色就是跨平台运行。使用JVM就是为了支持与操做系统无关,实现跨平台。
GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会致使程序或系统的不稳定甚至崩溃,Java提供的GC功能能够自动监测对象是否超过做用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操做方法。
主要有如下四方面:
1.抽象:抽象就是找出一些事物的类似和共性之处,而后将这些事物归为一个类,这个类只考虑这些事物的类似和共性之处。抽象包括两个方面,一是过程抽象,二是数据抽象。
2.继承:子类继承父类,子类共享父类属性和方法的同时能够扩展本身的属性和方法。提升了软件的可重用性和可扩展性 。
3.封装:把对象的属性和方法结合成一个总体,并隐藏内部的实现细节,提供对外的访问接口。
4. 多态性:不一样对象对同一消息作出不一样的响应处理,主要实现:子类继承父类,子类重写父类的方法,父类的引用指向子类的对象。
面向过程
优势:性能比面向对象高,由于类调用时须要实例化,开销比较大,比较消耗资源;好比单片机、嵌入式开发、Linux/Unix等通常采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展
面向对象
优势:易维护、易复用、易扩展,因为面向对象有封装、继承、多态性的特性,能够设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低
区别以下:
做用域 当前类 同一package 子孙类 其余package
public √ √ √ √
protected √ √ √ ×
friendly √ √ × ×
private √ × × ×
不写时默认为friendly
&:”与”是位运算符,表示按位与运算,左边是false右边还执行。&&:”而且”是逻辑运算符,表示逻辑与(and),左边是true右边才执行
assertion(断言)在软件开发中是一种经常使用的调试方式,不少开发语言中都支持这种机制。在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为true;若是该值为false,说明程序已经处于不正确的状态下,系统将给出警告或退出。通常来讲,assertion用于保证程序最基本、关键的正确性。assertion检查一般在开发和测试时开启。为了提升性能,在软件发布后,assertion检查一般是关闭的
默认是关闭的,通常是测试的junit中用,判断结果和本身的预想值是否一致。
java中的保留字,如今没有在java中使用
String类是final类故不能够继承,final是最终的。
会执行,在return前执行
2 << 3:表示2向左移3位,由于一个数左移 n位,就至关于 2的 n次方,那么一个数乘以 8只要将其左移 3位便可,而为运算符的效率最高,因此 2乘以 8等于几的最有效方法是 2<<3
不对,有相同的hash code,由于equals比较hash code值相同才会返回true
是值传递。Java 编程语言只有值传递参数。
*值传递:是对所传递参数进行一次副本拷贝,对参数的修改只是对副本的修改,函数调用结束,副本丢弃,原来的变量不变(即实参不变),基本数据类型都是值传递。
*引用传递:参数被传递到函数时,不复制副本,而是直接将参数自身传入到函数,函数内对参数的任何改变都将反映到原来的变量上。
*对于基本类型,传递的是基本类型的值,而对于引用类型传递的是地址。
在Java 5之前,switch(expr)中,expr只能是byte、short、char、int。从Java 5开始,Java中引入了枚举类型,expr也能够是enum类型,从Java 7开始,expr还能够是字符串(String),可是长整型(long)在目前全部的版本中都是不能够的。
String 是被final修饰的,长度是不可变的。StringBuffer和StringBuilder长度都是可变的
三者在执行速度方面的比较:StringBuilder > StringBuffer > String
若是要操做少许的数据用 String
单线程操做字符串缓冲区 下操做大量数据 = StringBuilder
多线程操做字符串缓冲区 下操做大量数据 = StringBuffer
StringBuffer是线程安全的有锁,StringBuilder是线程不安全的,string是线程安全的由于final最终的。若是最后须要String,那么使用StringBuffer的toString()方法
final—用于声明属性、类和方法,分别表示属性不可变,方法不可覆盖(重写),类不可被继承
finally—是异常处理语句结构的一部分,表示老是执行
finalize—是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法用于回收资源,能够覆盖此方法提供垃圾收集时的其余资源回收,例如关闭文件等
栈是一种线形集合,其添加和删除元素的操做应在同一段完成。栈按照先进后出的方式进行处理。栈是堆得组成元素,内存由堆,栈,池组成。堆是按照先进先出的原则。栈的空间由系统自动分配,堆的空间能够本身分配。
栈至关于你往泡菜坛子里装泡菜,从里面拿泡菜,先拿到的固然是上面的。也就是先进后出。堆(队列)至关于一个隧道,火车往里面开,不能回头,头先进去固然也先出来,这叫先进先出。
Java语言中一个显著的特色就是引入了垃圾回收机制,垃圾回收能够有效的防止内存泄露,有效的使用能够使用的内存。
对于GC来讲,当程序员建立对象时,GC就开始监控这个对象的地址、大小以及使用状况。一般,GC采用有向图的方式记录和管理堆(heap)中的全部对象。经过这种方式肯定哪些对象是"可达的",哪些对象是"不可达的"。当GC肯定一些对象为"不可达"时,GC就有责任回收这些内存空间。
回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。
*分代复制垃圾回收:根据对象的生命周期不一样,分为年轻代、年老代、持久代,分区回收。
*标记垃圾回收:1、标记阶段,标记全部可访问对象;2、收集阶段,标记垃圾回收算法回收首页未标记的对象,在此过程当中会出现程序无响应。
*增量垃圾回收:主要是为了解决标记垃圾回收时长停顿的问题,设置GC最多中断的时间10ms,分时间段来回收垃圾。
JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。
1.装载:查找和导入class文件;new对象隐式装载,反射类显示装载,看log日志就知道什么是隐式/显式
2.链接:
(1)检查:检查载入的class文件数据的正确性;
(2)准备:为类的静态变量分配存储空间;
(3)解析:将符号引用转换成直接引用(这一步是可选的)
3.初始化:初始化静态变量,静态代码块。
这样的过程在程序调用类的静态成员的时候开始执行,因此静态方法main()才会成为通常程序的入口方法。类的构造器也会引起该动做。
能够。只有一个public类,而且类名与文件名相同。
内存泄漏是指再也不被使用的对象或者变量一直被占据在内存中。检查java中的内存泄露,必定要让程序将各类分支状况都完整执行到程序结束,而后看某个对象是否被使用过,若是没有,则才能断定这个对象属于内存泄露。
会。长生命周期的对象持有短生命周期对象的引用就有可能发生内存泄露。好比像加载了一个对象放在缓存中而一直没有引用。
系统已经不能再分配出你所须要的空间,好比你须要100M的空间,系统只剩90M了,这就叫内存溢出。
内存泄露是对象没有引用的时候没有被回收,一直占据着内存。
对于GC来讲,当程序员建立对象时,GC就开始监控这个对象的地址、大小以及使用状况。一般,GC采用有向图的方式记录和管理堆(heap)中的全部对象。经过这种方式肯定哪些对象是"可达的",哪些对象是"不可达的"。当GC肯定一些对象为"不可达"时,GC就有责任回收这些内存空间。能够。程序员能够手动执行System.gc(),通知GC运行,可是Java语言规范并不保证GC必定会执行。
一、尽早释放无用对象的引用
好的办法是使用临时变量的时候,让引用变量在推出活动域后自动设置为null,暗示垃圾收集器来收集该对象,防止发生内存泄漏。
二、程序进行字符串处理时,尽可能避免使用String,而应该使用StringBuffer。
由于String类是不可变的,每个String对象都会独立占用内存一块区域。
三、尽可能少用静态变量
由于静态变量是全局的,存在方法区,GC不会回收。(用永久代实现的方法区,垃圾回收行为在这个区域是比较少出现的,垃圾回收器的主要目标是针对常量池和类型的卸载)
四、避免集中建立对象,尤为是大对象,若是能够的话尽可能使用流操做
JVM会忽然须要大量neicun,这时会出发GC优化系统内存环境
五、尽可能运用对象池技术以提升系统性能
生命周期长的对象拥有生命周期短的对象时容易引起内存泄漏,例如大集合对象拥有大数据量的业务对象的时候,能够考虑分块进行处理,而后解决一块释放一块的策略。
六、不要在常常调用的方法中建立对象,尤为忌讳在循环中建立对象
能够适当的使用hashtable,vector建立一组对象容器,而后从容器中去取这些对象,而不用每次new以后又丢弃。
七、优化配置
Java 内存溢出java.lang.OutOfMemoryError这个错误我相信大部分开发人员都有遇到过,产生该错误的缘由大都出于如下缘由:JVM内存太小、程序不严密,产生了过多的垃圾。
致使OutOfMemoryError异常的常见缘由有如下几种:
内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
代码中存在死循环或循环产生过多重复的对象实体;
使用的第三方软件中的BUG;
启动参数内存值设定的太小;
1、增长jvm的内存大小。方法有:
1)在执行某个class文件时候,能够使用java -Xmx256M aa.class来设置运行aa.class时jvm所容许占用的最大内存为256M。
2)对tomcat容器,能够在启动时对jvm设置内存限度。对tomcat,能够在catalina.bat中添加:
set CATALINA_OPTS=-Xms128M -Xmx256M
set JAVA_OPTS=-Xms128M -Xmx256M
或者把%CATALINA_OPTS%和%JAVA_OPTS%代替为-Xms128M -Xmx256M
3)对resin容器,一样能够在启动时对jvm设置内存限度。在bin文件夹下建立一个startup.bat文件,内容以下:
@echo off
call "httpd.exe" "-Xms128M" "-Xmx256M"
:end
其中"-Xms128M"为最小内存,"-Xmx256M"为最大内存。
static i = 10; //常量 class A a; a.i =10;//可变
1)在语法定义上的区别:静态变量前要加 static 关键字,而实例变量(成员变量)前则不加。
2)在程序运行时的区别:实例变量属于某个对象的属性,必须建立了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。
不能够,若是其中包含对象的method();不能保证对象初始化.
java赋值是复制对象引用,若是咱们想要获得一个对象的副本,就须要使用克隆clone()
Clone 有缺省行为,super.clone();他负责产生正确大小的空间,并逐位复制。
类使用克隆:
① 实现Cloneable接口,这是一个标记接口,自身没有方法。
② 对象.clone()方法获得克隆对象,Person p1=new Person();Person p2=(Person) p1.clone();
用break; return 方法。
经常使用的类:BufferedReader BufferedWriter FileReader FileWirter String Integer
经常使用的包:java.lang java.awt java.io java.util java.sql
经常使用的接口:Remote List Map Document NodeList
*值传递:是对所传递参数进行一次副本拷贝,对参数的修改只是对副本的修改,函数调用结束,副本丢弃,原来的变量不变(即实参不变),基本数据类型都是值传递。
*引用传递:参数被传递到函数时,不复制副本,而是直接将参数自身传入到函数,函数内对参数的任何改变都将反映到原来的变量上。
加载、验证、准备、初始化、引用和卸载这六个过程
加载类,验证语法,准备(分配内存),初始化(父子类构造函数,赋值),引用,卸载(垃圾回收)
a += 1这个和 a = a +1 这两个是同样的 只不过一个是简写
++a 和 a++ 这两个区别在于运算的前后
好比:
b = ++a; 这个是a先自增 而后才赋值
b = a++; 这个是先赋值 后自增
①clone():protected Object clone()建立并返回此对象的一个副本。
②equals():boolean equals(Object obj)指示其余某个对象是否与此对象“相等”。
③finalize():protected void finalize()当垃圾回收器肯定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
④getClass():Class<?> getClass()返回此 Object 的运行时类。
⑤hashCode():int hashCode()返回该对象的哈希码值。
⑥notify():void notify()唤醒在此对象监视器上等待的单个线程。
⑦notifyAll():void notifyAll()唤醒在此对象监视器上等待的全部线程。
⑧toString():String toString()返回该对象的字符串表示。
⑨wait():void wait()在其余线程调用此对象的 notify() 方法或 notifyAll() 方法前,致使当前线程等待。
void wait(long timeout)在其余线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,致使当前线程等待。
void wait(long timeout, int nanos)在其余线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其余某个线程中断当前线程,或者已超过某个实际时间量前,致使当前线程等待。
装箱:将值类型转换为引用类型的过程称为装箱。int i=1; Object o=i
拆箱:将引用类型转换为值类型称为拆箱。int j=(int)o;
*顺序结构:从上往下,依次执行
*选择结构:if—else结构、switch结构
*循环结构:while、do while、for循环
值类型:8大数据类型
引用类型:数组、类、字符串、接口等
以值传递方式传递值类型参数时,对它的修改不会保留;以值传递方式传递引用类型参数时,其值的修改会保留。
以引用传递方式传递值类型参数和引用类型参数时,其值的修改都会保留。
java中会自动区分参数类型,所以没有ref,所以没有引用传递方式。
*矩形表示实体
*椭圆表示属性
*菱形表示联系
*直线用来链接
基本数据类型包括整数型4种:byte(1字节8位)、short(2字节16位)、int(4字节32位)、long(8字节64位);浮点型2种:float(4季节32位)、double(8字节64位);布尔型一种:boolean(1字节8位);字符型一种:char(2字节16位)。
1字节=8位=8bit 1个汉子=2字节 这里的位是指二进制的位数
java.lang.String类是final类型的,所以不能够继承这个类、不能修改这个类。为了提升效率节省空间,咱们应该用StringBuffer类
Java 提供两种不一样的类型:引用类型和原始类型(或内置类型)。int是java的原始数据类型初始值是0,Integer是java为int提供的封装类,初始值是null。Java为每一个原始类型提供了封装类,8大数据类型首字母大写就是封装类。
数组没有length()这个方法,有length的属性。String有length()这个方法
两个,一个字符对象,一个字符对象引用对象
答: Math.round(11.5)==12;Math.round(-11.5)==-11;round方法返回与参数最接近的长整数,参数加1/2后求其floor
short s1 = 1; s1 = s1 + 1; (s1+1运算结果是int型,须要强制转换类型会报错)short s1 = 1; s1 += 1;(能够正确编译)自动转型小转大,大转小必须强制转型
i=i+2 比 i+=2多了一次对变量 i 的运算。i=i+2是先进行i+2的运算得出一个结果,再赋值给i。i+=2就是先赋值而后在进行加法,所以运算效率高,结果是同样的。
是可以定义成为一个中文的,由于java中以unicode编码,一个char占16个字节,因此放一个中文是没问题的
答:不正确。精度不许确,有小数点的默认是double,应该用强制类型转换,以下所示:float f=(float)3.4或float f=3.4f
error 是不可控制的unchecked,用来表示系统错误或底层资源错误,若是有可能应该在系统级别被捕捉
exception 是可控的或不可控的(黑客攻击),表示程序级的错误,应该在程序级别被捕捉
一、空指针异常类:NullPointerException
二、数据类型转换异常:java.lang.ClassCastException
三、没有访问权限:java.lang.IllegalAccessException
四、方法的参数错误:java.lang.IllegalArgumentException
五、数组下标越界异常:java.lang.IndexOutOfBoundsException
六、文件已结束异常:EOFException
七、文件未找到异常:FileNotFoundException
八、字符串转换为数字异常:NumberFormatException
九、指定的类不存在: java.lang.ClassNotFoundException
十、实例化异常:java.lang.InstantiationException
异常表示程序运行过程当中可能出现的非正常状态,运行时异常表示虚拟机的一般操做中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,可是并不要求必须声明抛出未被捕获的运行时异常。
出现运行时异常后,系统会把异常一直往上层抛,一直遇处处理代码。若是没有处理块,到最上层,若是是多线程就由Thread.run() 抛出 ,若是是单线程就被 main() 抛出 。
原理:有错直接转到异常处理部分或向上抛出。
应用:JAVA 的异常就是错误,有两种一种是运行时,编码能够不用捕捉。一种是通常异常,若是throws 声明了,必须进行处理。
当JAVA程序违反了JAVA的语义规则时,JAVA虚拟机就会将发生的错误表示为一个异常。违反语义规则包括2种状况。一种是JAVA类库内置的语义检查。例如数组下标越界,会引起IndexOutOfBoundsException;访问null的对象时会引起NullPointerException。另外一种状况就是JAVA容许程序员扩展这种语义检查,程序员能够建立本身的异常,并自由选择在什么时候用throw关键字引起异常。全部的异常都是java.lang.Thowable的子类。
Java经过面向对象的方法进行异常处理,把各类不一样的异常进行分类,并提供了良好的接口。在Java中,每一个异常都是一个对象,它是Throwable类或其它子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法能够捕获到这个异常并进行处理。Java的异常处理是经过5个关键词来实现的:try、catch、throw、throws和finally。通常状况下是用try来执行一段程序,若是出现异常,系统会抛出(throws)一个异常,这时候你能够经过它的类型来捕捉(catch)它,或最后(finally)由缺省处理器来处理。
用try来指定一块预防全部"异常"的程序。紧跟在try程序后面,应包含一个catch子句来指定你想要捕捉的"异常"的类型。
throw语句用来明确地抛出一个"异常"。
throws用来标明一个成员函数可能抛出的各类"异常"。
Finally为确保一段代码无论发生什么"异常"都被执行一段代码。
能够在一个成员函数调用的外面写一个try语句,在这个成员函数内部写另外一个try语句保护其余代码。每当遇到一个try语句,"异常"的框架就放到堆栈上面,直到全部的try语句都完成。若是下一级的try语句没有对某种"异常"进行处理,堆栈就会展开,直到遇到有处理这种"异常"的try语句。
1. 经常使用的日志框架
①Java Logging API(Oracle)—— Java默认的日志框架
②Log4j(Apache)——开源日志框架
③Logback(Logback Project)——开源项目,被设计成Log4j版本1的后续版本
④tinylog(tinylog)——轻量级开源logger
log4j定义了8个级别的log(除去OFF和ALL,能够说分为6个级别),优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。
ALL 最低等级的,用于打开全部日志记录。
TRACE designates finer-grained informational events than the DEBUG.Since:1.2.12,很低的日志级别,通常不会使用。
DEBUG 指出细粒度信息事件对调试应用程序是很是有帮助的,主要用于开发过程当中打印一些运行信息。
INFO 消息在粗粒度级别上突出强调应用程序的运行过程。打印一些你感兴趣的或者重要的信息,这个能够用于生产环境中输出程序运行的一些重要信息,可是不能滥用,避免打印过多的日志。
WARN 代表会出现潜在错误的情形,有些信息不是错误信息,可是也要给程序员的一些提示。
ERROR 指出虽然发生错误事件,但仍然不影响系统的继续运行。打印错误和异常信息,若是不想输出太多的日志,能够使用这个级别。
FATAL 指出每一个严重的错误事件将会致使应用程序的退出。这个级别比较高了。重大错误,这种级别你能够直接中止程序了。
OFF 最高等级的,用于关闭全部日志记录。
反射是在运行状态中,对于任意一个类,都可以知道这个类的全部属性和方法;对于任意一个对象,都可以调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
JDBC中,利用反射动态加载了数据库驱动程序。
Web服务器中利用反射调用了Sevlet的服务方法。
Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。
不少框架都用到反射机制,注入属性,调用方法,如Spring。
优势:能够动态执行,在运行期间根据业务功能动态执行方法、访问属性,最大限度发挥了java的灵活性。
缺点:对性能有影响,这类操做老是慢于直接执行java代码。
动态代理是运行时动态生成代理类。
动态代理的应用有 Spring AOP数据查询、测试框架的后端 mock、rpc,Java注解对象获取等。
JDK 原生动态代理(发射机制)和 cglib 动态代理。
JDK 原生动态代理是基于接口实现的,而 cglib 是基于继承当前类的子类实现的。
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具备的成员变量和方法
在运行时调用任意一个对象的方法
答:
- 方法1:类型.class,例如:String.class
- 方法2:对象.getClass(),例如:"hello".getClass()
- 方法3:Class.forName(),例如:Class.forName("java.lang.String")
答:
- 方法1:经过类对象调用newInstance()方法,例如:String.class.newInstance()
- 方法2:经过类对象的getConstructor()或getDeclaredConstructor()方法得到构造器(Constructor)对象并调用其newInstance()方法建立对象,例如:String.class.getConstructor(String.class).newInstance("Hello");
答:能够经过类对象的getDeclaredField()方法字段(Field)对象,而后再经过字段对象的setAccessible(true)将其设置为能够访问,接下来就能够经过get/set方法来获取/设置字段的值了。
数组是一种数据类型,即引用类型。数组是相同数据类型元素的集合。数组长度固定。
集合用来存放一组相同类型的对象,长度可变。
Array能够包含基本类型和对象类型,ArrayList只能包含对象类型。
Array大小是固定的,ArrayList的大小是动态变化的。
ArrayList提供了更多的方法和特性,好比:addAll(),removeAll(),iterator()等等。
对于基本类型数据,集合使用自动装箱来减小编码工做量。可是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。
list、set能够使用toArray()方法返回一个Obiect数组。数组转list能够使用Arrays.aslist()获得一个list,再用list构造set(强转)。
获得key的集合,List<String> result = new ArrayList(map.keySet());
获得values的集合,List<String> result2 = new ArrayList(map.values());
list转map没有意义,不是键值对。
Collection是集合类的上级接口,继承与他的接口主要有Set 和List.
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各类集合的搜索、排序、线程安全化等操做
Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等
equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值
List,Set是,Map不是,
Map是一个独立的接口
AbstractMap<K,V> implements Map<K,V>
HashMap<K,V> extends AbstractMap<K,V>
就ArrayList与Vector主要从二方面来讲.
一.同步性:Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同步的
二.数据增加:当须要增加时,Vector默认增加为原来一培,而ArrayList倒是原来的一半
就HashMap与HashTable主要从三方面来讲。
一.历史缘由:Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现
二.同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的
三.值:只有HashMap的key或value能够为空值
Collection FrameWork以下:
Collection
├List 有序,可重复
│├LinkedList 底层数据结构是链表,查询慢,增删快,线程安全
│├ArrayList 底层数据结构是数组,查询快,增删慢,非线程安全
│└Vector 底层数据结构是数组,查询快,增删慢,线程安全
│ └Stack 栈类,先进后出
└Set 无序,惟一
│├HashSet 底层数据结构是哈希表,哈希表结合了数组的快速查询的优势又能融合链表方便快捷的增长删除元素的优点。线程不安全,在HashSet中,底层源码,其实就是一个HashMap,HashMap的key为HashSet中的值,而value为一个Object对象常量。
*哈希表依赖两个方法hashcode()和equals(),首先判断hasdcode()是否相同,是:继续执行equals(),是true说明元素重复不添加,false添加到集合
│├LinkedHashSet 底层数据结构由哈希表和链表组成,由链表保证元素有序性,由哈希表保证元素惟一性。非线程安全
│├CopyOnWriteArraySet 底层数据结构是数组,CopyOnWriteArraySet是利用CopyOnWriteArrayList来实现的,由于CopyOnWriteArrayList是线程安全的,因此 CopyOnWriteArraySet操做也是线程安全的
Map
├Hashtable 底层数据结构是哈希表,线程安全的,效率低,不容许空值,无序
├HashMap 底层数据结构是哈希表,线程不安全,效率高,容许空值,无序
└linkedHashMap 底层数据由哈希表+链表组成,由链表保证元素有序,哈希保证元素惟一,非线程安全
arrayList初始长度是10,hashMap初始长度是16
Collection是最基本的集合接口,一个Collection表明一组Object,即Collection的元素(Elements)
Collections是集合的算法
ArrayList,Vector底层数据结构都是数组,查询快,增删慢。ArrayList线程不安全,Vector线程安全可是效率慢。LinkedList线程安全,底层数据结构是链表,查询慢,增删快。
HashMap和Hashtable底层数据结构都是哈希表。HashMap线程非安全,能够容许空值,效率高。HashTable线程安全,效率低。
最经常使用的集合类是 List 和 Map。 List 的具体实现包括 ArrayList 和 Vector,它们是可变大小的列表,比较适合构建、存储和操做任何类型对象的元素列表。 List 适用于按数值索引访问元素的情形。
Map 提供了一个更通用的元素存储方法。 Map 集合类用于存储元素对(称做"键"和"值"),其中每一个键映射到一个值。经过map.entrySet().iterator()或map.keySet().iterator()来获取迭代器(iterator).hasNext()遍历元素。
List 以特定次序来持有元素,可有重复元素。Set 没法拥有重复元素,内部排序。Map 保存key-value值,value可多值。
由于ArrayList的底层是数组实现,而且数组的默认值是10,若是插入10000条要不断的扩容,耗费时间,因此咱们调用ArrayList的指定容量的构造器方法ArrayList(int size) 就能够实现不扩容,就提升了性能。
1:抽象类用abstract关键字,接口用interface
2:接口只能定义方法,抽象类不只能够定义抽象方法,还能够有实现方法
3:抽象类只能单一继承,接口能够被多重实现
抽象定义的类叫抽象类,抽象类字段默认friendly(本包可见),用abstract关键字定义抽象类和抽象方法。
4:抽象层次不一样,抽象类是对类抽象,而接口是对行为的抽象。
抽象方法没有主体,有抽象方法的必定是抽象类。抽象类不必定必须有抽象方法,抽象类能够定义和实现。抽象类只能被单一继承extends。抽象类抽象方法不能使用private,由于不能被子类继承。抽象方法不能使用static,由于方法没有主体没有意义。
接口(interface)是抽象类的变体。在接口中,全部方法都是抽象的。接口能够被多重实现implements,接口的字段默认为public static final。instanceof 运算符能够用来决定某对象的类是否实现了接口
当存在继承关系时用抽象类,只须要使用单一功能用接口。
*抽象类与接口都用于抽象,可是抽象类(JAVA中)能够有本身的部分实现,而接口则彻底是一个标识(同时有多重继承的功能)。
Collection框架中实现比较要实现Comparable 接口和 Comparator 接口,这两个接口用于排序
都不能,abstract是没有被实现的,而static必定要被实现的。Synchronized是须要同步的,abstract只有被子类继承的时候才能添加同步。Native本地方法,它把具体的实现交给了本地的函数库,没有经过虚拟机,是java与其余语言通讯的一种机制。与抽象方法把实现交给子类实现冲突。
接口能够继承接口。抽象类能够实现(implements)接口,抽象类是否可继承实体类,但前提是抽象类必须能访问实体类的构造方法,子类默认调用父类的构造函数。若是实体了申明了一个private 的无参构造函数,则系统不会自动生成该类的无参构造函数,所以抽象类访问不到实体类的构造函数而编译失败。
构造器Constructor不能被继承,所以不能重写Overriding,但能够被重载Overloading(有参、无参、多参的构造函数)
Overloaded的方法是否能够改变返回值的类型
方法的重写Overriding和重载Overloading是Java多态性的不一样表现。重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。
子类重写父类的方法,方法名、参数类型个数、返回值类型必须相同。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被"屏蔽"了。发生在继承类中,被重写的方法不能有更高的权限。
若是在一个类中定义了多个同名的方法,方法名相同,参数类型或个数不一样,返回值的类型也能够不一样。则称为方法的重载(Overloading)。发生在同一个类中,通常用于构造函数,对权限没有要求。
匿名的内部类是没有名字的内部类。不能extends(继承) 其它类,但一个内部类能够做为一个接口,由另外一个内部类实现
因为Java不支持多继承,而有可能某个类或对象要使用分别在几个类或对象里面的方法或属性,现有的单继承机制就不能知足要求。与继承相比,接口有更高的灵活性,由于接口中没有任何实现代码。当一个类实现了接口之后,该类要实现接口里面全部的方法和属性,而且接口里面的属性在默认状态下面都是public static,全部方法默认状况下是public.一个类能够实现多个接口。
方法的重写Overriding和重载Overloading是Java多态性的不一样表现。重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。
1发挥多核CPU的优点,相似与食堂多个窗口打饭
2防止阻塞,四车道与单车道的比较
3便于建模,能够把前后顺序关联性不强的任务拆分红几个同步进行,提升效率。好比在生成一个汽车骨架的时候,相应的其余零件也在同步生成,最后组装。
一、继承Thread类,重写run方法
二、实现Runnable接口
三、经过Callable和FutureTack建立线程
四、经过线程池建立线程
知道前面两种便可,相比实现Runnable接口更快捷,一Java支持单继承,继承Thread类就不能集成其余类,继承扩展性被占,二线程可能只要求可执行便可,Thread类相比开销过大。
简而言之,进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程.进程在执行过程当中拥有独立的内存单元,而多个线程共享内存资源,减小切换次数,从而效率更高.
线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位.同一进程中的多个线程之间能够并发执行.
程序运行完毕,jvm会等待非守护线程完成后关闭,可是jvm不会等待守护线程.守护线程最典型的例子就是GC线程
多线程的上下文切换是指CPU控制权由一个已经正在运行的线程切换到另一个就绪并等待获取CPU执行权的线程的过程。
1)Sleeep来自Thread类,wait()来自Object类。
2)调用sleep()过程当中,线程不会释放对象锁,wait()方法会释放。
3)sleep()睡眠后不让出系统资源,wait()让出资源
4)sleep(millssecond)须要指定一个睡眠时间,时间一到会自动唤醒
只有调用了start()方法,才会表现出多线程的特性,不一样线程的run()方法里面的代码交替执行。若是只是调用run()方法,那么代码仍是同步执行的,必须等待一个线程的run()方法里面的代码所有执行完毕以后,另一个线程才能够执行其run()方法里面的代码。
通俗的说:加锁的就是是线程安全的,不加锁的就是是线程不安全的
线程安全: 就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其余线程不能进行访问,直到该线程读取完,其余线程才可以使用。不会出现数据不一致或者数据污染。
1)乐观锁:就像它的名字同样,对于并发间操做产生的线程安全问题持乐观状态,乐观锁认为竞争不老是会发生,所以它不须要持有锁,将比较-替换这两个动做做为一个原子操做尝试去修改内存中的变量,若是失败则表示发生冲突,那么就应该有相应的重试逻辑。
2)悲观锁:仍是像它的名字同样,对于并发间操做产生的线程安全问题持悲观状态,悲观锁认为竞争老是会发生,所以每次对某资源进行操做时,都会持有一个独占的锁,就像synchronized,无论三七二十一,直接上了锁就操做资源了。
CAS,全称为Compare and Swap,即比较-替换。假设有三个操做数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,才会将内存值修改成B并返回true,不然什么都不作并返回false。
新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。
*新建状态,当程序使用new关键字建立了一个线程以后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值
*就绪状态,当线程对象调用了start()方法以后,该线程处于就绪状态。Java虚拟机会为其建立方法调用栈和程序计数器,等待调度运行
*运行状态,若是处于就绪状态的线程得到了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态
*阻塞状态,当处于运行状态的线程失去所占用资源以后,便进入阻塞状态
*死亡状态,线程在run()方法执行结束后进入死亡状态。此外,若是线程执行了interrupt()或stop()方法,那么它也会以异常退出的方式进入死亡状态。
(1)线程睡眠:Thread.sleep (long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。
(2)线程等待:Object类中的wait()方法,致使当前的线程等待,直到其余线程调用此对象的 notify() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait() 同样。wait() 和 notify() 方法:两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种容许 指定以毫秒为单位的一段时间做为参数,另外一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程从新进入可执行状态,后者则必须对应的 notify() 被调用.
(3)线程礼让,Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。yield() 使得线程放弃当前分得的 CPU 时间,可是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另外一个线程.
(4)线程自闭,join()方法,等待其余线程终止。在当前线程中调用另外一个线程的join()方法,则当前线程转入阻塞状态,直到另外一个进程运行结束,当前线程再由阻塞转为就绪状态。
死锁是指两个以上的进程在执行过程当中,因为竞争资源或通讯形成的一种阻塞现象,若无外力做用,它们将没法推行下去。
1.互斥条件:一个资源每次只能被一个进程使用。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已得到的资源保持不放。
3.不剥夺条件:进程已得到的资源,在末使用完以前,不能强行剥夺。
4.循环等待条件:若干进程之间造成一种头尾相接的循环等待资源关系。
一、不让产生死锁的四个条件同时成立
二、合理分配资源
三、使用银行家算法,若是该进程请求的资源当前操做系统余量能够知足,就分配
(1)管道(Pipe):管道可用于具备亲缘关系进程间的通讯,容许一个进程和另外一个与它有共同祖先的进程之间进行通讯。
(2)命名管道(named pipe):命名管道克服了管道没有名字的限制,所以,除具备管道所具备的功能外,它还容许无亲缘关 系 进程间的通讯。命名管道在文件系统中有对应的文件名。命名管道经过命令mkfifo或系统调用mkfifo来建立。
(3)信号(Signal):信号是比较复杂的通讯方式,用于通知接受进程有某种事件发生,除了用于进程间通讯外,进程还能够发送 信号给进程自己;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又可以统一对外接口,用sigaction函数从新实现了signal函数)。
(4)消息(Message)队列:消息队列是消息的连接表,包括Posix消息队列system V消息队列。有足够权限的进程能够向队列中添加消息,被赋予读权限的进程则能够读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺
(5)共享内存:使得多个进程能够访问同一块内存空间,是最快的可用IPC形式。是针对其余通讯机制运行效率较低而设计的。每每与其它通讯机制,如信号量结合使用,来达到进程间的同步及互斥。
(6)内存映射(mapped memory):内存映射容许任何多个进程间通讯,每个使用该机制的进程经过把一个共享的文件映射到本身的进程地址空间来实现它。
(7)信号量(semaphore):主要做为进程间以及同一进程不一样线程之间的同步手段。
(8)套接口(Socket):更为通常的进程间通讯机制,可用于不一样机器之间的进程间通讯。起初是由Unix系统的BSD分支开发出来的,但如今通常能够移植到其它类Unix系统上:Linux和System V的变种都支持套接字。
1)自旋锁:自旋锁顾名思义,它会等待必定时间(自旋),在这期中会什么都不作就是等资源被释放,好处在于没有了内核态用户态切换的效率损失,可是若是它一直不能访问到资源的话就会一直占用cpu资源,因此它会循环一段时间后进入阻塞状态。
重量级锁:synchronized就是重量级锁的实现机制,抢不到资源的进程会进入阻塞状态
2)偏向锁:顾名思义,它会偏向第一个访问资源的进程,若是说只有一个进程执行同步代码块,那么就会上个偏向锁,若是有其余线程抢占资源,那么就会升级为轻量级锁
轻量级锁:偏向锁升级以后就是轻量级锁,锁只能够升级而不能够降级。轻量级锁中的其余进程会进入自选状态,若是说自选失败,就会升级会重量级锁
3)公平,非公平锁:主要是指线程是否先来后到拿到锁,synchronized是非公平的,而ReentrantLock默认非公平,能够设置为公平锁
4)悲观锁:老是假设最坏的状况,每次去拿数据的时候都认为别人会修改,因此每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了不少这种锁机制,好比行锁,表锁等,读锁,写锁等,都是在作操做以前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
5)乐观锁:老是假设最好的状况,每次去拿数据的时候都认为别人不会修改,因此不会上锁,可是在更新的时候会判断一下在此期间别人有没有去更新这个数据,能够使用版本号机制(数据库中)和CAS算法实现
1)sleep()给其它线程运行机会不会考虑线程优先级,yield()只会给相同优先级或更高优先级的线程机会
2)线程执行sleep()方法后进入阻塞状态,而执行yeild()进入就绪状态
3)sleep()方法须要抛出异常interupdateException,而yeild()不用抛异常
4)sleep()比yield()有更好的移植性
线程. setPriority()方法来设置线程的优先级,参数是1~10,默认是5。优先级和线程抢到资源的几率没有关系。结合yeild()就有用了。
一、吞吐量:你作WEB,容器帮你作了多线程,可是他只能帮你作请求层面的。简单的说,可能就是一个请求一个线程。或多个请求一个线程。若是是单线程,那同时只能处理一个用户的请求。
二、伸缩性:也就是说,你能够经过增长CPU核数来提高性能。若是是单线程,那程序执行到死也就利用了单核,确定没办法经过增长CPU核数来提高性能。鉴于你是作WEB的,第1点可能你几乎不涉及。那这里我就讲第二点吧。--举个简单的例子:假设有个请求,这个请求服务端的处理须要执行3个很缓慢的IO操做(好比数据库查询或文件查询),那么正常的顺序多是(括号里面表明执行时间):
a、读取文件1 (10ms)
b、处理1的数据(1ms)
c、读取文件2 (10ms)
d、处理2的数据(1ms)
e、读取文件3 (10ms)
f、处理3的数据(1ms)
若是作 java web 方面开发的话几乎用不到多线程!由于有多线程的地方 servlet 容器或者其余开发框架都已经实现掉了!
通常在网络应用程序中使用多线程的地方很是多!
另外,你说的拷贝文件使用多线程,那是没有用的!以多线程来提升效率的场景通常在 CPU 计算型,而不是在 IO 读写型。CPU 能够会有多个核心并行处理计算,可是磁盘 IO 就没这功能了,磁头只有一个,根本不可能靠多线程提升效率!
通常来讲,磁盘 IO 的并发能力为 0,也就是说没法支持并发!网络 IO 的话因为带宽的限制的,使用多线程处理最多也只能达到带宽的极值。
对于磁盘 IO 来讲,多线程能够用于一个线程专门用于读写文件,其余的线程用于对读取数据进行处理,这样才有可能更好地利用 CPU 资源。
若是仅仅是单纯的文件复制,使用多线程操做的话,会使用磁头在磁盘上不停地进行寻道操做,使得效率更为低下!
1.建立+执行+销毁线程>单线程时间
2.一个线程默认占用1M,内存销毁
3.频繁切换线程上下文影响性能
若是异常没有被捕获该线程将会中止执行。Thread.UncaughtExceptionHandler 是用于处理未捕获异常形成线程忽然中断状况的一个内嵌接口。当一个未捕获异常将形成线程中断的时候JVM会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler并将线程和异常做为参数传递给handler的uncaughtException()方法进行处理。
锁类、锁方法、锁代码块。
线程过多会形成栈溢出,也有可能会形成堆异常。
Java 中平时用的最多的 Map 集合就是 HashMap 了,它是线程不安全的
看下面两个场景:
一、当用在方法内的局部变量时,局部变量属于当前线程级别的变量,其余线程访问
不了,因此这时也不存在线程安全不安全的问题了。
二、当用在单例对象成员变量的时候呢?这时候多个线程过来访问的就是同一个
HashMap 了,对同个 HashMap 操做这时候就存在线程安全的问题了。
java.lang.Thread#holdsLock 方法
3二、线程同步须要注意什么?
一、尽可能缩小同步的范围,增长系统吞吐量。
二、分布式同步锁无心义,要使用分布式锁。
三、防止死锁,注意加锁顺序。
3五、线程之间如何传递数据?
通 过 在 线 程 之 间 共 享 对 象 就 可 以 了 , 然 后 通 过 wait/notify/notifyAll 、
await/signal/signalAll 进行唤起和等待,比方说阻塞队列 BlockingQueue 就是为线程之
间共享数据而设计的
1)工厂模式:有一个专门的类负责建立实例的过程。
2)单例模式:一个类始终只能建立一个实例。
3)代理模式:一个对象表明另外一个对象采起行动。是一种很是普遍的设计模式,例如hibernate中A对象管理B对象,加载A对象的时候先加载一个B的代理对象,只有在实际使用到B对象的时候才会加载B的实体。
4)观察模式:观察者模式定义了一对多关联依赖关系,让一个或多个对象观察一个主题对象。当主题对象发生变化时,系统通知全部的观察者对象自动更新。
懒汉模式:在类加载的时候不被初始化。
饿汉模式:在类加载时就完成了初始化,可是加载比较慢,获取对象比较快。
饿汉模式是线程安全的,在类建立好一个静态对象提供给系统使用,懒汉模式在建立对象时不加上synchronized,会致使对象的访问不是线程安全的
高内聚低耦合是软件设计的一个基本原则,说的是在程序的各个模块中,尽可能让每一个模块独立,相关的处理尽可能在单个模块中完成。优势:能提下降各模块的之间的联系,减小“牵一发而动全身”的概率,提升开发效率,下降升级维护成本,也便于进行单元测试,提升软件质量。
1)单一职责原则:一个类值负责一个功能的职责
2)开闭原则:扩展开放,修改关闭。
3)里氏代换原则:使用父类的地方都能使用子类对象
4)依赖倒转原则:针对接口编程,
5)接口隔离原则:针对不一样部分用专门接口,不用总接口,须要哪些接口就用哪些接口
6)迪米特法则: 软件实体类,尽可能不与其余实体类发生关系相互做用,对外都统一的暴露接口就好了
许多的设计模式,包括一些框架,都是参考高内聚低耦合这个点的。
答:首先 Java 的 IO 库提供了一种连接(Chaining)机制,能够将一个流处理器跟另外一个流处理器首尾相接,以其中之一的输出做为另外一个的输入而造成一个流管道连接,譬如常见的 new DataInputStream(new FileInputStream(file)) 就是把 FileInputStream 流看成 DataInputStream 流的管道连接。
其次,对于 Java IO 流还涉及一种对称性的设计策略,其表现为输入输出对称性(如 InputStream 和 OutputStream 的字节输入输出操做,Reader 和 Writer 的字符输入输出操做)和字节字符的对称性(InputStream 和 Reader 的字节字符输入操做,OutputStream 和 Writer 的字节字符输出操做)。
此外,对于 Java IO 流在总体设计上还涉及装饰者(Decorator)和适配器(Adapter)两种设计模式。
对于 IO 流涉及的装饰者设计模式例子以下:
//把InputStreamReader装饰成BufferedReader来成为具有缓冲能力的Reader。
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
对于 IO 流涉及的适配器设计模式例子以下:
//把FileInputStream文件字节流适配成InputStreamReader字符流来操做文件字符串。
FileInputStream fileInput = new FileInputStream(file);
InputStreamReader inputStreamReader = new InputStreamReader(fileInput);
而对于上面涉及的两种设计模式通俗总结以下。
装饰者模式就是给一个对象增长一些新的功能,并且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例(各类字符流间装饰,各类字节流间装饰)。
适配器模式就是将某个类的接口转换成咱们指望的另外一个接口表示,目的是消除因为接口不匹配所形成的类的兼容性问题(字符流与字节流间互相适配)。
答:计算机中的一切最终都是以二进制字节形式存在的,对于咱们常常操做的字符串,在写入时其实都是先获得了其对应的字节,而后将字节写入到输出流,在读取时其实都是先读到的是字节,而后将字节直接使用或者转换为字符给咱们使用。因为对于字节和字符两种操做的需求比较普遍,因此 Java 专门提供了字符流与字节流相关IO类。
对于程序运行的底层设备来讲永远都只接受字节数据,因此当咱们往设备写数据时不管是字节仍是字符最终都是写的字节流。字符流是字节流的包装类,因此当咱们将字符流向字节流转换时要注意编码问题(由于字符串转成字节数组的实质是转成该字符串的某种字节编码)。
字符流和字节流的使用很是类似,可是实际上字节流的操做不会通过缓冲区(内存)而是直接操做文本自己的,而字符流的操做会先通过缓冲区(内存)而后经过缓冲区再操做文件。
答:缓冲区就是一段特殊的内存区域,不少状况下当程序须要频繁地操做一个资源(如文件或数据库)则性能会很低,因此为了提高性能就能够将一部分数据暂时读写到缓存区,之后直接今后区域中读写数据便可,这样就显著提高了性能。
对于 Java 字符流的操做都是在缓冲区操做的,因此若是咱们想在字符流操做中主动将缓冲区刷新到文件则能够使用 flush() 方法操做。
答:大多数状况下使用字节流会更好,而大多数时候 IO 操做都是直接操做磁盘文件,因此这些流在传输时都是以字节的方式进行的(图片等都是按字节存储的)。
而若是对于操做须要经过 IO 在内存中频繁处理字符串的状况使用字符流会好些,由于字符流具有缓冲区,提升了性能。
字节流,字符流。字节流继承于InputStream OutputStream,字符流继承于InputStreamReader OutputStreamWriter。在java.io包中还有许多其余的流,主要是为了提升性能和使用方便。
计算机中的底层一切最终都是二进制的字节形式存在。首先读取的是字节,而后用字符流读取字符,字符流只能处理字符或者字符串。字节流通常用于传输,字符用于读取字符。字节流能够用于任何类型的对象,包括二进制,但它不能直接处理unicode字符,字符流能够。
*序列化:把Java对象转换为字节序列的过程。
*反序列化:把字节序列恢复为Java对象的过程。
序列化就是一种用来处理对象流的机制,把对象转换成字节流方便网络传输。
序列化的实现:将须要被序列化的类实现Serializable接口,该接口没有须要实现的方法。implements Serializable只是为了标注该对象是可被序列化的,而后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就能够将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
泛型,即“参数化类型”。建立集合时就指定集合元素的类型,该集合只能保存其指定类型的元素,避免使用强制类型转换。
Java编译器生成的字节码是不包涵泛型信息的,泛型类型信息将在编译处理是被擦除,这个过程即类型擦除。泛型擦除能够简单的理解为将泛型java代码转换为普通java代码,只不过编译器更直接点,将泛型java代码直接转换成普通java字节码。
类型擦除的主要过程以下:
1).将全部的泛型参数用其最左边界(最顶级的父类型)类型替换。
2).移除全部的类型参数。
泛型是经过类型擦除来实现的,编译器在编译时擦除了全部类型相关的信息,因此在运行时不存在任何类型相关的信息。例如List<String>在运行时仅用一个List来表示。这样作的目的,是确保能和Java 5以前的版本开发二进制类库进行兼容。你没法在运行时访问到类型参数,由于编译器已经把泛型类型转换成了原始类型。根据你对这个泛型问题的回答状况,你会获得一些后续提问,好比为何泛型是由类型擦除来实现的或者给你展现一些会致使编译器出错的错误泛型代码。请阅读个人Java中泛型是如何工做的来了解更多信息。
限定通配符对类型进行了限制。有两种限定通配符,一种是<? extends T>它经过确保类型必须是T的子类来设定类型的上界,另外一种是<? super T>它经过确保类型必须是T的父类来设定类型的下界。泛型类型必须用限定内的类型来进行初始化,不然会致使编译错误。另外一方面<?>表示了非限定通配符,由于<?>能够用任意类型来替代。更多信息请参阅个人文章泛型中限定通配符和非限定通配符之间的区别。
这两个List的声明都是限定通配符的例子,List<? extends T>能够接受任何继承自T的类型的List,而List<? super T>能够接受任何T的父类构成的List。例如List<? extends Number>能够接受List<Integer>或List<Float>。在本段出现的链接中能够找到更多信息。
编写泛型方法并不困难,你须要用泛型类型来替代原始类型,好比使用T, E or K,V等被普遍承认的类型占位符。泛型方法的例子请参阅Java集合类框架。最简单的状况下,一个泛型方法可能会像这样:
public V put(K key, V value) {
return cache.put(key, value);
}
这样作的话会致使编译错误。由于List<Object>能够存储任何类型的对象包括String, Integer等等,而List<String>却只能用来存储Strings。
List<Object> objectList;
List<String> stringList;
objectList = stringList;
Array不支持泛型,这也是List代替Array的一个缘由,由于List能够提供编译期的类型安全保证,而Array却不能。
若是你把泛型和原始类型混合起来使用,例以下列代码,Java 5的javac编译器会产生类型未检查的警告,例如
List<String> rawList = new ArrayList()
排序的方法有:插入排序(直接插入排序、希尔排序),交换排序(冒泡排序、快速排序),选择排序(直接选择排序、堆排序),归并排序,分配排序(箱排序、基数排序)
快速排序是一种经常使用的排序算法,比冒泡排序快不少。
**在快速排序中使用了大量的递归,快速排序的三个步骤:
一、选择基准值
二、将数组分红两个子数组;小于基准值的元素和大于基准值的元素
三、对这两个子数组进行快速排序(递归)
快速排序的速度取决于选择的基准值,运行速度记作 O(n longn ),大O表示法底数默认为2
**选择排序的数组位移在外层循环,而冒泡排序的位移在内循环。
最慢的算法,有一个旅行商要到N个城市去旅游,但愿计算出最短的路线。计算公式为n!=1×2×3×...×n。也就是n的阶乘。
区别:
xml是重量级、json是轻量级
xml比较占带宽、json占带宽小,易于压缩
json在webservice 用的比较少、xml用的较多
相同:
二者都用在项目交互下 例如 移动app接口用的就是json、在web项目中与其余项目对接用xml较多。
json经常使用解析方法 gson、jsonobject、jackson等 xml dom sax pull 解析
DAO模式的组成部分:
1.DAO接口
2.DAO实现类
3.实体类
4.数据库链接和关闭工具类
DAO模式的做用:
1.隔离业务逻辑代码和数据访问代码
2.隔离不一样数据库的实现
Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,经过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分红逻辑清晰的几部分,简化开发,减小出错,方便组内开发人员之间的配合。
轻量级是指代码的侵入性,能够理解为框架与业务代码的耦合程度。SpringMVC是经过注解或外部文件进行配置的,不须要继承框架里的类或显示调用框架里的类或方法,所以是轻量级的。
(1)用户发送请求至前端控制器DispatcherServlet;
(2) DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,查找匹配该url 的handle,并返回一个执行链,没有则返回404
(3)DispatcherServlet 调用 HandlerAdapter处理器适配器返回ModelAndView;
(4)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析并返回视图View;
(5)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中),并把数据装入到request域,返回给用户
(1)能够支持各类视图技术,而不只仅局限于JSP;
(2)与Spring框架集成(如IoC容器、AOP等);
(3)清晰的角色分配:前端控制器(dispatcherServlet) , 请求处处理器映射(handlerMapping), 处理器适配器(HandlerAdapter), 视图解析器(ViewResolver)。
(4) 支持各类请求资源的映射策略。
(1)前端控制器 DispatcherServlet(不须要程序员开发)
做用:接收请求、响应结果,至关于转发器,有了DispatcherServlet 就减小了其它组件之间的耦合度。
(2)处理器映射器HandlerMapping(不须要程序员开发)
做用:根据请求的URL来查找Handler
(3)处理器适配器HandlerAdapter
注意:在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才能够正确的去执行Handler。
(4)处理器Handler(须要程序员开发)
(5)视图解析器 ViewResolver(不须要程序员开发)
做用:进行视图的解析,根据视图逻辑名解析成真正的视图(view)
(6)视图View(须要程序员开发jsp)
View是一个接口, 它的实现类支持不一样的视图类型(jsp,freemarker,pdf等等)
(1)springmvc的入口是一个servlet即前端控制器(DispatchServlet),而struts2入口是一个filter过虑器(StrutsPrepareAndExecuteFilter)。
(2)springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,能够设计为单例或多例(建议单例),struts2是基于类开发,传递参数是经过类的属性,只能设计为多例。
(3)Struts采用值栈存储请求和响应的数据,经过OGNL存取数据,springmvc经过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据经过reques域传输到页面。Jsp视图解析器默认使用jstl。
(1)转发:在返回值前面加"forward:",譬如"forward:user.do?name=method4"
(2)重定向:在返回值前面加"redirect:",譬如"redirect:http://www.baidu.com"
七、SpringMvc怎么和AJAX相互调用的?
经过Jackson框架就能够把Java里面的对象直接转化成Js能够识别的Json对象。具体步骤以下 :
(1)加入Jackson.jar
(2)在配置文件中配置json的映射
(3)在接受Ajax方法里面能够直接返回Object,List等,但方法前面要加上@ResponseBody注解。
(1)解决post请求乱码问题:
在web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf-8;
(2)get请求中文参数出现乱码解决方法有两个:
①修改tomcat配置文件添加编码与工程编码一致,以下:
<ConnectorURIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
②另一种方法对参数进行从新编码:
String userName = new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")
ISO8859-1是tomcat默认编码,须要将tomcat编码后的内容按utf-8编码。
答:能够将异常抛给Spring框架,由Spring框架来处理;咱们只须要配置简单的异常处理器,在异常处理器中添视图页面便可。
答:是单例模式,因此在多线程访问的时候有线程安全问题,不要用同步,会影响性能的,解决方案是在控制器里面不能写字段。
@RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的全部响应请求的方法都是以该地址做为父路径。
@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。
@ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。
答:通常用@Controller注解,也能够使用@RestController,@RestController注解至关于@ResponseBody + @Controller,表示是表现层,除此以外,通常不用别的注解代替。
答:能够在@RequestMapping注解里面加上method=RequestMethod.GET。
答:直接在方法的形参中声明request,SpringMvc就自动把request对象传入。
答:直接在形参里面声明这个参数就能够,但必须名字和传过来的参数同样。
答:直接在方法中声明这个对象,SpringMvc就自动会把属性赋值到这个对象里面。
答:返回值能够有不少类型,有String, ModelAndView。ModelAndView类把视图和数据都合并的一块儿的,但通常用String比较好。
答:经过ModelMap对象,能够在这个对象里面调用put方法,把对象加到里面,前台就能够经过el表达式拿到。
答:能够在类上面加上@SessionAttributes注解,里面包含的字符串就是要放入session里面的key。
有两种写法,一种是实现HandlerInterceptor接口,另一种是继承适配器类,接着在接口方法当中,实现处理逻辑;而后在SpringMvc的配置文件中配置拦截器便可:
<!-- 配置SpringMvc的拦截器 -->
<mvc:interceptors>
<!-- 配置一个拦截器的Bean就能够了 默认是对全部请求都拦截 -->
<bean id="myInterceptor" class="com.zwp.action.MyHandlerInterceptor"></bean>
<!-- 只针对部分请求拦截 -->
<mvc:interceptor>
<mvc:mapping path="/modelMap.do" />
<bean class="com.zwp.action.MyHandlerInterceptorAdapter" />
</mvc:interceptor>
</mvc:interceptors>
注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。咱们经过反射获取注解时,返回的是Java运行时生成的动态代理对象。经过代理对象调用自定义注解的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。
Struts 2框架自己大体能够分为3个部分:核心控制器FilterDispatcher、业务控制器Action和用户实现的企业业务逻辑组件。 1)核心控制器FilterDispatcher是Struts 2框架的基础,包含了框架内部的控制流程和处理机制。
2)业务控制器Action和业务逻辑组件是须要用户来本身实现的。用户在开发Action和业务逻辑组件的同时,还须要编写相关的配置文件,供核心控制器FilterDispatcher来使用。
Struts 2的工做流程相对于Struts 1要简单,与WebWork框架基本相同,因此说Struts 2是WebWork的升级版本。
基本简要流程以下:
1)客户端浏览器发出HTTP请求。
2)根据web.xml配置,该请求被FilterDispatcher接收。
3)根据struts.xml配置,找到须要调用的Action类和方法, 并经过IoC方式,将值注入给Aciton。
4)Action调用业务逻辑组件处理业务逻辑,这一步包含表单验证。
5)Action执行完毕,根据struts.xml中的配置找到对应的返回结果result,并跳转到相应页面。
6)返回HTTP响应到客户端浏览器。
1)拦截器是struts2核心组成部分,它提供了一种机制,使得开发者能够定义一个特定的功能模块,这个模块会在Action执行以前或者以后执行,也能够在Action执行以前阻止Action执行。
2)经常使用的拦截器有:
chain:在不一样请求之间将请求参数在不一样名字件转换,请求内容不变
fileUpload:提供文件上传。
i18n:记录用户选择的区域环境
logger:输出Action的名字
params:将请求中的参数设置到Action中去。
1)在软件设计上Struts2的应用能够不依赖于Servlet API和struts API。 Struts2的这种设计属于无侵入式设计;
2)拦截器,实现如参数拦截注入等功能;
3)类型转换器,能够把特殊的请求参数转换成须要的类型;
4)多种表现层技术,如:JSP、freeMarker、Velocity等;
5)Struts2的输入校验能够对指定某个方法进行校验;
6)提供了全局范围、包范围和Action范围的国际化资源文件管理实现
7) 实现MVC模式,结构清晰,使开发者只关注业务逻辑的实现。有丰富的tag能够用,大大提升了开发效率。(简要)
OGNL是Object-Graph Navigation Language的缩写,也叫对象导航语言。它是Struts的一种功能强大的表达式语言列如:访问session中的user对象的username属性:注意的是:使用前须要在页面头部导入taglib prefix="s" uri="/struts-tags"
14六、什么是国际化,struts2实现国际化的原理?
国际化是根据不一样的国家和地区的语言文化的不一样,所设计的适用于不一样地区的编码格式。
实现方法:
1)首先在src目录下新建message_en.properties(英文);
2)页面获取国际化信息或者使用
原理:程序获得当前运行环境的国际/区域,语言环境并存放于Locale,ResourceBundle根据Locale中信息自动搜索对应的国际化资源文件并加载。
Ajax又叫异步刷新,(JavaScript和xml)原理:使用HttpRequest向服务器发送异步请求,服务器返回处理结果
反射,程序运行时动态获取类型信息,完成对象建立,方法调用等。
例如:
Class myclass=Class.forNama("包名.类名");
Student stu=Factory.createInstance("stu1");
在struts.xml中配置type="redirect"(重定向);type="redirectAction"(转发)
dispatcher:result type默认的类型,至关于servlet的foward方式跳转页面。客户端看到的是struts2中配置的地址,而不是真正页面的地址,通常用于跳转到jsp页面,页面能拿到值
redirect:页面重定向,客户端跳转,数据所有丢失,地址栏发生变化,页面不能拿到值
chain:将请求转发给一个Action,Action能经过getAttribute(“uname”)拿到值
redirect-action:通常用于跳转到Action,Action不能经过getAttribute(“uname”)拿到值十、struts2默认能解决get和post提交方式的乱码问题吗?
答:不能。struts.i18n.encoding=UTF-8属性值只能解析POST提交下的乱码问题。
MVC模式:
1)web应用程序启动时就会加载并初始化ActionServler。
2)用户提交表单时,一个配置好的ActionForm对象被建立,并被填入表单相应的数据,ActionServler根据Struts-config.xml文件配置好的设置决定是否须要表单验证,若是须要就调用ActionForm的Validate()验证后选择将请求发送到哪一个Action,若是Action不存在,ActionServlet会先建立这个对象,而后调用Action的execute()方法.
3)Execute()从ActionForm对象中获取数据,完成业务逻辑,返回一个ActionForward对象,ActionServlet再把客户请求转发给ActionForward对象指定的jsp组件,ActionForward对象指定的jsp生成动态的网页,返回给客户。
1)拦截器是基于java反射机制的,而过滤器是基于函数回调的。
2)过滤器依赖于servlet容器,而拦截器不依赖于servlet容器。
3)拦截器只能对Action请求起做用,而过滤器则能够对几乎全部请求起做用。
4)拦截器能够访问Action上下文、值栈里的对象,而过滤器不能。
5)在Action的生命周期中,拦截器能够屡次调用,而过滤器只能在容器初始化时被调用一次。
fileUpload 提供文件上传功能
i18n 记录用户选择的locale
cookies 使用配置的name,value来是指cookies
checkbox 添加了checkbox自动处理代码,将没有选中的checkbox的内容设定为false,而html默认状况下不提交没有选中的 checkbox。
chain 让前一个Action的属性能够被后一个Action访问,如今和chain类型的result()结合使用。
alias 在不一样请求之间将请求参数在不一样名字件转换,请求内容不变
1)ActionContext是当前的Action的上下文环境,经过ActionContext能够获取到request、session、ServletContext等与Action有关的对象的引用;
2)ServletContext是域对象,一个web应用中只有一个ServletContext,生命周期伴随整个web应用;
3)pageContext是JSP中的最重要的一个内置对象,能够经过pageContext获取其余域对象的应用,同时它是一个域对象,做用范围只针对当前页面,当前页面结束时,pageContext销毁, 生命周期是JSP四个域对象中最小的。
1)每一个拦截器都是实现了Interceptor接口的 Java 类;
2)init(): 该方法将在拦截器被建立后当即被调用, 它在拦截器的生命周期内只被调用一次. 能够在该方法中对相关资源进行必要的初始化;
3)intercept(ActionInvocation invocation): 每拦截一个动做请求, 该方法就会被调用一次;
4)destroy: 该方法将在拦截器被销毁以前被调用, 它在拦截器的生命周期内也只被调用一次;
5)struts2中有内置了18个拦截器。
(1)Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只须要关注SQL语句自己,不须要花费精力去处理加载驱动、建立链接、建立statement等繁杂的过程。程序员直接编写原生态sql,能够严格控制sql执行性能,灵活度高。
(2)MyBatis 能够使用 XML 或注解来配置和映射原生信息,将 POJO映射成数据库中的记录,避免了几乎全部的 JDBC 代码和手动设置参数以及获取结果集。
(3)经过xml 文件或注解的方式将要执行的各类 statement 配置起来,并经过java对象和 statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。(从执行sql到返回result的过程)。
(1)基于SQL语句编程,至关灵活,不会对应用程序或者数据库的现有设计形成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
(2)与JDBC相比,减小了50%以上的代码量,消除了JDBC大量冗余的代码,不须要手动开关链接;
(3)很好的与各类数据库兼容(由于MyBatis使用JDBC来链接数据库,因此只要JDBC支持的数据库MyBatis都支持)。
(4)可以与Spring很好的集成;
(5)提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
(1)SQL语句的编写工做量较大,尤为当字段多、关联表多时,对开发人员编写SQL语句的功底有必定要求。
(2)SQL语句依赖于数据库,致使数据库移植性差,不能随意更换数据库。
(1)MyBatis专一于SQL自己,是一个足够灵活的DAO层解决方案。
(2)对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。
(1)Mybatis和hibernate不一样,它不彻底是一个ORM框架,由于MyBatis须要程序员本身编写Sql语句。
(2)Mybatis直接编写原生态sql,能够严格控制sql执行性能,灵活度高,很是适合对关系数据模型要求不高的软件开发,由于这类软件需求变化频繁,一但需求变化要求迅速输出成果。可是灵活的前提是mybatis没法作到数据库无关性,若是须要实现支持多种数据库的软件,则须要自定义多套sql映射文件,工做量大。
(3)Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,若是用hibernate开发能够节省不少代码,提升效率。
#{}是预编译处理,${}是字符串替换。
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理${}时,就是把${}替换成变量的值。
使用#{}能够有效的防止SQL注入,提升系统安全性。
第1种: 经过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。
第2种: 经过<resultMap>来映射字段名和实体类属性名的一一对应的关系。
第1种:在Java代码中添加sql通配符。
第2种:在sql语句中拼接通配符,会引发sql注入
Dao接口即Mapper接口。接口的全限名,就是映射文件中的namespace的值;接口的方法名,就是映射文件中Mapper的Statement的id值;接口方法内的参数,就是传递给sql的参数。
Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串做为key值,可惟必定位一个MapperStatement。在Mybatis中,每个<select>、<insert>、<update>、<delete>标签,都会被解析为一个MapperStatement对象。
举例:com.mybatis3.mappers.StudentDao.findStudentById,能够惟一找到namespace为com.mybatis3.mappers.StudentDao下面 id 为 findStudentById 的 MapperStatement。
Mapper接口里的方法,是不能重载的,由于是使用 全限名+方法名 的保存和寻找策略。Mapper 接口的工做原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象proxy,代理对象会拦截接口方法,转而执行MapperStatement所表明的sql,而后将sql执行结果返回。
Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页。能够在sql内直接书写带有物理分页的参数来完成物理分页功能,也能够使用分页插件来完成物理分页。分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,而后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
第一种是使用<resultMap>标签,逐必定义数据库列名和对象属性名之间的映射关系。
第二种是使用sql列的别名功能,将列的别名书写为对象属性名。
有了列名与属性名的映射关系后,Mybatis经过反射建立对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是没法完成赋值的。
首先,建立一个简单的insert语句:mapper.xml中
而后在java代码中批量赋值:业务层调用dao层执行插入
在insert语句中开启usegeneratedkeys=”true” keyproperty=”id”
insert 方法老是返回一个int值 ,这个值表明的是插入的行数。
若是采用自增加策略,自动生成的键值在 insert 方法执行完后能够被设置到传入的参数对象中。
(1)第一种:
//DAO层的函数
Public UserselectUser(String name,String area);
//对应的xml,#{0}表明接收的是dao层中的第一个参数,#{1}表明dao层中第二参数,更多参数一致日后加便可。
<select id="selectUser"resultMap="BaseResultMap">
select * fromuser_user_t whereuser_name = #{0} anduser_area=#{1}
</select>
(2)第二种: 使用 @param 注解:
public interface usermapper {
user selectuser(@param(“username”) string username,@param(“hashedpassword”) string hashedpassword);
}
而后,就能够在xml像下面这样使用(推荐封装为一个map,做为单个参数传递给mapper):
<select id=”selectuser” resulttype=”user”>
select id, username, hashedpassword
from some_table
where username = #{username}
and hashedpassword = #{hashedpassword}
</select>
(3)第三种:多个参数封装成map
try{
//映射文件的命名空间.SQL片断的ID,就能够调用对应的映射文件中的SQL
//因为咱们的参数超过了两个,而方法中只有一个Object参数收集,所以咱们使用Map集合来装载咱们的参数
Map<String, Object> map = new HashMap();
map.put("start", start);
map.put("end", end);
return sqlSession.selectList("StudentID.pagination", map);
}catch(Exception e){
e.printStackTrace();
sqlSession.rollback();
throw e; }
finally{
MybatisUtil.closeSqlSession();
}
Mybatis动态sql能够在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断并动态拼接sql的功能。
Mybatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。
<resultMap>、<parameterMap>、<sql>、<include>、<selectKey>,加上动态sql的9个标签,其中<sql>为sql片断标签,经过<include>标签引入sql片断,<selectKey>为不支持自增的主键生成策略标签。
不一样的Xml映射文件,若是配置了namespace,那么id能够重复;若是没有配置namespace,那么id不能重复;
缘由就是namespace+id是做为Map<String, MapperStatement>的key使用的,若是没有namespace,就剩下id,那么,id重复会致使数据互相覆盖。有了namespace,天然id就能够重复,namespace不一样,namespace+id天然也就不一样。
可是,在之前的Mybatis版本的namespace是可选的,不过新版本的namespace已是必须的了。
Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,能够根据对象关系模型直接获取,因此它是全自动的。而Mybatis在查询关联对象或关联集合对象时,须要手动编写sql来完成,因此,称之为半自动ORM映射工具。
association 一对一, 一对多 collection,多对多 discrimination
有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次, 经过在resultMap里面配置association节点配置一对一的类就能够完成;
嵌套查询是先查一个表,根据这个表里面的结果的 外键id,去再另一个表里面查询数据,也是经过association配置,但另一个表的查询经过select属性配置。
有联合查询和嵌套查询。联合查询是几个表联合查询,只查询一次,经过在resultMap里面的collection节点配置一对多的类就能够完成;嵌套查询是先查一个表,根据这个表里面的 结果的外键id,去再另一个表里面查询数据,也是经过配置collection,但另一个表的查询经过select节点配置。
Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,能够配置是否启用延迟加载lazyLoadingEnabled=true|false。
它的原理是,使用CGLIB建立目标对象的代理对象,当调用目标方法时,进入拦截器方法,好比调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,而后调用a.setB(b),因而a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
固然了,不光是Mybatis,几乎全部的包括Hibernate,支持延迟加载的原理都是同样的。
1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储做用域为 Session,当 Session flush 或 close 以后,该 Session 中的全部 Cache 就将清空,默认打开一级缓存。
2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不一样在于其存储做用域为 Mapper(Namespace),而且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类须要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置<cache/> ;
3)对于缓存数据更新机制,当某一个做用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操做后,默认该做用域下全部 select 中的缓存将被 clear 掉并从新更新,若是开启了二级缓存,则只根据配置判断是否刷新。
接口绑定,就是在MyBatis中任意定义接口,而后把接口里面的方法和SQL语句绑定, 咱们直接调用接口方法就能够,这样比起原来了SqlSession提供的方法咱们能够有更加灵活的选择和设置。
接口绑定有两种实现方式,一种是经过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;另一种就是经过xml里面写SQL来绑定, 在这种状况下,要指定xml映射文件里面的namespace必须为接口的全路径名。当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,通常用xml绑定的比较多。
① Mapper接口方法名和mapper.xml中定义的每一个sql的id相同;
② Mapper接口方法的输入参数类型和mapper.xml中定义的每一个sql 的parameterType的类型相同;
③ Mapper接口方法的输出参数类型和mapper.xml中定义的每一个sql的resultType的类型相同;
③ Mapper.xml文件中的namespace便是mapper接口的类路径。
第一种:接口实现类继承SqlSessionDaoSupport:使用此种方法须要编写mapper接口,mapper接口实现类、mapper.xml文件。
(1)在sqlMapConfig.xml中配置mapper.xml的位置
<mappers>
<mapper resource="mapper.xml文件的地址" />
<mapper resource="mapper.xml文件的地址" />
</mappers>
(2)定义mapper接口
(3)实现类集成SqlSessionDaoSupport
mapper方法中能够this.getSqlSession()进行数据增删改查。
(4)spring 配置
<bean id=" " class="mapper接口的实现">
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
第二种:使用org.mybatis.spring.mapper.MapperFactoryBean:
(1)在sqlMapConfig.xml中配置mapper.xml的位置,若是mapper.xml和mappre接口的名称相同且在同一个目录,这里能够不用配置
<mappers>
<mapper resource="mapper.xml文件的地址" />
<mapper resource="mapper.xml文件的地址" />
</mappers>
(2)定义mapper接口:
①mapper.xml中的namespace为mapper接口的地址
②mapper接口中的方法名和mapper.xml中的定义的statement的id保持一致
③Spring中定义
<bean id="" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="mapper接口地址" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
第三种:使用mapper扫描器:
(1)mapper.xml文件编写:
mapper.xml中的namespace为mapper接口的地址;
mapper接口中的方法名和mapper.xml中的定义的statement的id保持一致;
若是将mapper.xml和mapper接口的名称保持一致则不用在sqlMapConfig.xml中进行配置。
(2)定义mapper接口:
注意mapper.xml的文件名和mapper的接口名称保持一致,且放在同一个目录
(3)配置mapper扫描器:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="mapper接口包地址"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
(4)使用扫描器后从spring容器中获取mapper的实现对象。
Mybatis仅能够编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为须要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,固然,只会拦截那些你指定须要拦截的方法。
编写插件:实现Mybatis的Interceptor接口并复写intercept()方法,而后在给插件编写注解,指定要拦截哪个接口的哪些方法便可,记住,别忘了在配置文件中配置你编写的插件。
Hibernate的核心思想是ROM对象关系映射机制。它是将表与表之间的操做映射成对象与对象之间的操做。从数据库中提取的信息会自动按照你设置的映射要求封装成特色的对象,使得对对象的修改对应数据行的修改。
读取并解析配置文件
读取并解析映射信息,建立SessionFactory
打开Sesssion
建立事务Transation
持久化操做
提交事务
关闭Session
关闭SesstionFactory
使用Hibernate框架就不用咱们写不少繁琐的SQL语句。Hibernate实现了ORM,可以将对象映射成数据库表,从而简化咱们的开发!
经过设置属性lazy进行设置是否须要懒加载
当Hibernate在查询数据的时候,数据并无存在与内存中,当程序真正对数据的操做时,对象才存在与内存中,就实现了延迟加载,他节省了服务器的内存开销,从而提升了服务器的性能。
它们经过配置文件中的many-to-one、one-to-many、many-to-many来实现类之间的关联关系的。
Hibernate中对象的状态:
临时/瞬时状态
持久化状态
游离状态
new出来的对象是瞬时状态->保存到数据库中(受Session管理)就是持久化状态->将session close掉就是游离状态
1)当即检索:
优势: 对应用程序彻底透明,无论对象处于持久化状态,仍是游离状态,应用程序均可以方便的从一个对象导航到与它关联的对象;
缺点: 1.select语句太多;2.可能会加载应用程序不须要访问的对象白白浪费许多内存空间;
当即检索:lazy=false;
2)延迟检索:
优势: 由应用程序决定须要加载哪些对象,能够避免可执行多余的select语句,以及避免加载应用程序不须要访问的对象。所以能提升检索性能,而且能节省内存空间;
缺点: 应用程序若是但愿访问游离状态代理类实例,必须保证他在持久化状态时已经被初始化;
延迟加载:lazy=true;
3)迫切左外链接检索:
优势: 1对应用程序彻底透明,无论对象处于持久化状态,仍是游离状态,应用程序均可以方便地冲一个对象导航到与它关联的对象。2使用了外链接,select语句数目少;
缺点: 1 可能会加载应用程序不须要访问的对象,白白浪费许多内存空间;2复杂的数据库表链接也会影响检索性能;
预先抓取: fetch=“join”;
4种:
放入二级缓存的对象,只读(Read-only);
非严格的读写(Nonstrict read/write)
读写; 放入二级缓存的对象能够读、写(Read/write);
基于事务的策略(Transactional)
sorted collection是在内存中经过Java比较器进行排序的
ordered collection是在数据库中经过order by进行排序的
对于比较大的数据集,为了不在内存中对它们进行排序而出现 Java中的OutOfMemoryError,最好使用ordered collection。
一级缓存:
Hibenate中一级缓存,也叫作session的缓存,它能够在session范围内减小数据库的访问次数! 只在session范围有效! Session关闭,一级缓存失效!
只要是持久化对象状态的,都受Session管理,也就是说,都会在Session缓存中!
Session的缓存由hibernate维护,用户不能操做缓存内容; 若是想操做缓存内容,必须经过hibernate提供的evit/clear方法操做。
二级缓存:
二级缓存是基于应用程序的缓存,全部的Session均可以使用
Hibernate提供的二级缓存有默认的实现,且是一种可插配的缓存框架!若是用户想用二级缓存,只须要在hibernate.cfg.xml中配置便可; 不想用,直接移除,不影响代码。
若是用户以为hibernate提供的框架框架很差用,本身能够换其余的缓存框架或本身实现缓存框架均可以。
Hibernate二级缓存:存储的是经常使用的类
1、对象导航查询(objectcomposition)
2、HQL查询
一、 属性查询
二、 参数查询、命名参数查询
三、 关联查询
四、 分页查询
五、 统计函数
3、Criteria 查询
4、SQLQuery本地SQL查询
Ø 数据库设计调整
Ø HQL优化
Ø API的正确使用(如根据不一样的业务类型选用不一样的集合及查询API)
Ø 主配置参数(日志,查询缓存,fetch_size, batch_size等)
Ø 映射文件优化(ID生成策略,二级缓存,延迟加载,关联优化)
Ø 一级缓存的管理
Ø 针对二级缓存,还有许多特有的策略
详情可参考资料:
inverse属性默认是false,就是说关系的两端都来维护关系。
好比Student和Teacher是多对多关系,用一个中间表TeacherStudent维护。Gp)
若是Student这边inverse=”true”, 那么关系由另外一端Teacher维护,就是说当插入Student时,不会操做TeacherStudent表(中间表)。只有Teacher插入或删除时才会触发对中间表的操做。因此两边都inverse=”true”是不对的,会致使任何操做都不触发对中间表的影响;当两边都inverse=”false”或默认时,会致使在中间表中插入两次关系。
若是表之间的关联关系是“一对多”的话,那么inverse只能在“一”的一方来配置!
jdbc:手动
手动写sql
delete、insert、update要将对象的值一个一个取出传到sql中,不能直接传入一个对象。
select:返回的是一个resultset,要从ResultSet中一行一行、一个字段一个字段的取出,而后封装到一个对象中,不直接返回一个对象。
ibatis的特色:半自动化
sql要手动写
delete、insert、update:直接传入一个对象
select:直接返回一个对象
hibernate:全自动
不写sql,自动封装
delete、insert、update:直接传入一个对象
select:直接返回一个对象
建索引
减小表之间的关联
优化sql,尽可能让sql很快定位数据,不要让sql作全表查询,应该走索引,把数据量大的表排在前面
简化查询字段,没用的字段不要,已经对返回结果的控制,尽可能返回少许数据
SessionFactory 是Hibrenate单例数据存储和线程安全的,以致于能够多线程同时访问。一个SessionFactory 在启动的时候只能创建一次。SessionFactory应该包装各类单例以致于它能很简单的在一个应用代码中储存.
get()当即查询
load()懒加载
1)get若是没有找到会返回null, load若是没有找到会抛出异常。
2)get会先查一级缓存, 再查二级缓存,而后查数据库;load会先查一级缓存,若是没有找到,就建立代理对象, 等须要的时候去查询二级缓存和数据库。
若是session中存在相同持久化标识(identifier)的实例,用用户给出的对象的状态覆盖旧有的持久实例
若是session没有相应的持久实例,则尝试从数据库中加载,或建立新的持久化实例,最后返回该持久实例
用户给出的这个对象没有被关联到session上,它依旧是托管的
persist不保证当即执行,可能要等到flush;
persist不更新缓存;
save, 把一个瞬态的实例持久化标识符,及时的产生,它要返回标识符,因此它会当即执行Sql insert
使用 save() 方法保存持久化对象时,该方法返回该持久化对象的标识属性值(即对应记录的主键值);
使用 persist() 方法来保存持久化对象时,该方法没有任何返回值。
主键的自动生成策略
identity 自增加(mysql,db2)
sequence 自增加(序列), oracle中自增加是以序列方法实现**
native 自增加【会根据底层数据库自增加的方式选择identity或sequence】
若是是mysql数据库, 采用的自增加方式是identity
若是是oracle数据库, 使用sequence序列的方式实现自增加
increment 自增加(会有并发访问的问题,通常在服务器集群环境使用会存在问题。)
指定主键生成策略为手动指定主键的值
assigned
指定主键生成策略为UUID生成的值
uuid
foreign(外键的方式)
一、getCurrentSession会绑定当前线程,而openSession不会,由于咱们把hibernate交给咱们的spring来管理以后,咱们是有事务配置,这个有事务的线程就会绑定当前的工厂里面的每个session,而openSession是建立一个新session。
二、getCurrentSession事务是有spring来控制的,而openSession须要咱们手动开启和手动提交事务,
三、getCurrentSession是不须要咱们手动关闭的,由于工厂会本身管理,而openSession须要咱们手动关闭。
四、而getCurrentSession须要咱们手动设置绑定事务的机制,有三种设置方式,jdbc本地的Thread、JTA、第三种是spring提供的事务管理机制org.springframework.orm.hibernate4.SpringSessionContext,并且srping默认使用该种事务管理机制
命名查询指的是用<sql-query>标签在影射文档中定义的SQL查询,能够经过使用Session.getNamedQuery()方法对它进行调用。命名查询使你能够使用你所指定的一个名字拿到某个特定的查询。
Hibernate中的命名查询能够使用注解来定义,也能够使用我前面提到的xml影射问句来定义。在Hibernate中,@NameQuery用来定义单个的命名查询,@NameQueries用来定义多个命名查询。
每一个Hibernate实体类必须包含一个 无参数的构造器, 这是由于Hibernate框架要使用Reflection API,经过调用Class.newInstance()来建立这些实体类的实例。若是在实体类中找不到无参数的构造器,这个方法就会抛出一个InstantiationException异常。
你能够将Hibernate的实体类定义为final类,但这种作法并很差。由于Hibernate会使用代理模式在延迟关联的状况下提升性能,若是你把实体类定义成final类以后,由于 Java不容许对final类进行扩展,因此Hibernate就没法再使用代理了, 如此一来就限制了使用能够提高性能的手段。
Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只须要关心业务需求。常见的配置方式有三种:基于XML的配置、基于注解的配置、基于Java的配置。
主要由如下几个模块组成:
Spring Core:核心类库,提供IOC服务;
Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等);
Spring AOP:AOP服务;
Spring DAO:对JDBC的抽象,简化了数据访问异常的处理;
Spring ORM:对现有的ORM框架的支持;
Spring Web:提供了基本的面向Web的综合特性,例如多方文件上传;
Spring MVC:提供面向Web应用的Model-View-Controller实现。
(1)spring属于低侵入式设计,代码的污染极低;
(2)spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;
(3)Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用。
(4)spring对于主流的应用框架提供了集成支持。
OOP面向对象,容许开发者定义纵向的关系,但并适用于定义横向的关系,致使了大量代码的重复,而不利于各个模块的重用。
AOP,通常称为面向切面,做为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减小系统中的重复代码,下降了模块间的耦合度,同时提升了系统的可维护性。可用于权限认证、日志、事务处理。
AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的表明为AspectJ;动态代理则以Spring AOP为表明。
(1)AspectJ是静态代理的加强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,所以也称为编译时加强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是加强以后的AOP对象。
(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的所有方法,而且在特定的切点作了加强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
①JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 经过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一块儿;接着,Proxy利用 InvocationHandler动态建立一个符合某一接口的的实例, 生成目标类的代理对象。
②若是代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,能够在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加加强代码,从而实现AOP。CGLIB是经过继承的方式作的动态代理,所以若是某个类被标记为final,那么它是没法使用CGLIB作动态代理的。
(3)静态代理与动态代理区别在于生成AOP代理对象的时机不一样,相对来讲AspectJ的静态代理方式具备更好的性能,可是AspectJ须要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。
(1)IOC就是控制反转,是指建立对象的控制权的转移,之前建立对象的主动权和时机是由本身把控的,而如今这种权力转移到Spring容器中,并由容器根据配置文件去建立实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。DI依赖注入,和控制反转是同一个概念的不一样角度的描述,即 应用程序在运行时依赖IoC容器来动态注入对象须要的外部资源。
(2)最直观的表达就是,IOC让对象的建立不用去new了,能够由spring自动生产,使用java的反射机制,根据配置文件在运行时动态的去建立对象以及管理对象,并调用对象的方法的。
(3)Spring的IOC有三种注入方式 :构造器注入、setter方法注入、根据注解注入。
IoC让相互协做的组件保持松散的耦合,而AOP编程容许你把遍及于应用各层的功能分离出来造成可重用的功能组件。
BeanFactory和ApplicationContext是Spring的两大核心接口,均可以当作Spring的容器。其中ApplicationContext是BeanFactory的子接口。
(1)BeanFactory:是Spring里面最底层的接口,包含了各类Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。ApplicationContext接口做为BeanFactory的派生,除了提供BeanFactory所具备的功能外,还提供了更完整的框架功能:
①继承MessageSource,所以支持国际化。
②统一的资源文件访问方式。
③提供在监听器中注册bean的事件。
④同时加载多个配置文件。
⑤载入多个(有继承关系)上下文 ,使得每个上下文都专一于一个特定的层次,好比应用的web层。
(2)①BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,咱们就不能发现一些存在的Spring的配置问题。若是Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
②ApplicationContext,它是在容器启动时,一次性建立了全部的Bean。这样,在容器启动时,咱们就能够发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入全部的单实例Bean,经过预载入单实例bean ,确保当你须要的时候,你就不用等待,由于它们已经建立好了。
③相对于基本的BeanFactory,ApplicationContext 惟一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
(3)BeanFactory一般以编程的方式被建立,ApplicationContext还能以声明的方式建立,如使用ContextLoader。
(4)BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但二者之间的区别是:BeanFactory须要手动注册,而ApplicationContext则是自动注册。
首先说一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;
Spring上下文中的Bean生命周期也相似,以下:
(1)实例化Bean:
对于BeanFactory容器,当客户向容器请求一个还没有初始化的bean时,或初始化bean的时候须要注入另外一个还没有初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,经过获取BeanDefinition对象中的信息,实例化全部的bean。
(2)设置对象属性(依赖注入):
实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 经过BeanWrapper提供的设置属性的接口完成依赖注入。
(3)处理Aware接口:
接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:
①若是这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值;
②若是这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
③若是这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;
(4)BeanPostProcessor:
若是想对Bean进行一些自定义的处理,那么可让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。
(5)InitializingBean 与 init-method:
若是Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。
(6)若是这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;因为这个方法是在Bean初始化结束时调用的,因此能够被应用于内存或缓存技术;
以上几个步骤完成后,Bean就已经被正确建立了,以后就能够使用这个Bean了。
(7)DisposableBean:
当Bean再也不须要时,会通过清理阶段,若是Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;
(8)destroy-method:
最后,若是这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
Spring容器中的bean能够分为5个范围:
(1)singleton:默认,每一个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。
(2)prototype:为每个bean请求提供一个实例。
(3)request:为每个网络请求建立一个实例,在请求完成之后,bean会失效并被垃圾回收器回收。
(4)session:与request范围相似,确保每一个session中有一个bean的实例,在session过时后,bean会随之失效。
(5)global-session:全局做用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工做时,它包含不少portlet。若是你想要声明让全部的portlet共用全局的存储变量的话,那么这全局变量须要存储在global-session中。全局做用域与Servlet中的session做用域效果相同。
Spring框架并无对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题须要开发者自行去搞定。但实际上,大部分的Spring bean并无可变的状态(好比Serview类和DAO类),因此在某种程度上说Spring的单例bean是线程安全的。若是你的bean有多种状态的话(好比 View Model 对象),就须要自行保证线程安全。最浅显的解决办法就是将多态bean的做用域由“singleton”变动为“prototype”。
在通常状况下,只有无状态的Bean才能够在多线程环境下共享,在Spring中,绝大部分Bean均可以声明为singleton做用域,由于Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不一样的线程在访问前须要获取锁,没得到锁的线程则须要排队。而ThreadLocal采用了“空间换时间”的方式。
ThreadLocal会为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。由于每个线程都拥有本身的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,能够把不安全的变量封装进ThreadLocal。
(1)Set方法注入;
(2)构造器注入:①经过index设置参数的位置;②经过type设置参数类型;
(3)静态工厂注入;
(4)实例工厂;
在spring中,对象无需本身查找或建立与其关联的其余对象,由容器负责把须要相互协做的对象引用赋予各个对象,使用autowire来配置自动装载模式。
在Spring框架xml配置中共有5种自动装配:
(1)no:默认的方式是不进行自动装配的,经过手工设置ref属性来进行装配bean。
(2)byName:经过bean的名称进行自动装配,若是一个bean的 property 与另外一bean 的name 相同,就进行自动装配。
(3)byType:经过参数的数据类型进行自动装配。
(4)constructor:利用构造函数进行装配,而且构造函数的参数经过byType进行装配。
(5)autodetect:自动探测,若是有构造方法,经过 construct的方式自动装配,不然使用 byType的方式自动装配。
基于注解的方式:
使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解以前须要在Spring配置文件进行配置,<context:annotation-config />。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找须要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:
若是查询结果恰好为一个,就将该bean装配给@Autowired指定的数据;
若是查询的结果不止一个,那么@Autowired会根据名称来查找;
若是上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。
@Autowired可用于:构造函数、成员变量、Setter方法
注:@Autowired和@Resource之间的区别
(1) @Autowired默认是按照类型装配注入的,默认状况下它要求依赖对象必须存在(能够设置它required属性为false)。
(2) @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。
(1)工厂模式:BeanFactory就是简单工厂模式的体现,用来建立对象的实例;
(2)单例模式:Bean默认为单例模式。
(3)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
(4)模板方法:用来解决代码重复的问题。好比. RestTemplate, JmsTemplate, JpaTemplate。
(5)观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,全部依赖于它的对象都会获得通知被制动更新,如Spring中listener的实现--ApplicationListener。
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是没法提供事务功能的。真正的数据库层的事务提交和回滚是经过binlog或者redo log实现的。
(1)Spring事务的种类:
spring支持编程式事务管理和声明式事务管理两种方式:
①编程式事务管理使用TransactionTemplate。
②声明式事务管理创建在AOP之上的。其本质是经过AOP功能,对方法先后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始以前加入一个事务,在执行完目标方法以后根据执行状况提交或者回滚事务。
声明式事务最大的优势就是不须要在业务逻辑代码中掺琐事务管理的代码,只需在配置文件中作相关的事务规则声明或经过@Transactional注解的方式,即可以将事务规则应用到业务逻辑中。
声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式,使业务代码不受污染,只要加上注解就能够得到彻底的事务支持。惟一不足地方是,最细粒度只能做用到方法级别,没法作到像编程式事务那样能够做用到代码块级别。
(2)spring的事务传播行为:
spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。
① PROPAGATION_REQUIRED:若是当前没有事务,就建立一个新事务,若是当前存在事务,就加入该事务,该设置是最经常使用的设置。
② PROPAGATION_SUPPORTS:支持当前事务,若是当前存在事务,就加入该事务,若是当前不存在事务,就以非事务执行。‘
③ PROPAGATION_MANDATORY:支持当前事务,若是当前存在事务,就加入该事务,若是当前不存在事务,就抛出异常。
④ PROPAGATION_REQUIRES_NEW:建立新事务,不管当前存不存在事务,都建立新事务。
⑤ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操做,若是当前存在事务,就把当前事务挂起。
⑥ PROPAGATION_NEVER:以非事务方式执行,若是当前存在事务,则抛出异常。
⑦ PROPAGATION_NESTED:若是当前存在事务,则在嵌套事务内执行。若是当前没有事务,则按REQUIRED属性执行。
(3)Spring中的隔离级别:
① ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。
② ISOLATION_READ_UNCOMMITTED:读未提交,容许另一个事务能够看到这个事务未提交的数据。
③ ISOLATION_READ_COMMITTED:读已提交,保证一个事务修改的数据提交后才能被另外一事务读取,并且能看到该事务对已有记录的更新。
④ ISOLATION_REPEATABLE_READ:可重复读,保证一个事务修改的数据提交后才能被另外一事务读取,可是不能看到该事务对已有记录的更新。
⑤ ISOLATION_SERIALIZABLE:一个事务在执行的过程当中彻底看不到其余事务对数据库所作的更新。
1三、Spring框架中有哪些不一样类型的事件?
Spring 提供了如下5种标准的事件:
(1)上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
(2)上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/从新开始容器时触发该事件。
(3)上下文中止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法中止容器时触发该事件。
(4)上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的全部单例Bean都被销毁。
(5)请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。
若是一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布之后,bean会自动被通知。
1四、解释一下Spring AOP里面的几个名词:
(1)切面(Aspect):被抽取的公共模块,可能会横切多个对象。 在Spring AOP中,切面能够使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。
(2)链接点(Join point):指方法,在Spring AOP中,一个链接点 老是 表明一个方法的执行。
(3)通知(Advice):在切面的某个特定的链接点(Join point)上执行的动做。通知有各类类型,其中包括“around”、“before”和“after”等通知。许多AOP框架,包括Spring,都是以拦截器作通知模型, 并维护一个以链接点为中心的拦截器链。
(4)切入点(Pointcut):切入点是指 咱们要对哪些Join point进行拦截的定义。经过切入点表达式,指定拦截的方法,好比指定拦截add*、search*。
(5)引入(Introduction):(也被称为内部类型声明(inter-type declaration))。声明额外的方法或者某个类型的字段。Spring容许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你能够使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。
(6)目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫作 被通知(adviced) 对象。 既然Spring AOP是经过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。
(7)织入(Weaving):指把加强应用到目标对象来建立新的代理对象的过程。Spring是在运行时完成织入。
切入点(pointcut)和链接点(join point)匹配的概念是AOP的关键,这使得AOP不一样于其它仅仅提供拦截功能的旧技术。 切入点使得定位通知(advice)可独立于OO层次。 例如,一个提供声明式事务管理的around通知能够被应用到一组横跨多个对象中的方法上(例如服务层的全部业务操做)。
(1)前置通知(Before advice):在某链接点(join point)以前执行的通知,但这个通知不能阻止链接点前的执行(除非它抛出一个异常)。
(2)返回后通知(After returning advice):在某链接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
(3)抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。
(4)后通知(After (finally) advice):当某链接点退出的时候执行的通知(不管是正常返回仍是异常退出)。
(5)环绕通知(Around Advice):包围一个链接点(join point)的通知,如方法调用。这是最强大的一种通知类型。 环绕通知能够在方法调用先后完成自定义的行为。它也会选择是否继续执行链接点或直接返回它们本身的返回值或抛出异常来结束执行。 环绕通知是最经常使用的一种通知类型。大部分基于拦截的AOP框架,例如Nanning和JBoss4,都只提供环绕通知。
同一个aspect,不一样advice的执行顺序:
①没有异常状况下的执行顺序:
around before advice
before advice
target method 执行
around after advice
after advice
afterReturning
②有异常状况下的执行顺序:
around before advice
before advice
target method 执行
around after advice
after advice
afterThrowing:异常发生
java.lang.RuntimeException: 异常发生
Spring Boot 是 Spring 开源组织下的子项目,是 Spring 组件一站式解决方案,主要是简化了使用 Spring 的难度,简省了繁重的配置,提供了各类启动器,开发者能快速上手。集成了44种服务。
Spring Boot 优势很是多,如:
独立运行
简化配置
应用监控(本地调试生产环境)
上手容易
Spring Boot 的核心配置文件是 application 和 bootstrap 配置文件。
application 配置文件这个容易理解,主要用于 Spring Boot 项目的自动化配置。
bootstrap 配置文件有如下几个应用场景。
使用 Spring Cloud Config 配置中心时,这时须要在 bootstrap 配置文件中添加链接到配置中心的配置属性来加载外部配置中心的配置信息;
一些固定的不能被覆盖的属性;
一些加密/解密的场景;
具体请看这篇文章《Spring Boot 核心配置文件详解》。
.properties 和 .yml,它们的区别主要是书写格式不一样。
1).properties
app.user.name = javastack
2).yml
app:
user:
name: javastack
另外,.yml 格式不支持 @PropertySource 注解导入配置。
他们都要求层级关系。
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了如下 3 个注解:
@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:打开自动配置的功能,也能够关闭某个自动配置的选项,如关闭数据源自动配置功能@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
@ComponentScan:Spring组件扫描。
1)继承spring-boot-starter-parent项目
2)导入spring-boot-dependencies项目依赖
能够不须要,内置了 Tomcat/ Jetty 等容器。
1)打包用命令或者放到容器中运行
2)用 Maven/ Gradle 插件运行
3)直接执行 main 方法运行
注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自动配置的核心,首先它得是一个配置文件,其次根据类路径下是否有这个类去自动配置。
cn
+- javastack
+- MyApplication.java
|
+- customer
| +- Customer.java
| +- CustomerController.java
| +- CustomerService.java
| +- CustomerRepository.java
|
+- order
+- Order.java
+- OrderController.java
+- OrderService.java
+- OrderRepository.java
Application类要处于bean的最上一层,不然扫描不到层级高于它的bean。这个目录结构是主流及推荐的作法,而在主入口类上加上 @SpringBootApplication 注解来开启 Spring Boot 的各项能力,如自动配置、组件扫描等。具体看这篇文章《Spring Boot 主类及目录结构介绍》。
Starters能够理解为启动器,它包含了一系列能够集成到应用里面的依赖包,你能够一站式集成 Spring 及其余技术,而不须要处处找示例代码和依赖包。如你想使用 Spring JPA 访问数据库,只要加入 spring-boot-starter-data-jpa 启动器依赖就能使用了。
Starters包含了许多项目中须要用到的依赖,它们能快速持续的运行,都是一系列获得支持的管理传递性依赖。具体请看这篇文章《Spring Boot Starters启动器》。
能够实现接口 ApplicationRunner 或者 CommandLineRunner,这两个接口实现方式同样,它们都只提供了一个 run 方法,具体请看这篇文章《Spring Boot Runner启动器》。
Spring Boot 能够经过 @PropertySource,@Value,@Environment, @ConfigurationProperties 来绑定变量,具体请看这篇文章《Spring Boot读取配置的几种方式》。
Spring Boot 支持 Java Util Logging, Log4j2, Lockback 做为日志框架,若是你使用 Starters 启动器,Spring Boot 将使用 Logback 做为默认日志框架,具体请看这篇文章《Spring Boot日志集成》。
主要有两种方式:
Spring Loaded
Spring-boot-devtools
在 Spring Boot 里面,能够使用如下几种方式来加载配置。
1)properties文件;
2)YAML文件;
3)系统环境变量;
4)命令行参数;
提供多套配置文件,如:
applcation.properties
application-dev.properties
application-test.properties
application-prod.properties
运行时指定具体的配置文件,具体请看这篇文章《Spring Boot Profile 不一样环境配置》。
能够兼容,使用 @ImportResource 注解导入老 Spring 项目配置文件。
在生产中使用HTTPS
使用Snyk检查你的依赖关系
升级到最新版本
启用CSRF保护
使用内容安全策略防止XSS攻击
配置变动
JDK 版本升级
第三方类库升级
响应式 Spring 编程支持
HTTP/2 支持
配置属性绑定
更多改进与增强…
事务是一种机制,一个操做序列,它包含了一组数据库操做命令,并当作一个总体提交。要么都执行,要么都不执行。
*原子性Atomicity:不可分割的操做单元,事务中全部操做,要么所有成功;要么撤回到执行事务以前的状态
*一致性Consistency:若是在执行事务以前数据库是一致的,那么在执行事务以后数据库也仍是一致的;
*隔离性Isolation:事务操做之间彼此独立和透明互不影响。事务独立运行。这一般使用锁来实现。一个事务处理后的结果,影响了其余事务,那么其余事务会撤回。事务的100%隔离,须要牺牲速度。
*持久性Durability:事务一旦提交,其结果就是永久的。即使发生系统故障,也能恢复。
①未提交读(Read Uncommitted):容许脏读,其余事务只要修改了数据,即便未提交,本事务也能看到修改后的数据值。也就是可能读取到其余会话中未提交事务修改的数据
②提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)。
③可重复读(Repeated Read):可重复读。不管其余事务是否修改并提交了数据,在这个事务中看到的数据值始终不受其余事务影响。
④串行读(Serializable):彻底串行化的读,每次读都须要得到表级共享锁,读写相互都会阻塞
MySQL数据库(InnoDB引擎)默认使用可重复读( Repeatable read)
数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。采起的是空间换时间的概念。
MyISAM引擎和InnoDB引擎使用B+Tree做为索引结构
index ---- 普通索引,数据能够重复,没有任何限制。
unique ---- 惟一索引,要求索引列的值必须惟一,但容许有空值;若是是组合索引,那么列值的组合必须惟一。
primary key ---- 主键索引,是一种特殊的惟一索引,一个表只能有一个主键,不容许有空值,通常是在建立表的同时建立主键索引。
组合索引 ---- 在多个字段上建立的索引,只有在查询条件中使用了建立索引时的第一个字段,索引才会被使用。
fulltext ---- 全文索引,是对于大表的文本域:char,varchar,text列才能建立全文索引,主要用于查找文本中的关键字,并非直接与索引中的值进行比较。fulltext更像是一个搜索引擎,配合match against操做使用,而不是通常的where语句加like。
注:全文索引目前只有MyISAM存储引擎支持全文索引,InnoDB引擎5.6如下版本还不支持全文索引
全部存储引擎对每一个表至少支持16个索引,总索引长度至少为256字节,索引有两种存储类型,包括B型树索引和哈希索引。
索引能够提升查询的速度,可是建立和维护索引须要耗费时间,同时也会影响插入的速度,若是须要插入大量的数据时,最好是先删除索引,插入数据后再创建索引。
假设index(a,b,c)
1)最左前缀匹配:模糊查询时,使用%匹配时:’a%‘会使用索引,’%a‘不会使用索引
2)条件中有or,索引不会生效
3) a and c,a生效,c不生效
b and c,都不生效
检测索引的效果:
show status like '%handler_read%'越大越好
DDL:数据定义语言(create drop)
DML:数据操做语句(insert update delete)
DQL:数据查询语句(select )
DCL:数据控制语句,进行受权和权限回收(grant revoke)
TPL:数据事务语句(commit collback savapoint)
第一范式:确保每列的原子性,确保每列都是最小的不可再分割的数据单元。
第二范式:确保表中的每列都和主键相关。
第三范式:确保每列都和主键直接相关,而不是间接相关。除了主键列,其余的列和列之间不存在依赖关系。
*脏读: 是指事务T1将某一值修改,而后事务T2读取该值,此后T1由于某种缘由撤销对该值的修改,这就致使了T2所读取到的数据是无效的。
*不可重复读 :是指在数据库访问时,一个事务范围内的两次相同查询却返回了不一样数据。在一个事务内屡次读同一数据。在这个事务尚未结束时,另一个事务也访问该同一数据。那么在第一个事务中的两次读数据之间,因为第二个事务的修改,第一个事务两次读到的的数据多是不同的。这样在一个事务内两次读到的数据是不同的,所以称为是不可重复读。
*幻读: 是指当事务不是独立执行时发生的一种现象,好比第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的所有数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么就会发生,操做第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉同样。
不可重复读&幻读区别:
若是使用锁机制来实现这两种隔离级别,在可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务没法修改这些数据,就能够实现可重复读了。但这种方法却没法锁住insert的数据,因此当事务A先前读取了数据,或者修改了所有数据,事务B仍是能够insert数据提交,这时事务A就会发现莫名其妙多了一条以前没有的数据,这就是幻读,不能经过行锁来避免。须要Serializable隔离级别 ,读用读锁,写用写锁,读锁和写锁互斥,这么作能够有效的避免幻读、不可重复读、脏读等问题,但会极大的下降数据库的并发能力。
不可重复读重点在于update和delete,而幻读的重点在于insert。如何经过锁机制来解决他们产生的问题
1)MyISAM 不支持事务,不支持外键,优点是访问速度快,对事务完整性没有要求,或者以select、insert为主的能够使用
2)InnoDB 支持事务,外键约束,自增,写的效率差一些,更占据空间
3)Memory 使用内存中的内容来建立表,访问速度很是快,使用哈希索引。可是一旦服务关闭,表中的数据就会丢失。
4)Merge 是一组MyISAM表的组合,这些表必须结构彻底相同,merge自己没有数据。对merge的查询、更新、删除实际是对MyISAM的修改。
1)InnoDB支持事务,MyISAM不支持。
2)MyISAM适合查询以及插入为主的应用,InnoDB适合频繁修改以及涉及到安全性较高的应用。
3)InnoDB支持外键,MyISAM不支持。
4)从MySQL5.5.5之后,InnoDB是默认引擎。
5)MyISAM支持全文类型索引,而InnoDB不支持全文索引。
6)InnoDB中不保存表的总行数,select count(*) from table时,InnoDB须要扫描整个表计算有多少行,但MyISAM只需简单读出保存好的总行数便可。注:当count(*)语句包含where条件时MyISAM也需扫描整个表。
7)对于自增加的字段,InnoDB中必须包含只有该字段的索引,可是在MyISAM表中能够和其余字段一块儿创建联合索引。
8)清空整个表时,InnoDB是一行一行的删除,效率很是慢。MyISAM则会重建表。MyisAM使用delete语句删除后并不会马上清理磁盘空间,须要定时清理,命令:OPTIMIZE table dept;
9)InnoDB支持行锁(某些状况下仍是锁整表,如 update table set a=1 where user like ‘%lee%’)
10)Myisam建立表生成三个文件:.frm 数据表结构 、 .myd 数据文件 、 .myi 索引文件,Innodb只生成一个 .frm文件,数据存放在ibdata1.log
如今通常都选用InnoDB,主要是MyISAM的全表锁,读写串行问题,并发效率锁表,效率低,MyISAM对于读写密集型应用通常是不会去选用的。
应用场景:
MyISAM不支持事务处理等高级功能,但它提供高速存储和检索,以及全文搜索能力。若是应用中须要执行大量的SELECT查询,那么MyISAM是更好的选择。
InnoDB用于须要事务处理的应用程序,包括ACID事务支持。若是应用中须要执行大量的INSERT或UPDATE操做,则应该使用InnoDB,这样能够提升多用户并发操做的性能。
CHAR和VARCHAR类型在存储和检索方面有所不一样
CHAR列长度固定为建立表时声明的长度,长度值范围是1到255
当CHAR值被存储时,它们被用空格填充到特定长度,检索CHAR值时需删除尾随空格。
1. 一些全表扫描的慎用,or、like、<>、distinct(排除)、not in、not exists,not null的慎用。若有必然请用正向逻辑。
2. 一些重要操做请开启事物
3. 数据库不擅长运算,请把你的运算放在逻辑代码中,或者放在=号右边。
4. Limit分页的提醒,limit 10000,10所查询到的结果是1到10010条,而不是你所想的10条。大量数据请记得优化。
5. Sql执行顺序:
(1)FROM [left_table]
(2)ON <join_condition>
(3)<join_type> JOIN <right_table>
(4)WHERE <where_condition>
(5)GROUP BY <group_by_list>
(6)WITH <CUBE | RollUP>
(7)HAVING <having_condition>
(8)SELECT
(9)DISTINCT
(10)ORDER BY <order_by_list>
(11)<Top Num> <select list>
1)避免所有扫描,好比对null值进行筛选判读;使用!=或<>、like、or等等都将放弃索引全表扫描
2)考虑在where及order by涉及的列上创建索引
3)使用正向逻辑(not in,not exists)
4)数据库不擅长运算,把运算交给逻辑代码,非要有把运算放在右边
5)合理建表,使用合理的字段,善用非空、外键约束保证数据的完整性
6)索引并非越多越好,一个表最好不要超过6个,多了影响增、删、改的性能。这个影响很大
7)多从业务逻辑方面考虑问题,合理使用中间件
8)对于数据量太大的数据分库分表,使用中间件好比mycat
①:垂直分割(并不经常使用)
就是将一个表按照字段来分,每张表保证有相同的主键就好。通常来讲,将经常使用字段和大字段分表来放。
优点:比没有分表来讲,提升了查询速度,下降了查询结果所用内存;
劣势:没有解决大量记录的问题,对于单表来讲随着记录增多,性能仍是降低很快;
②: 水平分割(重要,实际应用中使用最多)
水平分割是企业最经常使用到的,水平拆分就是大表按照记录分为不少子表:
水平分的规则彻底是自定义的,有如下几种参考设计:
1 hash、自增id取模:
对某个字段进行hash来肯定建立几张表,并根据hash结果存入不一样的表;
2 按时间
根据业务能够按照天、月、年来进行拆分;
3 按每一个表的固定记录数
通常按照自增ID进行拆表,一张表的数据行到了指定的数量,就自动保存到下一张表中。好比规定一张表只能存1-1000个记录;
4 将老数据迁移到一张历史表
好比日志表,通常只查询3个月以内的数据,对于超过3个月的记录将之迁移到历史子表中;
什么是物理冷备?科普一下:
(1)热备:在数据库运行时,直接进行备份,对运行的数据库没有影响。
(2)冷备:在数据库中止运行的时候进行备份,这种备份方式最为简单,只须要拷贝数据库物理文件便可。
(3)温备:一样是在数据库运行的时候进行备份的,但对当前数据库的操做会产生影响。
热备份的缺点:
1.尽可能不要出错,不然后果会很严重。
2.若是热备份不成功,所得结果不可用于时间点的数据恢复。
3.维护的工做比较困难。
冷备份的缺点:
1.单独使用时,只能提供到"某一时间点的上"的恢复。
2.再实施备份的全过程当中,数据库必须是关闭状态。
3.不能按表或按用户恢复。
综上,若是你不是大牛大能,物理备份仍是选择冷备份吧,自动保存7天数据。
MyISAM支持表锁,InnoDB支持表锁和行锁,默认为行锁
表级锁:开销小,加锁快,不会出现死锁。锁定粒度大,发生锁冲突的几率最高,并发量最低
行级锁:开销大,加锁慢,会出现死锁。锁力度小,发生锁冲突的几率小,并发度最高
咱们经常使用的操做数据库语言SQL语句在执行的时候须要要先编译,而后执行,而存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户经过指定存储过程的名字并给定参数(若是该存储过程带有参数)来调用执行它。
一个存储过程是一个可编程的函数,它在数据库中建立并保存。它能够有SQL语句和一些特殊的控制结构组成。当但愿在不一样的应用程序或平台上执行相同的函数,或者封装特定功能时,存储过程是很是有用的。数据库中的存储过程能够看作是对编程中面向对象方法的模拟。它容许控制数据的访问方式。
优势:
(1).存储过程加强了SQL语言的功能和灵活性。存储过程能够用流控制语句编写,有很强的灵活性,能够完成复杂的判断和较复杂的运算。
(2).存储过程容许标准组件是编程。存储过程被建立后,能够在程序中被屡次调用,而没必要从新编写该存储过程的SQL语句。并且数据库专业人员能够随时对存储过程进行修改,对应用程序源代码毫无影响。
(3).存储过程能实现较快的执行速度。若是某一操做包含大量的Transaction-SQL代码或分别被屡次执行,那么存储过程要比批处理的执行速度快不少。由于存储过程是预编译的。在首次运行一个存储过程时查询,优化器对其进行分析优化,而且给出最终被存储在系统表中的执行计划。而批处理的Transaction-SQL语句在每次运行时都要进行编译和优化,速度相对要慢一些。
(4).存储过程能过减小网络流量。针对同一个数据库对象的操做(如查询、修改),若是这一操做所涉及的Transaction-SQL语句被组织程存储过程,那么当在客户计算机上调用该存储过程时,网络中传送的只是该调用语句,从而大大增长了网络流量并下降了网络负载。
(5).存储过程可被做为一种安全机制来充分利用。系统管理员经过执行某一存储过程的权限进行限制,可以实现对相应的数据的访问权限的限制,避免了非受权用户对数据的访问,保证了数据的安全。
truncate 和 delete只删除数据,不删除表结构 ,drop删除表结构,而且释放所占的空间。
删除数据的速度,drop> truncate > delete
delete属于DML语言,须要事务管理,commit以后才能生效。drop和truncate属于DDL语言,操做马上生效,不可回滚。
使用场合:
当你再也不须要该表时, 用 drop;
当你仍要保留该表,但要删除全部记录时, 用 truncate;
当你要删除部分记录时(always with a where clause), 用 delete
CAP定理(CAP theorem),又被称做布鲁尔定理(Brewer's theorem),它指出对于一个分布式计算系统来讲,不可能同时知足如下三点:
一致性(Consistency) 全部节点访问同一份最新的数据副本;
可用性(Availability) 每次请求都能获取到非错的响应,可是不保证获取的数据为最新数据;
分区容错性(Partition tolerance) 分布式系统在遇到任何网络分区故障的时候,仍然可以对外提供知足一致性和可用性的服务,除非整个网络环境都发生了故障;
CAP之间是不能共存的,咱们最多只能知足两个条件:
CA (Consistency + Availability):关注一致性和可用性,表明做:关系型数据库MySQL。
CP (consistency + partition tolerance):关注一致性和分区容错性,表明做:分布式数据库hadoop。
AP (availability + partition tolerance):关心可用性和分区容错性,表明做:非关系型数据库Redis。
BASE 理论是对 CAP 理论的延伸,核心思想是即便没法作到强一致性(Strong Consistency,CAP 的一致性就是强一致性),但应用能够采用适合的方式达到最终一致性(Eventual Consitency)。
基本可用(Basically Available): 基本可用是指分布式系统在出现故障的时候,容许损失部分可用性,即保证核心可用。电商大促时,为了应对访问量激增,部分用户可能会被引导到降级页面,服务层也可能只提供降级服务。这就是损失部分可用性的体现。
软状态(Soft State): 软状态是指容许系统存在中间状态,而该中间状态不会影响系统总体可用性。分布式存储中通常一份数据至少会有三个副本,容许不一样节点间副本同步的延时就是软状态的体现。MySQL Replication 的异步复制也是一种体现。
最终一致性(Eventual Consistency): 最终一致性是指系统中的全部数据副本通过必定时间后,最终可以达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊状况。
Redis 是一个开源的使用 ANSI C 语言编写、遵照 BSD 协议、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API的非关系型数据库。
1)St ing字符串:格式: set key value
string类型是二进制安全的。意思是redis的string能够包含任何数据。好比jpg图片或者序列化的对象 。string类型是Redis最基本的数据类型,一个键最大能存储512MB。
2)Hash(哈希)格式: hmset name key1 value1 key2 value2
Redis hash 是一个键值(key=>value)对集合。Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
3) lsit(列表)Redis 列表是简单的字符串列表,按照插入顺序排序。你能够添加一个元素到列表的头部(左边)或者尾部(右边)
4)set(集合)
5)zset(有序集合)
Redis zset 和 set 同样也是string类型元素的集合,且不容许重复的成员。
不一样的是每一个元素都会关联一个double类型的分数。redis正是经过分数来为集合中的成员进行从小到大的排序。
zset的成员是惟一的,但分数(score)却能够重复。
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
Redis 提供了两种持久化方式:RDB(默认) 和AOF
RDB:rdb是Redis DataBase缩写
功能核心函数rdbSave(生成RDB文件)和rdbLoad(从文件加载内存)两个函数
AOF:Aof是Append-only file缩写
每当执行服务器(定时)任务或者函数时flushAppendOnlyFile 函数都会被调用, 这个函数执行如下两个工做
aof写入保存:
WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件
SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。
比较:
一、aof文件比rdb更新频率高,优先使用aof还原数据。
二、aof比rdb更安全也更大
三、rdb性能比aof好
四、若是两个都配了优先加载AOF
RESP 是redis客户端和服务端以前使用的一种通信协议;
RESP 的特色:实现简单、快速解析、可读性好
1)单机版
特色:简单
问题:一、内存容量有限 二、处理能力有限 三、没法高可用。
2)哨兵
Redis sentinel 是一个分布式系统中监控 redis 主从服务器,并在主服务器下线时自动进行故障转移。其中三个特性:
监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运做正常。
提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 能够经过 API 向管理员或者其余应用程序发送通知。
自动故障迁移(Automatic failover): 当一个主服务器不能正常工做时, Sentinel 会开始一次自动故障迁移操做。
特色:一、保证高可用二、监控各个节点三、自动故障迁移
缺点:主从模式,切换须要时间丢数据,没有解决 master 写的压力
3)集群(proxy 型):
Twemproxy 是一个 Twitter 开源的一个 redis 和 memcache 快速/轻量级代理服务器; Twemproxy 是一个快速的单线程代理程序,支持 Memcached ASCII 协议和 redis 协议。
特色:一、多种 hash 算法:MD五、CRC1六、CRC3二、CRC32a、hsieh、murmur、Jenkins
二、支持失败节点自动删除
三、后端 Sharding 分片逻辑对业务透明,业务方的读写方式和操做单个 Redis 一致
缺点:增长了新的 proxy,须要维护其高可用。failover 逻辑须要本身实现,其自己不能支持故障的自动转移可扩展性差,进行扩缩容都须要手动干预
4)集群(直连型):从redis 3.0以后版本支持redis-cluster集群,Redis-Cluster采用无中心结构,每一个节点保存数据和整个集群状态,每一个节点都和其余全部节点链接。
特色:一、无中心架构(不存在哪一个节点影响性能瓶颈),少了 proxy 层。
二、数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布。
三、可扩展性,可线性扩展到 1000 个节点,节点可动态添加或删除。
四、高可用性,部分节点不可用时,集群仍可用。经过增长 Slave 作备份数据副本
五、实现故障自动 failover,节点之间经过 gossip 协议交换状态信息,用投票机制完成 Slave到 Master 的角色提高。
缺点:
一、资源隔离性较差,容易出现相互影响的状况。
二、数据经过异步复制,不保证数据的强一致性
先拿setnx来争抢锁,抢到以后,再用expire给锁加一个过时时间防止锁忘记了释放。
set指令有很是复杂的参数,这个应该是能够同时把setnx和expire合成一条指令来用的!
通常使用list结构做为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
缺点:在消费者下线的状况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。
使用pub/sub主题订阅者模式,能够实现1:N的消息队列。
通常的缓存系统,都是按照key去缓存查询,若是不存在对应的value,就应该去后端系统查找(好比DB)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统形成很大的压力。这就叫作缓存穿透。
如何避免:
1:对查询结果为空的状况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了以后清理缓存。
2:对必定不存在的key进行过滤。能够把全部的可能存在的key放到一个大的Bitmap中,查询时经过该bitmap过滤。
缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。致使系统崩溃。
如何避免:
1:在缓存失效后,经过加锁或者队列来控制读数据库写缓存的线程数量。好比对某个key只容许一个线程查询数据和写缓存,其余线程等待。
2:作二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,能够访问A2,A1缓存失效时间设置为短时间,A2设置为长期
3:不一样的key,设置不一样的过时时间,让缓存失效的时间点尽可能均匀。
JAVA_HOME=JDK 的根目录
CATALINA_HOME=tomcat 的根目录
CATALINA-HOME\conf\server.xml:能够配置tomcat 的端口,能够配置tomcat 中下链接
池。
CATALINA-HOME\common\lib:存放公用的类包
jsp:include:在运行时调用另外一个页面,变量是能够重复的。
<%@include file=””%>:在转译时合在一块儿,会成为同一个类,变量不能够重复。
重定向是客户端行为,转发是服务器端行为
重定向时服务器产生两次请求,转发产生一次请求,重定向时能够转发到项目之外的任何网址,转发只能在当前项目里转发
重定向会致使request对象信息丢失。转发则不会
转发的url不会变,request.getRequestDispatch()。forward()
重定向的url会改变,response.getRedirect();
Servlet
GenericServlet
HttpServlet
自定义
extends HttpServlet 并覆盖doPost 或doGet 方法
在web.xml 中进行部署
Init
屡次执行doGet 或doPost
destroy
对
脚本
<%%> <%=%> <%!%> <%----%>
指令
<%@page contentType=”text/html;charset=utf-8” language=”java”
import=””%>
<%@include file=””%>
<%@taglib uri=”” prefix=””%>
动做:
<jsp:useBean class=”” id=”” scope=””> 在scope 中若是没有
实例化一个对象,若是有直接用之前的。
<jsp:getProperty name=”” property=””> 向一个bean 中设置属性
值
<jsp:forward > jsp 页的转发
<jsp:include page=””> 导入一个jsp 页面
JSP一共有9个内置对象:request、response、session、application、out、pagecontext、config、page、exception。
一、Request request 表示HttpServletRequest 对象。取客户端表单域信息及cookie,header, 和session
二、response 表示HttpServletResponse 对象,对客户端的响应返回文本、写cookies。
三、out 向客户端打印html 文本.
四、pageContext :当前jsp 页面的上下文环境,能够获得session、request、application等内置对象,在自定义标签中使用的不少。
五、session 表示一个请求的javax.servlet.http.HttpSession 对象。Session 一个用户多个页面共享同一变量。
六、applicaton 表示一个javax.servle.ServletContext 对象。存放容器级
的变量。
七、config 表示一个javax.servlet.ServletConfig 对象。该对象用于存取servlet实例的初始化参数。
八、page 表示从该页面产生的一个servlet 实例
九、exception:异常,当iserrorpage=true
1).隐藏表单域:<input type="hidden">,很是适合步须要大量数据存储的会话应用。
2).URL 重写:URL 能够在后面附加参数,和服务器的请求一块儿发送,这些参数为名字/值对。
3).Cookie:一个 Cookie 是一个小的,已命名数据元素。服务器使用 SET-Cookie 头标将它做为 HTTP响应的一部分传送到客户端,客户端被请求保存 Cookie 值,在对同一服务器的后续请求使用一个Cookie 头标将之返回到服务器。与其它技术比较,Cookie 的一个优势是在浏览器会话结束后,甚至在客户端计算机重启后它仍能够保留其值
4).Session:使用 setAttribute(String str,Object obj)方法将对象捆绑到一个会话
页面须要保存如下参数:(数据库的分页及比较)
总行数:根据sql 语句获得总行数
每页显示行数:设定值
当前页数:请求参数
页面根据当前页数和每页行数计算出当前页第一行行数,定位结果集到此行,对结果集
取出每页显示行数的行便可。
<@include file>:在将jsp 生成servlet 类前将两个文件和在一块儿,生成一个java 类,
一块儿运行的。因此是一家子,当中的变量名不能重名。
<jsp:include page>:是两个类,是一个调用关系,在运行时动态的调用,不是一家子,
能够重复变量。
转发: 保留上次的request
<jsp:forward>
actionMapping.findForWard(“”);
pageContext.forward();
request.getRequestDispacher(“a.jsp”).forward(request,response)
跳转:不保留上次的request
Response.setRedirect(“”)
Jsp 主要在于页面的显示动态生成页面,能够与html 标记一块儿使用,其仍是要生成为一个servlet。
Servlet:主要是控制的处理,如调用业务层,跳转不一样的jsp 页面。
Mvc
Jsp:v
Servlet:c
Request.getparameter(“”)
<%=application.getRealPath("aa.jsp") %>
Cookie:主要用在保存客户端,其值在客户端与服务端之间传送,不安全,存储的数据量有限。
Session:保存在服务端,每个session 在服务端有一个sessionID 做一个标识。存储的数据量大,安全性高。占用服务端的内存资源。
正常页面中
%@page erropage=”error.jsp”%
错误页面
<%@page iserrorpage=”true”%>
有一内置对象:exception
<jsp:useBean class=”” id=”” scope=””/>
<%
New 类();
%>
作一个标记处理类extends TagSupport
经过tld 说明标记处理的类的前缀及后缀
在web.xml 中说明tld 文件
<taglib>
<taglib-uri>
<taglib-location>
<taglib>
在jsp 页面是引用tld<%@taglib uri=”” prefix=””%>
ServletContext:容器,放置全局变量
setAtribute()
getAttribute()
ServletConfig:一个servlet 的配置
getInitParameter(”名称”)
HttpServletRequest:封装的全部的请求
getParameterValue(”名称”)
getParameterValues(”称”)
getSession();
getAttribute(” 名称”);
getRequestDispatch(”a.jsp”).forward(request,response)
HttpServletResponse:响应
getOut();
sendRedirect(””)
HttpSession:一个用户多个页面共享同一变量
setAttribute(””,””)
javax.servlet.*;javax.servlet.http.*;
Servlet 被服务器实例化后,容器运行其init 方法,请求到达时运行其service 方法,service 方法自动派遣运行与请求对应的doXXX 方法(doGet,doPost)等,当服务器决定将实例销毁的时候调用其destroy 方法。
与cgi 的区别在于servlet 处理服务器进程中,它经过多线程方式运行其service 方法,一个实例能够服务于多个请求,而且其实例通常不会销毁,而CGI 对每一个请求都产生新的进程,服务完成后就销毁,因此效率上低于servlet。
Jsp 页面中的form 标签里的method 属性为get 时调用doGet(),为post 时调用
doPost()。
在doGet 及doPost 方法前加入synchoronized
JSP:
<%@ page isThreadSafe="true"%>
setAttribute(String name,Object):设置名字为name 的request 的参数值
getAttribute(String name):返回由name 指定的属性值
getAttributeNames():返回request 对象全部属性的名字集合,结果是一个枚举的实例
getCookies():返回客户端的全部Cookie 对象,结果是一个Cookie 数组
getCharacterEncoding():返回请求中的字符编码方式
getContentLength():返回请求的Body 的长度
实例
getInputStream():返回请求的输入流,用于得到请求中的数据
getMethod():得到客户端向服务器端传送数据的方法
getParameter(String name):得到客户端传送给服务器端的有name 指定的参数
值
getParameterNames():得到客户端传送给服务器端的全部参数的名字,结果是一个枚举的实例
getParameterValues(String name):得到有name 指定的参数的全部值
getProtocol():获取客户端向服务器端传送数据所依据的协议名称
getQueryString():得到查询字符串
getRequestURI():获取发出请求字符串的客户端地址
getRemoteAddr():获取客户端的IP 地址
getRemoteHost():获取客户端的名字
getSession([Boolean create]):返回和请求相关Session
getServerName():获取服务器的名字
getServletPath():获取客户端所请求的脚本文件的路径
getServerPort():获取服务器的端口号
removeAttribute(String name):删除请求中的一个属性
Public String translate (String str) {
String tempStr = "";
try {
tempStr = new String(str.getBytes("ISO-8859-1"), "GBK");
tempStr = tempStr.trim();
}catch (Exception e) {
System.err.println(e.getMessage());
}
return tempStr;
}
public void init(ServletConfig config)
public ServletConfig getServletConfig()
public String getServletInfo()
public void service(ServletRequest request,ServletResponse response)
public void destroy()
Servlet的执行流程也就是servlet的生命周期,当服务器启动的时候生命周期开始,而后经过init()《启动顺序根据web.xml里的startup-on-load来肯定加载顺序》方法初始化servlet,再根据不一样请求调用doGet或doPost方法,最后再经过destroy()方法进行销毁。
doGet和doPost都是接受用户请求的方法,doGet处理get请求,doPost处理post请求,doGet用于地址栏提交,doPost用于表单提交,在页面提交数据时,get的数据大小有限制4k,post没有限制,get请求提交的数据会在地址栏显示,post不显示,因此post比get安全.
会出现线程不安全问题。不管是doGet仍是doPost去调用,服务器端处理的过程都是同样的,那么咱们能够把处理过程单独写在另一个方法handle里,让两个方法都去调用handle,根据不一样请求去调用不一样的方法。
线程安全就是多线程操做同一个对象不会有问题,线程同步通常来保护线程安全,因此能够在Servlet的线程里面加上同步方法或同步块。(Synchronized)能够保证在同一时间只有一个线程访问,(使用同步块会致使性能变差,最好不去使用实例变量)
jsp的可读性强,容易维护,而且jsp在最后会编译成servlet
servlet容易调试
九大内置对象:
pageContext :只对当前jsp页面有效,里面封装了基本的request和session的对象
Request :对当前请求进行封装
Session :浏览器会话对象,浏览器范围内有效
Application :应用程序对象,对整个web工程都有效
Out :页面打印对象,在jsp页面打印字符串
Response :返回服务器端信息给用户
Config :单个servlet的配置对象,至关于servletConfig对象
Page :当前页面对象,也就是this
Exception :错误页面的exception对象,若是指定的是错误页面,这个就是异常对象
三大指令:
Page :指令是针对当前页面的指令
Include :用于指定如何包含另外一个页面
Taglib :用于定义和指定自定义标签
七大动做:
Forward,执行页面跳转,将请求的处理转发到另外一个页面
Param :用于传递参数
Include :用于动态引入一个jsp页面
Plugin :用于下载javaBean或applet到客户端执行
useBean :使用javaBean
setProperty :修改javaBean实例的属性值
getProperty :获取javaBean实例的属性值
获取页面的元素和值有几种方式,分别说一下
request.getParameter() 返回客户端的请求参数与值
request.getParameterNames() 返回全部可用属性名的枚举
request.getParameterValues() 返回包含参数的全部值的数组
一个是服务端,一个是客户端
Servlet是独立于平台和协议的服务器端的java应用程序,能够动态生成web页面,并采用响应--请求的模式提供web服务
javaScript是一种解释性语言,用于向html页面提供交互行为,一般被直接嵌入在html页面中
servlet是java语言编写的web应用
js是基于html上的一种解释语言