标签:key formdata 思想 比较 适应 部门 过滤器 varchar 特点
一 .Java基础
抽象类:
含有abstract修饰符的class即为抽象类。
特点:
1.不能创建的实例对象
2.可以有抽象方法
3.可以有构造方法,但构造方法不能是抽象的
4.抽象方法必须子类进行实现,如果子类只实现部分方法,该子类必须是抽象的。
接口:
含有interface修饰符的就是 。
特点:
1.接口中的所有方法都必须是抽象的
2.接口中的方法定义默认为public abstract类型
3.接口中的成员变量类型默认为public static final
区别:
1.单继承多实现:一个类可以实现多个接口,但只能继承一个抽象类。
2.构造方法:抽象类可以有构造方法,接口中不能有构造方法。(JDK7之前)
3.变量:抽象类中可以有普通成员变量,接口中只有常量。
4.方法:抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
5.修饰符:抽象类中的抽象方法可以使用public和protected进行修饰,但接口中的抽象方法只能使用public abstract修饰。
6.静态方法:抽象类中可以包含静态方法,接口中不能包含静态方法
7.抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
子类继承抽象类时,构造函数不会被覆盖。
而且,在实例化子类对象时首先调用的是抽象类中的构造函数再调用子类中的。
在这个阶段初始化抽象类字段或执行其它与子类相关的代码
常用的集合有:
单列集合:List、Set
双列集合:Map
1、List(有序、可重复)
List接口:比较常用的实现类是LinkedList与ArrayList+
LinkedList:基于链表实现,(链表实现,查询慢,增删快)
ArrayList:非线程安全的,效率高;(数组实现,查询快,增删慢)
2、Set(无序、不能重复)
Set接口:有两个实现类(HashSet(底层由HashMap实现),LinkedHashSet)
HashSet特点:哈希表数据结构,查询速度块,HashSet底层使用HashMap,仅使用key。
LinkedHashSet继承自HashSet,LinkedHashSet中的元素顺序是可以保证的,也就是说遍历序和插入序是一致的。
3、Map(键值对、键唯一、值不唯一)
如果要向Map集合中存放两个相同的key,后赋值给key的value的会把先赋值给key的value覆盖掉。
Map接口:有三个实现类(HashMap,HashTable,LinkedHashMap)
HashMap:HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null,不是线程安全的
LinkedHashMap:LinkedHashMap简单来说是一个有序的HashMap,其是HashMap的子类,HashMap是无序的。
HashTable:Hashtable既不支持Null key也不支持Null value,是线程安全的
常见集合 描述 实现类
List 有序、可重复 LinkedList:基于链表实现,查询慢,增删快
ArrayList:数组实现,查询快,增删慢
Set 无序、不能重复 HashSet:哈希表数据结构,查询速度块
LinkedHashSet继承自HashSet,LinkedHashSet中的元素顺序是可以保证的,也就是说遍历序和插入序是一致的
Map 键值对、键唯一、值不唯一。
如果要向Map集合中存放两个相同的key,后赋值给key的value的会把先赋值给key的value覆盖掉。 HashMap:HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null,不是线程安全的
LinkedHashMap:LinkedHashMap简单来说是一个有序的HashMap,其是HashMap的子类,HashMap是无序的
HashTable:Hashtable既不支持Null key也不支持Null value,是线程安全的
HashSet保存数据底层使用的是HashMap,保存元素时,保存的值为key,而value值为null。通过计算key值的hash值,
如果hash相同,再采用equals()方法比较俩值是否相同,如果相同,则覆盖旧元素。
TreeSet:
1.如果指定了比较器Comparator,添加元素时会调用compare(T o1, T o2)方法进行比较俩个对象是否相同;
2.或TreeSet中保存的对象实现了Comparable接口,会使用Comparable接口的compareTo()方法判断俩个对象是否相同;相同则覆盖旧元素。
3.如果俩种方式都没有使用,则会报错
1.ArrayList的底层是动态数组;LinkedList的底层是双向链表
2.增加元素:
ArrayList将元素追加至最后,ArrayList数组的默认初始大小为10,如果新增元素后大小超过数组的容量,则会对数组进行扩容,默认扩容大小为1.5倍;同时需要讲旧数组中的元素copy到新数组中
LinkedList将元素添加到链表的末尾,新增的元素作为最后一个节点
3.删除元素
ArrayList根据index删除元素,index索引上的元素被删除,index索引后的元素向前平移一个单位,因此效率不高
LinkedList根据index删除元素,改变index前后元素的指向,效率高;
4.插入元素
ArrayList在index位置上插入元素,需将原先index及往后的元素向后平移一个单位,效率不高
LinkedList在index位置插入元素,只需改变前后元素指向,效率高
5.查询
ArrayList根据index能够直接定位到元素所在位置,效率高
LinkedList查询时,需要从首个元素沿着链表进行查找,直到找到index索引处的元素,然后返回,效率不高
1.借助set集合
2.利用List的contains方法循环遍历
1.数组:
查询多,增删少;
因为数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况,因此增删少,查询效率高;
2.链表:
查询少,增删多;
因为链表动态地进行存储分配,可以适应数据动态地增减的情况,因此链表结构增删多,查询少;
HashMap底层数据结构为数组+链表;在JDK1.8中当链表的长度超过8时,链表会转换为红黑树;
数组的默认数组长度为16,数组的长度总是为2的幂;当数组的某一个链表的长度超出扩容条件:数组长度*负载因子(默认0.75),如:数组长度16*0.75=12,那么当链表(JDK1.7)或红黑树(JDK1.8)大小超过12,就会进行自动扩容;
1、如果数组还没有初始化(数组长度是0),则先初始化,默认16
2、通过hash方法计算key的hash值,hash值对数组长度进行取余操作,进而计算得到应该放置到数组的位置
3、如果该位置为空,则直接放置此处
4、如果该位置不为空,而且元素是红黑树,则插入到其中
5、如果是链表,则遍历链表,如果找到相等的元素则替换,否则插入到链表尾部
6、如果链表的长度大于或等于8,则将链表转成红黑树
1、1.8中引入了红黑树,而1.7中没有
2、1.8中元素是插在链表的尾部,而1.7中新元素是插在链表的头部
3、扩容的时候,1.8中不会出现死循环,而1.7中容易出现死循环
(1)HashMap是非线程安全的,HashTable是线程安全的,内部的方法基本都经过synchronized修饰。
(2)因为同步、哈希性能等原因,性能肯定是HashMap更佳,因此HashTable已被淘汰。
(3) HashMap允许有null值的存在,而在HashTable中put进的键值只要有一个null,直接抛出NullPointerException。
(4)HashMap默认初始化数组的大小为16,HashTable为11。前者扩容时乘2,使用位运算取得哈希,效率高于取模。而后者为乘2加1,都是素数和奇数,这样取模哈希结果更均匀。
方法重写(overriding):子类对父类的重写。
方法重载(overloading):同一个类中重载已有的方法。
1)重写的特点:
1、签名一致:子类重写的方法,必须与父类的方法,返回值、方法名和参数列表都相同。
2、异常窄化:子类抛出的异常要么与父类方法抛出的异常相同,要么抛出的异常是父类方法异常的子类或不抛出。(优生:父类有病,子类没有病)
3、修饰符宽化:子类方法的的访问级别不能低于父类相应方法的访问级别(子类访问级别不能低于父类访问级别)
2)重载特点
拥有相同的方法名,但是参数列表不同(类型、个数、顺序)
与返回值无关
与参数变量名无关
throw和throws都于异常有关。
throw:用于抛出异常。由方法体内的语句处理。
throws:用于声明异常。由该方法的调用者来处理。
throw出现在方法体中,在不同的语句中可能有多处throw。每throw一次只能抛出一个异常。throw抛出异常后可以立即try,也可以在方法上使用throws进行声明。
throws在方法上进行声明,表示一个方法可能出现的异常,需要方法的调用者进行处理。一个方法可以声明多个异常
(1)java.lang.NullPointerException 【空指针异常】
(2)java.lang.ClassNotFoundException 【类找不到异常】
(3)java.lang.NumberFormatException 【数字格式化异常】
(4)java.lang.IndexOutOfBoundsException 【数组角标越界异常】或 【数组索引越界异常】
(5)java.lang.IllegalArgumentException 【非法参数异常】
(6)java.lang.ClassCastException 【类型转换异常】数据类型转换异常
(7)java.lang.NoClassDefFoundException 【类未定义异常】
(8)SQLException 操作数据库异常 【SQL异常】
(9)java.io.IOException 【IO异常】
(10)java.lang.NoSuchMethodException 【没有匹配方法异常】
(1)final:修饰符。
A).被声明为final的类,不能被继承。
B).被声明final的变量或者方法,不能被改变。
C).被声明final的方法只能使用,不能被重载。
(2)finally:异常处理机制的一部分
A).finally结构使代码总会执行,而不管有无无异常发生。
B).使用finally常用于释放资源。
(3)finalize:Java中垃圾回收器执行的方法。
A).它是一个方法,属于java.lang.Object类。
B).在垃圾收集器执行的时候会调用的此方法,进行对象的回收,但在调用垃圾回收gc()后,并不能立即执行回收,JVM根据算法定时执行。
1.从运行速度来看:
StringBuilder > StringBuffer > String
String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,因为String对象一旦创建之后该对象是不可更改的,只会重新赋值,
而后两者的对象是变量,是可以更改的,所以String运行速度最慢,而StringBuilder比StringBuffer 效率高,所以StringBuilder运行速度最快,其次StringBuffer 。
2.字符修改上的区别:
String:不可变字符串;显然访问权限 线程安全
StringBuffer:可变字符串、效率低、线程安全;
StringBuilder:可变字符序列、效率高、线程不安全;
3.关于单线程和多线程的推荐
如果程序不是多线程的,那么使用StringBuilder效率高于StringBuffer。
4.总结:
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
读写操作,我们通常称为输入/输入(IO)操作。Java通过流进行IO操作的。
Java 中的流,可以从不同的角度进行分类。
按流的方向分:输入流和输出流。
按处理数据单位分:字节流和字符流。
按实现功能分:节点流和处理流。
1.字节流: 字节输入输出流,处理二进制数据,它是按字节来处理的
InputStream是所有字节输入流的祖先,
OutputStream是所有字节输出流的祖先。
2.字符流: 字符输入输出流,处理字符数据
Reader是所有读取字符串输入流的祖先
writer是所有输出字符串的祖先
字节高效流:
BufferedOutputStream 、BufferedInputStream
字符高效流:
BufferedWriter、BufferedReader
3.转换流: 将字节流转换为字符流
OutputStreamWriter、InputStreamReader
4.缓冲流:读入/写出数据时,原理是现将数据缓冲起来,然后一起写入或者读取出来;降低了对文件或者其他目标频繁的操作
5.对象流:用于写入对象 的信息和读取对象的信息。 使得对象持久化。需要被持久化的类需要实现接口 Serializable
InputStream -> BufferedInputStream -> InputStreamReader -> Reader -> BufferedReader
1.字符流主要是读取文本文件内容的,可以一个字符一个字符的读取,也可以一行一行的读取文本文件内容,处理的单元为 2 个字节的 Unicode 字符,分别操作字符、字符数组或字符串。
2.而字节流读取单位为字节(byte),字节作为计算机存储最基本单位,可以用字节流来读取很多其他格式的文件,比如图片歌曲视频等等,处理单元为 1 个字节,操作字节和字节数组。
字符流(一次可以处理一个缓冲区)一次操作比字节流(一次一个字节)效率高。
只要是处理纯文本数据(只是读写文件,和文件内容无关的),就要优先考虑使用字符流,除此之外都用字节流。
字符流的底层就是字节流(注:除了字符和字节流之外,java还有一种叫对象流。)
(1)break:
常用于循环中,含义:结束循环,跳出循环体
其他应用场景:switch语句中,结束语句。
(2)continue:
结束本次循环,进行下一次循环;(注意:如果,循环进行到最后一次,continue执行,结束本次循环, 继续进行循环逻辑判断结束循环。循环结束与continue无关)
扩展:在循环嵌套中,可以通过“循环标号”使内层循环结束外层循环,如:
out : for(......){
for(...){
break out; //结束外层out循环
}
}
1. ==比较的是值
1.1== 如果比较的是基本数据类型,比较的则是变量值
1.2 == 如果比较的为引用数据类型,比较的则是地址值
2. equals比较的是引用数据类型
2.1如果没有重写hashCode和equals方法,比较的是地址值。因为Object的equals方法中使用是==。
2.2如果重写hashCode和equals方法,则比较的重写后的规则。
例如:两个String字符串比较时:比较的是内容。因为String底层重写了equals方法进行内容的比较。
& 位运算,求2个二进制数的与。也可以进行逻辑运算,表达式左边为false,表达式右边继续执行。
&&
逻辑运算符,表达式左边为false,整个表达式结果为false,因此表达式右边不执行。此现象称为逻辑短路。
表达式1 & 表达式2 true & true == true
表达式1 && 表达式2 false && …. == false
表达式1 || 表达式2 true || ….
== true
1. 区别:
i++ 是先赋值,后加减;
++i 是先加减,后赋值。
2.效率:
++i的效率高些,++i在运算过程中不产生临时对象,返回的就是i,是个左值,类似++i=1这样的表达式是合法的,而i++在运算的过程中会产生临时对象,返回的是零时对象的值,是个右值,像i++=1这样的表达式是非法的
对于内置类型,单独的i++和++i语句,现在的编译器基本上都会优化成++i,所以就没什么区别了xxxxxxxxxx java中的内存泄露的情况:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景Java中有垃圾回收机制,它可以保证一对象不再被引用的时候,对象将自动被垃圾回收器从内存中清除掉
"++i":先自增,后赋值
"i++":先赋值,后自增
"+=": 自身值相加 a+=b ->
a = a+b
运算符的概念:
运算符是用来进行数据 计算/运算的指令
常量和变量都可以参与运算
被运算符操作的数据又称操作数
注意:运算符一般只操作基本数据类型,不操作引用数据类型(String除外)
种类:
算术运算符、位运算符、赋值运算符、关系运算符、逻辑运算符、三目运算符、类型相关运算符
char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,所以,char型变量中当然可以存储汉字啦。不过,如果某个特殊的汉字没有被包含在unicode编码字符集中,那么,这个char型变量中就不能存储这个特殊汉字。补充说明:unicode编码占用两个字节,所以,char类型的变量也是占用两个字节。
一个或两个。
第一次执行 new String("xyz") ,"xyz"将在字符串常量池中创建一个对象。new String() 又在堆内存中创建一个对象,所以创建了两个对象。
执行第二个语句new String("xyz") ,"xyz"第一次已经在常量池中创建,直接使用不用再创建。每new一次都会在堆内存中创建一个对象,new String() 再次在堆内存中创建对象,所以创建了一个对象。
collection:
1. 单列集合的顶级父接口
Collections:
1. 操作集合的工具类
2. 常用于:集合排序、集合不可修改限制等
GC是垃圾收集的意思。
Java中没有提供手动分配内存的方式,也没有提供手动释放内存的方式。
分配和释放全部采用自动完成。GC就用于自动回收内存
System.gc() 或Runtime.getRuntime().gc() 。
一般情况下java中对象可被回收的前提是:该对象不再被引用。然后垃圾回收器在回收的时候便会把这个对象清理掉
1、静态集合类引起内存泄漏;
2、当集合里面的对象属性被修改后,再调用remove()方法时不起作用;
3、监听器
4、各种连接
5、内部类和外部模块的引用
6、单例模式( 1、死循环 2、递归)
https://blog.csdn.net/c_royi/article/details/79527518
如果使用迭代器遍历,使用List提供的方法进行操作,将抛异常,异常是java.util.ConcurrentModificationException并发修改异常。
如果使用迭代器遍历,使用迭代器提供对应方法,将不会抛异常。
不正确;存在编译时异常,3.4是属于Double类型的,转换为float需要强转
如:float f = (float)3.4 或 float f = 3.4f
思考:
short s = 1,
s=s+1; 是否有错误?
有错误,s为short类型,与int类型的1相加,s+1的返回值为int型 ,int赋值给short就会出现精准度不够的错误提示
思考
short s += 1; 是否有错误?
+= 计算结果就是short类型,不存在强制类型的问题。
传递值是不会改变对象的属性的,而传递地址是会改变对象的属性的。所以上面的情况是引用传递。
数组中只有 length 属性, 不是方法, 没有后面的那对括号, 而String 类中,没有length属性, 只有方法, 也就是 length() 后面有对括号. 属性你可以看成是 数组类中的 变量, 而 length()方法可以看成是 String 类中定义好的方法
数组没有length()这个方法,只有length的属性:
String[] arr = {“1”,”2”,”3”};
System.out.println(“数组长度:”+arr.length);
String只有length()这个方法,没有length属性:
String str = “123”;
System.out.println(“字符串长度:”+str.length());
Error和Exception都继承自Throwable
Exception:
1.可以是可被控制(checked) 或不可控制的(unchecked)。 2.表示一个由程序员导致的错误。 3.应该在应用程序级被处理。
Error:
1.总是不可控制的(unchecked)。 2.经常用来用于表示系统错误或低层资源的错误。 3.如何可能的话,应该在系统级被捕捉。 (一组xqy)
1.检查异常 : 编译时被检测的异常,checked 异常也就是我们经常遇到的IO异常,以及SQL异常都是这种异常。对于这种异常,JAVA编译器强制要求我们必需对出现的这些异常进行catch。所以,面对这种异常不管我们是否愿意,只能自己去写一大堆catch块去处理可能的异常。
2.运行时异常:编译时不被检查的异常,在程序运行的时候出现。当出现运行时异常,系统会把异常一直往上层抛,直到遇到try-catch对抛出的异常进行处理。如果异常一直没有被处理,到最上层被抛出,那么当前线程就会就退出了;如果是主程序抛出的异常,那么这整个程序也就退出了
如果想自定义检查性异常类,则需要继承 Exception 类。
如果想自定义运行时异常类,那么需要继承 RuntimeException
类。
(1)this代表本类对应的引用。
(2)super代表父类存储空间的标识(可以理解为父类引用可以操作父类的成员)
不同点:super从子类中调用父类的构造方法,this()在同一类内调用其它构造方法。
相同点:super()和this()都必须在构造函数的第一行进行调用,否则就是错误的
1.修饰的类不能被继承
2.修饰的方法不能被重写
3.修饰的变量是常量不能被改变
4.但是final关键字不能用来修饰构造方法:
// 原因:final修饰普通方法,将导致子类无法重写该方法,而构造方法本身就不能够被子类重写,故如果用final修饰,如同多此一举
static关键字为静态的
1.用来修饰成员变量,将其变为类的成员,从而实现所有对象对于该成员的共享;
2.用来修饰成员方法,将其变为类方法,可以直接使用“类名.方法名”的方式调用,常用于工具类;
3.静态代码块,在类被加载的时候就会被执行的代码块;
final关键字
1.用来修饰数据,包括成员变量和局部变量,该变量只能被赋值一次且它的值无法被改变。对于成员变量来讲,必须在声明时或者构造方法中对它赋值;
2.修饰方法,表示该方法无法被重写;
3.修饰类,表示该类无法被继承。
区别:static作用于成员变量用来表示只保存一份副本,而final的作用是用来保证变量不可变
1.同步方法使用synchronized修饰方法,在调用该方法前,需要获得内置锁(java每个对象都有一个内置锁),否则就处于阻塞状态
代码如: public synchronized void save(){//内容}
2.同步代码块使用synchronized(object){}进行修饰,在调用该代码块时,需要获得内置锁,否则就处于阻塞状态
代码如:
synchronized(object){
//内容
}
1. 栈:Java虚拟机内存中的一片区域
(1) 调用方法将在栈中开辟内存,称为入栈(压栈)。
(2) 栈内存存放基本类型值和引用数据类型的地址。
(3) 栈内存中的数据,没有默认初始化值,需要手动设置。
(4) 方法调用完成,栈内存立即释放,称为出栈(弹栈)。
2. 堆:Java虚拟机内存中的另一片区域。
(1) 用于存放使用new创建的对象或数组。
(2) 所有的对象都有内存地址值。
(3) 数据都有默认初始化值。
(4) 堆内存中的对象不再被指向时,JVM启动垃圾回收机制,自动清除。
1.堆:主要用于存储实例化的对象,数组。由JVM动态分配内存空间。一个JVM只有一个堆内存,线程是可以共享数据的。
2.栈:主要用于存储局部变量和对象的引用变量,每个线程都会有一个独立的栈空间,所以线程之间是不共享数据的。
3.方法区:又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
1.ArrayList使用普通for循环效率更高,因为对于数组结构的数据来说,for循环采用的是下标访问;
2.但是LinkedList是通过链表实现的,for循环时每获取第i个元素都必须从头开始遍历;foreach底层是用迭代器实现,是通过iterator实现的遍历,而iterator遍历就是从头开始遍历,遍历完只需要一次;所以使用增强for循环效率更高。
结论:for和foreach循环效率差不多,for循环稍微好一点;但是对于链表来说,for循环效率明显比foreach低
1.JDK是整个JAVA的核心,包括Java运行环境JRE,一堆Java工具和Java基础的类库。通过JDK开发人员将源文件(Java文件)编译成字节码文件(class文件) 。
2.JRE是Java运行环境,不含开发环境,即没有编译器和调试器。将class文件加载到内存准备运行
3.JVM就是java虚拟机,是java实现跨平台的最核心的部分。class文件运行整个环境。
传递基本数据类型,意味着传递了对象的一个副本。因此,就算是改变了对象副本,也不会影响源对象的值。
传递引用数据类型(对象),意味着传递的并不是实际的对象,而是对象的引用(即对象的地址)。因此,外部对引用对象所做的改变会反映到所有的对象上。
无论是否抛出异常,finally代码块总是会被执行。就算是没有catch语句同时又抛出异常的情况下,finally代码块仍然会被执行。最后要说的是,finally代码块主要用来释放资源,比如:I/O缓冲区,数据库连接。
实现多态主要有以下三种方式:
1. 接口实现
2. 继承父类重写方法
3. 同一类中进行方法重载
Java中,靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。
在jdk 1.7之前,switch只能支持byte, short, char, int或者其对应的封装类以及Enum类型。从jdk 1.7之后switch开始支持String。
1.基本数据类型:
1.1 数值型:
1)整数类型(byte,short,int,long)
2)浮点类型(float,double)
1.2 字符型(char)
1.3 布尔型(boolean)
2.引用数据类型
2.1类(class)
2.2接口(interface)
2.3数组
1. xxx类型转换字符串:String o = Xxx.toString( obj );
①.整型转换成字符型
String num = Integer.toString(int n);
②.Long型转换成字符型
String num = Long.toString(long n);
③.Short型转换成字符型
String num = Short.toString(Short n);
④.Float型转换成字符型
String num = Float.toString(Float n);
⑤.Double型转换成字符型
String num = Double.toString(Double n);
2. String类型转换为xxx:Xxx x = Xxx.parseXxx(String s);
①.转换成Int型
int/Integer num = Integer.parseInt(String str);
②.转换成long型
Long/long num = Long.parseLong(String str);
③.转换成short型
short/Short num = Short.parseShort(String str);
④.转换成float型
float/Float num = Float.parseFloat(String str);
⑤.转换成double型
double/Double num = Double.parseDouble(String str);
Static可以修饰内部类、方法、变量、代码块
Static修饰的类是静态内部类
Static修饰的方法是静态方法,表示该方法属于当前类的,而不属于某个对象的,静态方法也不能被重写,可以直接使用类名来调用。在static方法中不能使用this或者super关键字。
Static修饰变量是静态变量或者叫类变量,静态变量被所有实例所共享,不会依赖于对象。静态变量在内存中只有一份拷贝,在JVM加载类的时候,只为静态分配一次内存。
Static修饰的代码块叫静态代码块,通常用来做程序优化的。静态代码块中的代码在整个类加载的时候只会执行一次。静态代码块可以有多个,如果有多个,按照先后顺序依次执行。
A.抽象的来讲,多态的意思就是同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
b.实现的原理是动态绑定,程序调用的方法在运行期才动态绑定,追溯源码可以发现,JVM 通过参数的自动转型来找到合适的办法
a) 内部类可以直接调用外部类包括private的成员变量,使用外部类引用的this.关键字调用即可
b) 而外部类调用内部类需要建立内部类对象
拆箱:把包装类型转成基本数据类型
装箱:把基本数据类型转成包装类型
顺序结构
选择结构
循环结构
Equals Hashcode toString wait notify clone getClass
不能,数组一旦实例化,它的长度就是固定的
不是必须。抽象类可以没有抽象方法
含有抽象方法的类一定是抽象类
1.语法区别:静态变量需要static关键字修饰,实例变量不需要。
2.程序运行时的区别:静态变量从属于类,实例变量从属于对象。
3.存储区域不同:静态变量存储在静态存储区,普通变量存储在堆中
不可以。因为非static是要与对象关联在一起的,必须创建一个后,才可以在该对象上进行方法调用,而static方法调用时不需要创建对象,可以直接使用。
Integer是Java为int提供的封装类。Integer的默认为null。
int是Java的基本数据类型之一。int的默认为0。
Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况。
静态初始化只在Class对象首次被加载的时候进行一次。
成员变量初始化,在new对象的时候被初始化,在构造函数的隐式三步其中一步进行初始化,初始化完成之后才会执行构造器中的代码
调用属性
(1)this可以调用本类中的任何成员变量
调用方法(可省略)
(2)this调用本类中的成员方法(在main方法里面没有办法通过this调用)
调用构造方法
(3)this调用构造方法只能在本构造方法中调用另一个构造方法
(4)this 调用构造方法必须写在第一行
1.首先在环境变量中创建JAVA_HOME,给其赋值为jdk的安装路径
2.然后在编辑环境变量中的path值,添加%JAVA_HOME%\bin
3.最后cmd打开命令模式,输入java -version 检查jdk环境变量是否配置成功
JAVA_HOME与java相关的系统环境变量。用于配置JDK的安装目录。
1.抽象
将一些事物的共性抽离出来归为一个类。
如对于动物,具有生命体征、活动能力等区别于其它事物的共同特征
2.封装
有选择地隐藏和暴露数据和方法
比如有U盘这个类,我希望隐藏内部组成和实现,只暴露USB接口以供使用
3.继承
子类可以直接使用父类的部分数据和方法,可以有选择的扩展
比如鸟是动物,但鸟扩展了飞行的能力。
4.多态
同一类的对象调用相同方法可以表现出不同的行为
比如动物实现了say()方法,猴子、马等动物重写了say()方法来表现不同的交流语言。
在面向对象中封装是指隐藏对象的属性和实现的细节,仅对外提供公共访问方式。在类定义中用private关键字来实现封装。
封装的好处:
1.提高了数据的安全性和模块化
别人不能够通过 变量名.属性名 的方式来修改某个私有的成员属性
2.提高了代码的重用性
封装成工具类以后能够减少很多繁琐的步骤
3.隐藏内部属性和实现方式
实现过程对调用者是不可见的,只需调用方法即可,不知道具体实现过程,且保护内部状态
4.操作简单
封装后,多个调用者在使用的时候,只需调用方法即可,调用者不需要再进行判断
5.增加代码的可维护性和可靠性
一个类可以任意的改变存储数据的方式,只要仍旧使用相同的方法操作数据,那么其他
类就不知道或者介意这种变化
Java NIO中提供一种技术,是特定基本类型数据的容器。
Buffer是缓冲区顶层抽象类,常见的子类有:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer
每个缓冲区都是可读取的,但并非每个缓冲区都是可写入的。
缓冲区常见的3个特性:容量、限制和位置。
容量(capacity):缓冲区能够容纳元素的最大数量。
限制(limit):第一个不应该读取或写入的元素的索引。
位置(position):下一个要读取或写入的元素的索引
一共四种:private、default(一般省略)、public、protected
权限 类内 同包 不同包子类 不同包非子类
private √ × ×
×
default √ √ ×
×
protected √ √ √
×
public √ √ √
√
不可以,因为String类有final修饰符,而final不能被继承的
会执行,在方法返回调用者前执行
接口可以继承接口。抽象类可以实现(implements)接口;实体类必须有明确的构造函数,抽象类才可继承实体类
匿名的内部类是没有名字的内部类。不能extends(继承) 其它类,但一个内部类可以作为一个接口,由另一个内部类实现。
在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。
基本反射功能的实现
1、Class clazz1 = Class.forName("全限定类名");
2、Class clazz2 = 类名.class;
3、Class clazz3 = 类名.getClass();
JAVA反射机制:
是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意方法和属性;
这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
反射机制的应用:
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成动态代理。
while结构在循环的开始判断下一个迭代是否应该继续。do/while结构在循环的结尾来判断是否将继续下一轮迭代。do结构至少会执行一次循环体。
Java虚拟机是能移植到不同硬件平台上的软件系统。
向下转换是指由一个通用类型转换成一个具体的类型,在继承结构上向下进行。(可以理解成强转)
Object
数值提升是指数据从一个较小的数据类型转换成为一个更大的数据类型,byte,char,short值会被转化成int类型。需要的时候int类型也可能被提升成long。long和float则有可能会被转换成double类型。
数组是空的,没有任何元素。不像C或者C++,第一个元素默认是程序名。如果命令行没有提供任何参数的话,main方法中的String数组为空,但不是null。
输出array.length的值,如果是0,说明数组为空。如果是null的话,会抛出空指针异常。
静态变量、静态代码块、静态方法都是在编译期
可以,但只能有一个main方法拥有以下签名:
public static void main(String[] args) {}
否则程序将无法通过编译。编译器会警告你main方法已经存在。
JVM是一台抽象的计算机,就像真实的计算机那样,它们会先将.java文件编译成.class文件(.class文件就是字节码文件),然后用它的解释器来加载字节码。
不可以。因为非static方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方法调用,而static方法调用时不需要创建对象,可以直接调用。所以,一个static方法内部不可以发出对非static方法的调用。
对于short s1 = 1; s1 = s1 + 1; 由于s1+1运算时会自动提升表达式的类型,所以结果是int型,再赋值给short类型s1时,编译器将报告,需要强制转换类型的错误。对于short s1 = 1; s1 += 1;由于 += 是java语言规定的运算符,java编译器会对它进行特殊处理,因此可以正确编译。
构造器Constructor不能被继承,因此不能重写Override,但可以被重载Overload。
面向对象编程
显示转换就是类型强转,把一个大类型的数据强制赋值给小类型的数据;隐式转换就是大范围的变量能够接受小范围的数据;隐式转换和显式转换其实就是自动类型转换和强制类型转换。
Stringbuilder或者stringbuffer的reverse方法
Integer.parseInt()
有,log4j是用来日志记录的,记录一些关键敏感的信息,通常会将日志记录到本地文件或者数据库中。记录在本地文件中,会有频繁的io操作,会耗费一些系统资源。记录在数据库中,会频繁地操作数据库表,对系统性能也有一定的影响。但是为了程序安全以及数据的恢复或者bug的跟踪,这点资源消耗是可以承受的。
由低到高:debug、info、wran、error
不会,在下一个垃圾回收周期中,这个对象将是可被回收的。
序列化:将java对象转换为可保持或可传输的格式,即转换为二进制字节序列(字节流)的形式的过程。
反序列化:将二进制字节序列(字节流)形式的java对象解析出来的过程。
序列化可以实现数据的持久化,也就是说可以将数据永久的保存在磁盘上。
序列化可以实现远程通讯,即在网络上以二进制字节序列的格式传送对象。
序列化可以将对象状态保存,方便下次取出利用。
有了序列化,两个进程就可以在网络上任性的交互数据了。
被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。
实现Serializable接口
会执行,如果有finally,在finally之后被执行,如果没有finally,在catch之后被执行
https://www.cnblogs.com/Good-good-stady-day-day-up/p/9055698.html
java中的内存泄露的情况:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景
Java中有垃圾回收机制,它可以保证一对象不再被引用的时候,对象将自动被垃圾回收器从内存中清除掉
1.内存泄露:是指程序在申请内存后,无法释放已申请的内存空间。
2.常发性内存泄漏:发生内存泄漏的代码被多次执行到,每次执行都会导致一块内存泄漏。
3.偶发性内存泄漏:发生内存泄漏的代码只在某些特定环境或操作下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。
4.一次性内存泄漏:发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块且只有一块内存发生泄漏。
5.隐式内存泄漏:程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。
一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光,最终导致内存溢出。
1.clone()不会调用构造方法;new会调用构造方法。
2.new 操作符的本意是分配内存。程序执行到 new 操作符时,首先去看 new 操作符后面的类型,因为知道了类型, 才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。
clone 在第一步是和 new 相似的,都是分配内存,调用 clone 方法时,分配的内存和原对象(即调用 clone 方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完成之后,clone 方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。
1.为什么要克隆?
①方便,克隆的对象可能包含一些已经修改过的属性,而new出来的对象的属性都还是初始化时候的值,所以当需要一个新的对象来保存当前对象的“状态”使用clone方式很方便;
②速度快,clone方法最终将调用JVM中的原生方法完成复制也就是调用底层的c++代码,所以一般使用clone方法复制对象要比新建一个对象然后逐一进行元素复制效率要高。
2.实现clone
① 实现Cloneable接口并重写Object类中的clone()方法;
② 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
对象序列化后写入流中,再从流中读取,生成新的对象,新对象和原对象之间也是完全互不影响的。
1.浅克隆:只复制基本类型的数据,引用类型的数据只复制了引用的地址,引用的对象并没有复制,在新的对象中修改引用类型的数据会影响原对象中的引用。
2.深克隆:是在引用类型的类中也实现了clone,是clone的嵌套,复制后的对象与原对象之间完全不会影响。
1、类是对象的模版,对象是类的实例;
2、一个类可以创建N个对象,每个对象都是独一无二的。
OOP(object oriented programming),即面向对象编程,其中两个最重要的概念就是类和对象
类:对现实世界事物的抽象表示,包括事物的状态信息(成员变量)和行为信息(成员方法)。
对象:对抽象事物的具体表示,使其具有自身的特点。对类进行实例化可以得到对象。
Static Nested Class是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化
都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。
完全可以。如果不是静态内部类,那没有什么限制!
如果你把静态嵌套类当作内部类的一种特例,那在这种情况下不可以访问外部类的普通成员变量,而只能访问外部类中的静态成员
1).内省(Introspector)是Java 语言对Bean类属性、事件的一种缺省处理方法。例如类 A 中有属性 name, 那我们可以通过 getName,setName 来得到其值或者设置新的值。通过 getName/setName 来访问 name 属性,这就是默认的规则。
Java 中提供了一套 API 用来访问某个属性的 getter/setter 方法,通过这些 API 可以使你不需要了解这个规则(但你最好还是要搞清楚),这些 API 存放于包 java.beans 中。
2).直接通过属性的描述器java.beans.PropertyDescriptor类,来访问属性的getter/setter 方法;
String str = "sfds55d676455dfg32434eertre";
String longStr =new String(str.replaceAll("[^\\d]+", ""));
正则(扩展 如:s.replaceAll(“[^0-9]“,”"),这样非数字就全部去除了,只保留数字了
System.out.println("字符串=========:" + longStr);
没有。因为String是不可变类(immutable class),不可变类
分支语句(if语句,switch语句);
循环语句(for,while,do...while);
跳转语句(break,continue,return)
break
java中的内存泄露的情况:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收
Java的类是单继承的,即每个类只能继承一个类。但是,Java中也有多继承,接口支持多继承,即一个接口可以继承多个接口
第一种.class获取对象 :类型.class
Class ForClass = **.**.ClassName.class;(类在包中的路径加.class)
第二种:通过类本身来获得对象 :对象.getClass()
Class classname = this.getClass();
第三种方式:通过类名的字符串获取对象 :Class.forName()
Class ForName = Class.forName("**.**.ClassName");
在jdbc中常用到。
第四种方式:通过子类的实例获取父类的类对象,
User cn =
new User();
Class userClass = cn.getClass();
Class subUserClass = userClass.getSuperclass();
` 1.使用new关键字
2.使用Class类的newInstance方法
3.使用Constructor类的newInstance方法
4.使用Clone的方法:无论何时我们调用一个对象的clone方法,JVM就会创建一个新的对象,将前面的对象的内容全部复制,返回一个新的对象,用clone方法创建对象并不会调用任何构造函数。
5.使用反序列化:当我们序列化和反序列化一个对象,JVM会给我们创建一个单独的对象,在反序列化时,JVM创建对象并不会调用任何构造函数。
1)常见的三种方法,toString(),equals()和clone()
a)toString()方法是将对象中的内容转换成字符串的形式
b)equals()方法是将两个对象的内容进行比较,如果内容相同,则返回true,否则返回false
c)clone()方法是将一个存在的对象进行克隆
2) 要使用Object的clone()方法,得先继承Cloneable接口,为了达到真正的克隆效果 需要对clone()方法进行重写
3) 在使用Object类的toString() equals()
clone()方法时,通常会对这三个函数进行重写后再使用
4)要使用clone()方法得先继承Cloneable接口
1)什么是线程:
在一个进程中,每个独立的功能都需要独立的去运行,这时又需要把当前这个进程划分成多个运行区域,每个独立的小区域(小单元)称为一个线程。
例如:360杀毒软件,同时既可以安全体检电脑又可以清理电脑中的垃圾。那么这里的安全体检是360杀毒软件中的一个线程,清理电脑中的垃圾也是一个线程。
2)什么是多线程:
一个进程如果只有一条执行任务,则称为单线程程序。
一个进程如果有多条执行任务,也就是说在一个进程中,同时开启多个线程,让多个线程同时去完成某些任务(功能)。则称为多线程程序。
多线程环境中,且存在数据共享,一个线程访问的共享数据被其它线程修改了,那么就发生了线程安全问题;整个访问过程中,无一共享的数据被其他线程修改,就是线程安全的
1.使用线程同步机制,使得在同一时间只能由一个线程修改共享数据;
2.消除共享数据:即多个线程数据不共享或者共享的数据不做修改,将全局数据转换为局部数据;
如在SpringMVC中,就采用的该种方式解决线程安全问题。在Controller中,service为多个线程共享的数据,但是service为单例的,且不会被修改;controller中的方法,接收请求数据方式为局部变量,多个线程不共享数据。即不会产生线程安全问题
当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态
1)进程是一个具有独立功能的程序,一个程序必须有一个进程,一个进程至少有一个线程。
2)线程是进程的一个实体 ,是进程中的一个方法功能。
举例 : 一个人有一双手,但是有十个手指,一双手就好比是一个进程,十个手指就好比是十个线程。
有三种方式创建多线程
(1)通过继承 Thread 类创建
(2)通过实现 Runnable 接口创建
*优先使用实现 Runnable 接口创建,因为 Java 中是单继承,一个类只有一个父类,若继承了 Thread 类,就无法在继承其它类,
①避免了Java单继承的局限性;
②把线程代码和任务的代码分离,解耦合(解除线程代码和任务的代码模块之间的依赖关系)。代码的扩展性非常好;
③方式二可以更方便、灵活的实现数据的共享
(3)通过实现 Callable 接口创建(不常用)
1)wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
2)sleep(): 使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException
异常;
3)notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由 JVM 确定唤醒哪个线程,而且与优先级无关;
4)notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;
注意:java 5 通过 Lock 接口提供了显示的锁机制,Lock 接口中定义了加锁(lock()方法)和解锁(unLock()方法),增强了多线程编程的灵活性及对线程的协调
启动一个线程是调用start()方法,使线程就绪状态,以后可以被调度为运行状态,可以由JVM调度并执行,但不一定会立即运行。一个线程必须关联一些具体的执行代码,run()方法是该线程所关联的执行代码。
就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程
不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开
销。
常用线程池:
(1)newSingleThreadExecutor:创建一个单线程的线程池,此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
(2)newFixedThreadPool:创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。
(3)newCachedThreadPool:创建一个可缓存的线程池,此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说 JVM)能够创建的最大线程大小。
(4)newScheduledThreadPool:创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。
(5)newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。
两个进程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是两个进程 都陷入了无限的等待中
死锁是指每个线程都在等待被其它线程占用并堵塞了资源
例:如果线程A锁住了记录1并等待记录2,而线程B锁住了记录2并等待记录1,这样两个线程就发生了死锁现象
死锁分为2种锁:排它锁和共享锁
①系统资源的竞争
通常系统中拥有的不可剥夺资源,其数量不足以满足多个进程运行的需要,使得进程在 运行过程中,会因争夺资源而陷入僵局,如磁带机、打印机等。只有对不可剥夺资源的竞争 才可能产生死锁,对可剥夺资源的竞争是不会引起死锁的。
②进程推进顺序非法
进程在运行过程中,请求和释放资源的顺序不当,也同样会导致死锁。例如,并发进程 P1、P2分别保持了资源R1、R2,而进程P1申请资源R2,进程P2申请资源R1时,两者都 会因为所需资源被占用而阻塞
产生死锁必须同时满足以下四个条件,只要其中任一条件不成立,死锁就不会发生。
①互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程使用。
②请求与保持条件(Hold and wait):进程已获得了一些资源,但因请求其它资源被阻塞时,对已获得的资源保持不放。
③不可抢占条件(No pre-emption) :有些系统资源是不可抢占的,当某个进程已获得这种资源后,系统不能强行收回,只能由进程使用完时自己释放。
④循环等待条件(Circular wait):若干个进程形成环形链,每个都占用对方申请的下一个资源。
①加锁顺序(线程按照一定的顺序加锁,只有获得了从顺序上排在前面的锁之后,才能获取后面的锁)
②加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
③死锁检测 (判断系统是否处于死锁状态)
④死锁避免(指进程在每次申请资源时判断这些操作是否安全。例如银行家算法:在分配资源之前先看清楚,资源分配后是否会导致系统死锁。如果会死锁,则不分配,否则就分配。)
使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。
有些业务逻辑在执行过程中要求对数据进行排他性的访问,于是需要通过一些机制保证在此过程中数据被锁住不会被外界修改,这就是所谓的锁机制。
优点:保证资源同步
缺点:有等待肯定会慢
悲观锁
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
乐观锁
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。乐观锁适用于多读的应用类型,这样可以提高吞吐量。可以使用版本号机制和CAS算法实现(自己了解)
悲观锁:
悲观锁并发模式假定系统中存在足够多的数据修改操作,以致于任何确定的读操作都可能会受到由个别的用户所制造的数据修改的影响。也就是说悲观锁假定冲突总会发生,通过独占正在被读取的数据来避免冲突。但是独占数据会导致其他进程无法修改该数据,进而产生阻塞,读数据和写数据会相互阻塞。
乐观锁:
乐观锁假定系统的数据修改只会产生非常少的冲突,也就是说任何进程都不大可能修改别的进程正在访问的数据。乐观并发模式下,读数据和写数据之间不会发生冲突,只有写数据与写数据之间会发生冲突。即读数据不会产生阻塞,只有写数据才会产生阻塞。
一、设计模式的分类(了解)
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、 状态模式、访问者模式、中介者模式、解释器模式。
二、单例模式
单例设计模式
单例模式的好处:
1.某些类的创建比较频繁,对于一些大型的对象,这是一笔很大的开销。
2.省去了new的操作,节省了内存的开销,减轻了GC的压力。
3.有些情况只能用单例的设计模式,例如,一些交易核心,只能用一个对象,不然就会乱套了。
饿汉式:(线程安全的)
public class Singleton {
// 私有的构造方法
private Singleton(){
}
// 指向自己实例的私有静态引用
private static Singleton singleton
= new Singleton();
// 以自己实例为返回值的静态的公有的方法
public static Singleton getInstance() {
return singleton;
}
}
懒汉式(线程不安全)
public class Singleton {
// 私有的构造方法
private Singleton() {
}
// 不建立对象
private static Singleton singleton
= null;
// 以自己实例为返回值的静态的公有的方法
public static Singleton getInstance() {
if (singleton == null) {// 先判断是否为空
singleton = new Singleton();// 如果为空建立对象
}
return singleton;
}
}
注:可以通过枚举实现单例模式的绝对单例
三、工厂模式
1.意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
2.主要解决:主要解决接口选择的问题。
3.何时使用:我们明确地计划不同条件下创建不同实例时。
4.如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
5.关键代码:创建过程在其子类执行
四、适配器模式
1.意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
2.主要解决:主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。
3.何时使用:
1)系统需要使用现有的类,而此类的接口不符合系统的需要。
2)想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。
3)通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
4.如何解决:继承或依赖(推荐)。
5.关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
1、这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类。
sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用了b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。
Java中可在方法名前加关键字syschronized来处理当有多个线程同时访问共享资源时候的问题。syschronized相当于一把锁,当有申请者申请该资源时,如果该资源没有被占用,那么将资源交付给这个申请者使用,在此期间,其他申请者只能申请而不能使用该资源,当该资源被使用完成后将释放该资源上的锁,其他申请者可申请使用。并发控制主要是为了多线程操作时带来的资源读写问题。如果不加以空间可能会出现死锁,读脏数据、不可重复读、丢失更新等异常。
并发操作可以通过加锁的方式进行控制,锁又可分为乐观锁和悲观锁。
新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态
1、TCP(面向连接如打电话要先拨号建立连接),建立TCP连接需经过三次握手,释放TCP连接需经过四次挥手;UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP对系统资源要求较多,UDP对系统资源要求较少。
Servlet是服务器程序,能够响应客户端请求,动态的响应给客户端web内容。
(1)加载和实例化:当Servlet容器启动或客户端发送一个请求时,Servlet容器会查找内存中是否存在该Servlet实例,若存在,则直接读取该实例响应请求;如果不存在,就创建一个Servlet实例。
(2) 初始化init():实例化后,Servlet容器将调用Servlet的init()方法进行初始化(一些准备工作或资源预加载工作)。
(3)服务service():初始化后,Servlet处于能响应请求的就绪状态。当接收到客户端请求时,调用service()的方法处理客户端请求,HttpServlet的service()方法会根据不同的请求 转调doGet()/doPost()方法。
(4)销毁destroy():当Servlet容器关闭时,Servlet实例也随时销毁。其间,Servlet容器会调用Servlet 的destroy()方法去判断该Servlet是否应当被释放(或回收资源)。
init():Servlet实例化后,Servlet容器会调用init()方法来初始化该对象 service():容器调用service()方法来处理客户端的请求
destroy():Servlet对象应该从服务中被移除的时候,容器会调用该对象的destroy()方法
getServletConfig():ServletConfig对象包含了Servlet的初始化参数
getServletInfo():返回一个String类型的字符串,其中包括了关于Servlet的信息
异步处理允许Servlet重新发起一条新线程去调用 耗时业务方法,这样就可以避免等待
1、request.getParameterValues("参数"); //获得指定参数名的一组参数值 (String[])
2、request.getParameter("参数");
//获得指定参数名的一个参数值(String) , UserServlet?username=jack , 通过username获得值jack
Servlet不是线程安全的。Servlet 默认是单例模式,在web 容器中只创建一个实例,所以多个线程同时访问servlet的时候,Servlet是线程不安全的。
jsp是动态web页面,其本质上就是一个Servlet,它是Servlet的一种特殊形式,我们即可以在jsp页面中书写html、css、JavaScript等前台代码,也可以书写java代码。
Servlet是服务器程序,能够响应客户端请求,动态的响应给客户端web内容。
jsp和servlet的区别:
jsp是html页面中内嵌Java代码,侧重页面显示;Servlet是中书写Java代码,侧重逻辑控制;
mvc设计思想中jsp位于视图层,servlet位于控制层
1.page: 当前页(this)
2.config: 配置对象ServletConfig
3.servletContext: servlet上下对象
4.request:请求对象ServletRequest
6.session:服务器会话对象,HTTPSession
5.response:响应对象 ServletResponse
7.out: 输出对象 PrintWriter,response.getWriter()
8.exception: 异常对象,记录service出现所有的错误
9.application:PageContext jsp页面的上下文对象
request:是客户端发送到服务器的请求,包含客户端的请求信息,使用request进行数据的传递,一次请求之间的数据共享;
response:是服务器传回客户端的响应信息,使用response将服务器数据响应给客户端;
session:一次会话,其中保存着一次会话需要共享的所有数据,如SessionID、用户信息等;
pageContext:管理当前页面的数据;
application:服务器启动时创建,服务器关闭时停止,为多个应用程序保存信息
out 用来传送回应的输出
config
servlet的构架部件
page
JSP网页本身
exception
针对错误网页,未捕捉的例外
application:在所有应用程序中有效 session:在当前会话中有效 request:在当前请求中有效 page:在当前页面有效
通过page指令设置<%@ page isThreadSafe="false"%>,默认Servlet支持多线程模式
<% %>里边放的是java代码 <%= %> 相当于 <%out.print();%> <% %>是脚本 <%= %>是表达式
1)查看jsp文件头是否设置了编码格式:
2)查看项目的编码格式:设置为UTF-8
3)在请求头响应头设置编码:
//设置编码格式
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
1. 过滤器是Servlet中对用户请求服务器资源拦截的技术
2. 当客户端请求服务器的web资源,如:Jsp,Servlet,静态图片文件或静态HTML等时,通过Filter可以对这些请求进行拦截,在执行真正请求之前做一些通用的工作,如统一编码、统一用户认证等工作。认证成功后,过滤器执行放行动作,再执行真正的请求。
3. 应用场景:统一编码、统一用户认证、屏蔽非法文字,进行响应数据压缩,等等
1. 什么是监听器:所谓的监听器是指对WEB环境中Request、Session和ServletContext等的监听,当被监视的对象的创建、销毁或者属性发生变化时,便调用相应的方法进行处理。
2. 使用场景:网站在线人数统计、监听用户的行为(管理员踢人),等等
3. 网站在线人数统计:使用监听器监听session的创建与销毁,当session会话被创建时,在线用户人数+1,当session会话被销毁时,在线用户人数-1
Cookie是web服务器发送给浏览器的一块信息,浏览器会在本地一个文件中给每个web服务器存储cookie。浏览器再给特定的web服务器发送请求时,同时会发送所有该服务器存储的cookie。
Session是存储在web服务器的一块信息。session对象存储特定用户会话所需的属性及配置信息。当用户在应用程序Web页面之间跳转时,存储在Session对象中的变量将不会丢失,而是在整个会话中一直存在下去。
区别:
1.cookie存储在客户端(浏览器),session存储在服务器端;
2.数据量:cookie只能存储少量的数据,session可以存放大量数据(服务器内存有多大,就能存储多大的数据)
3.在存储数据方面:session能存储任意java对象,cookie只能存储英文。
4.从生命周期方面:
session默认的生命周期时间间隔是30分钟(session的生命周期是间隔的,从创建时,开始计时如在30分钟,没有访问session,那么session生命周期被销毁,但是,如果在30分钟内(如在第19分钟时)访问过session,那么,将重新计算session的生命周期);
如果没有设置cookie的存活时间,当浏览器关闭的时候,cookie便消失了。如果设置cookie有效时间,过期后,浏览器在发送请求前,将删除对应的cookie。
session.setMaxInactiveInterval(设置时间/秒)
1.提示用户不要禁用cookie;
2.使用URL重写技术解决cookie被禁用问题:https://blog.csdn.net/m0_38039437/article/details/76718541
3.使用html5的localStorage替代cookie
token只是一个32位的UUID:SSSSSSSSUDUDUDUDUDUUDUDUDUD
session是浏览器与服务器的一次会话,在这次会话中包含多次的request(请求)
在HTML5中,新加入了一个localStorage特性,这个特性主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题(cookie中每条cookie的存储空间为4k),localStorage中一般浏览器支持的是5M大小,这个在不同的浏览器中localStorage会有所不同。
1xx(请求成功):用于表示临时响应并需要请求者执行操作才能继续的状态代码;
2xx:用于表示服务器已成功处理了请求的状态代码;
3xx(请求被重定向):通常这些状态代码是用于重定向的;
4xx(客户端错误):用于指出客户端的错误;
5xx(服务器错误):这些状态代码表示,服务器在尝试处理请求时发生内部错误
常见状态码:
200,服务器已成功处理了请求。一般用于查询
301/302,请求重定向
400,请求路径语法错误
404,请求资源不存在
405,请求方式不正确,如:一个资源只支持GET方法,但是客户端使用PUT方法访问。
500,服务器遇到错误,无法完成请求。
URL 重写是拦截客户端传入 Web 请求URL,并获取其URL重新写成网站能处理的URL的过程。比如浏览器发来请求
http://blog.mocoder.com/hello.html ,服务器自动将这个请求中定向为http://blog.mocoder.com/test.do?method=hello。
URL重写的作用:
1.提供安全性:有些大网站采用不同的技术开发不同功能的页面。而把拓展名改掉,让黑客无法确认此页面用的技术是什么,从而就无从下手。
2.方便访问者使用。访问者不是程序员,他们不明白什么是.jsp,.php.aspx,他们只知道URL。所以统一把拓展名拿掉,或者同意把拓展名换为html,htm,有利于用户的使用。
3.使用URL重写技术可以有效解决cookie被禁用问题。
1.是什么:Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术
2.使用ajax的好处有:
①通过异步模式,提升了用户体验;
②优化了浏览器和服务器之间的传输,减少不必要的数据往返,减少了带宽占用;
③Ajax引擎在客户端运行,承担了一部分本来由服务器承担的工作,从而减少了大用户量下的服务器负载
3.如何使用
①jQuery中发送ajax请求:$.ajax({type:
"POST",url: "",data: "name=John",success:
function(msg){}});
②Vue发送get请求:axios.get(url[,options]);发送post请求:axios.post(url,data,[options]);
ServletContext对象是上下文对象 Servlet直接调用getServletContext方法返回ServletContext对象
1.基于Servlet,在项目中加入Apache的commons-fileupload文件上传组件的相关Jar包
文件上传:修改表单请求为enctype="multipart/form-data"方式,使用文件上传解析器获取上传的文件数据;
文件下载:通过输入流读取文件,通过响应获取输出流response.getOutputStream(),通过输出流将数据响应给浏览器;
需要通过设置不让客户端(浏览器)解析文件,而是以附件形式打开(下载):response.setHeader("Content-Disposition",
"attachment;filename="+需要下载的文件名称);
参考文档:https://www.cnblogs.com/xdp-gacl/p/4200090.html
2.基于Spring MVC,在项目中加入Apache的commons-fileupload文件上传组件的相关Jar包,配置文件上传解析器
文件上传:修改表单请求为enctype="multipart/form-data"方式,使用MultipartFile类型接收数据并获取输入流,再写入到对应地方完成文件上传;
文件下载:通过输入流读取文件,通过响应获取输出流response.getOutputStream(),通过输出流将数据响应给浏览器;
通知浏览器以attachment(下载方式)下载文件:headers.setContentDispositionFormData("attachment",
downloadFielName);
设置响应类型为application/octet-stream : 二进制流数据(最常见的文件下载)。headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
1. 常见用法:get一般用于从服务器上获取数据,post一般用于向服务器传送数据;
2. 表现形式:请求的时候参数的位置有区别,get的参数是拼接在url后面,用户在浏览器地址栏可以看到。post是放在http包的包体中;
3. 安全性:get请求的参数直接拼接在url后面,post发送的数据对客户端是不可见的,get较post安全性低;
4. 数据大小:能提交的数据有区别,get方式能提交的数据只能是文本,且大小有限制,而post不仅可以提交文本还可以二进制文件,理论上大小是没有限制的,起限制作用的是服务器的处理程序的处理能力
1.从地址栏表现看:
forward是服务器内部的重定向,服务器直接访问目标地址的 url网址,把里面的东西读取出来,但是客户端并不知道,因此用forward的话,客户端浏览器的网址是不会发生变化的;
redirect是客户端的重定向,是完全的跳转。即服务器返回的一个url给客户端浏览器,然后客户端浏览器会重新发送一次请求,到新的url里面,因此浏览器中显示的url网址会发生变化;
2.从request数据共享角度看:
由于在整个定向的过程中用的是同一个request,因此forward会将request的信息带到被重定向的jsp或者servlet中使用;
redirect是俩次请求,俩个request,数据不能共享
3.从应用层面上看(扩展)
forward一般用于类似用户登陆的时候,根据角色转发到相应的模块等业务;
redirect一般用于用户注销登陆时返回主页面和跳转到其它的网站等
1. 是什么:
json一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性。可在不同平台之间进行数据交换。
xml是扩展标记语言 (Extensible
Markup Language, XML) ,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言
2. 区别:
①可读性:xml有很多标记性字符,json只是一个有用的数据块而缺少一些标记字符,xml的可读性更高;
②数据传输:JSON相对于XML来讲,数据的体积小,传递的速度更快些;
③数据描述:XML的语法格式统一,JSON对数据的描述性比XML较差
3. 适用场景:
json的体积小,较少包含除有用数据外的数据,比较适合用户数据的传输:如JavaScript与后台的数据交互,各个系统之间数据传输等;
xml的格式统一,并可以为xml文件提供约束文件,对xml数据进行预定义,适合用于标记文档,在配置文件、webService(了解)等方面具有优势;
json对象:{"key":"value","key":"value",....}
JSON数组:[ obj , obj , obj , ....]
基于TCP的应用层协议,它不关心数据传输的细节,主要是用来规定客户端和服务端的数据传输格式,最初是用来向客户端传输HTML页面的内容
HTTPS(全称:Hyper Text
Transfer Protocol over Secure Socket Layer 或 Hypertext Transfer Protocol Secure,超文本传输安全协议),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。
Https工作原理:
客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤,如图所示。
(1)客户使用https的URL访问Web服务器,要求与Web服务器建立SSL连接。
(2)Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
(3)客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
(4)客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
(5)Web服务器利用自己的私钥解密出会话密钥。
(6)Web服务器利用会话密钥加密与客户端之间的通信。
常见的加密:RC5、RC4、SHA(信息摘要算法,同MD5一样属于不可逆算法)
HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。https://www.cnblogs.com/wqhwe/p/5407468.html
HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。
HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。
(1)对数据进行加密,并建立一个信息安全通道,来保证传输过程中的数据安全;
(2)对网站服务器进行真实身份认证。
HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。
区别:
1、https是加密传输协议,http是名文传输协议;
2、https需要到ca申请证书且需要一定费用,而http不用;
3、https标准端口443,http标准端口80(完全不同的连接方式);
4、https协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全,而http的连接很简单,是无状态的;
5、https基于传输层,http基于应用层;
6、https比http更加安全,对搜索引擎更友好;
7、https在浏览器显示绿色安全锁,http没有显示;
链接:https://www.cnblogs.com/wudaoyongchang/p/6253451.html
短链接:客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。
长连接:用以保持连接特性。 长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。因为用短连接频繁的通信会造成socket错误 WEB网站的http服务一般都用短链接。因为长连接对于服务端来说会耗费一定的资源
每一条css样式定义由两部分组成,例: [code] 选择器{样式} [/code] 在{}之前的部分就是“选择器”。
“选择器”指明了{}中的“样式”的作用对象,也就是“样式”作用于网页中的哪些元素。
主要的Css选择器:
标签选择器
类选择器
ID选择器
全局选择器
组合选择器
字符串匹配的属性选择符
在日常开发中最常用的就是标签选择器、类选择器和id选择器。
标签名选择器:可以为整个XHTML文档中的同一个标签指定相同的CSS样式。
类选择器:可以给相同的标签赋予不同的CSS样式。
id选择器:id选择器是唯一的,只可以获取独一无二的一个元素。
三者都是删除的意思,区别
1、drop (删除表):删除表结构 (删除内容和定义,释放空间。简单来说就是把整个表去掉.以后要新增数据是不可能的,除非新增一个表)。
2、truncate (清空表中的数据):清空表中所有的数据,速度快,不可回滚;实质是删除整张表包括数据再重新创建表,使自动增长列重置;
3、delete (删除表中的数据):逐行删除数据,每步删除都是有日志记录的,可以回滚数据;实质是逐行删除表中的数据,但自动增长列没有重置
4、执行速度上,一般来说: drop> truncate
> delete。
5、保留表而想删除所有的数据时用truncate。
6 想删除部分数据时, delete 删除时要带上where语句,回滚段要足够大
7、应用范围上,truncate 只能对table;delete 可以是table和view
(什么是范式:范式是符合某一种级别的关系模式的集合。关系数据库中的关系必须满足一定的要求,满足不同程度要求的为不同范式。)
1、第一范式(1NF):数据表中的每一列(每个字段)必须是不可拆分的最小单元,也就是确保每一列的原子性(原子性:指事务包含的所有操作要么全部成功,要么全部失败回滚)
2、第二范式(2NF):满足1NF后,要求表中的所有列,都必须依赖于主键,而不能有任何一列与主键没有关系,也就是说一个表只描述一件事情;
3、 第三范式(3NF):必须先满足第二范式(2NF),要求:表中的每一列只与主键直接相关而不是间接相关,(表中的每一列只能依赖于主键);
第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、第四范式(4NF)、第五范式(5NF)和第六范式(6NF)
1. 主键是能确定一条记录的唯一标识,也是作为一个可以被外键有效引用的对象(比如,一条记录包括身份正号,姓名,年龄)
2. 外键用于与另一张表的关联。是能确定另一张表记录的字段,用于保持数据的一致性
1.建索引 (必要时建立多级索引,分析MySQL的执行计划,通过表数据统计等方式协助数据库走正确的查询方式)
2.减少表之间的关联
3.优化sql,尽量让sql很快定位数据,不要让sql做全表查询,应该走索引,把数据 量大的表排在前面
4.简化查询字段,没用的字段不要,已经对返回结果的控制,尽量返回少量数据
5.尽量用PreparedStatement来查询,不要用Statement
6. 从磁盘上做文章,数据存放的在磁盘的内、外磁道上,数据获取的效率都是不一样的;
7、数据库表的大字段剥离(假如一个表的字段数有100多个,学会拆分字段,保证单条记录的数据量很小)
都是将两个结果集合并为一个
1)union在进行表链接后会筛选掉重复的记录;union all不会去掉重复记录
2)union将会按照字段的顺序进行排序;union all只是简单的将两个结果集合并后就返回
3)union all比union快很多
order by:
用来对数据库的一组数据进行排序
desc:降序
asc:升序
group
by:
“By”指定的规则对数据进行分组,所谓的分组就是将一个“数据集”划分成若干个“小区域”,然后针对若干个“小区域”进行数据处理。
1)根据(by)一定的规则对数据进行分组(Group)
2)就是将一个“数据集”划分成若干个“小区域”,然后针对若干个“小区域”进行数据处理。
1)在SELECT 列表中所有未包含在聚合函数中的列都应该包含在 GROUP BY 子句中,除非对这一列使用了聚合函数(avg/sum/count..)如:有部门表和员工表,员工表根据部门id进行分组查询,那么select 不能跟*,应该跟部门id或者聚合函数
2)不能Group By非标量基元类型的列,如不能Group By text,image或bit类型的列;
3)进行分组前可以使用Where子句消除不满足条件的行;
4)使用Group By子句返回的组没有特定的顺序,可以使用Order By子句指定次序。
1) distinct是将所有查询的字段进行对比去重,所有字段都完全相同才会去重 distinct 必须放在查询字段开头进行查询
2) group by 根据字段进行去重,字段相同就会去重 至于那个效率更高 这个我没有研究过
1)使用decode函数
2)使用case when语句
PL/SQL是一种程序语言,叫做过程化SQL语言(Procedural Language/SQL)。PL/SQL是Oracle数据库对SQL语句的扩展。在普通SQL语句的使用上增加了编程语言的特点,所以PL/SQL把数据操作和查询语句组织在PL/SQL代码的过程性单元中,通过逻辑判断、循环等操作实现复杂的功能或者计算。PL/SQL 只有 Oracle 数据库有。 MySQL 目前不支持 PL/SQL 的。
1)使用distinct关键字
2)借助group by也可以进去去重:GROUP BY [去除重复的字段名列表,....]
①资源重用
由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,增进了系统环境的平稳性(减少内存碎片以级数据库临时进程、线程的数量)
②更快的系统响应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池内备用。此时连接池的初始化操作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
③新的资源分配手段
对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接技术。
④统一的连接管理,避免数据库连接泄露
在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用的连接,从而避免了常规数据库连接操作中可能出现的资源泄露
关系型数据库mysql
1、关系型数据库,是指采用了关系模型来组织数据的数据库,
2.关系型数据库的最大特点就是事务的一致性;
非关系型数据库 特性
1、使用键值对存储数据;
2、分布式;
优点
无需经过sql层的解析,读写性能很高
基于键值对,数据没有耦合性,容易扩展
存储数据的格式:nosql的存储格式是key,value形式
缺点
不支持事务
不提供sql支持
1)内连接:显示表之间连接匹配的所有行
2)左连接(左外连接):以左表作为基准进行查询,左表数据会全部显示出来,条件成立,右表数据显示;条件不成立,显示null
select
* from 左表
left
outer join 右表 on 条件
3)右连接(右外连接):以右表作为基准进行查询,右表数据会全部显示出来,条件成立,左表数据显示;条件不成立,显示null
在判定左表和右表时,要根据表名出现在Outer Join的左右位置关系。
1.交叉连接:没有where条件的交叉连接将产生连接表所涉及的笛卡尔积
2.左右外连接:左连接以左表为基表 右表为从表 基表全部展示 从表数据对应匹配条件进行展示
3.内连接:是比较运算符比较要连接的列的值的连接 不匹配的行不会被显示
4.全外连接:左表记录全部展示,右表没有则显示为null;右表记录全部展示,左表没有则显示为null
如果两张表存在主外键关系,那么在删除主键表的记录时,如果从表有相关联的记录,那么将导致删除失败。
在定义外键约束时,可以同时指定3种删除策略:一是将从表记录一并删除(级联删除);二是将从表记录外键字段设置为NULL;三是将从表记录外键字段设置为默认值。
级联删除示例:
alter
table 从表名
add
constraint 外键名
foreign
key(字段名) references 主表名(字段名)
on
delete cascade
NOT NULL: 用于控制字段的内容一定不能为空(NULL)。
UNIQUE:
控件字段内容不能重复,一个表允许有多个 Unique 约束。
PRIMARY
KEY: 也是用于控件字段内容不能重复,但它在一个表只允许出现一个。
FOREIGN
KEY: 用于预防破坏表之间连接的动作,也能防止非法数据插入外键列,因为它必须是它指向的那个表中的值之一。
CHECK: 用于控制字段的值范围。
1. 主键约束(PRIMARY KEY):在主键上使用
2. 外键约束(FOREIGN KEY):建立俩个表之间的关系时使用
3. 唯一约束(UNIQUE):保证数据表中某个字段数据的唯一性时使用
4. 非空约束(NOT NULL):需要保证数据库字段值不为空时使用
5. 自动增长列:数据值自增,需要和用在主键列上
1)、表的主键、外键必须有索引;
2)、数据量超过300的表应该有索引;
3)、经常与其他表进行连接的表,在连接字段上应该建立索引;
4)、经常出现在Where子句中的字段,特别是大表的字段,应该建立索引;
5)、索引应该建在选择性高的字段上;
6)、索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引;
7)、复合索引的建立需要进行仔细分析;尽量考虑用单字段索引代替:
A、正确选择复合索引中的主列字段,一般是选择性较好的字段;
B、复合索引的几个字段是否经常同时以AND方式出现在Where子句中?单字段查询是否极少甚至没有?如果是,则可以建立复合索引;否则考虑单字段索引;
C、如果复合索引中包含的字段经常单独出现在Where子句中,则分解为多个单字段索引;
E、如果既有单字段索引,又有这几个字段上的复合索引,一般可以删除复合索引;
8)、频繁进行数据操作的表,不要建立太多的索引;
9)、删除无用的索引,避免对执行计划造成负面影响;
https://www.cnblogs.com/tangchuanyang/p/6013151.html
1) concat(string1,string2,...)
string1,string2代表字符串,concat函数在连接字符串的时候,只要其中一个是null,那么就返回null
2)concat_ws(separator,str1,str2,...)
str1,str2代表字符串,concat_ws代表concat with separator,第一个参数是其它参数的分隔符。分隔符的位置放在要连接的两个字符串之间。分隔符可以是一个字符串,也可以是其它参数。如果分隔符为 NULL,则结果为 NULL。函数会忽略任何分隔符参数后的 NULL 值。
1)having关键字通常是与group by关键字联合使用
2)用于过滤group by语句返回的结果集
3)having关键字弥补了where关键字不能与聚合函数联合使用的不足
1)就是一种虚拟的表 ,是一个逻辑表 ,本身不包含数据,作为一个select语句存在数据字典中的
2)通过视图 ,可以展示基表的部分数据; 视图数据来自定义视图的查询中使用的表
3)基表: 用来创建视图的表叫做基表 base table
1)视图是一种数据库对象
2)是基于 SQL 语句的结果集的可视化的表
3)方便操作数据库的
联系:
视图(view)是在基本表之上建立的表,它的结构(即所定义的列)和内容(即所有数据行)都来自基本表,它依据基本表存在而存在。一个视图可以对应一个基本表,也可以对应多个基本表。视图是基本表的抽象和在逻辑意义上建立的新关系。
区别:
1、视图是已经编译好的sql语句;而表不是。
2、视图没有实际的物理记录;而表有。
3、视图是窗口;表是内容
4、标致用物理空间而视图不占用物理空间,视图只是逻辑概念的存在;表可以即使对它修改,但视图只能有创建的语句来修改。
5、表是内模式,视图是外模式。
6、视图是查看数据表的一种方法,可以查询数据表中某些字段构成的数据,只是一些sql语句的集合。从安全的角度来说,视图可以不给用户接触数据表,从而不知道表结构。
7、表属于全局模式中的表,是实表;视图属于局部模式的表,是虚表。
8、视图的建立和删除只影响视图本身,不影响对应的基本表。
---------------------------------------------------------------------
1. 视图 : 就是一种虚拟的表 ,是一个逻辑表 ,本身不包含数据,作为一个select语句存在数据字典中的
2. 通过视图 ,可以展示基表的部分数据; 视图数据来自定义视图的查询中使用的表
3. 基表: 用来创建视图的表叫做基表 base table
1. 表中保存的是数据 而视图中保存的是一个或者多个条件组合而成的结果集
2. 表是物理存在的 视图是虚拟的内存表
事务是一组业务逻辑,要么全部成功,要么全部失败,一种技术(机制)。
例如,银行转账工作:从一个账号扣款并使另一个账号增款,这两个操作要么都执行,要么都不执行
1)开启事务:程序开始时,必须先开启事务
2)提交事务:程序正常执行完成后,必须提交事务
3)回滚事务:程序发生异常后,必须回滚事务
1)原子性:整个事务是一个整体,不可分割。
2)一致性:事务操作的前后数据保持一致(数据完整性)。
3)隔离性:两个事务同时操作并发访问问题
4)持久性:事务一旦提交,将不能再回滚。
1)脏读:一个事务读到了另一个事务没有提交的数据。
2)不可重复读:一个事务读到了另一个事务已经提交的数据。(要求的是一个事务中多次读取时数据是一致的,这是事务update时引发的问题)
3)虚读(幻读):一个事务读到了另一个事务已经提交的数据。(要求在一个事务多次读取的数据的数量是一致的,这是insert或delete时引发的问题)
1)读未提交:read uncommitted,一个事务读到了另一个事务没有提交的数据。存在3个问题。
2)读已提交:read committed,一个事务读到另一个事务已经提交的数据。解决:脏读;存在2个问题。
3)可重复读:repeatable read,在一个事务中,读到的数据一致。解决:脏读、不可重复读;存在1个问题。
4)串行化:serializable,一个事务在操作时,另一个事务必须等待。(单事务)。解决所有问题。
事务隔离级别 脏读 不可重复读
幻读
读未提交(read-uncommitted) 是 是 是
读已提交(read-committed) 否 是 是
可重复读(repeatable-read) 否 否 是
串行化(serializable) 否 否 否
MYSQL 事务处理主要有两种方法:
1、用 BEGIN, ROLLBACK, COMMIT来实现
BEGIN
开始一个事务
ROLLBACK
事务回滚
COMMIT
事务确认
2、直接用 SET 来改变 MySQL 的自动提交模式:
SET
AUTOCOMMIT=0 禁止自动提交
SET
AUTOCOMMIT=1 开启自动提交
在事务的处理过程中,如果发生错误并且使用rollback进行回滚,那么整个事务对数据库的操作都将被撤销,在一个庞大的事务中,这是很浪费资源的,比如事务前半部分是正确的,事务的后半部分是错误的,那么我想回滚到前半部分结束位置,这时就可以使用保存点。
事务的回滚点有什么作用?类似于单机游戏的存档和读档:
1、如果没有游戏的存档功能,每次玩单机游戏都会从第一关重新开始。
2、如果使用了游戏的存档功能,下次在玩游戏时,就会从存档处开始。关系型数据库中不存在直接多对多关系,采用将一个多对多拆分成2个一对多。
(多对多其实就是:一对多 和 多对一 的一个组合。)
1) 两个表如果是多对多关系,已经存在2个主表。
2) 提供一个中间表
关系型数据库中不存在直接多对多关系,采用将一个多对多拆分成2个一对多。
(多对多其实就是:一对多 和 多对一 的一个组合。)
1) 两个表如果是多对多关系,已经存在2个主表。
2) 提供一个中间表
1)Sum()函数里面的参数是列名的时候,是计算列名的值的相加,而不是有值项的总数
2)count()函数里面的参数是列名的的时候,那么会计算有值项的次数.(NULL 不计入, 但是‘‘值计入)
记录的列值为NULL时,COUNT(列名)和SUM(列名)都不计 这条记录。
count(*)和count(1)无任何差别,永远优于其他count,无论加不加任何索引
1)加索引显著快于不加索引
2)主键索引快于普通索引
3)没有索引时,与列含不含空值无关
一样
1)avg():返回指定组中的平均值,空值被忽略;
2)count():返回指定组中的项目个数
3)max():返回指定数据中的最大值;
4)min():返回指定数据中的最小值;
5)sum():返回指定数据的和,只能用于数字列,空值忽略;
什么是存储过程:存储过程可以说是一个记录集吧,它是由一些T-SQL语句组成的代码块,这些T-SQL语句代码像一个方法一样实现一些功能(对单表或多表的增删改查),然后再给这个代码块取一个名字,在用到这个功能的时候调用他就行了
小结:总之存储过程是好东西,在做项目时属于必备利器,下面介绍存储过程的基本语法。
①重复使用。存储过程可以重复使用,从而可以减少数据库开发人员的工作量。
②提高性能。存储过程在创建的时候在进行了编译,将来使用的时候不再重新翻译。一般的SQL语句每执行一次就需要编译一次,所以使用存储过程提高了效率。
③减少网络流量。存储过程位于服务器上,调用的时候只需要传递存储过程的名称以及参数就可以了,因此降低了网络传输的数据量。
④安全性。参数化的存储过程可以防止SQL注入式攻击,而且可以将Grant、Deny以及Revoke权限应用于存储过程。
缺点
1:调试麻烦,但是用PL/SQL Developer 调试很方便!弥补这个缺点。
2:移植问题,数据库端代码当然是与数据库相关的。但是如果是做工程型项目,基本不存在移植问题。
3:重新编译问题,因为后端代码是运行前编译的,如果带有引用关系的对象发生改变时,受影响的存储过程、包将需要重新编译(不过也可以设置成运行时刻自动编译)。
4:如果在一个程序系统中大量的使用存储过程,到程序交付使用的时候随着用户需求的增加会导致数据结构的变化,接着就是系统的相关问题了,最后如果用户想维护该系统可以说是很难很难、而且代价是空前的,维护起来更麻烦
总结:
1. 适当的使用存储过程,能够提高我们SQL查询的性能,
2. 存储过程不应该大规模使用,滥用。
3. 随着众多ORM 的出现,存储过程很多优势已经不明显。
4. SQL最大的缺点还是SQL语言本身的局限性——SQL本身是一种结构化查询语言,我们不应该用存储过程处理复杂的业务逻辑——让SQL回归它“结构化查询语言”的功用。复杂的业务逻辑,还是交给代码去处理吧。
1)超键:在关系模式中,能唯一标识元组的属性集称为关系模式的超键。
2)候选键:如果一个属性集能唯一标识元组,且有不包含多余属性,那么这个属性集称为候选键;也即:不含有多余属性的超键称为候选键。
3)主键:关系模式中用户正在使用的候选键称主键(primary key)
4)外键:在关系模式R中,如果某属性集是其他模式的候选键,那么该属性集对模式R来说就是外键。
主要:可能存在完全相同的数据,无法区分
如果设置优势:有助于提高数据的检索速度
1)主键是能确定一条记录的唯一标识,也是作为一个可以被外键有效引用的对象,不能有重复的,不允许为空
2)外键用于与另一张表的关联.是确定另一张表记录的字段,用于保持数据的一致性,可以有重复的, 可以是空值
1)可以直观的操作数据 查看数据,
2)可以借助图形化界面轻松完成对数据库的各种操作
1)使用索引
2)使用连接(JOIN)来代替子查询(Sub-Queries)
3)选取最适用的字段属性
4)优化的查询语句
5)使用联合(UNION)来代替手动创建的临时表
1)建索引
2)减少表之间的关联
3)优化SQL语句,尽量让SQL很快定位数据,不让SQL做全表查询,应走索引,把数据量大的表排在前面
4)简化查询字段,只返回必要的字段
5)尽量用PreparedStatement查询,不要用Statement
6)从磁盘上做文章,数据存放在磁盘的内,外磁道上,数据获取的效率是不同的
7)数据库表的大字段剥离(拆分字段)
1).Sql Server 数据库
select * into 新表名 from 原表名 Where [条件1,条件2];
2).Oracle
数据库
--创建表结构
create table 新表名 as select * from 原表名 where 1=2;
--备份指定数据
insert into 新表名 select * from 原表名 Where [条件1,条件2]
使用DATE_FORMAT(date,format)
函数
1)date需要格式化字段名
2)format格式
select
DATE_FORMAT( updated_at, ‘%Y-%m-%d‘ ) from tb_sku;
delimiter 自定义符号
create
function 函数名(形参列表) returns 返回类型
begin
函数体
set
@x = 1;
return
end
自定义符号
delimiter;
delimiter
$$
create
function myfun3(ia int, ib int) returns int
begin
return ia + ib;
end
$$
delimiter;
char是一种固定长度的字符串类型,varchar是一种可变长度的字符串类型;
适用场景:
char一般用来存储长度固定字段,如:手机号,身份证号等
varchar一般用来存储不固定长度的字段:如:用户名,昵称等
概念:
数据库连接池是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的连接进行申请,使用,释放。
作用:
1.资源重用,避免了数据库连接频繁建立、关闭的开销
2.更快的系统响应速度,直接从连接池中获取连接,响应速度加快
3.控制资源的使用。如果不使用连接池,每次访问数据库都需要创建一个连接,这样系统的稳定性受系统连接需求影响很大,很容易产生资源浪费和高负载异常。连接池能够使性能最大化,将资源利用控制在一定的水平之下。连接池能控制池中的连接数量,增强了系统在大量用户应用时的稳定性。
常用的数据库连接池:DBCP、C3P0、Druid
池化技术(扩展):
简单点来说,就是提前保存大量的资源,以备不时之需。
对于线程,内存,数据库的连接对象等等,这些都是资源,程序中当创建一个线程或者在堆上申请一块内存时,都涉及到很多系统调用,也是非常消耗CPU的,如果程序需要很多类似的工作线程或者需要频繁的申请释放小块内存,如果没有在这方面进行优化,那很有可能这部分代码将会成为影响整个程序性能的瓶颈。
池化技术主要有线程池,内存池,数据库连接池,对象池等等,
对象池就是提前创建很多对象,将用过的对象保存起来,等下一次需要这种对象的时候,再拿出来使用,使用完成之后再交还保存,使得可以被重复使用
索引是一种独立于表的数据库对象,可以存储在于表不同的磁盘中间或表空间;索引相当于字典的目录,作用在于提升查询效率,降低磁盘读写,提升数据库性能。
1. 直接创建索引和间接创建索引:
自动创建--------即在创建主键 primarykey 或唯一性约束 unique 的时候,数据库会自动在相应的列上创建唯一性索引
手动创建--------即在不唯一的列上创建非唯一的索引,加速查询效率
2. 普通索引和唯一性索引
3. 单一索引和复合索引
参考:https://blog.csdn.net/csdn265/article/details/51889508
单一索引:即在单列上建立索引
复合索引:又叫组合索引,在索引建立语句中同时包含多列
4. 聚簇索引和非聚簇索引
参考:https://blog.csdn.net/zc474235918/article/details/50580639
索引的优点:
1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性;
2. 可以大大加快数据的检索速度,这也是创建索引的最主要的原因;
索引的缺点:
1. 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加;
2. 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大;
3. 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度
MySQL 的插件式存储引擎主要包括MyISAM,Innodb,NDB Cluster,Maria,Falcon,Memory,Archive,Merge,Federated 等,其中最著名而且使用最为广泛的MyISAM 和Innodb两种存储引擎。
MyISAM拥有较高的插入、查询速度,但不支持事物;
InnoDB是事务型数据库的首选引擎,支持事务安全表(ACID),支持行锁定和外键,上图也看到了,InnoDB是默认的MySQL引擎。
两种类型最主要的差别就是Innodb 支持事务处理与外键和行级锁。
https://blog.csdn.net/zhangyuan19880606/article/details/51217952
https://blog.csdn.net/xifeijian/article/details/20316775
MySQL默认使用InnoDB存储引擎。
InnoDB是事务型数据库的首选引擎,支持事务安全表(ACID),
其它存储引擎都是非事务安全表,支持行锁定和外键,
InnoDB主要特性
为MySQL提供了具有提交、回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎。
InnoDB锁定在行级并且也在 SELECT语句中提供一个类似Oracle的非锁定读。这些功能增加了多用户部署和性能。
在SQL查询中,可以自由地将InnoDB类型的表和其他MySQL的表类型混合起来,甚至在同一个查询中也可以混合。
Redis 可以存储键与5种不同数据结构类型之间的映射,这5种数据结构类型分别为String(字符串)、List(列表)、Set(集合)、Hash(散列)和 Zset(有序集合)。
mybatis是一个持久层ORM框架。它内部封装了jdbc,使得开发更简洁,更高效。
Mybatis使开发者只需要关注sql语句本身,简化JDBC操作,不需要在关注加载驱动、创建连接、处理SQL语句等繁杂的过程。
MyBatis可以通过xml或注解完成ORM映射关系配置。
JDBC是Java提供的一个操作数据库的API; MyBatis是一个持久层ORM框架,底层是对JDBC的封装。
MyBatis对JDBC操作数据库做了一系列的优化:
(1) mybatis使用已有的连接池管理,避免浪费资源,提高程序可靠性。
(2) mybatis提供插件自动生成DAO层代码,提高编码效率和准确性。
(3)mybatis 提供了一级和二级缓存,提高了程序性能。
(4) mybatis使用动态SQL语句,提高了SQL维护。(此优势是基于XML配置)
(5) mybatis对数据库操作结果进行自动映射
ORM的全称是Object Relational
Mapping,即对象关系映射。
描述的是对象和表之间的映射。操作Java对象,通过映射关系,就可以自动操作数据库。
在ORM关系中,数据库表对应Java中的类,一条记录对应一个对象,一个属性对应一个列。
常见的ORM框架:Mybatis、Hibernate
注解、XML
1.#{}实现的是sql语句的预处理参数,之后再sql中用?号代替,使用时不需要关注数据类型,Mybatis自动实现数据类型的转换,并且可以防止SQL注入;
2.${}实现的是sql语句的直接拼接,不做数据类型转换,需要自行判断数据类型,不能防止SQL注入;
3.在某些特定情况下必须使用${},如:在分表存储的情况下,对哪张表的查询是不确定的,也就是sql语句不能写死,表明是动态的,查询条件是固定的,此时表明只能使用${}方式进行字符串拼接,这样:slect * from
${tableName} where id = #{id}
总结:#{}占位符,用于参数传递; ${}用于SQL拼接
1. 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致
2.使用通用Mapper时,可以通过@Column注解设置
3.如果进行自定义查询,可以通过@Result进行设置
【了解】4.如果使用的xml配置,通过<resultMap>进行配置
1.使用通用Mapper的Criteria进行like语句的拼凑
2.使用#{}占位符方式,参数前后拼凑%。(select * from t_user where username like #{username} ,username数据为"%jack%")
【了解】3.使用${}字符串拼接方式,可能引发sql注入问题:select * from
t_user where username like ‘%${username}%‘
Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行接口方法所对应的MappedStatement所代表的sql,然后将sql执行结果返回。
MappedStatement解释:MappedStatement维护了一条<select|update|delete|insert>节点的封装,包括了传入参数映射配置、执行的SQL语句、结果映射配置等信息。如以下一个<select/>节点
<select
id="selectAuthorLinkedHashMap"
resultType="java.util.LinkedHashMap">
select id, username from author where
id = #{value}
</select>
Dao接口在使用时,MyBatis创建接口对应的代理类。
在调用对应的方法时,执行的是代理类对应的方法。
代理类的方法进行方法上添加的注解完成增删改查对应的操作,如果是查询将结果封装到方法返回值类型声明的对象中。
不能重载的,
因为是全限定名+方法名的必须唯一。
使用第三方分页助手:PageHelper
使用方式:
在将要执行查询sql语句之前使用分页助手:PageHelper.startPage(pageNum:页码, pageSize:每页显示数量);
使用MyBatis插件(也称为拦截器)机制,对需要使用分页的功能进行增强。也就是重写SQL,根据不同的数据库生产不同的分页语句。
Mysql生成limit语句,Oracle借助rownum生产对应子查询语句。
提供POJO和表之间的映射关系,查询结果就可以完成封装。
1.使用通用Mapper,需要在POJO上字段上使用@Column注解
2.如果是自定义查询,需要通过Dao接口里使用@Result注解
3.如果是xml,需要使用<resultMap>配置映射关系
动态SQL标签主要是基于XML进行配置的,在校主要学习的是注解,没有使用过XML。
不过注解也支持部分动态标签,@Select("<script>
select * from user where 1=1 <if
test=‘username != null ‘> and username = #{username} </if> </script>")
不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复。
MyBatis通过namespace+id来进行不同XML标识。
Hibernate是全自动的ORM框架,也就是使用hibernate不用编写任何SQL语句。关联对象直接调用对应方法,可以自动完成数据的查询。
MyBatis封装了JDBC基本操作,但仍需要编写SQL语句,也称为半自动ORM框架。使用通过Mapper可以简化MyBatis单表操作,多表仍需要自己编写SQL语句。
namespace用于标识不同的XML配置文件。这样不同xml文件中sql语句的id相同也不会有冲突。
注解开发没有类似要求,只要保证接口全限定名不同即可(包+类名)
在xml配置文件,根据数据的不同,动态拼凑对应的SQL语句。
例如:
<if>用于处理条件成立时显示部分SQL语句
<where>
如果有条件就显示 where 子句,如果没有条件则不显示。
Mybatis的缓存机制分为一级缓存和二级缓存
1.一级缓存:一级缓存的作用域是sqlSession会话级,相当于JDBC的Connection连接。一级缓存默认开启,用户不能手动配置。当在同一个sqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率;
2.二级缓存:二级缓存的作用域为同一个mapper的namespace,是跨sqlSession的,同一个namespace中不同的查询SQL可以从二级缓存中命中。二级缓存默认关闭,但是可以通过配置进行开启。
二级缓存的对象必须序列化,例如:User对象必须实现Serializable接口。
Mybatis中,执行select时,查询数据会被缓存;当执行insert、update、delete等操作的时候,缓存则会被清除
Spring是一个分层的JavaSE/EEfull-stack(一站式)轻量级开源框架。
Spring为不同的层都提供的企业级解决方案:
web层:spring mvc
service层:spring
dao层:JDBCTemplate
Spring的核心思想是IOC(控制反转)和AOP(面向切面编程)
1.方便解耦,简化开发 (高内聚低耦合)
Spring就是一个大工厂(容器),用于创建对象(bean)和维护对象间的依赖关系。
2.AOP编程的支持
Spring提供面向切面编程,方便实现对程序进行权限拦截、运行监控等功能.
声明式事务的支持 ,只需要通过配置就可以完成对事务的管理,而无需手动编程
3.方便程序的测试
Spring对Junit4支持,可以通过注解的测试Spring程序
4.方便集成各种优秀框架
Spring支持各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)
5.降低JavaEE API的使用难度
Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail等),都提供了封装,降低这些API应用难度
1.Spring Core:Spring框架的核心容器,提供了Spring框架的基本功能。这个模块中最主要的一个组件为BeanFactory,它使用工厂模式来创建所需的对象。同时BeanFactory使用IOC思想,通过读取XML文件的方式来实例化对象,即BeanFactory提供了组件生命周期的管理,组件的创建,装配以及销毁等功能;
2.Spring
AOP:采用了面向切面编程的思想,使Spring框架管理的对象支持AOP,并也提供事务管理,可以不依赖具体的EJB组件,就可以将事务管理集成到应用程序中;
3.Spring
Web:提供Servlet监听器的Context和Web应用的上下文。并集成一些现有的Web框架,例如Struts;
4.Spring
Context:扩展核心容器,提供Spring上下文环境,给开发人员提供很多有用的服务,例如国际化,Email和JNDI访问等;
5.Spring
Web MVC:提供一个构建Web应用程序的MVC的实现
6.Spring
ORM:提供对现有的ORM框架的支持,例如Hibernate等;
7.Spring
DAO:提供对DAO(Data Access Object,数据访问对象)模式和JDBC的支持。DAO可以实现将业务逻辑与数据库访问的代码分离,从而降低代码的耦合度。通过对JDBC的抽象,简化开发工作,并简化对异常的处理(可以很好的处理不同数据库厂商抛出的异常);
1.基于XML配置
在XML文件中通过<bean>元素定义Bean,如:<bean
class="com.czxy.UserService">
2.基于注解配置
在Bean实现类处通过标注@Component或衍型类(@Repository、@Service及@Controller)定义Bean
3.基于Java类配置
在标注了@Configuration的Java类中,通过在类方法上标注@Bean定义一个Bean。方法必须提供Bean的实例化逻辑
Spring Bean生命周期共分10个步骤,简述如下:
1.实例化:实例化一个Bean
2.注入:按照Spring上下文对实例化的Bean进行依赖注入
7.初始化:自动调用init-method初始化方法
//此处就是项目中使用的bean
10.销毁:当bean不再需要时,执行销毁方法destroy-method
专业解释,参考文档如下:
https://blog.csdn.net/fuzhongmin05/article/details/73389779
https://www.jianshu.com/p/3944792a5fff
1.singleton:单例,默认值,在Spring IOC容器只有一个Bean实例;
2.prototype:多例,每次从容器中获得Bean时,都返回一个新的实例;
3.request:一次请求,请求开始创建bean,请求结束销毁bean。只有在Web应用中使用Spring时,该作用域才有效
4.session:一次会话,同一个会话共享一个Bean实例,在session过期后,bean会随之失效。且只有在Web应用中使用Spring时,该作用域才有效
5.global-session:全局会话,在不同的portlet间共享session。(Portal是一个基于Web的应用)
线程安全产生的原因是,多个线程对共享数据同时进行修改。
该问题分以下俩种情况:
1.有问题:如果类中存在成员变量(即共享数据),且成员变量中的数据在方法中会被修改,此时可能会产生线程安全问题;
2.没有问题:如果Bean没有成员变量,或者成员变量数据在访问的时候不会被修改,则不会产生线程安全问题。
1.Spring组件扫描:
在spring的配置类中使用@ComponentScan(basePackages =
"com.czxy")可以扫描com.czxy包以及子包下添加的指定注解的类。
常见的注解有:@Component、@Service、@Controller、@Repository
2.SpringBoot扫描机制
1)默认扫描规则,SpringBoot中会自动扫描启动类所在包及其子包中的所有添加的指定注解类
2)使用@ComponentScan注解可以覆盖默认扫描规则。
自动装配,自动完成各个bean之间的依赖关系装配。
Spring提供了5个自动装配模式:
1.no:默认值,没有使用自动装配
2.byName
按照属性名自动装配。
3.byType
按照属性的数据类型自动装配。如果存在多个这样的bean,将抛出异常。
4.constructor
按照构造函数参数类型自动装配。
5.autodetect
自动。先constructor自动装配,如果不执行,再按照 byType 自动装配
1. 显示的依赖注入 @Resource 会重写自动装配
2. 不能自动装配基本数据类型、字符串、数组等
3. 自动装配不如显示依赖注入精确
1.
@Autowired : 默认是以byType按类型自动注入。
@Autowired + @Qualifier("名称"):将按照名称自动注入
2.
@Resource() :
1)如果没有指定name属性:
① 当注解写在字段上时,默认取字段名进行按照名称注入,
② 如果注解写在setter方法上默认取属性名进行注入。
2)当找不到与名称匹配的bean时才按照类型进行装配。
3)如果指定了name属性,就只会按照名称进行装配。 @Resource(name="") 将按照名称自动注入
1.在指定类中添加指定注解,配置类添加@ComponentScan注解进行包扫描,就可以完成bean的配置。
2.相关注解
@Component 通用组件类
@Controller 用于配置web层的controller类
@Service
用于配置service层的service类
@Repository 用于配置dao层dao类
@Bean 用于配置第三方实例,只能修饰方法。
1.通过ApplicationContext手动获取:手动加载xml或配置类获得上下文对象,通过getBean()获得指定名称的bean。
2.实现BeanFactoryAware接口:先获得BeanFactory,再getBean()获得指定名称的bean。
3.实现ApplicationContextAware 接口 :先获得ApplicationContext,再getBean()获得指定名称的bean。
https://www.aliyun.com/jiaocheng/812869.html
事务就是对一组逻辑进行操作,要么全部成功,要么全部失败
Spring通过平台事务管理器(PlatformTransactionManager)进行事务管理
事务管理器通过事务定义(TransactionDefinition)进行具体的事务操作。
事务定义通过4个方面对事务进行详细描述:
1.readOnly:是否只读
2.timeout:超时时间
3.isolationLevel:事务隔离级别,隔离级别4种:读未提交、读已提交、可重复读、串行化
4.propagationBehavior:传播行为,共7种,常见的有:必须有事务
PROPAGATION_REQUIRED、每次必须新的
PROPAGATION_REQUIRES_NEW
Spring事务管理高层抽象主要包括3个接口,Spring的事务主要是由他们共同完成的:
PlatformTransactionManager:事务管理器—主要用于平台相关事务的管理
TransactionDefinition: 事务定义信息(隔离、传播、超时、只读)—通过配置确定如何进行事务管理。
TransactionStatus:事务具体运行状态—事务管理过程中,每个时间点事务的状态信息。
有异常就会回滚
可以通过@Transactional(rollbackFor=Exception.class,notRollbackFor=RunTimeException.class)修改默认行为
rollbackFor用于设置那些异常将发生回滚
notRollbackFor用于设置那些异常不会发生回滚
DEFAULT(默认):使用后端数据库默认的隔离级别(spring中的的选择项)
READ_UNCOMMITTED(读未提交):允许读取还未提交的改变了的数据。问题: 脏、幻、不可重复读
READ_COMMITTED(读已提交):允许在并发事务已经提交后读取。解决: 脏读,问题: 幻读 , 不可重复读
REPEATABLE_READ(可重复读):对相同字段的多次读取是一致的,除非数据被事务本身改变。解决: 脏、不可重复读,问题: 幻读。
SERIALIZABLE(串行化):完全服从ACID的隔离级别,解决: 脏、幻、不可重复读。是隔离级别中最慢的,是典型的通过完全锁定在事务中涉及的数据表来完成的。
事务传播行:多个事务方法相互调用时,事务如何在这些方法间传播
作用:事务的传播行为就是一个个的事务策略,根据需求、设备等条件选择对应的策略,从而完成需求或优化程序。
1.只开启了一个事务,Spring默认的传播行为是PROPAGATION_REQUIRED,即如果当前存在事务,即加入该事务;如果当前没有事务,则新建一个新的事务。因此只开启了一个事务。
Spring事务的传播行为的7种类型
TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
IOC:Inversion of
Control,即“控制反转”,不是什么技术,而是一种设计思想。IOC意味着将你设计好的对象交给容器控制,而不是new。
IOC 控制反转,指将对象的创建权,反转到Spring容器
IOC容器是具有依赖注入功能的容器,负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。应用程序无需直接在代码中new相关的对象,应用程序由IOC容器进行组装。在Spring中BeanFactory是IOC容器的实际代表者。
1.减少代码的耦合,可以使应用更加模块化
2.增加代码的复用率
3.资源更加统一管理
4.维护代码更加方便,一般只需要修改一下配置文件就ok了。
5.提升了程序的测试性
链接:https://www.imooc.com/article/35099
DI:Dependency Injection 依赖注入,在Spring框架创建Bean对象时,动态的将依赖对象注入到Bean组件
1.实例化Bean对象:服务器启动时,初始化spring容器,创建由XML配置或添加了相应注解的类的实例,并将实例保存到Spring容器中;
2.完成ID操作:通过XML描述或对应注解,完成对象之间的依赖关系,此过程称为依赖注入,spring支持3种注入实现:
①注解注入:通过注解显示注入。常用注解@Resource。
②XML注入:通过xml显示注入。
③自动注入:隐式进行bean搜索并自动装配
IOC 控制反转,指将对象的创建权,反转到Spring容器 ,
DI 依赖注入,指Spring创建对象的过程中,将对象依赖属性通过配置进行注入
DI依赖注入不能单独存在,必须在IOC控制反转的基础上才能完成。
在web项目中配置Spring的IOC容器其实就是创建web应用的上下文(WebApplicationContext)
1.如果使用Spring Boot,默认扫描启动类所在包以及子包,启动完成上下文对象创建。
2.使用注解的普通web项目,需要在配置类中使用@ComponentScan确定扫描包位置,启动完成上下文对象创建。
3.使用xml的普通web项目,需要在web.xml配置初始化参数确定xml文件位置,从而确定加载的内容,启动完成上下文对象创建。
参考:
http://www.cnblogs.com/qf123/p/8488404.html
1.单例设计模式:
Spring中Bean默认是单例的,底层采用的是单例设计模式;
2.工厂模式:
BeanFactory,作用就是配置、新建、管理 各种Bean
3.代理模式:
代理模式的定义是为其它对象提供一种代理,以控制这个对象的访问;在Spring AOP中就使用了代理模式,使用代理模式完成在不修改代码的情况下,实现对功能的增强;
4.适配器模式:
将一个类的接口变换成客户所期待的另一种接口,从而使原本不匹配而无法一起工作的俩个类能够在一起工作;SpringMVC中的适配器HandlerAdatper,HandlerAdatper根据不同的handler规则执行不同的handler;
5.模板方法模式:
父类定义了骨架(调用哪些方法及顺序),某些特定方法由子类实现;在Spring的 JdbcTemplate,RestTemplate,
JmsTemplate等地方使用了模板方法模式;
链接:https://blog.csdn.net/z69183787/article/details/65628166;
AOP (Aspect Oriented Programing) 称为:面向切面编程,它是一种编程思想。在程序运行的时候,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
基于代理思想,对原来目标对象,创建代理对象,在不修改原对象代码情况下,对原有业务方法进行增强 !
代理模式:分为静态代理、JDK动态代理、cglib代理
1.静态代理
代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的引用,实际执行时通过调用代理类的方法、实际执行的是被代理类的方法。
2.jdk动态代理:必须有接口和实现类(目标类),通过工具类Proxy生产接口对应的代理类,从而对目标类进行增强。
3.Cglib代理:Cglib代理生成的代理类是目标类的子类,所以目标类不能使用final修饰。
AOP
Spring
AOP通过代理的方式,对目标类进行增强。
Spring
AOP底层默认使用JDK动态代理,通过配置可以修改成cglib代理。
描述切面必须先了解以下几个概念:
1.目标类:需要被增强的类。
2.连接点:可能被增强的点,目标类中的所有方法。
3.切入点:将会被增强的连接点,目标类中被增强的方法。
4.通知/增强:对切入点增强的内容。增强的内容通常以方法的形式体现的。增强执行的位置不同,称呼不同。
(前置通知、后置通知、环绕通知、抛出异常通知、最终通知)
通知方法所在的类,通常称为切面类。
5.切面:通知和切入点的结合。一个通知对应一个切入点就形成一条线,多个通知对应多个切入点形成多条线,多条线形成了一个面,称为切面。
通知:对切入点增强的内容。增强的内容通常以方法的形式体现的。
在spring中根据增强执行位置的不同,将通知分成以下5种:
1.前置通知(before):在一个方法执行前被调用。
2.后置通知(after-returning):仅当方法成功完成后执行。
3.环绕通知(around):在方法执行之前和之后调用。
4.抛出异常通知(after-throwing):在方法抛出异常退出时执行。
5.最终通知(after): 在方法执行之后调用,无论方法执行是否成功。
实现细节,方便理解
try{
//1 前置通知
// 目标方法
//2 后置通知
}
catch(){
//4 抛出异常通知
}
finally{
//5 最终通知
}
在说切入点前,需要先说连接点
连接点:可能被增强的点,目标类中的所有方法。
切入点:将会被增强的连接点,目标类中被增强的方法。
目标类:需要被增强的类。
AOP通过生成代理对象,对目标类进行增强。
在spring AOP中,代理对象,将通知和切入点连接起来,从而实现增强的过程。
使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。
业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。
横切关注点的一个特点是,经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。
AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
参考:https://www.cnblogs.com/hongwz/p/5764917.html
1.目标类(target):需要被增强的类。
2.连接点(Joinpoint):可能被增强的点,目标类中的所有方法。
3.切入点(Pointcut):将会被增强的连接点,目标类中被增强的方法。
4.通知/增强(Advice):对切入点增强的内容。增强的内容通常以方法的形式体现的。增强执行的位置不同,称呼不同。
(前置通知、后置通知、环绕通知、抛出异常通知、最终通知)
通知方法所在的类,通常称为切面类。
5.切面(Aspect):通知和切入点的结合。一个通知对应一个切入点就形成一条线,多个通知对应多个切入点形成多条线,多条线形成了一个面,称为切面。
6.织入(Weaving): 生成切面并创建代理对象的过程。(将通知和切入点的结合,并创建代理对象的过程)
f. 切面(Aspect):
切面是由切点和增强(引介)组成的,它包括了对横切关注功能的定义,也包括了对连接点的定义。
参考:https://www.cnblogs.com/hongwz/p/5764917.html
1.事务 Transaction
2.日志处理 logging, tracing, profiling and monitoring
3.缓存 Caching
4.权限控制 Authentication
5.错误处理 Error handling
6.懒加载 Lazy loading
链接:http://blog.sina.com.cn/s/blog_7045cb9e01010a9r.html
https://blog.csdn.net/qq513165077/article/details/78288999
IOC控制反转:指将对象的创建权,反转到Spring容器
方式1:创建工厂
创建一个工厂,用于创建目标对象,就是最简单的IOC。但工厂和目标类耦合。
方式2:工厂 + 配置文件
1)配置文件中存放类的全限定名称
2)编写工厂,用于读取配置文件中的内容,并通过反射创建对象。
问题:工厂只能生产一个对象
方式3:工厂 + properties文件
1)properties配置文件存放一组,key是标识,value是全限定名称
2)编写工厂类,读取配置文件
3)提供getBean( name ) 用于生成实例:通过name获得对应的全限定名称,再通过反射创建对应的实例。
MVC设计模式:
Model-View-Controller简写。
MVC是软件工程中的一种软件架构模式,它是一种分离业务逻辑与显示界面的设计方法。它把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
① 控制器Controller:对请求进行处理,负责选择视图;
② 视图View:用户与程序交互的界面;
③ 模型Model:用于业务处理
SpringMVC是一个MVC框架,springmvc的工作流程:
1. 发送请求:在浏览器输入请求路径,发送请求,前端控制器连接所有的请求。
2. 获得处理器:核心控制器(DispatcherServlet)接收到请求,通过处理器映射器(handlerMapping)获得对应的处理器(Handler)
即根据请求路径获得对应的controller。
3. 执行处理器:通过处理器适配器(HandlerAdapter)执行处理器(Handler),并返回视图模型对象(ModelAndView)
即执行controller方法。如果方法返回的字符串,底层也会转换成ModelAndView。
4. 解析视图:通过视图解析器(ViewResolver)解析ModelAndView中的视图,将逻辑视图解析成真正的视图。
5. 渲染视图:将Model中的数据渲染到视图(View)中
6. 响应结果
即将模型数据(查询结果)渲染到jsp页面中,并将处理结果响应给浏览器。
Spring框架的核心控制器(前端控制器)是DispatherServlet
他的核心功能就是分发请求,请求会被分发给对应处理的类handler。
使用@ResponseBody注解,将返回json数据。
1.如果在方法使用该注解,当前方法返回json数据。
2.如果在类上使用该注解,当前类中所有的方法,都将返回json数据。
springmvc默认用jackson对json数据进行转换,但需要添加jackson的包
扩展阅读:
消息转换器原理解析:https://blog.csdn.net/shadabing/article/details/84664402
SpringBoot配置Fastjson:https://blog.csdn.net/cjq2013/article/details/76421101
SpringMVC配置Fastjson:https://blog.csdn.net/do_bset_yourself/article/details/51324186
优点:
1、开发人员可以只关注整个结构中的其中某一层;
2、容易用新的实现来替换原有层次的实现;
3、降低层与层之间的依赖;
4、有利于标准化;
5、有利于各层逻辑的复用
缺点:
1、降低了系统的性能。这是不言而喻的。如果不采用分层式结构,很多业务可以直接造访数据库,以此获取相应的数据,如今却必须通过中间层来完成。
2、有时会导致级联的修改。这种修改尤其体现在自上而下的方向。如果在表示层中需要增加一个功能,为保证其设计符合分层式结构,可能需要在相应的业务逻辑层和数据访问层中都增加相应的代码。
1.Spring相关注解
1.1)声明bean的注解
@Component
通用注解,用于声明bean。可以配置任意组件。
@Repository
派生注解,与@Component等效,Dao实现类推荐使用的注解,不仅可以声明bean,而且提高可读性。
@Service
派生注解,与@Component等效,Service实现类推荐使用的注解,不仅可以声明bean,而且提高可读性。
@Controller
派生注解,与@Component等效,Controller实现类推荐使用的注解,不仅可以声明bean,而且提高可读性。
1.2)bean注入的相关注解
@Autowired:按类型注入,
@Resource(name=""):按名称注入
@Resource():先按名称注入,如果没有再按照类型。
1.3)配置类相关注解
@Bean
在方法上,声明当前方法的返回值为一个bean。
@Configuration
声明当前类为配置类,内部常使用@Bean进行详细配置。取代了xml文件配置。
@ComponentScan
用于对Component进行扫描,可以配置扫描的包路径
1.4)@Value:为属性注入简单值
2.SpringMVC相关注解
@RequestMapping
修饰方法,将请求路径映射到Controller对应的处理方法上。
修饰类,给请求路径添加访问前缀(二级路径)
@ResponseBody
将返回值转换成json数据,并响应给浏览器。
@RequestBody
将json格式的请求参数,封装到指定的JavaBean中。
@PathVariable
用于接收路径参数,比如@RequestMapping(“/hello/{name}”)申明的路径,参数前使用注解@PathVariable("name"),就可以获取name对应的值。
@RestController
该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都响应JSON格式数据。
1.地址栏参数或普通表单请求,如:age=18&gender=man,SpringMVC方法参数需要使用@RequestParam("参数名")获得参数值。
2.提交json类型数据,SpringMVC 方法参数需要使用@RequestBody 将数据封装到java对象中。
3.路径占位符:如 http://localhost:8080/user/findById/1,使用@RequestMapping("findById/{id}")匹配路径,使用@PathVariable("id")获得对应的参数值。
4.文件上传,修改表单请求为"multipart/form-data"方式,SpringMVC采用MultipartFile参数类型接收数据
DispatcherServlet用来处理所有的HTTP请求和请求响应
回答SpringMVC的执行流程即可:
1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;
2.
DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
3.
DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)
4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
5. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
6. 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet
;
7.
ViewResolver 结合Model和View,来渲染视图
8. 将渲染结果返回给客户端。
@Controller 是@Component的派生注解,功能就是将被修饰的类添加到spring容器。
用于修饰Controller层的组件,提高可读性。
修饰方法,将请求路径映射到Controller对应的处理方法上。
修饰类,给请求路径添加访问前缀(二级路径)
使用 @ResponseBody 注解将Java集合转换json数组,并将结果响应给浏览器。
标签:key formdata 思想 比较 适应 部门 过滤器 varchar 特点
原文地址:https://www.cnblogs.com/hailianna/p/10284837.html