-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 395 KB
/
content.json
1
{"pages":[{"title":"欢迎来到yhy的个人blog网站","text":"前言借助github和腾讯云买的域名 联系author:yhycontact:可以在友链那里看到我的github以及博客园和csdn网站email:1065205068@qq.com","link":"/about/index.html"},{"title":"","text":"","link":"/%E9%9A%8F%E8%AE%B0/index.html"},{"title":"目录分类","text":"","link":"/categories/index.html"},{"title":"标签","text":"","link":"/tags/index.html"},{"title":"ssm","text":"","link":"/ssm/index.html"}],"posts":[{"title":"0系列/0-HashMap底层实现","text":"","link":"/2020/01/01/0%E7%B3%BB%E5%88%97/0-HashMap%E5%BA%95%E5%B1%82%E5%AE%9E%E7%8E%B0/"},{"title":"0系列/0-volatile","text":"","link":"/2020/01/01/0%E7%B3%BB%E5%88%97/0-volatile/"},{"title":"0系列/0-多线程并发","text":"考虑线程安全问题主要从以下三个方面 1231.是否具有多线程2.是否共享线程3.是否有多个操作同时修改共有的资源 注意不同线程加的锁要加的是同一把锁才可以 等待唤醒机制wait()、notify()、notifyAll()方法都是Object类的方法;注意wait方法实现的时候会将锁释放,将CPU的使用权让出去给别人。","link":"/2020/01/01/0%E7%B3%BB%E5%88%97/0-%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%B9%B6%E5%8F%91/"},{"title":"0系列/0-面试准备之集合(数据结构)","text":"","link":"/2020/01/01/0%E7%B3%BB%E5%88%97/0-%E9%9D%A2%E8%AF%95%E5%87%86%E5%A4%87%E4%B9%8B%E9%9B%86%E5%90%88%EF%BC%88%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%EF%BC%89/"},{"title":"浅谈java封装","text":"[TOC] 前言英语:Encapsulation——封装,包装。 面向对象的封装与真实世界的目的是一样的。封装能够使外部访问者不能随意存取对象的内部数据,隐藏了对象的内部细节,只保留有限的对外接口。外部访问者不用关心对象的内部细节,使得操作对象变得简单。 好处先通过阅读下面的文字,再回来理解可能会更好一点 减少了耦合(物理学上类似相互影响的概念) 便于维护和修改 可以隐藏信息,一些具体实现代码,安全性 对成员变量精准控制 介绍(实现方式) Java 面向对象的封装性是通过对成员变量和方法进行访问控制实现的。这里就涉及到了访问控制的四个关键字了。 类别\\不同地方 同一个类 同一个包 不同包的子类 不同包非子类 私有的(private) Yes No No No 默认 Yes Yes No No 保护的(protected) Yes Yes Yes No 公有的(public) Yes Yes Yes Yes 封装是一种保护作用,将其变量以及方法保护起来,防止被随机访问,所以一定程度上增强了代码的安全性。 示例 下面是代码示例,私有类只能在同一个类中被访问,否则会报错。 1234567891011121314151617 private还可以防止直接访问特定的构造器(或全部的构造器) package music.daima.ebook;class TestClass{ private TestClass(){} static TestClass make(){ return new TestClass(); }}public class PrivateDaiMa { public static void main(String args[]){// TestClass x = new TestClass(); TestClass x = TestClass.make();// 不能通过构造器来创建TestClass,要通过调用make方法才可以 }} 报错信息说明TestClass的private将其构造器给私有化了,不能直接访问。 访问不同包的protected方法(非子类的),先是定义在Music类中的BaoHu方法。 在其它包访问,就会报错,因为在Music里面这个BaoHu的方法用的是protected修饰的。 其实在Idea写代码时候,定义的属性之类可以在左下角有个概览,可以查看是什么类型的 整体的test代码 12345678910111213141516171819202122232425import music.Music;//导入了music包的Music类class test{ test(int i){ i = i + 1; } private String name;//将其变为public String name;的话下面就可以访问 public int num=2;//公有的可以被访问 double i = 2.2;//默认的定义,也可以被访问,在本包里面}public class FengZhuang { public static void main(String args[]){ test a = new test(6); System.out.println(a.num); //a.name就会报错,因为在上面的name是私有的 Music ceshi = new Music();//导入了不同的包中的类// ceshi.BaoHu();报错,是在其它包里的protected方法 }} 小结访问成员有两种方式:一种是调用,即通过类或对象调用它的成员,如p.printX()语句;另一种是继承,即子类继承父类的成员变量和方法。在开发使用的时候,应该尽量限制类中成员的可见性 公有访问级别任何情况下两种方式都可以;(public) 默认访问级别在同一包中两种访问方式都可以,不能在包之外访问;() 保护访问级别在同一包中与默认访问级别一样,两种访问方式都可以。但是在不同包之 外只能继承访问;(protected) 私有访问级别只能在本类中通过调用方法访问,不能继承访问。(private) 感谢阅读!!!才疏学浅,有不对的地方请指出。","link":"/2020/01/15/Java-SE/%E5%B0%81%E8%A3%85/"},{"title":"浅谈java继承","text":"[TOC] 前言 类的继承性是面向对象语言的基本特性,多态性前提是继承性。Java 支持继承性和多态性。——Java从小白到大牛 继承:三大特性之一,关键词:extends 简单用伪代码去介绍一些。 假设需要定义个student类 12345public class Student{ private String name;//名字 private int age;//年龄 private int sex;//性别} 再来个需求,需要定义一个学生Andy,不用继承时候,需要一个个去定义; 123456public class Andy{ private String name; private int age; private int sex; private String country;//新增一个国家信息} 从上面看两者有着很多的相似的重复定义的东西,如果使用继承去写这段代码就可以如下: 123public class Andy extends student{ private String country} 所以从上面可以看出类的继承大致用途,Andy类已经继承了Student类,上面的父类便是student,extends后面跟着的。继承的时候,要明确指出,要不然就是在Java标准根类Object进行继承。 再者,在继承的过程中,除了你可以在新的类中去添加东西,也不一定非得使用基类的方法属性等。 还可以有多层继承关系(下面的输出是随便定义的伪代码,可以是直接输出某一些语句,方便运行查看) 12345678910111213class Art{Art(){输出1}}class Music extends Art{Music(输出2)}class Dance extends music{public Dance(){输出3}public static void main(String args[]){dance x = new dance();}}//输出的顺序是1 2 3 父类与子类的构造函数问题 若不在类中定义构造函数,会默认生成一个无参的函数构造器,如果有了就不会。(构造器即构造函数,与类同名,在初始化的时候就被调用,,默认的构造函数是不带任何参数的) 123456789class Art{}//等同于下面class Art{ public Art(){ // }} super关键字 Java用super表示超类的医生,当前类是从超类继承来的。 1234567891011121314151617class Game{ int h; Game(int i){//构造函数 System.out.println(\"this is a game!\"); } public static void printg(int d){//普通函数 System.out.println(\"12\"); }}class ballGame extends Game{ ballGame(int i){ super(i);//调用父类的构造器,漏了这句就会报错,显示there is no default constructor available System.out.println(\"ballGame is coming!\"); super.h = 3;//访问父类的属性 }} super可以用于访问父类定义的属性,用于调用父类中的成员方法,super代表父类的内存空间。可以访问父类的父类 1234567891011121314151617181920212223242526272829303132333435363738class Game{ int h; Game(int i){//构造函数 System.out.println(i);//输出 System.out.println(\"第一层\"); } public static void printg(int d){//普通函数 System.out.println(\"1\"); }}class ballGame extends Game{ ballGame(int i){ super(i);//调用父类的构造器 System.out.println(\"第二层\"); super.h = 2;//访问父类的属性 }}public class jichengSuper extends ballGame{ jichengSuper(int b) { super(b);//调用的是上面父类的构造函数,将输出3 int a = super.h ;//将第二层里的3赋值给a System.out.println(a);//打印 } public static void main(String args[]){ new jichengSuper(3);//这里的3将代进去上面的构造函数jichengSuper中 System.out.print(\"最后一层\"); }}/** *output: 3第一层第二层2最后一层 */ 接口在实现的时候,在类中一定要实现接口的抽象方法 而继承不用,想要用哪个再用 others可以有多个接口但是只能有一个继承。一般情况下,一个子类只能继承一个父类,这称为“单继承”,但有的情况下一个子类可以有多个不同的父类,这称为“多重继承”。在Java中,类的继承只能是单继承,而多重继承可以通过实现多个接口实现。","link":"/2020/01/18/Java-SE/%E7%BB%A7%E6%89%BF/"},{"title":"0系列/0-ArrayList与LinkedList","text":"ArrayList与LinkedList底层实现差别运用在什么场合","link":"/2020/01/01/0%E7%B3%BB%E5%88%97/0-ArrayList%E4%B8%8ELinkedList/"},{"title":"0系列/0-SQL高级方面","text":"group、by、having、左连接、子查询 建表方面——三范式、反范式是什么? 优化?","link":"/2020/01/01/0%E7%B3%BB%E5%88%97/0-SQL%E9%AB%98%E7%BA%A7%E6%96%B9%E9%9D%A2/"},{"title":"0系列/0-String与stringbuffer等","text":"String与stringbuffer等","link":"/2020/01/01/0%E7%B3%BB%E5%88%97/0-String%E4%B8%8Estringbuffer%E7%AD%89/"},{"title":"浅谈java版本","text":"Java SE ——Java Platform,Standard Edition以前称谓J2SE,包含支持Java web服务开发的类,并为Java作基础 Java EE——Java Platform,Enterprise Edition以前称为 J2EE,提供web服务、组件模型和管理和通信API 简单来说Java SE 是做电脑运行的软件,EE是用来做网站服务的。","link":"/2020/01/08/Java-SE/Java%20SE%20%E4%B8%8EJava%20EE/"},{"title":"Java-SE/包","text":"package包访问权限:类可以访问在同一个包中其它类的成员 定义 作用","link":"/2020/01/01/Java-SE/%E5%8C%85/"},{"title":"记录java关键字","text":"assert(断言 ) default(默认 ) if(如果 ) abstract(抽象的) continue(继续) for(当…的时候) new(新建) switch(转换) package(打包 ) synchronized(同步) boolean(布尔) do(做) goto(跳转到) private(私有的) this(这个) break(中断) double(双精度) implements(实现) protected(受保护的) throw(抛出,动词) else(否则) import(引入) :将包引进来 public(公共的) throws(抛出,介词):处理异常时候会遇到 case(情形 ) enum(枚举 ) :用来定义枚举 instanceof(是…的实例 ) return(返回 ):编程语言中的老熟客 transient(瞬时的) extends(继承):三大特性之一 int(整数):4位 short(短整数):2位 byte(字节):八大类型之一,字节型,byte(-128~127)是1个字节 char(字符 ):2个字节,不管是中文还是英文,用来存储Unicode字符( unicode编码固定占用两个字节,所以,char类型的变量也是占用两个字节。Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码包括了汉字,有些特殊的汉字可能就不允许),0-65535范围 try(尝试):直接运行后面的语句 catch(捕获):捕获异常 final(最终的 ) interface(接口 ) static(静态的 ) void(空的) class(类) finally(最终地) long(长整数) strictfp(精确浮点) volatile(易变的) const(常 量 ) float(单 精 度 浮 点 ) native(本 地 的 ) super(超 级 的 ) while(当…的时候)****","link":"/2019/12/08/Java-SE/java%E5%85%B3%E9%94%AE%E5%AD%97/"},{"title":"Java-SE/Java总结","text":"title: Java小总结toc: truetags: JavaEEcategories: Java学习date: 2019-11-8 Java学习总结今天就总结理清一下关于面向对象和面向过程的程序设计的一些不同特点。 [TOC] 概论 现在接触的Java是面向对象的,我现在不看书,光靠回忆去想这个特性的解释性语言,只能想到看书时很深刻的一句话,面向对象就是一个更加具体描述一个事件的一种设计方法,就像在公司里面吃饭,面向对象的话就可以按照你自己的想法去决定你想吃什么,喝什么,吃多久,坐在哪里,这些都是可以实例具体差异化展开。 关于面向对象的程序设计三个特点:封装性、继承性、多态性。首先封装性是把对象属性、行为看成一个密不可分的整体封装在独立单位中,一种信息隐蔽的特性。其次是继承性:父类与子类的表达与描述就是一种继承性,拥有反映事物一般特性的类,在其基础上派生出反映特殊事物的类。(类是由属性和方法组成)。最后是多态性,包括方法重载和对象多态。子类与父类可以相互转换,根据其使用的子类不同完成的功能也不同。 类的构造方法:与类同名的方法。 java的四块内存空间 堆内存空间:保存所有对象的名称(堆内存空间的地址) 栈内存空间:保存每个对象的具体属性内容 全局数据区:保留static类型的属性 全局代码区:保存所有的方法定义 全局和局部变量是相对而言的。 访问属性,建议加上this。 #面向对象的基本概念面试数组 数组固定长度,无法改变 数组插入删除比较麻烦,需要移动其它元素。 随机读取比较快速,高效 定义:(多维的话类型,将[]变为[][]) 数据类型 名字[] = new 数据类型[长度]——动态初始化 数据类型 名字[] = new 数据类型[]{直接具体的数值}——动态初始化 不建议使用多维数组 例子: 数组倒置(reverse)——1定义两个数组,一个是原数组,另一个空数组,将空数组的最后一个元素,对应原数组的第一个,以此类推。这是会产生垃圾,这个空数组会变成垃圾2可以通过数组长度/2次循环。实现首尾的交换 数组复制:System.arraycopy(原数组,复制开始索引,目标数组,目标开始索引,长度) 数组排序:java.util.Arrays.sort(数组); 对象数组:数组是引用类型 ,就是除了数据类型改变了,这里是类对象,之前是数据类型,其它没有任何区别 类名称 对象数组名称=new 类名称[长度]——静态初始化。动态初始化和普通的没区别。 Arraylist与数组 String类 ==与equals():前者是用于所有的引用数据类型的比较,比较的是地址的数值内容。后者是比较字符串的内容 String的两种赋值方法 直接赋值 String str = “随便”,只要内容相同就不会产生新的堆内存空间。 构造方法赋值 用了new:String str1 = new String(“随便”)——这个会产生垃圾空间,new会开辟新的堆内存空间。 所以构造方法new不会主动入池,需要在后面加上.intern()才可以。所以不主动入池,上面的str=str1的结果是false String频繁改变就会产生垃圾空间,一般都是用StringBuffer或者StringBuilder来代替 string——char charAt(int index)会报错IndexOutOfBoundsException - if the index argument is negative or not less than the length of this string. public int indexOf(String str)由前向后查找指定字符串的位置,如果找到了则返回第一个字母的位置索引。如果找不到就返回-1。 str.split()字符串拆分 toLowerCase()-转小写 toUpperCase()-转大写。 字符串的length()与数组的length要注意,一个没有() 特意回忆下,八种数据类型:boolean byte double float int short char long final关键字当用final修饰一个类时,表明这个类不能被继承。如果是修饰基本数据类型就表明数值一旦初始化之后就不可以再被更改了。 this关键字 总的来说叫,调用本类属性,调用本类方法,表示当前对象。 本类属性——用this.title=xx,前提是前面的形参有这个title。传统的是可以直接title=xx,但是为了不必要的麻烦(类中的属性可能会和参数重名) 调用本类方法:普通的就直接this.方法(),构造的话可以this()就可以。有参数就变为this(参数)——这个方法在解决构造方法成千上百行时候会显得效率奇高。 表示当前对象:返回当前的值。 引用传递 同一块堆内存空间可以被不同的栈内存所指向,不同栈内存可以对同一堆内存进行内容修改。 static关键字 一个人类的主要组成就是属性和方法(构造方法和普通方法),如果一个类中的某个属性想被所有对象访问,就变成公共的,可以在属性前面加多一个static。 Java中的变量分为成员变量和局部变量;局部变量还分为三种:形参、方法内的局部变量、代码块内的局部变量(作用范围看名字就可以明白,如代码块内的,代码块结束后就开始失效消亡)。成员变量:类体内定义的变量,包括了非静态变量又称实例变量,和静态变量又称类变量。 static字面意思是静态;但是在Java程序中,它是一个标志,作用是将实例成员变为类成员。而且只能修饰在类里定义的成员部分,包括成员变量、方法,内部类、初始化块。不用static去修饰类里的这些成员,它们就属于该类的实例。eg:static int num2 = 20;(这是一个类变量)eg: int num1 = 238;(实例变量) static修饰的成员属于类,类变量会随着类初始化得到初始化;而没有static修饰的成员变量则属于实例,实例变量随着对象的初始化而初始化。因为在初始化之前一个对象之前,肯定得先初始化该对象所属的类,所以static修饰的时机肯定较实例变量早。 static方法一般称作静态方法,不依赖任何对象就可以进行访问,因此对于静态方法来说,没有this的。 代码块 使用{}——可以分为普通代码块(写在方法里)、构造块(写在类里面)、静态块(用static定义的代码块)-作用可以为static属性初始化和同步代码块(等待多线程时候) //写在方法里 public static void main(String args[]){ { System.out.println(); } } //代码写在类里。构造块 class test{ { System.out.println(); } } //静态块,运行时候会优先调用。而且之调用一次,若主函数里面用了两次new test ,下面的2也只是第一次输出 class test{ static{ System.out.println(\"2\"); } } <!--0--> 在主函数中实例化内部类的格式——outer.inner in = new outer.inner(); 若在inner前面加上private,则该内部类只能服务于外部类。 链表 链表逻辑简单、比较实用,几乎被所有程序设计语言支持。是一种根据元素节点逻辑关系排列起来的数据结构。 链表也分单向和双向(一个数据域,两个指针域-指向后继,指向前驱,在最后指向表头结点),还有循环链表(和单链表基本相同,但是最后一个结点的指针域不空,指向表头结点)。 node类有两个属性:(数据)data和(下一节点)next(指明自己有什么,还提醒下一个在哪,看起来是非常友好的结构。)——单向的 动态的对象数组,优势在与没有长度限制。 python与java的区别 在编程书写上的区别 java的核心是虚拟机,而 python的核心是可以方便使用各种函数库(c语言或者c++) 应用的领域有所不同;java适合应用于商业逻辑强的领域,比如商城系统等;适合软件工程式的多人开发,而python适合应用于数据分析,科学计算、金融分析等,适合快速开发团队或者个人敏捷模式。 python用途最多的是脚本,java用途最多的是web,pyhotn是胶水,可以把各类不相关的东西粘在一起用,而java像hi基佬工作,可以通过多人合作 JAVA 基本上是类/结构操作,也就是面向对象处理,Python 可以以独立的函数模块来处理逻辑而不需要放到类中。 java与javascript java是真正面向对象的语言,即使是简单的开发简单程序,必须设计对象;javascript是脚本语言,用来制作与网络无关,与用户交互作用的 java需要编译,而javascript无需编译,直接由浏览器解释执行 强类型变量和类型弱变量:Java采用强类型变量检查,即所有变量在编译之前必须作声明;JavaScript中变量是弱类型的,甚至在使用变量前可以不作声明,JavaScript的解释器在运行时检查推断其数据类型。 ==与equals==基本数据类型就是比较值,如果是引用类型的话,就是比较地址。 equals在string类里面,“equals()”比较字符串中所包含的内容是否相同,equals方法是可以重写的,所以在一些类型中,已经被重写了. 原本的object类下的equals方法是先比较 string类下的equals array类下的equals 请你解释为什么重写equals还要重写hashcode?HashMap中,如果要比较key是否相等,要同时使用这两个函数!因为自定义的类的hashcode()方法继承于Object类,其hashcode码为默认的内存地址,这样即便有相同含义的两个对象,比较也是不相等的。HashMap中的比较key是这样的,先求出key的hashcode(),比较其值是否相等,若相等再比较equals(),若相等则认为他们是相等的。若equals()不相等则认为他们不相等。如果只重写hashcode()不重写equals()方法,当比较equals()时只是看他们是否为同一对象(即进行内存地址的比较),所以必定要两个方法一起重写。HashMap用来判断key是否相等的方法,其实是调用了HashSet判断加入元素 是否相等。重载hashCode()是为了对同一个key,能得到相同的Hash Code,这样HashMap就可以定位到我们指定的key上。重载equals()是为了向HashMap表明当前对象和key上所保存的对象是相等的,这样我们才真正地获得了这个key所对应的这个键值对 简单来说就是,为了比较同一个key对象得到相同的hashcode。 两个同时重写是因为在hashcode生成hash值时候,会避免不了存在哈希值冲突的现象,当hashcode相同的时候,需要调用equals再次比较。 int 和Integerint是基本数据类型,Integer是包装类;Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer,从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。 做题随记 在重写继承的时候,子类的权限不能比父类低 类对接口的实现,其实体现了多态性,因为类需要重写接口中所有的抽象方法。 构造函数:特殊类型的方法,没有返回值,与void的普通方法不一样, 归并排序:稳定,时间复杂度 O(nlog n) 快速排序:不稳定,时间复杂度 最理想 O(nlogn) 最差时间O(n^2) 堆排序:不稳定,时间复杂度 O(nlog n) 归并排序时间复杂度 O(n),消耗空间最多。 比特成帧,帧成报文,报文成包,包成段(物理层,数据链路层,网络层,传输层的单位) IP地址分类 A:0.0.0.0~127.255.255.255 B:128.0.0.0~191.255.255.255 C:192.0.0.0~223.255.255.255 ARP协议(Address Resolution Protocol,地址解析协议),属于IPv4协议簇,工作在数据链路层。其功能是将IP地址解析为对应的MAC地址。 RARP协议(Reverse ARP,反向ARP协议),其功能是将MAC地址解析为对应的IP地址。 JDK1.8后,接口中可以有静态方法,静态方法必须有方法体。 Java中可以接受抽象类、类与接口对象作为方法的参数 三步走学习方法-分散知识点学习、刻意训练、寻求反馈 做题法:读题、用尽所有想法、尝试解答、查看答案、套用多个条件、更换不同测试用例。 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。 Java中的Iterator功能比较简单,并且只能单向移动: (1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。 (2) 使用next()获得序列中的下一个元素。 (3) 使用hasNext()检查序列中是否还有元素。 (4) 使用remove()将迭代器新返回的元素删除。 Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。 正则表达式就是记录文本规则的代码 array与arraylist数组不可以更改长度,而arraylist可以,实现动态数组的感觉,同时arraylist提供了很多的内在方法,支持操作 Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。 值传递和引用传递一个是变量的副本,操作不会改变影响原变量,而一个是操作对象的地址,不是对象的本身,所以对引用对象操作是会改变原变量的。一般认为java传递都是值传递。 请你谈谈关于Synchronized和locksynchronized:java关键字,用来修饰一个方法或者代码块时候,保证只有一个线程执行该代码。","link":"/2020/01/01/Java-SE/Java%E6%80%BB%E7%BB%93/"},{"title":"浅谈垃圾回收","text":"前言Java的垃圾回收是Java语言的重要功能之一。当程序创建对象、数组等引用类型实体时,系统都会在堆内存中为之分配一块内存区,对象就保存在这块内存区中,当这块内存不再被任何引用变量引用时,这块内存就变成垃圾,等待垃圾回收机制进行回收。垃圾回收机制具有如下特征。 finalize()方法终结处理和垃圾回收。 Java允许在类中定义一个finalize()的方法:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize(),并在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。当用上finalize方法时候,就能在垃圾回收时刻做一些必要的工作。","link":"/2020/01/28/Java-SE/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%9C%BA%E5%88%B6/"},{"title":"浅谈java多态","text":"一、前言这篇讲下三大特性之一的多态,可以通俗得去理解,多种形态,多种姿态。。。 那么在面向对象的程序设计中,多态是 指什么? Java引用变量有两个类型;一个是编译类型,一个是运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定.如果编译类型和运行时类型不一致,就可能出现所谓的多态(Polymorphism) “多形性”(Polymorphism)从另一个角度将接口从具体的实施细节中分离出来,亦即实现了“是什么”与“怎样做”两个模块的分离。利用多形性的概念,代码的组织以及可读性均能获得改善。此外,还能创建“易于扩展”的程序。无论在项目的创建过程中,还是在需要加入新特性的时候,它们都可以方便地“成长”。——ThinkinJava 直接网易百科-Polymorphism 二、发生的条件 继承。多态发生一定要子类和父类之间。 覆盖。子类覆盖父类的方法 声明的变量类型是父类,但是实际指向的是子类。程序中的new后面就是子类 12//f4变量是子类类型,指向子类实例 Triangle f4 = new Triangle(); 三、多态包含(或者说体现在)两个方面: 首先是同一个方法,不同的实现效果,就具体的方法是怎么实现的,是可以不同的。就是方法的多态性。 1吃苹果方法,可以剥皮吃,也可以不剥皮吃,甚至是榨苹果汁喝。 规范讲这种就是可以通过重载和覆写来实现 重载:同一个方法名称,因为不同的参数类型和个数,因此可以达到不同的效果。 覆写:同一个方法,根据实例化的子类对象不同,最后的效果和实现功能不一样。 对象的多态性 引用类型转换也是通过小括号运算符实现,类型转换有两个方向:将父类引用类型 变量转换为子类类型,这种转换称为向下转型(downcast);将子类引用类型变量转换为 父类类型,这种转换称为向上转型(upcast)。向下转型需要强制转换,而向上转型是自 动的。 向上转型:子类对象变为父类 向下转型:父类对象变为子类 四、示例 简单的示例多态,代码里面的@Override是伪代码,表示方法重写。一般你重写正确了,IDE会自动加上去的。也是一种验证方法。 12345678910111213141516171819202122232425262728293031323334353637383940package music.daima.ebook;class Figure{ //绘制几何图形方法 public void onDraw() { System.out.println(\"绘制Figure...\"); }}class Ellipse extends Figure{ @Override public void onDraw() { System.out.println(\"绘制椭圆形\"); }}class Triangle extends Figure { // 绘制几何图形方法 @Override public void onDraw() { System.out.println(\"绘制三角形...\"); } }public class PolymorphismEasy { public static void main(String[] args) { // f1变量是父类类型,指向父类实例 Figure f1 = new Figure(); f1.onDraw(); //f2变量是父类类型,指向子类实例,发生多态 Figure f2 = new Triangle(); f2.onDraw(); //f3变量是父类类型,指向子类实例,发生多态 Figure f3 = new Ellipse(); f3.onDraw(); //f4变量是子类类型,指向子类实例 Triangle f4 = new Triangle( ); f4.onDraw(); }}//output:绘制Figure...绘制三角形...绘制椭圆形绘制三角形... 不能覆盖private方法 123456789101112131415161718package music.daima.ebook;public class PolymorphismFugai { private void p(){//if public...就可以覆盖 System.out.println(\"1\"); } public static void main(String[] args) { PolymorphismFugai s = new jicheng(); s.p();//将输出的是上面的1而不是我们想要的jicheng类中的方法 }}class jicheng extends PolymorphismFugai{ public void p(){ System.out.println(\"2\"); }}//output:1 Static静态方法与多态的关系 123456789101112131415161718192021222324package music.daima.ebook;//这一篇是用来研究静态方法与多态的class A { public static String get(){//定义一个方法 return \"A is base\"; }}//继承Aclass B extends A { public static String get(){//同样的方法 return \"B is not base\"; }}public class StaticUpcast { public static void main(String[] args) { A a = new B();//upcast 向上转型 System.out.println(a.get()); }}//output://A is base//从这里发现静态方法是与类相关联的,不是与单个对象关联的,不能覆盖掉 感谢阅读才疏学浅,有不对的地方欢迎指教!","link":"/2019/11/08/Java-SE/%E5%A4%9A%E6%80%81/"},{"title":"Java-SE/常量变量数据类型","text":"[TOC] 前言按照xmind文件总结所有的基础,开始第二阶段学习。 常量 有些数据在程序运行中值不能被改变,这些数据在程序中就被称为常量。 Java中用final修饰的成员变量表示常量,值一旦给定就无法改变! 1. final double PI = 3.14; 2. final char MAN=‘M’,FEMALE=‘F’; <!--0--> 还分为静态常量和局部常量;局部常量放在class里面,也可以被调用时候引用。静态常量往往由于访问效率比较高。不过一般会把他们放在以下三个地方。 一:放到Interface,因为 Java interface中声明的字段在编译时会自动加上static final的修饰符,即声明为常量 二:放到文件,如Properties文件 三:放到Class,使用final String 变量 变量分为基本类型和引用类型。 基本类型包括boolean类型和数值类型。数值类型有整数类型和浮点类型。整数类型包括byte、short、int、long、char,浮点类型包括float和double。(八大类型,不包括string) byte(-128~127)是1个字节,short两位,int是四位,long是八位,float是四位,double是八位(保存范围最广的),char(字符)是两位。 建议在选择小数操作时候都使用double型;每一个单引号中只保存一个字符 引用类型包括类(class)、接口(interface)和数组类型,还有NULL型。所谓引用数据类型就是对一个对象的引用,对象包括实例和数组两种。这个引用类型在Java中非常非常重要。 注释 单行注释:/ 多行注释/**/ API开发一个大型软件时,需要定义成千上万的类,而且需要很多人参与开发。每个人都会开发一些类,并在类里定义一些方法、Field提供给其他人使用,但其他人怎么知道如何使用这些类和方法呢?这时候就需要提供一份说明文档,用于说明每个类、每个方法的用途。当其他人使用一个类或一个方法时,他无须关心这个类或这个方法的具体实现,他只要知道这个类或这个方法的功能即可,然后使用这个类或方法来实现具体的目的,也就是通过调用应用程序接口(API)来编程。API文档就是用以说明这些应用程序接口的文档。对于Java语言而言,API文档通常详细说明了每个类、每个方法的功能及用法等。调用API可以实现更快的开发。Java的API可以在官网下载jdk文档来看。 符号圆点(.)通常用做类/对象和它的成员(包括Field、方法和内部类)之间的分隔符,表明调用某个类或某个实例的指定成员。 运算符就不讲了,遗忘的话可以多查看。 标识符标识符不能以数字开头,不能用关键字,不能空格。标识符只能包含美元符($),不能包含@、#等其他特殊字符。 关键字上面的enum是用来定义一个枚举。从Java 5增加的。","link":"/2020/01/01/Java-SE/%E5%B8%B8%E9%87%8F%E5%8F%98%E9%87%8F%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B/"},{"title":"Electron demo实战","text":"前言 Electron 是一个用 HTML,CSS 和 JavaScript 来构建跨平台桌面应用程序的一个开源库。由GitHub开发的。 其原理是 Electron 通过将 Chromium 和 Node.js 合并到同一个运行时环境中,并将其打包为 Mac,Windows 和 Linux 系统下的应用来实现这一目的。 在公司里用到了Linux系统,所以就花了一点时间试着搭了一个体验一下。 安装这个网站走完安装部署步骤 创建electron桌面项目代码等结构参照以下链接github 一开始按照网上进行操作,最后会报错,无法执行,直接抛出——throw new Error(‘Electron failed toinstall correctly, please delete node_modules/electron and tryinstalling again’) 如图 操作的一些记录 查阅了一小会资料,原来是我的Ubuntu上缺少了一些库环境,不过每个人的情况不一样,先是确保你的Python版本在2.7.x,以及安装了node.js那些,就是以及完成了那些该走的步骤,再来就是安装下面的库了。 12345sudo apt-get install build-essential clang libdbus-1-dev libgtk-3-dev \\ libnotify-dev libgnome-keyring-dev \\ libasound2-dev libcap-dev libcups2-dev libxtst-dev \\ libxss1 libnss3-dev gcc-multilib g++-multilib curl \\ gperf bison python-dbusmock openjdk-8-jre 我的就是上面库没装,再次试了一遍之后就成功运行了。 最后运行成功如下) 参考解决方法stackoverflow上的解决方法官方文档 参考搭建","link":"/2020/04/16/%E5%85%B6%E5%AE%83/Electron/"},{"title":"浅谈java异常","text":"前言:异常:程序出现了不正常的情况,比较严重不可修复与可修复的异常,在编译前与编译时出现的异常。 throwable下面有两个子类:Error和Exception;首先是Error异常是比较严重的异常,从字面的含义也可以了解到,error的语气是要重于exception的,它主要指的是不可修复的异常,程序员都无法修复,只能终止的异常,在现阶段我们遇到的情况应该不多。 而exception指的是可以恢复的异常,指程序员可以修正掌控的异常。常见的空指针,算术的除零异常等。在很多书中大篇幅介绍的异常也都指这一类异常 Exception分为运行异常和受检查异常 throwable类的三个重要方法 String getMessage():获得发生异常的详细消息。 void printStackTrace():打印异常堆栈跟踪信息。 public void printStackTrace() 将此throwable和其追溯打印到标准错误流。 此方法在错误输出流上为该Throwable对象打印一个堆栈跟踪,该值为字段System.err的值。 第一行输出包含该对象的tostring方法的结果。 剩余行表示先前通过方法fillInStackTrace()记录的数据。 该信息的格式取决于实现,但以下示例可能被认为是典型的: String toString():获得异常对的描述。","link":"/2020/01/01/Java-SE/%E5%BC%82%E5%B8%B8/"},{"title":"其它/git-leanring","text":"git reset HEAD 用来取消暂存文件,就是git add 1和2,但是你不想提交2了。那么就可以用该命令去取消。 git checkout – 用来撤销对文件的修改,在changes not staged for commit 中经常看到,这个是可以还原成它上次提交的时候的样子。需要注意的是,你自上次修改后的内容将全部作废,所以这个命令还是蛮危险的","link":"/2020/01/01/%E5%85%B6%E5%AE%83/git-leanring/"},{"title":"网络","text":"http长轮询:都是浪费性能长连接websocket全双工通信,实时通信,互相的,服务器主动通知客户端,用HTTP请求协议,HTTP头部有WebSocket协议,握手后,就使用TCP协议交流 node.js一开始就支持websocket协议。 HTTP-和HTTPS缺点分析123●通信使用明文(不加密),内容可能会被窃听●不验证通信方的身份,因此有可能遭遇伪装●无法证明报文的完整性,所以有可能已遭篡改 这些缺点导致了很多隐患,无法确认来请求的是谁,甚至无法阻挡海量请求的DoS攻击(拒绝服务攻击) 解决可以通过加密–SSL(安全套接字层)或者TLS(安全传输层协议)就有了HTTPS(超文本安全传输层协议) HTTPSHTTP是直接和TCP进行通信,而HTTPS是通过中间人SSL和TCP进行交流通信的。 加密为了做到有效的内容加密,前提是要求客户端和服务器同时具备加密和解密机制;注意的是对报文主体进行加密; SSL不仅提供加密处理,而且还使用了一种被称为证书的手段,可用于确定方(这里的证书是由可信赖的第三方提供的) 1这里为了解决加密的安全性问题和可行性,HTTPS采用共享密钥加密和公开密钥加密两者并用的混合加密机制。公开密钥加密方法,其使用一对非对称的密钥。一把叫做私有密钥(private key),另一把叫做公开密钥(public key。 认证","link":"/2020/04/16/%E5%85%B6%E5%AE%83/http/"},{"title":"其它/网络结构知识","text":"网络结构知识分布式:一个业务分拆多个子业务,部署在不同的服务器上集群:同一个业务,部署在多个服务器上 “小饭店原来只有一个厨师,切菜洗菜备料炒菜全干。后来客人多了,厨房一个厨师忙不过来,又请了个厨师,两个厨师都能炒一样的菜,这两个厨师的关系是集群。为了让厨师专心炒菜,把菜做到极致,又请了个配菜师负责切菜,备菜,备料,厨师和配菜师的关系是分布式,一个配菜师也忙不过来了,又请了个配菜师,两个配菜师关系是集群”","link":"/2020/01/01/%E5%85%B6%E5%AE%83/%E7%BD%91%E7%BB%9C%E7%BB%93%E6%9E%84%E7%9F%A5%E8%AF%86/"},{"title":"Linux学习记录","text":"1. 介绍Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和Unix的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的Unix工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统(度娘)。Linux系统大致分为两种,debian系和redhat系的。现在国产的也有很多,例如优麒麟、以及安全操作系统凝思。 debian系 redhat系 Debian RedHat |Ubuntu Fedora Mint Centos 2. 好处 免费开源:感觉这也是受欢迎的关键点,在国内也有很多自己的版本,比如最近在使用的国产ubuntu优麒麟,正是由于Linux的免费,许多程序员可以根据自己的兴趣去修改,这也让Linux不断壮大发展。 支持多用户、多用户:Linux支持多用户,各个用户对于自己的文件设备有自己特殊的权利,保证了各用户之间互不影响。 良好的界面:有着字符界面和图形界面,在Linux上,可以通过终端来完成所有工作基本上。其实也是一种给人很牛皮的感觉。 支持多平台:Linux可以运行在多种硬件平台上,如具有x86、680x0、SPARC、Alpha等处理器的平台。此外Linux还是一种嵌入式操作系统,可以运行在掌上电脑、机顶盒或游戏机上。之前玩了一阵子的树莓派,树莓派上就可以跑Linux的。 3. 常见操作4. Linux命令操作dpkg: package manager for Debian - 安装: dpkg -i package(命令行操作常用) - 卸载: dpkg -r package - 卸载并删除配置文件: dpkg -P |–purge package - 如果安装一个包时、说依赖某些库、 可以先 apt-get install somelib - 查看软件包安装内容 :dpkg -L package - 查看文件由哪个软件包提供: dpkg -S filename - 另外 dpkg还有 dselect和aptitude 两个frontend apt - 安装: apt-get install packs - 更新源:apt-get update - 升级系统:apt-get upgrade(安装东西的时候,都要进行这一步。这里一般都很花费较长时间,有时比较慢,可以考虑换成国内的源。) - 智能升级、安装新软件包,删除废弃的软件包:apt-get dist-upgrade f –fix broken 修复依赖:apt-get f install - 自动删除无用的软件:apt-get autoremove 删除软件:apt-get remove packages mount - 用来挂载linux外的文件,非常重要的一个命令,笔者也算是入门水平,还没真正熟悉用这个命令。大家可以多查阅资料去了解更多的内容。 - 基本格式:mount -t type [-o options] device dir (device:指定要挂载的设备,磁盘、光驱等 dir 指定把文件系统挂载到哪个目录 type 指定挂载的文件系统类型 options 指定挂载参数)。 - 菜鸟教程的链接 - 示例:#mount -o ro /a/b/c——将a/b挂在/c下面 - [-o options]—— -o auto打开挂上模式 -o ro 只读 -o rw 读写模式挂载 man- 这个命令是在Linux下提供的帮助手册,也很重要,对于哪个命令不清楚的话。就直接man +命令即可查阅。 格式有下面这些;退出阅读界面就直接按q即可。 格式 说明 NAME 名称,可以最简洁得了解该命令 SYNOPSIS 概要,用法说明和能够使用的选项 DESCRIPTION 对各个功能命令的详细解释说明 OPTIONS 说明每一项的意义 AUTHORS 伟大的发明创作这个的作者 SEE ALSO 另外参照 COPYRIGHT 版权说明,在linux都是可以更改的 free 常用命令 - df -h——查看磁盘空间(h是以友好的方式进行查看) find+文件——可以查看当前目录下的文件,若是文件夹,存在就会列出文件夹中的文件。 netstat -ant——用来查看本地一些端口信息等,监听使用等,这个命令很重要,之前在设置mysql的时候都有用到,解决一些端口问题上都会用到这个命令。 mkdir ——创建文件夹 eg:mkdir 文件夹名 touch ——创建文件eg:touch 文件名。 du -h 文件——查看文件的大小,有时要查看文件的大小,在使用vim之前一般都会建议查看一下大小,因为大于100M的文件直接vim打开会出现问题。 su ——切换用户,比如su -uroot -p切换到root用户下。 ping命令——这个其实不管哪个系统都很重要,是关于tcp/ip的,Ping 是一个十分好用的TCP/IP工具。它是用来检测网络的连通情况和分析网络速度。 推荐在这个标题下推荐一些好的链接(书籍、网站、博客等) \\1. 书籍 鸟哥的Linux私房菜 鸟哥(作者) The Linux Command Line(linux命令行大全) william E 个人感觉要想形成自己的知识体系,就要不断得去学习和阅读书籍,现在的学习环境总体而言太复杂了,太浮躁了,学技术要定下心,沉住气,所以阅读经典实用的书籍是必不可少的,日后对某一种知识遗忘时,随手一翻也可以快速得拾起记忆。 一般这些技术书,我都是上一些二手网站淘回来,例如有路网等,当然京东和当当网也会搞活动有时候,也很划算,刚刚过去的双十一就买了两本。比较划算,包括上面那个Linux command line 双十一折扣回来两本70多,很香的价格。 下面的截图是鸟哥的Linux私房菜截图,个人强烈推荐,对于学生来讲是非常友好的,用易懂的语言去讲解,并不枯燥。 \\2. Github [大佬总结pdf(强烈推荐)] https://github.com/xjjdog/javaok/blob/master/%E6%9C%80%E6%9C%89%E7%94%A8Linux%E7%B3%BB%E5%88%97-%E7%99%BE%E9%A1%B5%E7%B2%BE%E5%8D%8E.pdf\\3. 网站 个人感觉挺不错的一个学习实践开源中国社区。 菜鸟教程-这是一个比较快速入门的网站,但有些地方没细讲,大家可以自行查看。","link":"/2020/04/16/%E5%85%B6%E5%AE%83/Linux/"},{"title":"谈谈内存空间","text":"内存空间前言这一部分涉及到一些理解Java虚拟机的东西,但是今天这篇就只想总结栈与堆空间。 正题首先Java 内存可以粗糙的区分为堆内存(Heap)和栈内存(Stack)。 栈现在的Heap也指的是虚拟机栈,其下还包括栈,栈再由栈帧组成。 堆Java虚拟机里面管理内存中的最大一块,堆也是所有线程共享的一块内存区域。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。 123Dog myDog = new Dog();//new Dog()要求虚拟机分配堆空间给新建的Dog对象//上述的程序先是对象的声明,然后创建与赋值三个步骤。","link":"/2020/01/08/Java-SE/%E6%A0%88%E4%B8%8E%E5%A0%86/"},{"title":"通信协议学习记录","text":"前言局域网(Local Area Network,LAN)是最常见到的,也是应用最多的一种计算机网络,大到各行各业的企业内部网络,小到千家万户的家庭网络都属于局域网(仅指内部网络部分)。我们常说的校园网通常也是一种局域网 广域网(Wide Area Network,WAN)是规模最大的一种计算机网络,分布的地理范围可以非常广。 这篇讲下通信协议,现在是想讲下ssh与ssl 发送端是把通信连接建立指令和用户应用数据从上层向下层传输的,直到最低的物理层;而接收端是把通信连接建立指令和用户应用数据从下层(从最低的物理层开始)向上层传输的,直到与发送端发起通信的对等层。 下面图是计算机网络互连体系结构图OSI/RM:包括了广域网中不同局域网间通信的功能层次(上面五层),也给出了局域网内部通信所必需的两个层次(最下面两层)。 OSI/RM低四层(从物理层到传输层)定义了如何进行端到端的数据传输,也就是定义了如何通过网卡、物理电缆、交换机和路由器进行数据传输;而高三层(从会话层到应用层)定义了终端系统的应用程序和用户如何彼此通信,也即定义了如何重建从发送方到目的方的应用程序数据流。更多的是把OSI/RM的七层结构分成低三层和高四层的,低三层负责创建网络通信所需的网络连接(面向网络),属于“通信子网”部分,高四层具体负责端到端的用户数据通信(面向用户),属于“资源子网”部分。 ssh(安全外壳协议)SSH仅仅是一协议标准,其具体的实现有很多,既有开源实现的OpenSSH,也有商业实现方案。使用范围最广泛的当然是开源实现OpenSSH。其目的是为了实现安全远程登录以及其它安全网络服务。ssh属于上述计算机网络体系中的应用层的。 ssh root@IP","link":"/2020/04/14/%E5%85%B6%E5%AE%83/%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE/"},{"title":"英语记录","text":"英语积累前言这一篇blog是记录自己在学习过程中遇到的一些英文单词,特别是频率较高的。 在linux上的 单词 翻译 synopsis 概要、大纲 mount 直译是组织安排,用来挂载Linux外的文件 Java上的 单词 翻译 subsequent 参数 allocate 分配 surrogate 替代的 radix 基数、根值 pattern 模式,方法 cache 隐藏物,储存物 delimiter 定界符 invocation 调用 semicolons 分号","link":"/2019/11/02/0%E7%B3%BB%E5%88%97/English/"},{"title":"随记笔记","text":"在重写继承的时候,子类的权限不能比父类低 类对接口的实现,其实体现了多态性,因为类需要重写接口中所有的抽象方法。 构造函数:特殊类型的方法,没有返回值,与void的普通方法不一样, 归并排序:稳定,时间复杂度 O(nlog n) 快速排序:不稳定,时间复杂度 最理想 O(nlogn) 最差时间O(n^2) 堆排序:不稳定,时间复杂度 O(nlog n) 归并排序时间复杂度 O(n),消耗空间最多。 比特成帧,帧成报文,报文成包,包成段(物理层,数据链路层,网络层,传输层的单位) IP地址分类 A:0.0.0.0~127.255.255.255 B:128.0.0.0~191.255.255.255 C:192.0.0.0~223.255.255.255 ARP协议(Address Resolution Protocol,地址解析协议),属于IPv4协议簇,工作在数据链路层。其功能是将IP地址解析为对应的MAC地址。 RARP协议(Reverse ARP,反向ARP协议),其功能是将MAC地址解析为对应的IP地址。 JDK1.8后,接口中可以有静态方法,静态方法必须有方法体。 Java中可以接受抽象类、类与接口对象作为方法的参数 三步走学习方法-分散知识点学习、刻意训练、寻求反馈 做题法:读题、用尽所有想法、尝试解答、查看答案、套用多个条件、更换不同测试用例。 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。 Java中的Iterator功能比较简单,并且只能单向移动: (1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。 (2) 使用next()获得序列中的下一个元素。 (3) 使用hasNext()检查序列中是否还有元素。 (4) 使用remove()将迭代器新返回的元素删除。 Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。","link":"/2019/11/08/0%E7%B3%BB%E5%88%97/%E9%9A%8F%E8%AE%B0/"},{"title":"标签测试","text":"总结2019 展望2020完成完成第一份意义上的实习 参加了校内技术性比赛,获得了名次,虽然不大,但是也算是新的尝试 篮球上获得了很多,下半年集中在自己的目标上,明确了自己的目标 展望要在新的一年斩获好的多的offer 补充自己的知识,不断学习,Javaweb框架,看书,看视频 落实自己的Java100计划,以及后期推出的每日25页计划 好好学习,好好准备,愿2020是个丰收的一年 共勉!!!!","link":"/2020/04/01/%E9%9A%8F%E8%AE%B0%E5%8F%8A%E6%80%BB%E7%BB%93/%E6%80%BB%E7%BB%932019%20%E5%B1%95%E6%9C%9B2020/"},{"title":"随记及总结/Git问题记录","text":"title: github问题记录toc: truetags: githubcategories: Git学习date: 2019-11-8 git问题:fatal:HttpRequestException encountered解决 记录删除后的文件恢复 因为自己的一波猛操作,将本地文件不小心给干掉了 找不到了 通过找head的id 最后操作回去,将其还原","link":"/2020/04/01/%E9%9A%8F%E8%AE%B0%E5%8F%8A%E6%80%BB%E7%BB%93/Git%E9%97%AE%E9%A2%98%E8%AE%B0%E5%BD%95/"},{"title":"0系列/0-equals与==","text":"==基本数据类型就是比较值,如果是引用类型的话,就是比较地址 equals在string类里面,“equals()”比较字符串中所包含的内容是否相同,equals方法是可以重写的,所以在一些类型中,已经被重写了. 原本的object类下的equals方法是先比较 string类下的equals array类下的equals","link":"/2020/04/01/0%E7%B3%BB%E5%88%97/0-equals%E4%B8%8E==/"},{"title":"html css js快速了解应用","text":"html css js快速了解应用“HTML是名词,CSS是形容词,JavaScript是动词” css CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明: CSS注释以 “/*” 开始, 以 “*/“ 结束, 如果你要在HTML元素中设置CSS样式,你需要在元素中设置”id” 和 “class”选择器,在css中使用#来匹配id class 选择器用于描述一组元素的样式,class 选择器有别于id选择器,class可以在多个元素中使用。使用的是.匹配 三大选择器 12345678910111213141516171819202122232425262728293031323334353637383940414243<!DOCTYPE html><head> <title>css学习</title><!-- 选择器 --><link rel=\"stylesheet\" href=\"./style.css\" ><style> /* 标签选择器 */ /* 优先级从小到大,往下 h2{ color: antiquewhite; } .h3{ color: black; } #id{ color: blue; }</style></head><body> <!-- 选择器 选择页面的某一个元素 --> <!-- 标签 --> <!-- 类 多个选择渲染--> <!-- id选择器 保持唯一--> <h1> 学习css </h1> <h2> 什么东西 </h2> <h3 class=\"h3\"> 类选择器学习 </h3> <h4 id=\"id\"> id选择器 </h4></body> backgound属性 text-decoration 属性用来设置或删除文本的装饰。 p {text-indent:0px;} text-indent用来缩进 font-style:用来设置字体样式,用em来设置字体大小 列表序号 表格属性 边框 border-style:用来定义边框属性","link":"/2019/12/08/0%E7%B3%BB%E5%88%97/html%20css%20js%E5%BF%AB%E9%80%9F%E4%BA%86%E8%A7%A3%E5%BA%94%E7%94%A8/"},{"title":"0系列/javaweb","text":"title: tomcat记录2toc: truetags: Javawebcategories: Javaweb学习date: 2019-11-8 tomcat war文件复制,也可以 部署文件代码的做法,不用设置将其放置在webapps目录下。以下方法就使用虚拟目录的(文件的名称)去访问,这个xml文件。热部署方式 后缀bak临时文件 web-inf目录——动态资源文件目录 在webapps的root里面 tomcat执行servlet的原理及过程 详解servlet的三个方法 servlet配置 request 12345//重点注意String getContextPath()---获取虚拟目录的方法 //获取请求url String getRequestURI()--不完全的网址路径 String getRequestURL()--完整的网址路径--http://loacalhost...","link":"/2020/04/01/0%E7%B3%BB%E5%88%97/javaweb/"},{"title":"学习总结","text":"2月到今天的总结收获 学习完了java的全部知识,过了一遍 leetcode刷题差不多30篇,还是有点懒,应该做多一点,一开始的每天一道也没有做 还有数据结构,现在学到查找算法,还是可以,但是就是有点慢 看书也看了差不多一百页吧,杂七杂八的,不包括Java编程思想 大的收获是可以自己不看答案做出了一些leetcode题,编程代码能力有明显提高 也开始看了javaweb的东西,今天看了20p差不多 看来80多p的mysql的sql语句篇,知道了一些语句的用法,如having、group by,like等 对整体的学习情况,有一定的了解 缺点以及要改进的 要指定计划,起码一些基础的练习要形成习惯,比如leercode刷题,看视频,做笔记 博客要跟上,不能落下太多,多向别人学习 多总结,要及时整理笔记,明天开始,要每天拿出一点时间来复查 多收集面试的一些情况,做好准备了,找时间去拍个照片 真的不能懒下去了,没有什么时间了 多看书,多看视频","link":"/2020/03/03/0%E7%B3%BB%E5%88%97/%E6%80%BB%E7%BB%93/"},{"title":"工具分享","text":"Intellij Idea2019版本激活,目测可以激活到2089!!前言最近的idea都突然过期,即之前的激活码的都失败了,自己也试了两次自己激活码注册,导致时不时又失效了,今天查到了一些办法,总结出来给需要的人。这个方法可以激活到2089年。用了半个小时泄露这篇文章,想给大伙一点参考。都是借鉴来的,有不对的地方,多多指教! 步骤(直接激活新下载的) 先是链接: https://pan.baidu.com/s/1709ltiM5fIxZFYMjnROBKQ 提取码: r9dt 里面有激活码破解码,破解补丁,需要两个配合使用的 将里面的.jar文件放置到你电脑装idea的路径里面的bin目录下 下载链接——idea下载,选择你想要的打开idea, 新装的或者你重新装的话,可以免费试用30天进去,在help栏下点击-edit custom VM options 在最后一行加多一行 -javaagent:你的安装路径\\jetbrains-agent.jar(就是idea的安装路径) 成功然后将重新打开idea,点击Activation code方式激活,选择一个激活码填进去,激活即可发现ok就可以点进去成功了 注意(不想重新下载的小伙伴)有些人已经是失效了,已经进不去idea,也不想重新下载,那就先随便找个激活码,进去idea先,你才可以修改文件配置 这里给出一个参考的链接——这个链接可以生成下载激活码,你也可以直接用但是这种感觉随时失效的,还是进去之后按上面的步骤下来做。 感谢阅读参考链接 一篇博客 51博客 激活网站","link":"/2020/01/08/0%E7%B3%BB%E5%88%97/%E6%BF%80%E6%B4%BB/"},{"title":"tomcat记录","text":"解决tomcat中文控制台乱码输出乱码情况 解决后 解决方法找到你的电脑上tomcat文件夹下的conf配置文件中的logging.properties–用来配置tomcat的日志输出方式,这里表示文件输出和控制台输出 里面很多输出都是默认设置UTF-8的编码,找到logging.Console…那一行修改即可,想要正常中文的就将其修改为GBK编码。 最后其实还有设置电脑注册表配置来解决,网上也都有,大家可以自行参考。","link":"/2019/11/08/0%E7%B3%BB%E5%88%97/%E8%A7%A3%E5%86%B3tomcat%E4%B8%AD%E6%96%87%E6%8E%A7%E5%88%B6%E5%8F%B0%E4%B9%B1%E7%A0%81%E8%BE%93%E5%87%BA/"},{"title":"Java-SE/synchronized","text":"title: 浅谈java并发toc: truetags: java并发categories: Java学习date: 2020-01-8 浅谈synchronized[toc] 前言看多线程的相关书籍的时候,会经常阅读到一个使用前景,就是银行的取钱存钱操作,假设我们使用两个线程来模拟取钱操作,模拟两个人使用同一个账户并发取钱的问题,我们都知道存款不可能为负的,但是往往并发操作的时候,可能就会导致系统出错导致出现负的数字(假设一开始都是200元余额,两边同时操作取出150,系统可能就会出错)。出现类似这种问题就是缺少同步安全性,为了解决这个问题,Java就增进了同步监视器来解决这个,也就是本文讲的synchronized的作用。 是什么synchronized是Java中的关键字。同步的意思,用在解决线程安全问题上。有添加在方法上,和直接修饰代码块。有种保护的作用,使用修饰之后,家门上了锁,别人进不来。用synchronized修饰的方法只允许一个线程执行,其他线程无法进入该方法。(原子性操作),银行使用该操作之后,就会变成一个排队操作一样,像前言的例子,就会变成一个人等另一个操作取出150之后才可以取钱,这样就不会出现负的余额。 基本上所有的并发模式 在解决线程冲突的时候,都是采用序列化访问共享资源的方案。就是在给定的时间间隔内只允许一个任务访问共享资源。 1所以synchronized保护的是线程遭受破坏,必须按照允许的权限进行资源访问。 记得synchronized关键字可以修饰方法,可以修饰代码块,但不能修饰构造函数、属性等。 1//long与double的操作不是原子的 格式 同步代码块123456//synchronized代码块//obj对象即表示线程开始执行同步代码块之前,必须先获得对同步监视器的锁定//步骤:加锁--修改--释放锁synchronized (obj) { ...} 任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对该同步监视器的锁定。具体的格式参见上面。 同步方法12345678910111213//synchronized方法,可以分为静态方法和普通方法synchronized void method(){ ...}//等价于public void method(){ synchronized(this) { // todo }}//无论哪种形式都可以看做是“{”处获取锁,“}”释放锁 与同步代码块对应,Java 的多线程安全支持还提供了同步方法,同步方法就是使用synchronized关键字来修饰某个方法(放在权限词的后面即可),则该方法称为同步方法。对于同步方法而言,无须显式指定同步监视器,同步方法的同步监视器是this,也就是该对象本身。在格式上可以看到一些用法,但是具体来讲,synchronized用在方法体上还分为用在普通方法和静态方法两种,区别在作用锁对象的不同。 12修饰普通方法作用的是调用这个方法的对象,修饰静态方法作用是调用这个类的所有对象。synchronized static方法可以在类的范围内防止对static数据的并发访问, 使用同步方法可以很方便得实现线程安全的类,这样类的对象可以被多个线程同时安全得访问。 注意 synchronized不能被继承。 collection中的线程不安全的集合可以变成使用Collections工具类的,具体用法语句可以参考API文档,写的还算比较详细。 最后 关于什么使用同步,也就是上锁,引用书上的话 如果你在写一个变量,它可能接下来将被另一个线程读取,或者正在读取上一个已经被另一个线程写过的变量,那么你必须使用同步,并且,读取线程都必须用相同的监视器锁同步 ——Brain Goetz《Java Concurrency in Pactice》的作者 个人学习记录,有错误欢迎指点,谢谢!加油!","link":"/2020/04/01/Java-SE/synchronized/"},{"title":"浅谈java内部类","text":"前言内部类,讲完前面的特性,今天就讲下内部类这个用的比较多,出现频率挺高的知识点。 [toc] what is that?首先,顾名思义,内部类就是在类的内部,也就是类的类,嵌套在里面的。直接代码介绍,现一般分为成员内部类和局部内部类,还有一种匿名类。内部类拥有对外围对象的引用。大部分使用的都是成员内部类。成员内部类是一种与Field、方法、构造器和初始化块相似的类成员;局部内部类和匿名内部类则不是类成员。 成员内部类定义在类里面的成员变量域的类,就是成员内部类。此时的内部类作为其外部类的成员,所以可以使用任意访问控制符如private、protected和public等修饰。 1234class A { //成员内部类,这种包含类的结构 class b{}} 成员内部类可以分为静态内部类和非静态内部类。注意:根据静态成员不能访问非静态成员的规则,外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例等。 静态内部类(带static) static关键字的作用是把类的成员变成类相关,而不是实例相关,即static修饰的成员属于整个类,而不属于单个对象。静态内部类只可以访问静态的外部类的方法和对象。但是静态内部类可以有非静态成员。 123456789101112131415161718192021/** * 这一篇是用来测试静态内部类的使用的 * @author yhy * @date 1/14 */public class StaticInner { private int num = 7; /** * 定义静态常量,这个才可以被静态内部类调用 */ private static int num2 = 6; static class Innerclass{ private static int num3; public void innerfangfa(){// System.out.println(\"调用外部类的成员变量:\"+num);,这个会报错,因为不能调用非静态的 System.out.println(\"调用外部类的成员变量:\"+num2); } }} 报错信息 外部类使用静态类的代码。 12345678910111213141516171819202122232425262728293031public class StaticInner { private int num = 7; /** * 定义静态常量,这个才可以被静态内部类调用 */ private static int num2 = 6; /** * 定义一个静态内部类 */ static class Innerclass{ private static int num3; private int num4 = 4; public void innerfangfa(){// System.out.println(\"调用外部类的成员变量:\"+num);,这个会报错,因为不能调用非静态的 System.out.println(\"调用外部类的成员变量:\"+num2); } } /** * 这是外部类的方法,用来验证访问内部类的方法 */ public static void inneracess(){// 通过类名访问静态内部类的类成员 System.out.println(\"访问静态内部类:\"+Innerclass.num3);// 通过实例访问静态内部类的实例成员 System.out.println(\"访问静态内部类:\"+new Innerclass().num4); }} 非静态内部类(不带static) 内部类可以直接访问外部类的变量、方法等,而外部类只能通过显式创建类才可以。包括可以访问外部类的private。同时,在非静态内部类里不能有静态方法、静态field、静态初始化块。 12345678910111213141516171819202122232425262728293031323334353637/** * 这一篇是用来讲内部类的一些定义的 * @author yhy * @date 1/14 */public class InnerClass { private int outernum = 9; /** * 创建内部类,同时还定义一个常量,为private */ class Inner{ private int innernum = 8;// 定义内部类的方法,去调用外部类的成员常量 public void outAcess(){ System.out.println(\"外部类的值:\"+outernum); } } /** * 这个是外部类的成员方法 * 这里直接调用内部类会报错,不可以直接innernum使用 * 要通过显式创建内部类对象才可以实现 */ public void innerAcess(){ System.out.println(\"内部类的值:\"+new Inner().innernum); } public static void main(String[] args) {// 创建外部类对象,没创建内部类对象 InnerClass i = new InnerClass(); i.innerAcess(); }}//非静态内部类对象必须寄存在外部类对象里,而外部类对象则不必一定有非静态内部类对象寄存其中 非静态内部类的方法内访问某个变量时,系统优先在该方法内查找(如果存在就使用)——if not,则到该方法所在的内部类中(存在则使用)——if not,则到该内部类所在的外部类(如果存在则使用)——if not,系统将出现编译错误。 局部内部类局部内部类是定义在类的内部方法或者其他作用域里面的内部类。 123456class A { //局部内部类 public void B(){ class B{} }} 局部内部类的使用 1234567891011121314151617181920212223242526272829303132333435package music.daima.ebook; /** * * 这一篇是用来观察局部内部类的定义使用等,thinking in java的练习九 * * @author yhy * * @date 1/14 * */ interface Ex9Interface{ /** * @param s string类型在下面给内部类调用 * 定义一个接口,同时包含一个say方法,给下面使用 */ void say(String s); }public class JuBuInner { /** * @return Inner()返回该类对此接口的引用 * // 这是一个方法,下面在里面定义一个内部类 */ Ex9Interface f() { class Inner implements Ex9Interface { @Override public void say(String s) { System.out.println(s); } } return new Inner(); } public static void main(String[] args) { JuBuInner x = new JuBuInner();// 调用局部内部类的say方法 x.f().say(\"hi\"); }} 局部内部类不可以为public 和 private来修饰。而且因为它的上一级是方法,所以用static来修饰它是没有意义的,所以也不能用static来修饰 匿名内部类这个知识点就直接看代码了解一下,次数少的时候可以用它,因为用完就是垃圾了,是内部类的简化写版 ,本质不是类而是匿名对象(继承该类或者实现了改接口的)。 前提得提前存在一个类或者是接口给它用,可以是具体类也可以是抽象类。 格式new 类名or接口名() {重写方法}; 123456789101112131415161718192021222324252627282930313233343536package music.daima.ebook;/** * thinking in java * @author yhy * 匿名内部类 */public class AnonymousInner { public static test1 getTest(int i){// 匿名类的结构如下,这里的i会传给基类的构造器 return new test1(i) { { System.out.println(22); }// 重写f方法 @Override public void f() { System.out.println(\"在匿名类内\"); } };//这里要注意有分号 } public static void main(String[] args) { test1 abc = getTest(55); abc.f(); }}/** *创建一个抽象类 */abstract class test1{// 基类的构造器 public test1(int i){ System.out.println(\"i is :\"+i); } public abstract void f();} 《Java的编程思想》中的练习题答案——使用匿名内部类 1234567891011121314151617181920212223interface Inner12 { void modifyOuter();}public class Outer12 { private int oi = 1; private void hi() { System.out.println(\"Outer hi\"); } public Inner12 inner() { return new Inner12() { public void modifyOuter() { oi *= 2; hi(); } }; } public void showOi() { System.out.println(oi); } public static void main(String[] args) { Outer12 out = new Outer12(); out.showOi(); out.inner().modifyOuter(); out.showOi(); }} 下面的pd.method()方法调用结果是一样的,下面用的就是匿名内部类,相对简单一点,但是不好理解。记住它是一个对象而不是类。 why use it? 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据 内部类可以对同一个包内的其他类隐藏起来 当想要定义一个回调函数且不想编写大量代码,可以考虑使用匿名内部类比较便捷 ——《Java核心技术卷一》 个人理解: 内部类可以将其封装起来,很好的隐藏,可以用protected 和private权限来修饰。 拥有访问外部类的所有元素的权限,普通的内部类对象隐式地保存了一个引用,指向创建它的外围类对象。 比如可以很好处理类的办法同名重叠情况,更加有效地去引用。 可以间接实现了多重继承,内部类继承其它类,然后实例化外部类可以实现这种间接效果 how to use?1.在外部类以外使用静态内部类(因为静态内部类是外部类类相关的,因此创建内部类对象时无须创建外部类对象。) 12345678910111213141516171819202122232425/**这是用来测试在外部类以外使用静态内部类 * @author yhy */public class innerOut { public static void main(String[] args) { Out1.In1 abc = new Out1.In1(); }}/** * 定义一个外部类 * @author yhy */class Out1{ /** * 定义一个静态内部类,静态内部类是直接与外部类相关的 */ static class In1{ void FangFa(){ System.out.println(\"这是静态内部类\"); } }}//output:这是静态内部类 2.在外部类以外使用非静态内部类123456789101112131415161718192021222324252627/** * 使用教程——外部类以外使用非静态内部类 * @author yhy */public class InnerTest { public static void main(String[] args) {// 格式,先外部再内部 = new 外部.new 内部 Out.In test = new Out().new In(); test.inner(); }}/** * 定义一个外部类 */class Out{ /** * 定义一个非静态内部类 */ class In{ void inner(){ System.out.println(\"打印出内部类的信息\"); } }}//output:打印出内部类的信息 3.在外部类内部使用内部类 在上面有使用的代码 总结静态内部类和非静态内部类区别只是在创建内部类对象时,静态的只需使用外部类即可调用构造器,而非静态则一定使用外部类对象来调用里面的方法。","link":"/2020/02/24/Java-SE/%E5%86%85%E9%83%A8%E7%B1%BB/"},{"title":"浅谈java接口","text":"[TOC] 抽象类和抽象方法定义 抽象方法和抽象类都必须被abstract关键字修饰。 抽象——abstract,抽象类的方法不一定是抽象的,但抽象方法出现的类一定是抽象类。 12//抽象方法,没有方法体(即没有{}),只有声明abstract void f(); 最重要的是:抽象类,抽象既不是真的,所以,抽象类不可以实例化。但可以通过抽象类的实例化来使用 12345678910111213141516171819202122232425262728293031323334353637383940/** * @author yhy * 用来完成9.2的练习 * 验证抽象类与抽象方法的使用 */public class YanZheng { public static void main(String[] args) {// 不能被实例化,抽象类,会报错// ChouXiang chouxi = new ChouXiang() ;// 可以实例child类// 即通过继承其子类来实现不能继承抽象类 Child test = new Child(); }}abstract class AbstractChouXiang{ /** * 构造函数 */ AbstractChouXiang() { } /** * 定义一个抽象类的抽象方法 */ abstract void chouxiang();}class Child extends AbstractChouXiang{ Child(){ System.out.println(\"实例时候就打印出来\"); } /** * 注意这里不是abstract就不要讲方法定义为abstract */ @Override void chouxiang(){ System.out.println(\"继承抽象类\"); }} 子类可以不是抽象类,但要实现抽象父类中的所有抽象方法,否则必须定义为abstract类型。(下面的代码中,我将其子类的重写方法注释掉之后,就会报错must be declared abstract or implentment abstract method) 与普通类的区别以及注意点: 抽象类也是可以与普通类那样,可以直接extends,区别在于抽象类不能直接实例化,可以通过实例化其子类,通过子类重写方法实现等——设置抽象方法就是让子类来实现的,否则毫无意义。 与普通方法的区别 抽象方法和空方法体的方法不是同一个概念。例如,public abstract void test();是一个抽象方法,它根本没方法体,即方法定义后面没有一对花括号;但public void test(){}方法是一个普通方法,它已经定义了方法体,只是方法体为空,即它的方法体什么也不做,因此这个方法不可使用abstract来修饰。——疯狂的Java讲义 abstract不能用于修饰Field,不能用于修饰局部变量,即没有抽象变量、没有抽象Field等说法;abstract也不能用于修饰构造器,没有抽象构造器,抽象类里定义的构造器只能是普通构造器。 抽象类的作用 《thinking in java》 抽象类是普通的类与接口之间的一种中庸之道。 抽象方法、抽象类可以使类的抽象性明确起来,告诉用户和编译器怎么使用它们; 同时,抽象类是很好的重构工具,在后期的工作中,可以实现重用性。 体现一种模板的效果,从一群相似的子类提炼出一个抽象类的感觉一样,提供了一种规范,子类可以在其原来的基础上进行扩展。 抽象父类可以只定义需要使用的某些方法,把不能实现的部分抽象成抽象方法,就是一中留给下一代去实现,一开始没有能力去实现,那可就给厉害的人去做,留给其子类去实现。 接口定义 特殊的“抽象类”——接口(interface):比抽象类更加抽象的是接口,在接口中所有的方法都是抽象的。就不能像上面的抽象类一样还可以有普通方法。 1234567//省略public就变为默认级别,只能在当前包所访问public interface Figure { //接口中静态成员变量String name = \"几何图形\";//省略public static final // 绘制几何图形方法void onDraw(); //省略public 这里是抽象方法} Java中可以implements多个接口,多继承的含义便是接入多个接口(继承只能单继承) 一个类可以实现一个或多个接口,继承使用extends关键字(但接口只能继承接口),实现则使用implements关键字。 示例 JieKou.java 1234567891011121314151617181920import java.text.SimpleDateFormat;/** * @author yhy * 这个是实现接口定义的代码,在其它地方去调用 * 这里的接口不用public的话,其它的包就访问不了 */public interface JieKou {// 定义了两个常量 /** * 这里定义一个df变量来获取当前时间 */ SimpleDateFormat df = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");//设置日期格式 String AUTHOR = \"yhycoder\"; /** * 定义一个接口的方法 * 这里的public是多余的,在接口里面是自动为public */ /*public*/ void print();} 使用接口的Java代码 123456789101112131415161718192021222324252627282930313233//访问了其它包的接口,就是下面这个地址import music.daima.ebook.JieKou;import java.util.Date;public class UseInterfaces { public static void main(String[] args) {//实例化using类,实现查看代码的运行情况 Using Shuchu = new Using(); Shuchu.print(); }}/** * 这里是接口继承接口 */interface Jiekou2 extends JieKou{ String num = \"接口2\";}/** * 这里是Using类实现了JieKou和Jiekou2接口,逗号隔开 */class Using implements JieKou,Jiekou2 { /** * 重写了方法,调用接口定义的常量 */ @Override public void print() { System.out.println(AUTHOR+\"在新的包里面使用接口的时间:\"+df.format(new Date())+\" 同时还有\"+num); }} 注意 接口与抽象类一样都不能被实例化 实现接口时接口中原有的抽象方法在实现类中必须实现。默认方法可以根据需要有选择实现(覆盖)。静态方法不需要实现,实现类中不能拥有接口中的静态方法。(Java 8之后) 123456789101112131415161718//InterfaceA.java文件,定义一个接口 public interface InterfaceA {//抽象方法 void methodA(); String methodB();// 默认方法 default int methodC() { return \"6666\"; }// 默认方法 default String methodD() { return \"这是默认方法\"; }// 静态方法 static double methodE() { return 0.0; } } 实现接口代码 12345678910111213141516171819202122232425262728293031import xxxx.InterfaceA;public class ABC implements InterfaceA { //重写 @Override public void methodA() { } @Override public String methodB() { return \"实现methodB方法...\"; } //重写覆盖,根据自己的需要来。 @Override public int methodC() { return 500; } }//实现类中不能有接口中的静态方法,最后一行public class HelloWorld { public static void main(String[] args) {//声明接口类型,对象是实现类,发生多态 InterfaceA abc = new ABC();// 访问实现类methodB方法 System.out.println(abc.methodB());// 访问默认方法methodC System.out.println(abc.methodC()); // 访问默认方法methodD System.out.println(abc.methodD()); // 访问InterfaceA静态方法methodE,这里不能通过实现类去使用接口的静态方法,只能通过接口名调用 System.out.println(InterfaceA.methodE()); } } 作用 规范,在分配不同人的任务时,接口就像是总纲一样,告诉大家去实现哪些功能模块等。(命名规范都有限制到) 最后:接口与抽象类的异同不同 接口interface,实现接口则使用implements;抽象类abstract 抽象类可以有普通方法。Java 8 之前接口中只有抽象方法,而 Java 8 之后接口中也可以声明具体方法,具体方法通过声明默认方法实现。 可以继承多个接口,而抽象类不可以。 和类继承相似,子接口扩展某个父接口,将会获得父接口里定义的所有抽象方法、常量Field、内部类和枚举类定义。 实现父接口的所有:一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法(也就是重写这些抽象方法);否则,该类将保留从父接口那里继承到的抽象方法,该类也必须定义成抽象类。 接口定义的是一种规范,因此接口里不能包含构造器和初始化块定义。接口里可以包含Field(只能是常量)、方法(只能是抽象实例方法)、内部类(包括内部接口、枚举)定义。但抽象类与普通类一样,可以有构造器,初始化模块等。 接口只有常量——接口中不能有实例成员变量,接口所声明的成员变量全部是静态常量,即便是变量不加 public static final 修饰符也是静态常量。抽象类与普通类一样各种形式的成员变量都可以声明。 相同 都不能直接实例化来使用。 接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。 使用场景 想要多重继承的时候——接口(功能性强,规范性)-要创建不带任何方法定义和成员变量的基类 想要底层基础功能模块不断改变——抽象类(模板设计) 感谢才疏学浅,不对的地方多多指教! 借鉴博客园的小伙伴 也是园内小伙伴","link":"/2020/01/08/Java-SE/%E6%8E%A5%E5%8F%A3/"},{"title":"浅谈java泛型","text":"泛型泛型定义在类上。 泛型类:把泛型定义在类上 123public <T> void show (T t){ //这里将泛型定义在方法上。而且这个可以将任意转换} 泛型定义在接口上不知道接口是什么类型的 12public static void main A<T> implements B<T> //将其两边都标记泛型 泛型高级类型 1Animal的子类是Dog Cat 父类是Object 明确写的时候 12//前后需要一致Collection<Object> c = new ArrayList<Object> ?表示任意类型都可以 1Collection<?> c = new ArrayList<Object> ?extends E :向下限定,E及其子类 123Collection<? extends Animal> c = new ArrayList<Animal>();Collection<? extends Animal> c = new ArrayList<Dog>();Collection<? extends Animal> c = new ArrayList<Cat>(); ?super E:向上限定,E及其父类 12Collection <? super Animal> c = new ArrayList<Object>();Collection <? super Animal> c = new ArrayList<Animal>();","link":"/2020/01/18/Java-SE/%E6%B3%9B%E5%9E%8B/"},{"title":"Java-SE/网络编程","text":"网络编程网络模型分为了OSI模型(七层),和TCP/IP模型(四层)-应用层、网络层、链路层、以及物理层。 网络编程三要素IP地址–网络中的计算机的唯一标识分为了四类:A、B、C、D类(常用的是后两种) 在DOS命令里可以通过ipconfig查看本机的ip地址、和使用ping命令查看本机与相应的ip地址是否存在通信问题。 端口–程序运行的标识范围是0-65535,0-1024为系统所使用,一般建议使用10000以上。 协议–通行规则分为UDP和TCP两大类 UDP 速度快,但信息传输不可靠,不建立连接,多用于数据打包,大小不大于64k,不在乎数据是否传送完成以及成功。 TCP 速度慢,但是传输可靠,建立连接,在连接中进行大量的数据传输,但数据传输效率较低。 Socketsocket包含了ip和端口信息,通信两端都有socket,也叫做套接字。 网络通信其实也是socke之间的通信,数据在两个socket之间相互传输。","link":"/2020/04/01/Java-SE/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/"},{"title":"浅谈java集合","text":"集合[TOC] 摘要集合只用于存储对象,集合分为了collection(单列)和map(双列)两种。 因为我们要讲的以下都是继承了父类collection,所以可以先对父类的方法进行研究,这些父类的方法在子类中也是会重复出现的,如果熟悉了,以后遇到了哪一个集合类都可以对这些方法运用得很好了。直接看jdk上的这些方法,当然而不需要背,有个印象,但需要的时候可以查仔细的用法就行了。 list前言list是只关心顺序的,所以会有重复的,而且,List必须按照插入的顺序来保存元素。 是什么? List 接口的实现类有:ArrayList 和 LinkedList。ArrayList 是基于动态数组数据结构的实现,LinkedList 是基于链表数据结构的实现。ArrayList 访问元素速优于 LinkedList,LinkedList占用的内存空间比较大,但 LinkedList 在批量插入或删除数据时优于 ArrayList。 怎么用? 定义创建 1234List s = new List();ArrayList s = new ArrayList();LinkedList s = new LinkedList();Vector s = new vector(); List中特有的方法 注意点set前言set不能有重复的元素 map","link":"/2020/01/18/Java-SE/%E9%9B%86%E5%90%88/"},{"title":"浅谈java中的Collection","text":"谈谈Collection前言这一篇讲的collection接口;首先,集合是用来存储数据的,它是基于某种数据结构数据容器。常见的数据结构:数组(Array)、集(Set)、队列(Queue)、链表(Linkedlist)、树(Tree)、堆(Heap)、栈(Stack)和映射(Map)等结构。集合大类分为了Collection和Map。如下图 Collection这一篇文章的内容——现在讲下collection,“集合、容器”,用来存储数据的,它是一个接口,不能直接实例化使用;只能通过它的子类来完成,从上图来看,Collection分为List和Set,List集合中的元素是有序的、可重复的,而Set集合中的元素是无序的、不能重复的。List集合强调的是有序,Set集合强调的是不重复。下面是它的已知实现类。 1Collection是描述所有序列容器的共性的根接口,可能会被认为是一个附属接口,即因为要表示其他若干个接口的共性而出现的接口。同时为什么要将其作为接口?是因为可以使我们能够创建更通用的代码。通过针对接口而非具体实现来编写代码,这样,才可以更好地应用于更多的对象类型。 下面的是Collection更加仔细一点的分类。今天这篇重点介绍一下Collection的几个方法,掌握了这些,对于它的子类都是可以直接拿来用的。子类的介绍后面再总结出来。 方法遇到一些自己不熟的知识点,在IDEA上可以直接查看该接口的详细源码(ctrl加鼠标左键),点开旁边的结构图按钮,可以直观得去查看这些方法 1.boolean add(E)这个一看就知道了,就是添加功能,往集合里面添加元素或者对象的方法,若是list这种,因为它返回都是true,一般情况下,可以随便往里面添加。 123Collection s = new Collection();s.add(你要添加的);s.add(\"你要添加的\"); 有时需要注意的时候,如果操作的集合是不允许重复值的,往里面添加就会报错。返回的是false. 2.void clear()移除容器中的所有元素,该集合不支持移除的话就会抛出UnsupportedOperationException异常(不支持该操作) 123//接着用上面的s集合s.clear();//这时候集合为空了 3.boolean contains(Object o)用来检查此集合是否包含指定元素或者对象,包含则返回true不包含就返回false;会返回两种错误。 4.boolean isEmpty()用来检查集合中是否为空,如果为空就返回true。 5.Iterator iterator()返回一个Iterator迭代器,这个方法是用来遍历集合中的元素的,在一些需要获取集合中的元素(包括打印输出,调用等场景下),依赖于集合而存在的,有next方法和hasNext方法。 这两个方法一般情况下都是绑定一起用的。形式如下。 1234567//其主要的用法如下,遍历的功能//通过集合对象获取迭代器对象Iterator i = 集合.iterator();//hasNext方法是布尔型的返回值,有元素在集合里面的时候就会返回truewhile(it.hasNext()){ System.out.println(it.next());} 6.int size()用来返回的集合的长度,也就是集合里面的元素的个数。 12//直接使用,返回长度集合.size(); 7.Boolean remove()用来删除集合中的元素,对象,只要有移除动作就会返回true。 总结什么是可选操作在看源码的时候,会发现有些方法在后面会标记为可选操作 (optional operation)。 执行各种不同的添加和移除的方法在Colletion接口中都是可选操作,这意味着实现类并不需要为这些方法提供功能定义。……如果一个操作是可选的,编译器仍旧会严格要求你只能调用该接口中的方法。 它声明调用某些方法将不会执行有意义的行为,相反,它们会抛出异常。 ——《Java编程思想》 异常UnsupportedOperationException(这个和上面讲remove时候不支持操作的时候抛出一样的异常) 大概意思是collection子类可以重写这个方法以达到子类自己的目的,也可以不覆盖这个方法但是。没有实现这个方法的子类使用这个方法的话会抛出UnsupportedOperationException异常。(这个未获得支持的异常在运行时候才能探测到,属于动态类型异常),先看下报错的代码示例 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758package music.daima.ebook;import java.util.ArrayList;import java.util.Arrays;import java.util.Collection;import java.util.List;/** * @author yhy * 这个是用来观察java中的可选操作方法的一些特点以及定义使用以及报错的原因,这是书上的代码 */public class Unsupport { static void test(String msg, List<String> list) { System.out.println(\"--- \" + msg + \" ---\"); Collection<String> c = list; Collection<String> subList = list.subList(1,8); Collection<String> c2 = new ArrayList<String>(subList); try { c.retainAll(c2); } catch(Exception e) { System.out.println(\"retainAll(): \" + e); } try { c.removeAll(c2); } catch(Exception e) { System.out.println(\"removeAll(): \" + e); } try { c.clear(); } catch(Exception e) { System.out.println(\"clear(): \" + e); } try { c.add(\"X\"); } catch(Exception e) { System.out.println(\"add(): \" + e); } try { c.addAll(c2); } catch(Exception e) { System.out.println(\"addAll(): \" + e); } try { c.remove(\"C\"); } catch(Exception e) { System.out.println(\"remove(): \" + e); } // The List.set() 虽然改变了值但没有改变它的数据结构尺寸 try { list.set(0, \"X\"); } catch(Exception e) { System.out.println(\"List.set(): \" + e); } } public static void main(String[] args) { //asList方法:返回由指定数组支持的固定大小的列表。 (将返回的列表更改为“写入数组”。)该方法作为基于数组和基于集合的API之间的桥梁,与Collection.toArray()相结合 。 //返回的列表是可序列化的,并实现RandomAccess 。 此方法还提供了一种方便的方式来创建一个初始化为包含几个元素的固定大小的列表: List<String> list = Arrays.asList(\"A B C D E F G H I J K L\".split(\" \")); System.out.println(list.getClass()); test(\"Arrays.asList()\", list); // System.out.println(list1.getClass()); test(\"Modifiable Copy\", new ArrayList<String>(list)); //test(\"unmodifiableList()\",Collections.unmodifiableList(new ArrayList<String>(list))); }} output(输出): 集合使用的整体框架(步骤)","link":"/2019/11/08/Java-SE/%E9%9B%86%E5%90%88%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95/"},{"title":"算法笔记","text":"笔记所谓算法,基于特定的计算模型,旨在解决某一种信息处理问题而设计的一个指令序列 需要具备的条件正确性、有序性、可行性、重用性、确定性,同时还有输入输出。 bubble sort经过k趟扫描后,最大的前k元素必然就位,;同时待解决问题的规模也减少至n-k 两层循环,内循环从前向后,依次比较各对相邻元素,有必要则将其交换。外循环至多执行n-1次 这里的交换和比较都是不同操作,所以内循环是执行了2(n-1)次 在上面的while外层里面就有一个n–在对问题规模减少在监控 big-O notation 下面也就是在计算时间复杂度的时候可以省略前面的常数项,或者是一些比较小的项","link":"/2019/11/08/%E9%9A%8F%E8%AE%B0%E5%8F%8A%E6%80%BB%E7%BB%93/reading%20notes/"},{"title":"随记及总结/快捷键","text":"title: 快捷键toc: truetags: 随记categories:随记date: 2019-10-8 前言工欲善其事,必将利其器,当然,不需要背诵这些快捷键,这篇博客就是给个参考,你可以通过阅读去掌握这些技巧。 windows其实windows上很多的热键也可以修改的_,键盘的一些键位也.可以通过软件去修改,按照个人的习惯来做就行了BIOS(Basic Input Output System)写死在内存的一套程序。开机选择时候经常用到。开机首先使用的小程序。 F1:打开帮助信息F2:选定了一个桌面的信息F3:打开搜素功能F4:打开浏览器的地址菜单列表F5:刷新F6:迅速定位到页面的编辑框F11:全屏浏览器上的操作ctrl+w:关闭当前的页面Windows+shift+s:截屏 键盘灯的快捷键fn+上下(方向键)可以调节亮度fn+insertfn+homefn+delete 博客编辑网站很多网站的博客文章编辑时的快捷键都是相通相似的,加粗、撤销等都是一样的,个人感觉如果熟悉掌握之后,在平时编辑记录时候是非常方便高效的,像csdn的markdown编辑快捷键可以直接显示在帮助文档中,可以在编辑的时候进行查看自己所需要的快捷键。Notepad++notepad这个就无需多介绍了,这个开源代码编辑器功能强大,支持多种语言,以开源、小巧、免费等特点广受各类脱发人士深爱。 CTRL+L可以快速删除空行,这个是最爱的,经常copy一些文件代码的时候会存在一大堆空行。 CTRL+Q可以添加单行注释,将当前行变为注释。 IDEA快捷键alt+ctrl+l代码格式化,将对齐代码 ctrl+/注释,多行是鼠标选定行即可 选择整个单词 shift + CTRL + 箭头 选择的是光标前后的单词 ctrl+D是可以复制当行,并将其复制在光标的下一行中。–这里将其设置为删除 select in main menu导航 选择打开的,显示当前文件选择目标弹出层,弹出层中有很多目标可以进行选择 alt + f1 我将其设置为alt+s (这个是必备的) 查看api文档的时候想查看文件结构的时候可以快速查看 select next tab 我将其设置为CTRL+ alt + 向左箭头 generate将导入一些原始的方法构造器等 也非常好用的快捷方式 我将其设置为alt + g shift + enter 将直接开始新的一行,并且将光标放在开始处 –必备","link":"/2020/04/01/%E9%9A%8F%E8%AE%B0%E5%8F%8A%E6%80%BB%E7%BB%93/%E5%BF%AB%E6%8D%B7%E9%94%AE/"},{"title":"动态代理","text":"动态代理代理是基本的设计模式之一,代理相当于中间人角色,例如在租房中,就相当于中介,租客和房东之间借助代理中介来交流沟通。","link":"/2020/04/17/Java-SE/%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86/"},{"title":"Mysql基础","text":"本单元目标难点: SQL高级语句:group by ,having ,左连接,,子查询(in),行转列等 建表方面的第三范式和反 MySQL集群 基础 懂得查看日志,排查问题 less命令打开文件 通过grep方式查关键字,grep 关键字 文件名 通过vi编辑文件 通过chmod来设置文件的权限 一、为什么要学习数据库1.持久化数据 2.方便检索 3.存储大量数据 4.共享、安全 5.通过组合分析,获取新的数据 二、数据库的相关概念 DBMS、DB、SQL DB:database数据库,存储一系列有组织数据的容器 DBMS:Database Management System数据库管理系统,使用DBMS管理和维护DB SQL:StructureQueryLanguage结构化查询语言,程序员用于和DBMS通信的语言 三、数据库存储数据的特点1、数据先放在表中,表再放在库中 2、一个库可以有多张表,每张表都有自己的唯一标识名 3、一张表的设计,类似于java中”类”的设计 表中的字段的设计,类似于属性的设计 表中的单条记录,类似于一个对象 表中的所有的记录,类似于对象的集合 orm :object relation mapping 对象关系映射 四、初始MySQL1、MySQL产品的介绍MySQL 前身属于瑞典的一家公司AB,2008年被sun公司收购,2009年sun被oracle公司收购。 特点: ① 体积小、安装较方便 ②开源、免费 ③性能高、稳定性好 ④兼容性好 2、MySQL产品的安装 ★基于C/S架构的DBMS,需要安装服务端和客户端 www.oracle.com MySQL5.5 3、MySQL服务的启动和停止 ★方式一:图形化 右击——计算机管理——服务——MySQL服务 方式二:通过管理员身份运行dos net start 服务名 net stop 服务名 4、MySQL服务的登录和退出 ★方式一:通过dos命令 mysql -h主机名 -P端口号 -u用户名 -p密码 注意: 如果是本机,则-h主机名 可以省略 如果端口号是3306,则-P端口号可以省略 方式二:通过图形化界面客户端 通过sqlyog,直接输入用户名、密码等连接进去即可 5、MySQL的常见命令和语法规范①常见命令 show databases 显示当前连接下所有数据库 show tables 显示当前库中所有表 show tables from 库名 显示指定库中所有表 show columns from 表名 显示指定表中所有列 use 库名 打开/使用指定库 ②语法规范 不区分大小写 每条命令结尾建议用分号 注释: #单行注释 – 单行注释 /多行注释/ 五、DQL语言的学习 ★DQL:Data Query Language 数据查询语言 select 1、基础查询 ★语法: select 查询列表 from 表名; 特点: ①查询结果集是一个虚拟表 ②查询列表可以是单个字段、多个字段、常量、表达式、函数,可以是以上的组合 引申1:起别名 select 字段名 as “别名” from 表名; select 字段名 “ 别名” from 表名; 引申2:+的作用 作用:加法运算 如果两个操作数都是数值型,则直接做加法运算 如果其中一个为非数值型,则将强值转换成数值型,如果转换失败,则当做0 ‘123’+4====》127 ‘abc’+4====》 4 如果其中一个为null,则结果直接为null 引申3:去重 select distinct department_id from employees; 引申4:补充函数 select version(); select database(); select user(); select ifnull(字段名,表达式); select concat(字符1,字符2,字符3); select length(字符/字段);获取字节长度 2、条件查询 ★语法: select 查询列表 from 表名 where 筛选条件; 特点: 筛选条件的分类: ①按条件表达式筛选 关系运算符:>= < <= > <> = ②按逻辑表达式筛选 逻辑运算符:and or not ③模糊查询 like:一般和通配符搭配使用 _ 任意单个字符 % 任意多个字符 between and:一般用于判断某字段是否在指定的区间 a between 10 and 100 in:一般用于判断某字段是否在指定的列表 a in(10,30,50) is null:判断null值 3、排序查询 ★语法: select 查询列表 from 表名 where 筛选条件 order by 排序列表 ; 特点: ①排序列表可以是 单个字段、多个字段、函数、表达式、别名、列的索引,以及以上的组合 ②升序,通过asc 降序,通过desc 4、常见函数 ★说明:sql中的函数分为单行函数和分组函数 调用语法:select 函数名(实参列表); 1》字符函数 concat(str1,str2,..):拼接字符 substr(str,pos):截取从pos开始的所有字符, 起始索引从1开始 substr(str,pos,len):截取len个从pos开始的字符,起始索引从1开始 length(str):获取字节个数 char_length(str):获取字符个数 upper(str):变大写 lower(str):变小写 trim(【substr from】str):去前后指定字符,默认是去空格 left(str,len):从左边截取指定len个数的 字符 right(str,len):从右边截取指定len个数的 字符 lpad(str,substr,len):左填充 rpad(str,substr,len):右填充 strcmp(str1,str2):比较两个字符的大小 instr(str,substr):获取substr在str中第一次出现的索引 2》数学函数 ceil(x):向上取整 floor(x):向下取整 round(x,d):四舍五入 mod(x,y):取模/取余 truncate(x,d):截断,保留小数点后d位 abs(x):求绝对值 3》日期函数 now():获取当前日期——时间 curtime():只有时间 curdate():只有日期 date_format(date,格式):格式日期为字符 str_to_date(str,格式):将字符转换成日期 datediff(date1,date2):获取两个日期之间的天数差 year(date) 指定的格式输出 month(date) 一些标准日期格式符的写法 4》流程控制函数 ①if(条件,表达式1,表达式2):如果条件成立,返回表达式1,否则返回表达式2 ②case 表达式 when 值1 then 结果1 when 值2 then 结果2 … else 结果n end ③ case when 条件1 then 结果1 when 条件2 then 结果2 … else 结果n end 5、分组函数 ★sum求和 avg平均 max最大 min最小 count个数 特点: 1》实参的字段的类型,sum和avg只支持数值型,其他三个可以支持任意类型 2》这五个函数都忽略null值 3》count可以支持以下参数 count(字段):查询该字段非空值 的个数 count(*) :查询结果集的行数 count(1):查询结果集的行数 4》分组函数可以和distinct搭配使用,实现去重的统计 select count(distinct 字段) from 表; 6、分组查询 ★语法: select 分组函数,分组的字段 from 表名 where 分组前的筛选条件 group by 分组列表group by 要放在where 之后,order by 的前面 having 分组后的筛选条件having是过滤操作 HAVING和WHERE的差别 这里有另一种理解方法,WHERE在数据分组前进行过滤,HAVING在数据分组后进行过滤。这是一个重要的区别,WHERE排除的行不包括在分组中。这可能会改变计算值,从而影响HAVING子句中基于这些值过滤掉的分组。 order by 排序列表; 特点: 1》分组列表可以是单个字段、多个字段 2》筛选条件分为两类 筛选的基表 使用的关键字 位置 分组前筛选 原始表 where group by前面 分组后筛选 分组后的结果集 having group by后面 7、连接查询 ★说明:当查询中涉及到了多个字段,则需要通过多表连接 笛卡尔乘积: 出现原因:没有有效的连接条件 解决办法:添加有效的连接条件 JOIN查询图解 ————————–SQL92语法———————— 语法: select 查询列表 ① *from 表1 别名,表2 别名,… *② where 连接条件 ③ and 筛选条件④ group by 分组列表⑤ having 分组后筛选⑥ order by 排序列表;⑦ 执行顺序: ②③④⑤⑥①⑦ ————————–SQL99语法———————— 1》内连接 即所连接的表中都包含相同列。 语法: select 查询列表 ① from 表1 别名② 【inner】 join 表2 别名 on 连接条件 ③ 【inner】 join 表3 别名 on 连接条件 where 筛选条件 ④ group by 分组列表⑤ having 分组后的筛选⑥ order by 排序列表⑦ 执行顺序: ②③④⑤⑥①⑦ 外连接 右外连接意思是在右边的数据有多的行而左边没有匹配到。 8、子查询 √12345select first_name from employees where department_id >( # 子查询 select department_id from departments where location_id=1700) 1、子查询语句需要放在小括号内,提高代码的阅读性 2、子查询先于主查询执行,一般来讲,主查询会用到子查询的结果 3、如果子查询放在条件中,一般来讲,子查询需要放在条件的右侧 示例:where job_id>(子查询) 不能写成:where (子查询)<job_id 9、分页查询 ★10、union联合查询 √11、字符函数 INSTR-返回在字符串中指定的字母索引 SUBSTR-截取字符或者字母 CONCAT-连接 六、DML语言的学习 ★1、插入语句1INSERT INTO 表名(列名1,列名2, …) VALUES(值1, 值2) 2、修改语句123# 格式UPDATE 表名 SET 列名1=值1, … 列名n=值n [WHERE 条件]UPDATE stus SET sname=’zhangSanSan’, age=’32’, gender=’female’ WHERE sid=’s_1001’; 3、删除语句12DELETE FROM 表名 [WHERE 条件] # 可以回滚TRUNCATE TABLE 表名 # 不可以回滚 七、DDL语言的学习1、库和表的管理 √1234567891011121314151617181920212223242526272829l 查看当前数据库中所有表名称:SHOW TABLES; l 查看指定表的创建语句:SHOW CREATE TABLE emps;l 查看表结构:DESC emps;l 删除表:DROP TABLE empsl 修改表结构:a) 修改之添加列:给stus表添加classname列:ALTER TABLE stu ADD (classname varchar(100));b) 修改之修改列类型:修改stu表的gender列类型为CHAR(2):ALTER TABLE stus MODIFY gender CHAR(2);c) 修改之修改列名:修改stu表的gender列名为sex:ALTER TABLE stus change gender sex CHAR(2);d) 修改之删除列:删除stsu表的classname列:ALTER TABLE stus DROP classname;e) 修改之修改表名称:修改stu表名称为student:ALTER TABLE stus RENAME TO student; 2、常见数据类型介绍 √3、常见约束 √八、TCL语言的学习事务和事务处理事务的四大特性:ACID 脏读 两个事务之间的不同步,即B读到A要撤回改变之前的数据,导致两边提交之后的信息不对等 不可重复读 一个事务内在读取某些数据之后,在读取,发现数据发生了变更,因为有其它事务改变了数据 幻读 事务A在按查询条件读取某个范围内的记录,事务B又在该范围内插入了新的满足条件的记录,当事务A再次按条件查询记录时候,会产生新的满足条件的记录。 不可重复读和幻读的区别 不可重复读的重点是修改;而幻读重点在于新增或者删除。 四个隔离级别 未提交读:全支持读 提交读 可重复读 可串行化:全部不支持读 九、数据库连接池的使用是什么数据库连接池:数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。 数据库连接池的作用 可以资源重用 更快的系统反应速度 新的资源分配手段 统一的连接管理 使用12JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现:Druid-阿里提供的数据连接池 学这个开源框架 12345678910111213141516171819import java.sql.Connection;import java.util.Properties;import javax.sql.DataSource;import com.alibaba.druid.pool.DruidDataSourceFactory;//用法参考public class TestDruid { public static void main(String[] args) throws Exception { Properties pro = new Properties(); //pro.load(new FileInputStream(\"\\druid.properties\")) pro.load(TestDruid.class.getClassLoader().getResourceAsStream(\"druid.properties\")); //创建一个指定参数的数据库连接池 DataSource ds = DruidDataSourceFactory.createDataSource(pro); //从数据库连接池中获取可用的连接对象 Connection conn = ds.getConnection(); System.out.println(conn); }} druid.properties 123456789url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=trueusername=rootpassword=123456driverClassName=com.mysql.jdbc.DriverinitialSize=10maxActive=20maxWait=1000filters=wall<--!配置文件--> X`","link":"/2020/04/17/mysql/MySQL%E5%9F%BA%E7%A1%80/"},{"title":"docker中使用MySQL","text":"在 docker 中使用 mysql 安装完docker之后,在命令行中输入docker images可以查看自己创建的image(安装下载docker的教程很多了,大家需要可以去查一下就可以了)这里我之前已经创建了 如果没有的小伙伴,要先pull一下MySQL,版本可以根据自己的需要去选择,弄好之后可以再执行docker images查看。 docker run –name=mysql -it -p 3306:3306 -e MYSQL_ROOT_PASSWORD=password -d mysql 这里是新建一个MySQL数据库容器,命名、端口映射设置以及设置密码。这一句比较重要,设置好对应下面的远程连接。这里的-d指的是后台运行。 在我执行选择对应容器id,进入容器时候开始报错,在报错信息中得知没有启动该容器 docker ps -a可以显示所有容器(默认是在运行的容器) ps 可以接着 -a -f -1等提示语进行(-1表示的是显示上一个create的容器),也可以直接docker ps-显示出来的是2个连接的容器 输入docker start选择id的前六位即可 start之后便可以进入容器,进入容器,使用root登陆进去MySQL中,这里的密码便是之前设置好的密码,输入成功之后便是welcome。 在命令行操作成功之后,便可以使用一些远程软件去连接,实现可视化操作,这里我用的是navicat,其实类似sql developer一些都可以进行登陆,还有plsql developer也可以登陆使用不同的数据库,包括Oracle、MySQL等。Navicat 是以直觉化的图形用户界面而建的,让你可以以安全并且简单的方式创建、组织、访问并共用信息。 这里的连接名可以自定义,所有的连接软件都是这样,连接名都是可以自定义的。端口就是前面设置好的。这里的ip地址都是localhost的,在自己电脑上便是自己的localhost。远程时候需要ip地址。) 上面可以看到在数据库里面的table是一致的,说明测试连接使用是正确的。后面会继续保持更新的,如果你正巧看到这篇文章,麻烦点个赞留个言交流下,谢谢你!加油,共勉!","link":"/2020/04/13/mysql/docker%E9%87%8C%E9%9D%A2%E5%88%9B%E5%BB%BAmysql%E8%BF%9E%E6%8E%A5navicat/"},{"title":"mysql事务","text":"事务[toc] 是什么事务由单独单元的一个或多个SQL语句组成,在这个单元中,每一个mysql语句是相互依赖的;由于整个单独单元作为不可分割的整体,那么如果单元中某条SQL语句一旦执行失败或产生失败,整个单元就会回滚。要么全部成功,要么全部失败。 在mysql中用的最多的存储引擎有:innodb,myisam,memory 等。其中innodb支持事务,而myisam、memory等不支持事务 1查看mysql支持的引擎:SHOW engines 事务的四大特性 原子性:要么成功,要么失败。 一致性:状态与其他业务规则保持一致。 隔离性:在并发中使用。一个事务不被另一个事务所影响 持久性:一旦改变提交之后,数据就持久化被操作了。 分类12隐式事务:没有明显的开启和结束标记显式事务:有明显的开启和结束标记 使用步骤:插个小知识 1234DDL(Data Definition Language):数据定义语言,用来定义数据库对象:库、表、列等;DML(Data Manipulation Language):数据操作语言,用来定义数据库记录(数据);DCL(Data Control Language):数据控制语言,用来定义访问权限和安全级别;DQL(Data Query Language):数据查询语言,用来查询记录(数据)。 一般情况下,如果在一个事务中包含多个SQL语句,那么就要开启事务和结束事务; 12开启事务:start transaction;结束事务:commit或rollback。 开始是以第一个DML语句执行开始 以下面的其中之一作为结束: COMMIT 或ROLLBACK 语句(回滚,即回滚到事务的起点,之前做的所有操作都被撤消了!)DDL 或DCL 语句(自动提交)用户会话正常结束系统异常终了 START TRANSACTION; UPDATE account SET balance=balance-10000 WHERE id=1; UPDATE account SET balance=balance+10000 WHERE id=2; COMMIT;","link":"/2020/04/16/mysql/mysql-%E4%BA%8B%E5%8A%A1/"},{"title":"DFS和BFS","text":"约束[toc] 约束是SQL规范以约束的方式对表数据进行额外的条件限制,创建时候规定(CREATE TABLE),创建之后规定也可以(使用ALTER TABLE语句) 分类具体有以下几种 123456NOT NULL 非空约束,规定某个字段不能为空UNIQUE 唯一约束,规定某个字段在整个表中是唯一的PRIMARY KEY 主键(非空且唯一)FOREIGN KEY 外键CHECK 检查约束(mysql不支持,但可以使用,没有效果便是)DEFAULT 默认值 还可以分为单列和多列约束;列级和表级约束; 使用方法NOT NULL123456789101.CRATE TABLE table1(id INT(10) NOT NULL);# 创建之后的2.ALTER TABLE emp MODIFY sex VARCHAR(30) NOT NULL;# 还可以取消1.ALTER TABLE empMODIFY sex VARCHAR(30) NULL;#(使用DEFAULT 'abc'增加默认值) UNIQUE :唯一约束,允许出现多个空值:NULL。12345ALTER TABLE USER ADD UNIQUE(NAME,PASSWORD);# 删除约束ALTER TABLE USER DROP INDEX uk_name_pwd;# 表级约束ALTER TABLE USER ADD CONSTRAINT uk_name_pwd UNIQUE(NAME,PASSWORD PRIMARY KEY 约束 :相当于唯一约束和非空约束1234567891011/*MySQL的主键名总是PRIMARY,当创建主键约束时,系统默认会在所在的列和列组合上建立对应的唯一索引*/# 组合模式CREATE TABLE emp6(id INT NOT NULL,NAME VARCHAR(20),pwd VARCHAR(15),CONSTRAINT emp7_pk PRIMARY KEY(NAME,pwd));# 删除 DROP 修改 MODIFY 添加 ADDALTER TABLE emp5 DROP PRIMARY KEY; FOREIGN KEY 约束:外键约束是保证一个或两个表之间的参照完整性,外键是构建于一个表的两个字段或是两个表的两个字段之间的参照关系。12345678910111213141516171819202122/*外键约束的参照列,在主表中引用的只能是主键或唯一键约束的列-FOREIGN KEY: 在表级指定子表中的列–REFERENCES: 标示在父表中的列*/# 主表CREATE TABLE classes( id INT, NAME VARCHAR(20), number INT, PRIMARY KEY(NAME,number) ); # 从表CREATE TABLE student( id INT AUTO_INCREMENT PRIMARY KEY, classes_name VARCHAR(20), classes_number INT, FOREIGN KEY(classes_name,classes_number) REFERENCES classes(NAME,number) ); #删除 添加和之前那些约束差不多 MySQL中使用limit实现分页格式 1234select 查询列表from 表【where 条件】limit 【起始条目索引,】查询的条目数; 1234limit子句必须放在整个查询语句的最后!前10条记录:SELECT * FROM table LIMIT 0,10;第11至20条记录:SELECT * FROM table LIMIT 10,10;第21至30条记录:SELECT * FROM table LIMIT 20,10; mysql中的索引‘索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。 索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。就相当于目录。 缺点的一个是索引也是文件,占物理空间 创建索引 在create table就创建 123456789101.CREATE TABLE user_index2 ( id INT auto_increment PRIMARY KEY, first_name VARCHAR (16), last_name VARCHAR (16), id_card VARCHAR (18), information text, KEY name (first_name, last_name), FULLTEXT KEY (information), UNIQUE KEY (id_card)); 创建table之后创建—使用ALTER TABLE 1LTER TABLE table_name ADD INDEX index_name (column_list); 使用CREATE INDEX命令创建 1CREATE INDEX index_name ON table_name(column_list); 删除索引 格式:alter table 表名 drop KEY 索引名 1alter table user_index drop KEY name; 注意的是,如果主键自增长,则不可直接删除,要先解除自增长;不过很少删除主键 B树和B+树 在B树中,你可以将键和值存放在内部节点和叶子节点;但在B+树中,内部节点都是键,没有值,叶子节点同时存放键和值。 B+树的叶子节点有一条链相连,而B树的叶子节点各自独立。 比较123451、B树只适合随机检索,而B+树同时支持随机检索和顺序检索;2、B+树空间利用率更高,可减少I/O次数,磁盘读写代价更低。一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗。B+树的内部结点并没有指向关键字具体信息的指针,只是作为索引使用,其内部结点比B树小,盘块能容纳的结点中关键字数量更多,一次性读入内存中可以查找的关键字也就越多,相对的,IO读写次数也就降低了。而IO读写次数是影响索引检索效率的最大因素;3、B+树的查询效率更加稳定。B树搜索有可能会在非叶子结点结束,越靠近根节点的记录查找时间越短,只要找到关键字即可确定记录的存在,其性能等价于在关键字全集内做一次二分查找。而在B+树中,顺序检索比较明显,随机检索时,任何关键字的查找都必须走一条从根节点到叶节点的路,所有关键字的查找路径长度相同,导致每一个关键字的查询效率相当。4、B-树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。B+树的叶子节点使用指针顺序连接在一起,只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作。增删文件(节点)时,效率更高。因为B+树的叶子节点包含所有关键字,并以有序的链表结构存储,这样可很好提高增删效率。","link":"/2020/04/26/mysql/mysql-%E7%BA%A6%E6%9D%9F/"},{"title":"mysql数据类型","text":"数据类型 字符串类型:字符串类型指CHAR、VARCHAR、BINARY、VARBINARY、BLOB、TEXT、ENUM和SET。该节描述了这些类型如何工作以及如何在查询中使用这些类型。 CHAR 、 VARCHAR char是长度固定的,不变的,长度在0-255字节 ;而varchar是长度可变的,长度在0-65535。同时,char后面的空格不会保留,而varchar会保留。还需注意的是oracle中的两个字节表示一个汉字,mysql中只需一个。 1234char定长:磁盘空间比较浪费,但是效率高,确定数据长度都一样,就使用定长比如:生日时间、身份证号varchar变长:比较节省空间,但是效率低,数据不能确定长度(不同数据长度有变化),不过例如varchar(5)所占的字节数是实际长度的基础上加1,后面还有结束标识符比如:地址 BINARY、VARBINARY 两者的关系和上面char和varchar很像,只不过它们是存储二进制字符串,包含字节字符串。 BLOB BLOB 是一个二进制大对象,可以容纳可变数量的数据,分为TINYBLOB(0-255)、BLOB(0-65535)、MEDIUMBLOB 和 LONGBLOB。 MySQL的四种BLOB类型(除了在存储的最大信息量上不同外,他们是等同的。 但是有时存储的文件过大,数据库性能就会下降。 TEXT 存储文字,大的一些文本等,也是四种类型,大小关系和上面的一样 上图的就可以看出分类,不过这个数据类型不怎么使用。 ENUM和SET ENUM看名字就知道了,这是一种枚举类型的数据类型,事先将可能出现的结果都定义好,实际存储的数据必须是枚举中的一个,不能随意存储。 12格式:字段名 enum('值1','值2',...,'值n') NULL/NOT NULL列表中的每个值都有一个顺序排列的编号,MySQL中存入的是这个编号,就是可以对应其位置来取到相应的值,同时如果存储不是枚举的值,如果类型后面加上了null属性,其默认值为取值列表中的第一个元素。如果不加not null属性,enum类型将允许插入null,而且null为默认值. SET支持去重,可以存储多个元素,如果要选取列表中多个值的组合,就要选择set类型 12格式:字段名 set('值1','值2',...,'值n') NULL/NOT NULL这里的同enum类型一样,列表中的每个值都有一个顺序排列的编号 记住这两种类型的数据都不是直接将数据存入数据库,而是将其列表中的编号存入数据库。","link":"/2020/04/17/mysql/mysql%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"},{"title":"多表查询","text":"完成对MYSQL数据库的多表查询及建表的操作教学目标掌握MYSQL中多表的创建及多表的查询掌握MYSQL中的表关系分析并能正确建表 昨天内容回顾: 数据库的创建 : create database 数据库的名 character set 字符集 collate 校对规则 数据库的删除: drop database 数据库名 修改: alter database 数据库 character set 字符集(utf8) 查询: show databases; show create database 数据库的名字 select database(); 切换数据库 : use 数据库的名字 表结构的操作: 创建: create table 表名( 列名 列的类型 列的约束, 列名 列的类型 列的约 ) 列的类型: char / varchar 列的约束: primary key 主键约束 unique : 唯一约束 not null 非空约束 自动增长 : auto_increment 删除 : drop table 表名 修改: alter table 表名 (add, modify, change , drop) rename table 旧表名 to 新表名 alter table 表名 character set 字符集 查询表结构: show tables; 查询出所有的表 show create table 表名: 表的创建语句, 表的定义 desc 表名: 表的结构 表中数据的操作 插入: insert into 表名(列名,列名) values(值1,值2); 删除: delete from 表名 [where 条件] 修改: update 表名 set 列名=’值’ ,列名=’值’ [where 条件]; 查询: select [distinct] * [列名1,列名2] from 表名 [where 条件] as关键字: 别名 where条件后面: 关系运算符: > >= < <= != <> –判断某一列是否为空: is null is not null in 在某范围内 between…and 逻辑运算符: and or not 模糊查询: like _ : 代表单个字符 %: 代表的是多个字符 分组: group by 分组之后条件过滤: having 聚合函数: sum() ,avg() , count() ,max(), min() 排序: order by (asc 升序, desc 降序) SQL 会创建多表及多表的关系需求:分类表和商品表之间是不是有关系? 如果有关系,在数据库中如何表示这种关系 123456789101112131415161718192021222324252627282930313233343536373839404142create table category( cid int primary key auto_increment, cname varchar(10), cdesc varchar(31));insert into category values(null,'手机数码','电子产品,黑马生产');insert into category values(null,'鞋靴箱包','江南皮鞋厂倾情打造');insert into category values(null,'香烟酒水','黄鹤楼,茅台,二锅头');insert into category values(null,'酸奶饼干','娃哈哈,蒙牛酸酸乳');insert into category values(null,'馋嘴零食','瓜子花生,八宝粥,辣条');select * from category;select cname,cdesc from category;--所有商品1.商品ID2.商品名称3.商品的价格4.生产日期5.商品分类ID商品和商品分类 : 所属关系create table product( pid int primary key auto_increment, pname varchar(10), price double, pdate timestamp, cno int);insert into product values(null,'小米mix4',998,null,1);insert into product values(null,'锤子',2888,null,1);insert into product values(null,'阿迪王',99,null,2);insert into product values(null,'老村长',88,null,3);insert into product values(null,'劲酒',35,null,3);insert into product values(null,'小熊饼干',1,null,4);insert into product values(null,'卫龙辣条',1,null,5);insert into product values(null,'旺旺大饼',1,null,5);//插入数据会失败insert into product values(null,'充气的',1,null,12); 技术分析: 多表之间的关系如何来维护 外键约束: foreign key 给product中的这个cno 添加一个外键约束 alter table product add foreign key(cno) references category(cid); 自己挖坑 从分类表中,删除分类为5信息, delete from category where cid =5; //删除失败 首先得去product表, 删除所有分类ID5 商品 建数据库原则: 通常情况下,一个项目/应用建一个数据库 多表之间的建表原则 一对多 : 商品和分类 建表原则: 在多的一方添加一个外键,指向一的一方的主键 多对多: 老师和学生, 学生和课程 建表原则: 建立一张中间表,将多对多的关系,拆分成一对多的关系,中间表至少要有两个外键,分别指向原来的那两张表 一对一: 班级和班长, 公民和身份证, 国家和国旗 建表原则: 将一对一的情况,当作是一对多情况处理,在任意一张表添加一个外键,并且这个外键要唯一,指向另外一张表 直接将两张表合并成一张表 将两张表的主键建立起连接,让两张表里面主键相等 实际用途: 用的不是很多. (拆表操作 ) 相亲网站: 个人信息 : 姓名,性别,年龄,身高,体重,三围,兴趣爱好,(年收入, 特长,学历, 职业, 择偶目标,要求) 拆表操作 : 将个人的常用信息和不常用信息,减少表的臃肿, 网上商城表实例的分析: 用户购物流程 用户表 (用户的ID,用户名,密码,手机) 12345678create table user( uid int primary key auto_increment, username varchar(31), password varchar(31), phone varchar(11));insert into user values(1,'zhangsan','123','13811118888'); 订单表 (订单编号,总价,订单时间 ,地址,外键用户的ID) 12345678910create table orders( oid int primary key auto_increment, sum int not null, otime timestamp, address varchar(100), uno int, foreign key(uno) references user(uid));insert into orders values(1,200,null,'黑马前台旁边小黑屋',1);insert into orders values(2,250,null,'黑马后台旁边1702',1); 商品表 (商品ID, 商品名称,商品价格,外键cno) 12345678910111213141516create table product( pid int primary key auto_increment, pname varchar(10), price double, cno int, foreign key(cno) references category(cid));insert into product values(null,'小米mix4',998,1);insert into product values(null,'锤子',2888,1);insert into product values(null,'阿迪王',99,2);insert into product values(null,'老村长',88,3);insert into product values(null,'劲酒',35,3);insert into product values(null,'小熊饼干',1,4);insert into product values(null,'卫龙辣条',1,5);insert into product values(null,'旺旺大饼',1,5); 订单项: 中间表(订单ID,商品ID,商品数量,订单项总价) 123456789101112131415create table orderitem( ono int, pno int, foreign key(ono) references orders(oid), foreign key(pno) references product(pid), ocount int, subsum double);--给1号订单添加商品 200块钱的商品insert into orderitem values(1,7,100,100);insert into orderitem values(1,8,101,100);--给2号订单添加商品 250块钱的商品 ()insert into orderitem values(2,5,1,35);insert into orderitem values(2,3,3,99); 商品分类表(分类ID,分类名称,分类描述) 1234567891011create table category( cid int primary key auto_increment, cname varchar(15), cdesc varchar(100));insert into category values(null,'手机数码','电子产品,黑马生产');insert into category values(null,'鞋靴箱包','江南皮鞋厂倾情打造');insert into category values(null,'香烟酒水','黄鹤楼,茅台,二锅头');insert into category values(null,'酸奶饼干','娃哈哈,蒙牛酸酸乳');insert into category values(null,'馋嘴零食','瓜子花生,八宝粥,辣条'); 多表之间的关系如何维护: 外键约束 : foreign key 添加一个外键: alter table product add foreign key(cno) references category(cid); foreign key(cno) references category(cid) 删除的时候, 先删除外键关联的所有数据,再才能删除分类的数据 建表原则: 一对多: 建表原则: 在多的一方增加一个外键,指向一的一方 多对多: 建表原则: 将多对多转成一对多的关系,创建一张中间表 一对一: 不常用, 拆表操作 建表原则: 将两张表合并成一张表 将两张表的主键建立起关系 将一对一的关系当作一对多的关系去处理 主键约束: 默认就是不能为空, 唯一 外键都是指向另外一张表的主键 主键一张表只能有一个 唯一约束: 列面的内容, 必须是唯一, 不能出现重复情况, 为空 唯一约束不可以作为其它表的外键 可以有多个唯一约束 一对多 : 建表原则: 在多的一方添加一个外键,指向一的一方 多对多: 建表原则: 拆成一对多 创建一张中间表, 至少要有两个外键, 指向原来的表 一对一: 建表原则: 合并一张表, 将主键建立关系 , 将它当作一对多的情况来处理 数据库客户端软件 使用商城表完成对商品信息的多表查询需求分析:在我们的商城案例中,我的订单中包含很多信息.打开我的订单需要去查询表 技术分析:多表查询 交叉连接查询 笛卡尔积 内连接查询 左外连接 右外连接 分页查询 每页数据数据3 起始索引从0 第1页: 0 第2页: 3 起始索引: index 代表显示第几页 页数从1开始 每页显示3条数据 startIndex = (index-1)*3 第一个参数是索引 第二个参数显示的个数 select * from product limit 0,3; select * from product limit 3,3; 子查询(了解的内容,非常重要)查询出(商品名称,商品分类名称)信息 12 查询分类名称为手机数码的所有商品 1select * from product where cname ='手机数码'; 练习题 按照商品分类的名称统计商品的个数: 1 查询1号订单的订单项信息和商品信息 12 多表查询练习数据 员工信息表 123456789101112131415161718192021222324252627--员工信息表CREATE TABLE emp( empno INT, ename VARCHAR(50), job VARCHAR(50), mgr INT, hiredate DATE, sal DECIMAL(7,2), comm DECIMAL(7,2), deptno INT) ;INSERT INTO emp values(7369,'SMITH','CLERK',7902,'1980-12-17',800,NULL,20);INSERT INTO emp values(7499,'ALLEN','SALESMAN',7698,'1981-02-20',1600,300,30);INSERT INTO emp values(7521,'WARD','SALESMAN',7698,'1981-02-22',1250,500,30);INSERT INTO emp values(7566,'JONES','MANAGER',7839,'1981-04-02',2975,NULL,20);INSERT INTO emp values(7654,'MARTIN','SALESMAN',7698,'1981-09-28',1250,1400,30);INSERT INTO emp values(7698,'BLAKE','MANAGER',7839,'1981-05-01',2850,NULL,30);INSERT INTO emp values(7782,'CLARK','MANAGER',7839,'1981-06-09',2450,NULL,10);INSERT INTO emp values(7788,'SCOTT','ANALYST',7566,'1987-04-19',3000,NULL,20);INSERT INTO emp values(7839,'KING','PRESIDENT',NULL,'1981-11-17',5000,NULL,10);INSERT INTO emp values(7844,'TURNER','SALESMAN',7698,'1981-09-08',1500,0,30);INSERT INTO emp values(7876,'ADAMS','CLERK',7788,'1987-05-23',1100,NULL,20);INSERT INTO emp values(7900,'JAMES','CLERK',7698,'1981-12-03',950,NULL,30);INSERT INTO emp values(7902,'FORD','ANALYST',7566,'1981-12-03',3000,NULL,20);INSERT INTO emp values(7934,'MILLER','CLERK',7782,'1982-01-23',1300,NULL,10);INSERT INTO emp values(7981,'MILLER','CLERK',7788,'1992-01-23',2600,500,20); 部门信息表 12345678910CREATE TABLE dept( deptno INT, dname varchar(14), loc varchar(13));INSERT INTO dept values(10, 'ACCOUNTING', 'NEW YORK');INSERT INTO dept values(20, 'RESEARCH', 'DALLAS');INSERT INTO dept values(30, 'SALES', 'CHICAGO');INSERT INTO dept values(40, 'OPERATIONS', 'BOSTON'); 基本查询 12345678910111213--所有员工的信息--薪资大于等于1000并且小于等于2000的员工信息--从员工表中查询出所有的部门编号--查询出名字以A开头的员工的信息--查询出名字第二个字母是L的员工信息--查询出没有奖金的员工信息--所有员工的平均工资--所有员工的工资总和--所有员工的数量--最高工资--最少工资--最高工资的员工信息--最低工资的员工信息 分组查询 1--每个部门的平均工资 子查询 1234567891011121314151617181920212223-- 单行子查询(> < >= <= = <>) -- 查询出高于10号部门的平均工资的员工信息 -- 多行子查询(in not in any all) >any >all -- 查询出比10号部门任何员工薪资高的员工信息-- 多列子查询(实际使用较少) in -- 和10号部门同名同工作的员工信息-- Select接子查询 -- 获取员工的名字和部门的名字-- from后面接子查询 -- 查询emp表中经理信息-- where 接子查询 -- 薪资高于10号部门平均工资的所有员工信息-- having后面接子查询 -- 有哪些部门的平均工资高于30号部门的平均工资-- 工资>JONES工资-- 查询与SCOTT同一个部门的员工-- 工资高于30号部门所有人的员工信息-- 查询工作和工资与MARTIN完全相同的员工信息-- 有两个以上直接下属的员工信息-- 查询员工编号为7788的员工名称,员工工资,部门名称,部门地址 SQL查询的综合案例 查询出高于本部门平均工资的员工信息 列出达拉斯加工作的人中,比纽约平均工资高的人 查询7369员工编号,姓名,经理编号和经理姓名 查询出各个部门薪水最高的员工所有信息 面试题1234567891011121314CREATE TABLE test( name CHAR(20), kecheng CHAR(20), fenshu CHAR(20));INSERT INTO test VALUES('张三','语文',81),('张三','数学',75),('李四','语文',76),('李四','数学',90),('王五','语文',81),('王五','数学',82);--请用一条Sql语句查处分数大于80的学生","link":"/2020/04/13/mysql/mysql%E5%A4%9A%E8%A1%A8%E6%9F%A5%E8%AF%A2/"},{"title":"mysql总结","text":"","link":"/2020/04/14/mysql/mysql%E6%80%BB%E7%BB%93/"},{"title":"SQL语句学习","text":"sql语句 条件查询","link":"/2020/04/14/mysql/sql%E8%AF%AD%E5%8F%A5/"},{"title":"再谈JDBC","text":"前言 Java数据库连接,是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。JDBC是面向关系型数据库的。 在J2SE中,提供了一个称之为JDBC-ODBC桥的API。 –维基百科 sun公司提供的java连接数据库的规范,包括一些常见的数据库,MySQL Oracle等。 要想通过IDEA创建项目连接借助JDBC使用mysql数据库等,这里以我使用的MySQL数据库为例, 要下载好相应的jar包,这在官网中都可以找到。通过IDEA的导包,将其添加到所在的项目列表里,就可以使用JDBC。 作用JDBC就是对于java代码来说,在应用程序和数据库之间的一个中间层 API,也就是中间商;JDBC允许操作者按照所需要的形式去访问获取数据库信息,可以进行查询,有点类似那些数据库可视化软件的部分功能,同时支持更新指令,处理其响应并返回结果。 JDBC的架构分为了三层和两层架构; 三层架构,中间相较于两层架构,多一个应用服务层,区分层次就是为了符合”高内聚,低耦合“的思想,可以降低层与层之间的依赖,减少依赖性,开发人员可以只用关注某一层。 双层架构 这种架构,支持直接连接数据库进行操作,这种架构,效率会高很多,因为直接访问数据库,但是有时维护成本和时间会增加,相对于三层可靠性和安全性会欠缺一些 下面讲一下一些操作步骤。 步骤可以查阅官方文档查看,比较详细,不过是全英文的,可以翻译来看看。 注册驱动 123456//这样写是由问题的,里面的Driver类有静态代码块会自动注册了很容易造成二次注册,没必要二次注册//DriverManager.registerDriver(new com.mysql.jdbc.Driver());Class.forName(\"com.mysql.jdbc.Driver\");//里面加的是全路径//返回与给定字符串名称的类或接口相关联的类对象。 调用此方法相当于: //Class.forName(className, true, currentLoader) //其中currentLoader表示当前类的定义类加载器。 建立连接 123//这里我一开始的写法没有加上useSSL和时区,导致IDEA报错,这里写成这样会比较好一点DriverManager.getConnection(\"jdbc:mysql://localhost:3306/myemployees?useSSL=false&serverTimezone=Asia/Shanghai\",'user','password');//这里使用这种格式之后,我把加载驱动程序的class.forName注释掉了,都是可以使用的,只是会出现警告 创建statement 12//填充sql语句PreparedStatement pre = con.prepareStatement(\"select * from employees where first_name = 'Lex';\"); 执行sql语句,获得resultSet 123//executes sql //这里使用了 ResultSet 的executeQuery()方法,执行查询的sql语句,并返回结果ResultSet rs = pre.executeQuery(); 遍历得到所需的结果 123456//和遍历文件的输出输入流一样//可以使用同样的框架//@param getString方法中的参数就是columnLabel(下面我所使用的salary字段)使用SQL AS子句指定的列的标签。如果未指定SQL AS子句,则标签为列的名称 while (rs.next()){ System.out.println(\"输出:\"+rs.getString(\"salary\"));//因此返回的是查询salary的内容 } 关闭,释放资源 1234567891011121314151617//Statement stmt = null;//ResultSet rs = null;//下面是官方文档中推荐的释放资源的写法if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { } // ignore rs = null; } if (stmt != null) { try { stmt.close(); } catch (SQLException sqlEx) { } // ignore stmt = null; 完整代码 1234567891011121314151617181920212223242526272829303132package mysql;import java.sql.*;/** * @Author: yhy * @Date: 2020/2/27 * @Time: 20:53 */public class ConFirst { public static void main(String[] args) throws SQLException, ClassNotFoundException { // register Class.forName(\"com.mysql.cj.jdbc.Driver\"); // DriverManager.registerDriver(new com.mysql.jdbc.Driver());// connect message Connection con = DriverManager.getConnection(\"jdbc:mysql://localhost:3306/myemployees?useSSL=false&serverTimezone=Asia/Shanghai\",\"user\",\"password\");// sql statement PreparedStatement pre = con.prepareStatement(\"select * from employees where first_name = 'Lex';\");// accept the result ResultSet rs = pre.executeQuery(); while (rs.next()){ System.out.println(\"输出:\"+rs.getString(\"salary\")); }// close the resource //应该按照官方那样写,我这里偷懒没写详细 rs.close(); con.close(); }} JDBC常见的关键字解释 DriverManager: 这个类管理数据库驱动程序的列表。内容是否符合从Java应用程序使用的通信子协议正确的数据库驱动程序的连接请求。识别JDBC在一定子协议的第一个驱动器将被用来建立数据库连接。 Driver: 此接口处理与数据库服务器通信。很少直接驱动程序对象. 12345// 每个驱动程序类必须实现的接口。// Java SQL框架允许多个数据库驱动程序。// 每个驱动程序都应该提供一个实现Driver接口的类。// DriverManager将尝试加载尽可能多的驱动程序,然后对于任何给定的连接请求,它会依次要求每个驱动程序 尝试连接到目标URL。// 强烈建议每个Driver类应该是小型且独立的,以便可以加载和查询Driver类,而不需要大量的支持代码。 Connection : 此接口与接触数据库的所有方法。连接对象表示通信上下文,即,与数据库中的所有的通信是通过唯一的连接对象。 Statement:用于执行静态SQL语句并返回其生成的结果的对象。创建执行SQL语句的连接中间人。 SQLException: 这个类处理发生在一个数据库应用程序的任何错误,提供有关数据库访问错误或其他错误的信息的异常,而且每一个都提供了信息,包括了描述错误的字符串 ResultSet:表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。ResultSet对象保持一个光标指向其当前的数据行。最初,光标位于第一行之前。 next方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回false,因此可以在while循环中使用循环来遍历结果集。使用之后一般选择close。","link":"/2020/04/20/mysql/%E5%86%8D%E8%B0%88JDBC/"},{"title":"Linux基础","text":"Linux再回顾下面是自己之前centos7的笔记总结第二篇,第一篇是19年就写过了一些,记住Linux中一切皆文件。 这里提下,使用xshell+xftp来使用云服务器是很不错的,强烈建议小伙伴这样操作。 命令网络配置 ifconfig 这个和windows中的ipconfig一样。 查看 more查看文件-支持空格翻页 less和more差不多,空格翻页,但是支持上下查看行信息(可以在文件中使用/来向下查找指定字符,向上用?) head看头部 grep-其实算是查找命令,但常常在阅读信息使用,加上下面也使用了,这里就记录一下 grep分析信息,如果有我们所需的,就会提炼归纳出来 grep [-acinv] [--color=auto] '查找字符串' filename [参数] -a : 将binary文件以text文件的方式查找数据 -c : 计算找到 '查找字符串'的次数 -i : 忽略大小写的不同 -n : 输出行号 -v : 反向选择,显示没有查找内容的行 --color=auto : 将找到的关键字部分加上颜色显示 123456789101112131415161718192021222324252627282930313233343536373839 ### 链接**ln**直接使用不加参数,是默认创建硬链接,加上-s是软链接- 硬链接 - 一个文件可以有多个访问路径,防止重要文件被删- 软链接 - 类似windows的快捷方式,依赖于源- code ```linux 实例 [root@VM_0_3_centos sysconfig]# cd /home [root@VM_0_3_centos home]# ls [root@VM_0_3_centos home]# touch file1 [root@VM_0_3_centos home]# ls file1 [root@VM_0_3_centos home]# ln file1 file2 //创建硬链接 [root@VM_0_3_centos home]# ls file1 file2 [root@VM_0_3_centos home]# ln -s file1 file3 //软链接 [root@VM_0_3_centos home]# ls file1 file2 file3 [root@VM_0_3_centos home]# ll total 0 -rw-r--r-- 2 root root 0 Apr 17 23:49 file1 -rw-r--r-- 2 root root 0 Apr 17 23:49 file2 lrwxrwxrwx 1 root root 5 Apr 17 23:49 file3 -> file1 [root@VM_0_3_centos home]# rm -rf file1 [root@VM_0_3_centos home]# ls file2 file3 //这里的file3会显示颜色,因为删除了file1,但是硬链接却没有事 [root@VM_0_3_centos home]# Vim之前有写过vim的文章,但是现在还是想记录下一些重要的命令 :set nu显示行号(按住ESC之后进入底下命令模式即可) ZZ-这是退出快捷键,修改的会保存的 u-复原前面的一个动作 账号管理添加用户记得添加删除用户等操作都是基于root下的 1useradd 加上-m就是添加用户并且将其名字作为了其默认home路径 删除用户1userdel 用户 可以添加密码给用户root用户下操作 1passwd username 锁定账号1passwd -l username(l相当lock) 登录会拒绝这个usernames 修改hostname命令如下,修改的是一开始linux服务器默认的名字,为了好看点,可以自己修改 点击重连就可以看到效果了 用户组12345678910添加groupadd 选项 用户组-g GID 指定新用户组的组标识号(GID)。-o 一般与-g选项同时使用,表示新用户组的GID可以与系统已有用户组的GID相同。删除groupdel 用户组该命令是将该用户组从系统中删除 用户组信息放在/etc/group /etc/passwd管理用户信息的文件,包括全部用户权限,主目录等,密码是用x掩盖了。除此之外,加密的密码在/etc/shadow文件中 加密的密码想要解密得自己去研究一下,一般不用浪费时间去管这个 进程管理和window下的进程一样,linux也是可以查看和管理进程的。现在写下进程的管理查看命令 ps当前进程的快照(通过下面图片的英文理解) 123456789101112131415ps 参数 -a: 除了会话的第一个进程组的首进程和不与终端有通讯的进程。 -u: 指定用户的所有进程 -x: -aux:显示所有包含其他使用者的行程 -e:显示所有进程 -f:以完整的格式显示 -ef:查看全格式的全部进程 详细命令参数解释 实例 12ps -aux | grep 关键字表示在进程搜索的所有中去筛选该关键字的进程,|是管道过滤信息,grep用来查找 下面的USER-进程的所属者,%CPU: 占用的 CPU的使用情况 STAT该进程的当前的状态:S-静止状态,R-在执行,T-暂停执行,Z-不存在但暂时无法消除,<-高优先序的行程,N-低优先序的行程 pstree目录树,将进程的情况以目录树的格式输出 kill结束进程,直接杀死, 1- 进程的id,直接结束 管道命令上面的命令有涉及到管道命令,这里就提一下,借助一些博客来记录下。 管道是一种通信机制,通常用于进程间的通信(也可通过socket来通信),它表现出来的形式将前面每一个进程的输出(stdout)直接作为下一个进程的输入(stdin)。 管道命令使用|作为界定符号,管道命令必须要能够接受来自前一个命令的数据成为standard input继续处理才行。 12ls -a /etc | more将ls的输出作为more的输入,常常使用管道是为了方便查看和管理搜索 实例 12cat /etc/passwd | grep -n '关键字'找在文件中的所在行 最后在电脑上的笔记很多,但是都很随意很乱,希望自己能够更加高效点总结出来,发出来!加油!!欢迎有缘的你给点意见和批评。 参考1 参考2 参考3","link":"/2020/04/17/%E5%85%B6%E5%AE%83/Linux%E5%9F%BA%E7%A1%80/"},{"title":"springboot.md","text":"微服务分离解耦-架构分格 部署和运维是有难题的,将不同功能单元分散开发, 好处是快速构建项目,加删功能 功能元素的复制部署,每一个服务都是可替代的 Springboot 热部署 可以实现在线修改 12345<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> <!-- 这个需要为 true 热部署才有效 --></dependency> 快速打包-使用springboot的package工具 @springbootApplication 1234567891011121314151617主要配置类//里面有着很多注解@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration //配置类@EnableAutoConfiguration //开启自动配置 springboot自动配置@ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class}), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class})})","link":"/2020/04/05/%E6%A1%86%E6%9E%B6/springboot-md/"},{"title":"YAML","text":"YAML基本语法介绍基本语法K : (空格) V表示一对键值对,空格表示缩进链表层次关系,不允许使用缩进代替空格,大小写敏感 值 字面量(数字、字符、布尔值等单个的、不可再分的值)-其中字符串不用加双引号或者单引号(表转义) 数组(list、set)-单行写法[,,,,] 12345#多行写法list: - lisi - zhangsan - wangwu 对象、Map(属性或者值)-单行写法{,,,,} 1234# 键值对的形式 多行写法,注意空格friends: name: 张三 age: 20 用法可以使用yaml文件代替类中的属性定义,然后通过注解进行调用,方便和简化代码","link":"/2020/04/07/%E6%A1%86%E6%9E%B6/YAML/"},{"title":"ssm.md","text":"ssm框架学习mvc-model-view-controller是一种设计模式,MVC是指,C控制层,M模块层,V显示层这样的设计理念","link":"/2020/04/05/%E6%A1%86%E6%9E%B6/ssm-md/"},{"title":"浅谈ArrayList","text":"ArrayList 与数组的“纠缠不清”的暧昧[TOC] 前言 是什么 有什么用 前言能不能有一种数组可以在删除掉某些元素自动缩小就好了。可是话说哪里学的Java?数组能删除元素吗?今天讲一下一个特殊对象——ArrayList,它的出现与存在和数组有着几分类似。下面按照它是什么-有什么用-怎么用来讲?再讲讲与数组的区别? 正话(个人的见解,有误请多指教)惯例先明白它是什么? 源码搞上!!!表示泛型(只能引用类型),也就是装在集合当中的所有元素,全都是统一的什么类型. 很明显它是一个类,继承(extends)了AbstractList,实现(implements)了List、RandomAcess等。(extends可以理解为全盘继承了父类的功能。implements可以理解为为这个类附加一些额外的功能)。它是一个容量可以自动增长的动态数组(并非数组),可以支持删除操作。 它的初始容量为10。下面的构造方法第一个就是构造容量为10的。要想改变这个容量,就像第二个的构造方法那样就可以了。第三个是构造一个包含指定集合的元素的列表,按照他们由集合的迭代器返回的顺序。 值得一提的是,Java集合框架中定义了List接口,而且只有两个实现,除了这个ArrayList还有LinkedList。在数据结构与算法分析书上有着这么两句结论,具体得到是在书中通过了算法得来的。LinkedList在Java和C#中实际上是在使用双链表。 ArrayList优势是在末尾添加,删除,获取和设置(set and get)。 LinkedList优势是从开头添加级删除开头元素。 数组表(ArrayList)和数组很相似在于,读取的时候为O(1),在插入时候就变为O(n)了,不同在于这个数组表可以增加长度,在添加时候,只需表尾编号加一便可以确定新元素了。 ArrayList不是线程安全的。线程安全是说一个对象可以完全被多个线程同时使用,不出问题。这个在后面的深入学习虚拟机会经常遇到——使用多线程之间同步synchronized或使用锁(lock)可以解决。 基本用法 动态的增加和减少元素,实现了ICollection和list接口。当然还可以灵活得选择数组的大小。 常用方法 删除作用,可以实现数组不能的删除 实现添加整个容量,添加元素 怎么用 想要存储数字,也就是基本数据类型,就可以使用包装类(位于java.lang下)。 基本类型 包装类 int Integer byte Byte short Short long Long float Float doublr Double char Character boolean Boolean ArrayList<Integer> list = new ArrayList<>;//这样创建不会报错了 <!--0--> *","link":"/2020/01/02/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/ArrayList%E4%BB%8B%E7%BB%8D/"},{"title":"复杂度","text":"参考网站 前言常常分析一段代码的好坏,程序员来说,先判断其正确与否,再者,看效率,即包括运行时候的效率等,常常便看到O(n)等字眼,大O符号,其实就是在分析复杂度,包括时间复杂度和空间复杂度。 计算机科学有一种专门用来测量算法最坏情况的速记法,即所谓的“大O”符号。 大O符号的目的不是使用分钟和秒钟来表示算法的性能,而是方便我们讨论问题规模和程序运行时间之间的关系。由于大O符号故意剔除了细枝末节的内容,所以展示在我们眼前的是将问题分成不同大类的概略情况。 ——《算法之美》 需要注意的是这个大O表示的是个大概整体的情况,并非真实的速度之类的,一个程序的运行总时间主要包括 执行每条语句的耗时; 执行每条有的频率; 简介计算 补充一些数学知识 常用常见的大小比较公式","link":"/2019/11/08/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/%E5%A4%8D%E6%9D%82%E5%BA%A6/"},{"title":"数据结构与算法/Hashmap","text":"HashMap方法 computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) 123456789101112131415//如果指定的键尚未与值相关联(或映射到null ),则尝试使用给定的映射函数计算其值,并将其输入到此映射中,除非null 。 // 方法定义default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { ...}// java8之前。从map中根据key获取value操作可能会有下面的操作Object key = map.get(\"key\");if (key == null) { key = new Object(); map.put(\"key\", key);}// java8之后。上面的操作可以简化为一行,若key对应的value为空,会将第二个参数的返回值存入并返回Object key2 = map.computeIfAbsent(\"key\", k -> new Object()); 总的来说就是 存在且value不为null,则返回value不满足上述条件下,先检查Function返回值,若为null,返回null存在但value为null,则将Function返回值作为value,返回value不存在,新建节点,将Function返回值作为value,返回value V putIfAbsent(K,V) 12如果指定的键尚未与某个值相关联(或映射到 `1null` ),则将其与给定值相关联并返回 `null` ,否则返回当前值。如果 Key 不存在或者对应的值是 null,则将 Value 设置进去,然后返回 null;否则只返回 Map 当中对应的值,而不做其他操作。 getOrDefault(Object key, V defaultValue) 12返回指定键所映射的值,如果此映射不包含该键的映射则将defaultValue作为该键的映射,简单来说就是,有key就用它对应的值,没有就使用defaultvalue,但注意的是这个默认的值没有加进去这个hashmap里面","link":"/2020/04/06/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/Hashmap/"},{"title":"算法总结","text":"经典算法总结1总结下一些经常遇到的算法中的一些,包括自己面试遇到过了。 回溯法动态规划1例题1:2、5、7块的硬币,求组成27元所需的最少硬币数。 这道题假设我们使用递归来做,就会发现重复计算的数太多了,严重浪费, 123456789101112private int find(int x ){ int res =0; int max = Integer.MAX_VALUE; if (x>=2){ res = Math.min(find(x-2)+1,max); } if (x>=5){ res = Math.min(find(x-5)+1,max); } if (x>=7){ res = Math.min(find(x-7)+1,max); } return res; } 八皇后算法贪心算法","link":"/2020/04/15/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/%E7%AE%97%E6%B3%95/"},{"title":"Springboot quick start","text":"一、Spring Boot 入门1转载尚硅谷 1、Spring Boot 简介 简化Spring应用开发的一个框架; 整个Spring技术栈的一个大整合; J2EE开发的一站式解决方案; 2、微服务2014,martin fowler 微服务:架构风格(服务微化) 一个应用应该是一组小型服务;可以通过HTTP的方式进行互通; 单体应用:ALL IN ONE 微服务:每一个功能元素最终都是一个可独立替换和独立升级的软件单元; 详细参照微服务文档 3、环境准备http://www.gulixueyuan.com/ 谷粒学院 环境约束 –jdk1.8:Spring Boot 推荐jdk1.7及以上;java version “1.8.0_112” –maven3.x:maven 3.3以上版本;Apache Maven 3.3.9 –IntelliJIDEA2017:IntelliJ IDEA 2017.2.2 x64、STS –SpringBoot 1.5.9.RELEASE:1.5.9; 统一环境; 1、MAVEN设置;给maven 的settings.xml配置文件的profiles标签添加 123456789101112<profile> <id>jdk-1.8</id> <activation> <activeByDefault>true</activeByDefault> <jdk>1.8</jdk> </activation> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> </properties></profile> 2、IDEA设置整合maven进来; ![idea设置](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180129151045.png) ![images/](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180129151112.png) 4、Spring Boot HelloWorld一个功能: 浏览器发送hello请求,服务器接受请求并处理,响应Hello World字符串; 1、创建一个maven工程;(jar)2、导入spring boot相关的依赖1234567891011<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version></parent><dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency></dependencies> 3、编写一个主程序;启动Spring Boot应用123456789101112/** * @SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用 */@SpringBootApplicationpublic class HelloWorldMainApplication { public static void main(String[] args) { // Spring应用启动起来 SpringApplication.run(HelloWorldMainApplication.class,args); }} 4、编写相关的Controller、Service123456789@Controllerpublic class HelloController { @ResponseBody @RequestMapping(\"/hello\") public String hello(){ return \"Hello World!\"; }} 5、运行主程序测试6、简化部署123456789<!-- 这个插件,可以将应用打包成一个可执行的jar包;--> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> 将这个应用打成jar包,直接使用java -jar的命令进行执行; 5、Hello World探究1、POM文件1、父项目1234567891011121314<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version></parent>他的父项目是<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>1.5.9.RELEASE</version> <relativePath>../../spring-boot-dependencies</relativePath></parent>他来真正管理Spring Boot应用里面的所有依赖版本; Spring Boot的版本仲裁中心; 以后我们导入依赖默认是不需要写版本;(没有在dependencies里面管理的依赖自然需要声明版本号) 2、启动器1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency> spring-boot-starter-==web==: spring-boot-starter:spring-boot场景启动器;帮我们导入了web模块正常运行所依赖的组件; Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器 2、主程序类,主入口类123456789101112/** * @SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用 */@SpringBootApplicationpublic class HelloWorldMainApplication { public static void main(String[] args) { // Spring应用启动起来 SpringApplication.run(HelloWorldMainApplication.class,args); }} @SpringBootApplication: Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用; 12345678910@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication { @SpringBootConfiguration:Spring Boot的配置类; 标注在某个类上,表示这是一个Spring Boot的配置类; @Configuration:配置类上来标注这个注解; 配置类 —– 配置文件;配置类也是容器中的一个组件;@Component @EnableAutoConfiguration:开启自动配置功能; 以前我们需要配置的东西,Spring Boot帮我们自动配置;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能;这样自动配置才能生效; 123@AutoConfigurationPackage@Import(EnableAutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration { @AutoConfigurationPackage:自动配置包 @Import(AutoConfigurationPackages.Registrar.class): Spring的底层注解@Import,给容器中导入一个组件;导入的组件由AutoConfigurationPackages.Registrar.class; ==将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器;== @Import(EnableAutoConfigurationImportSelector.class); 给容器中导入组件? EnableAutoConfigurationImportSelector:导入哪些组件的选择器; 将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中; 会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件; ![自动配置类](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180129224104.png) 有了自动配置类,免去了我们手动编写配置注入功能组件等的工作; SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader); ==Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作;==以前我们需要自己配置的东西,自动配置类都帮我们; J2EE的整体整合解决方案和自动配置都在spring-boot-autoconfigure-1.5.9.RELEASE.jar; ==Spring注解版(谷粒学院)== 6、使用Spring Initializer快速创建Spring Boot项目1、IDEA:使用 Spring Initializer快速创建项目IDE都支持使用Spring的项目创建向导快速创建一个Spring Boot项目; 选择我们需要的模块;向导会联网创建Spring Boot项目; 默认生成的Spring Boot项目; 主程序已经生成好了,我们只需要我们自己的逻辑 resources文件夹中目录结构 static:保存所有的静态资源; js css images; templates:保存所有的模板页面;(Spring Boot默认jar包使用嵌入式的Tomcat,默认不支持JSP页面);可以使用模板引擎(freemarker、thymeleaf); application.properties:Spring Boot应用的配置文件;可以修改一些默认设置; 2、STS使用 Spring Starter Project快速创建项目 二、配置文件1、配置文件SpringBoot使用一个全局的配置文件,配置文件名是固定的; •application.properties •application.yml 配置文件的作用:修改SpringBoot自动配置的默认值;SpringBoot在底层都给我们自动配置好; YAML(YAML Ain’t Markup Language) YAML A Markup Language:是一个标记语言 YAML isn’t Markup Language:不是一个标记语言; 标记语言: 以前的配置文件;大多都使用的是 xxxx.xml文件; YAML:以数据为中心,比json、xml等更适合做配置文件; YAML:配置例子 12server: port: 8081 XML: 123<server> <port>8081</port></server> 2、YAML语法:1、基本语法k:(空格)v:表示一对键值对(空格必须有); 以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的 123server: port: 8081 path: /hello 属性和值也是大小写敏感; 2、值的写法字面量:普通的值(数字,字符串,布尔) k: v:字面直接来写; 字符串默认不用加上单引号或者双引号; “”:双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思 name: “zhangsan \\n lisi”:输出;zhangsan 换行 lisi ‘’:单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据 name: ‘zhangsan \\n lisi’:输出;zhangsan \\n lisi 对象、Map(属性和值)(键值对): k: v:在下一行来写对象的属性和值的关系;注意缩进 对象还是k: v的方式 123friends: lastName: zhangsan age: 20 行内写法: 1friends: {lastName: zhangsan,age: 18} 数组(List、Set):用- 值表示数组中的一个元素 1234pets: - cat - dog - pig 行内写法 1pets: [cat,dog,pig] 3、配置文件值注入配置文件 123456789101112person: lastName: hello age: 18 boss: false birth: 2017/12/12 maps: {k1: v1,k2: 12} lists: - lisi - zhaoliu dog: name: 小狗 age: 12 javaBean: 1234567891011121314151617181920/** * 将配置文件中配置的每一个属性的值,映射到这个组件中 * @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定; * prefix = \"person\":配置文件中哪个下面的所有属性进行一一映射 * * 只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能; * */@Component@ConfigurationProperties(prefix = \"person\")public class Person { private String lastName; private Integer age; private Boolean boss; private Date birth; private Map<String,Object> maps; private List<Object> lists; private Dog dog; 我们可以导入配置文件处理器,以后编写配置就有提示了 123456<!--导入配置文件处理器,配置文件进行绑定就会有提示--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> 1、properties配置文件在idea中默认utf-8可能会乱码调整 ![idea配置乱码](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180130161620.png) 2、@Value获取值和@ConfigurationProperties获取值比较 @ConfigurationProperties @Value 功能 批量注入配置文件中的属性 一个个指定 松散绑定(松散语法) 支持 不支持 SpEL 不支持 支持 JSR303数据校验 支持 不支持 复杂类型封装 支持 不支持 配置文件yml还是properties他们都能获取到值; 如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value; 如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties; 3、配置文件注入值数据校验123456789101112131415161718192021222324@Component@ConfigurationProperties(prefix = \"person\")@Validatedpublic class Person { /** * <bean class=\"Person\"> * <property name=\"lastName\" value=\"字面量/${key}从环境变量、配置文件中获取值/#{SpEL}\"></property> * <bean/> */ //lastName必须是邮箱格式 @Email //@Value(\"${person.last-name}\") private String lastName; //@Value(\"#{11*2}\") private Integer age; //@Value(\"true\") private Boolean boss; private Date birth; private Map<String,Object> maps; private List<Object> lists; private Dog dog; 4、@PropertySource&@ImportResource&@Bean@PropertySource:加载指定的配置文件; 1234567891011121314151617181920212223242526272829/** * 将配置文件中配置的每一个属性的值,映射到这个组件中 * @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定; * prefix = \"person\":配置文件中哪个下面的所有属性进行一一映射 * * 只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能; * @ConfigurationProperties(prefix = \"person\")默认从全局配置文件中获取值; * */@PropertySource(value = {\"classpath:person.properties\"})@Component@ConfigurationProperties(prefix = \"person\")//@Validatedpublic class Person { /** * <bean class=\"Person\"> * <property name=\"lastName\" value=\"字面量/${key}从环境变量、配置文件中获取值/#{SpEL}\"></property> * <bean/> */ //lastName必须是邮箱格式 // @Email //@Value(\"${person.last-name}\") private String lastName; //@Value(\"#{11*2}\") private Integer age; //@Value(\"true\") private Boolean boss; @ImportResource:导入Spring的配置文件,让配置文件里面的内容生效; Spring Boot里面没有Spring的配置文件,我们自己编写的配置文件,也不能自动识别; 想让Spring的配置文件生效,加载进来;@ImportResource标注在一个配置类上 12@ImportResource(locations = {\"classpath:beans.xml\"})导入Spring的配置文件让其生效 不来编写Spring的配置文件 12345678<?xml version=\"1.0\" encoding=\"UTF-8\"?><beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\"> <bean id=\"helloService\" class=\"com.atguigu.springboot.service.HelloService\"></bean></beans> SpringBoot推荐给容器中添加组件的方式;推荐使用全注解的方式 1、配置类@Configuration——>Spring配置文件 2、使用@Bean给容器中添加组件 12345678910111213141516/** * @Configuration:指明当前类是一个配置类;就是来替代之前的Spring配置文件 * * 在配置文件中用<bean><bean/>标签添加组件 * */@Configurationpublic class MyAppConfig { //将方法的返回值添加到容器中;容器中这个组件默认的id就是方法名 @Bean public HelloService helloService02(){ System.out.println(\"配置类@Bean给容器中添加组件了...\"); return new HelloService(); }} ##4、配置文件占位符 1、随机数12${random.value}、${random.int}、${random.long}${random.int(10)}、${random.int[1024,65536]} 2、占位符获取之前配置的值,如果没有可以是用:指定默认值123456789person.last-name=张三${random.uuid}person.age=${random.int}person.birth=2017/12/15person.boss=falseperson.maps.k1=v1person.maps.k2=14person.lists=a,b,cperson.dog.name=${person.hello:hello}_dogperson.dog.age=15 5、Profile1、多Profile文件我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml 默认使用application.properties的配置; 2、yml支持多文档块方式12345678910111213141516171819server: port: 8081spring: profiles: active: prod---server: port: 8083spring: profiles: dev---server: port: 8084spring: profiles: prod #指定属于哪个环境 3、激活指定profile 1、在配置文件中指定 spring.profiles.active=dev 2、命令行: java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar –spring.profiles.active=dev; 可以直接在测试的时候,配置传入命令行参数 3、虚拟机参数; -Dspring.profiles.active=dev 6、配置文件加载位置springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件 –file:./config/ –file:./ –classpath:/config/ –classpath:/ 优先级由高到底,高优先级的配置会覆盖低优先级的配置; SpringBoot会从这四个位置全部加载主配置文件;互补配置; ==我们还可以通过spring.config.location来改变默认的配置文件位置== 项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形成互补配置; java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar –spring.config.location=G:/application.properties(这个会起作用) 7、外部配置加载顺序==SpringBoot也可以从以下位置加载配置; 优先级从高到低;高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置== 1.命令行参数 所有的配置都可以在命令行上进行指定 java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar –server.port=8087 –server.context-path=/abc 多个配置用空格分开; –配置项=值 2.来自java:comp/env的JNDI属性 3.Java系统属性(System.getProperties()) 4.操作系统环境变量 5.RandomValuePropertySource配置的random.*属性值 ==由jar包外向jar包内进行寻找;== ==优先加载带profile== 6.jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件 7.jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件 ==再来加载不带profile== 8.jar包外部的application.properties或application.yml(不带spring.profile)配置文件 9.jar包内部的application.properties或application.yml(不带spring.profile)配置文件 10.@Configuration注解类上的@PropertySource 11.通过SpringApplication.setDefaultProperties指定的默认属性 所有支持的配置加载来源; 参考官方文档 8、自动配置原理配置文件到底能写什么?怎么写?自动配置原理; 配置文件能配置的属性参照 1、自动配置原理:1)、SpringBoot启动的时候加载主配置类,开启了自动配置功能 ==@EnableAutoConfiguration== 2)、@EnableAutoConfiguration 作用: 利用EnableAutoConfigurationImportSelector给容器中导入一些组件? 可以查看selectImports()方法的内容; List configurations = getCandidateConfigurations(annotationMetadata, attributes);获取候选的配置 SpringFactoriesLoader.loadFactoryNames() 扫描所有jar包类路径下 META-INF/spring.factories 把扫描到的这些文件的内容包装成properties对象 从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 **==将 类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中;==**```properties# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\\org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\\org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\\org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\\org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\\org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\\org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\\org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\\org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\\org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\\org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\\org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\\org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\\org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\\org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\\org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\\org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\\org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\\org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\\org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\\org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\\org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\\org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\\org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\\org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\\org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\\org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\\org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\\org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\\org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\\org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\\org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\\org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\\org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\\org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\\org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\\org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\\org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\\org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\\org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\\org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\\org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\\org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\\org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\\org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\\org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\\org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\\org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\\org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\\org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\\org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\\org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\\org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\\org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\\org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\\org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\\org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\\org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\\org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\\org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\\org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\\org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\\org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\\org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\\org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\\org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\\org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\\org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\\org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\\org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\\org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\\org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\\org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\\org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\\org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\\org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\\org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\\org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\\org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\\org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\\org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\\org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\\org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\\org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\\org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\\org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\\org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\\org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\\org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\\org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\\org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\\org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\\org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\\org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\\org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\\org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\\org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration 每一个这样的 xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中;用他们来做自动配置; 3)、每一个自动配置类进行自动配置功能; 4)、以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理; 12345678910111213141516171819202122232425262728@Configuration //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件@EnableConfigurationProperties(HttpEncodingProperties.class) //启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把HttpEncodingProperties加入到ioc容器中@ConditionalOnWebApplication //Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效; 判断当前应用是否是web应用,如果是,当前配置类生效@ConditionalOnClass(CharacterEncodingFilter.class) //判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;@ConditionalOnProperty(prefix = \"spring.http.encoding\", value = \"enabled\", matchIfMissing = true) //判断配置文件中是否存在某个配置 spring.http.encoding.enabled;如果不存在,判断也是成立的//即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;public class HttpEncodingAutoConfiguration { //他已经和SpringBoot的配置文件映射了 private final HttpEncodingProperties properties; //只有一个有参构造器的情况下,参数的值就会从容器中拿 public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) { this.properties = properties; } @Bean //给容器中添加一个组件,这个组件的某些值需要从properties中获取 @ConditionalOnMissingBean(CharacterEncodingFilter.class) //判断容器没有这个组件? public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE)); return filter; } 根据当前不同的条件判断,决定这个配置类是否生效? 一但这个配置类生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的; 5)、所有在配置文件中能配置的属性都是在xxxxProperties类中封装者‘;配置文件能配置什么就可以参照某个功能对应的这个属性类 1234@ConfigurationProperties(prefix = \"spring.http.encoding\") //从配置文件中获取指定的值和bean的属性进行绑定public class HttpEncodingProperties { public static final Charset DEFAULT_CHARSET = Charset.forName(\"UTF-8\"); 精髓: 1)、SpringBoot启动会加载大量的自动配置类 2)、我们看我们需要的功能有没有SpringBoot默认写好的自动配置类; 3)、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了) 4)、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这些属性的值; xxxxAutoConfigurartion:自动配置类; 给容器中添加组件 xxxxProperties:封装配置文件中相关属性; 2、细节1、@Conditional派生注解(Spring注解版原生的@Conditional作用)作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效; @Conditional扩展注解 作用(判断是否满足当前指定条件) @ConditionalOnJava 系统的java版本是否符合要求 @ConditionalOnBean 容器中存在指定Bean; @ConditionalOnMissingBean 容器中不存在指定Bean; @ConditionalOnExpression 满足SpEL表达式指定 @ConditionalOnClass 系统中有指定的类 @ConditionalOnMissingClass 系统中没有指定的类 @ConditionalOnSingleCandidate 容器中只有一个指定的Bean,或者这个Bean是首选Bean @ConditionalOnProperty 系统中指定的属性是否有指定的值 @ConditionalOnResource 类路径下是否存在指定资源文件 @ConditionalOnWebApplication 当前是web环境 @ConditionalOnNotWebApplication 当前不是web环境 @ConditionalOnJndi JNDI存在指定项 自动配置类必须在一定的条件下才能生效; 我们怎么知道哪些自动配置类生效; ==我们可以通过启用 debug=true属性;来让控制台打印自动配置报告==,这样我们就可以很方便的知道哪些自动配置类生效; 1234567891011121314151617181920212223=========================AUTO-CONFIGURATION REPORT=========================Positive matches:(自动配置类启用的)----------------- DispatcherServletAutoConfiguration matched: - @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) - @ConditionalOnWebApplication (required) found StandardServletEnvironment (OnWebApplicationCondition) Negative matches:(没有启动,没有匹配成功的自动配置类)----------------- ActiveMQAutoConfiguration: Did not match: - @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition) AopAutoConfiguration: Did not match: - @ConditionalOnClass did not find required classes 'org.aspectj.lang.annotation.Aspect', 'org.aspectj.lang.reflect.Advice' (OnClassCondition) 三、日志1、日志框架 小张;开发一个大型系统; 1、System.out.println(“”);将关键数据打印在控制台;去掉?写在一个文件? 2、框架来记录系统的一些运行时信息;日志框架 ; zhanglogging.jar; 3、高大上的几个功能?异步模式?自动归档?xxxx? zhanglogging-good.jar? 4、将以前框架卸下来?换上新的框架,重新修改之前相关的API;zhanglogging-prefect.jar; 5、JDBC—数据库驱动; 写了一个统一的接口层;日志门面(日志的一个抽象层);logging-abstract.jar; 给项目中导入具体的日志实现就行了;我们之前的日志框架都是实现的抽象层; 市面上的日志框架; JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j…. 日志门面 (日志的抽象层) 日志实现 JCL(Jakarta Commons Logging) SLF4j(Simple Logging Facade for Java) jboss-logging Log4j JUL(java.util.logging) Log4j2 Logback 左边选一个门面(抽象层)、右边来选一个实现; 日志门面: SLF4J; 日志实现:Logback; SpringBoot:底层是Spring框架,Spring框架默认是用JCL;‘ ==SpringBoot选用 SLF4j和logback;== 2、SLF4j使用1、如何在系统中使用SLF4j https://www.slf4j.org以后开发的时候,日志记录方法的调用,不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法; 给系统里面导入slf4j的jar和 logback的实现jar 123456789import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class HelloWorld { public static void main(String[] args) { Logger logger = LoggerFactory.getLogger(HelloWorld.class); logger.info(\"Hello World\"); }} 图示; ![images/concrete-bindings.png](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/concrete-bindings.png) 每一个日志的实现框架都有自己的配置文件。使用slf4j以后,配置文件还是做成日志实现框架自己本身的配置文件; 2、遗留问题a(slf4j+logback): Spring(commons-logging)、Hibernate(jboss-logging)、MyBatis、xxxx 统一日志记录,即使是别的框架和我一起统一使用slf4j进行输出? ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/legacy.png) 如何让系统中所有的日志都统一到slf4j; ==1、将系统中其他日志框架先排除出去;== ==2、用中间包来替换原有的日志框架;== ==3、我们导入slf4j其他的实现== 3、SpringBoot日志关系1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId></dependency> SpringBoot使用它来做日志功能; 1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </dependency> 底层依赖关系 ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180131220946.png) 总结: 1)、SpringBoot底层也是使用slf4j+logback的方式进行日志记录 2)、SpringBoot也把其他的日志都替换成了slf4j; 3)、中间替换包? 123456@SuppressWarnings(\"rawtypes\")public abstract class LogFactory { static String UNSUPPORTED_OPERATION_IN_JCL_OVER_SLF4J = \"http://www.slf4j.org/codes.html#unsupported_operation_in_jcl_over_slf4j\"; static LogFactory logFactory = new SLF4JLogFactory(); ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180131221411.png) 4)、如果我们要引入其他框架?一定要把这个框架的默认日志依赖移除掉? Spring框架用的是commons-logging; 12345678910<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions></dependency> ==SpringBoot能自动适配所有的日志,而且底层使用slf4j+logback的方式记录日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉即可;== 4、日志使用;1、默认配置SpringBoot默认帮我们配置好了日志; 123456789101112131415161718//记录器Logger logger = LoggerFactory.getLogger(getClass());@Testpublic void contextLoads() { //System.out.println(); //日志的级别; //由低到高 trace<debug<info<warn<error //可以调整输出的日志级别;日志就只会在这个级别以以后的高级别生效 logger.trace(\"这是trace日志...\"); logger.debug(\"这是debug日志...\"); //SpringBoot默认给我们使用的是info级别的,没有指定级别的就用SpringBoot默认规定的级别;root级别 logger.info(\"这是info日志...\"); logger.warn(\"这是warn日志...\"); logger.error(\"这是error日志...\");} 日志输出格式: %d表示日期时间, %thread表示线程名, %-5level:级别从左显示5个字符宽度 %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 %msg:日志消息, %n是换行符 --> %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%nSpringBoot修改日志的默认配置 123456789101112131415logging.level.com.atguigu=trace#logging.path=# 不指定路径在当前项目下生成springboot.log日志# 可以指定完整的路径;#logging.file=G:/springboot.log# 在当前磁盘的根路径下创建spring文件夹和里面的log文件夹;使用 spring.log 作为默认文件logging.path=/spring/log# 在控制台输出的日志的格式logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n# 指定文件中日志输出的格式logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n logging.file logging.path Example Description (none) (none) 只在控制台输出 指定文件名 (none) my.log 输出日志到my.log文件 (none) 指定目录 /var/log 输出到指定目录的 spring.log 文件中 2、指定配置给类路径下放上每个日志框架自己的配置文件即可;SpringBoot就不使用他默认配置的了 Logging System Customization Logback logback-spring.xml, logback-spring.groovy, logback.xml or logback.groovy Log4j2 log4j2-spring.xml or log4j2.xml JDK (Java Util Logging) logging.properties logback.xml:直接就被日志框架识别了; logback-spring.xml:日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot的高级Profile功能 1234<springProfile name=\"staging\"> <!-- configuration to be enabled when the \"staging\" profile is active --> 可以指定某段配置只在某个环境下生效</springProfile> 如: 12345678910111213141516171819<appender name=\"stdout\" class=\"ch.qos.logback.core.ConsoleAppender\"> <!-- 日志输出格式: %d表示日期时间, %thread表示线程名, %-5level:级别从左显示5个字符宽度 %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 %msg:日志消息, %n是换行符 --> <layout class=\"ch.qos.logback.classic.PatternLayout\"> <springProfile name=\"dev\"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern> </springProfile> <springProfile name=\"!dev\"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern> </springProfile> </layout> </appender> 如果使用logback.xml作为日志配置文件,还要使用profile功能,会有以下错误 no applicable action for [springProfile] 5、切换日志框架可以按照slf4j的日志适配图,进行相关的切换; slf4j+log4j的方式; 12345678910111213141516171819<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <artifactId>logback-classic</artifactId> <groupId>ch.qos.logback</groupId> </exclusion> <exclusion> <artifactId>log4j-over-slf4j</artifactId> <groupId>org.slf4j</groupId> </exclusion> </exclusions></dependency><dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId></dependency> 切换为log4j2 123456789101112131415 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <artifactId>spring-boot-starter-logging</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId></dependency> 四、Web开发1、简介使用SpringBoot; 1)、创建SpringBoot应用,选中我们需要的模块; 2)、SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来 3)、自己编写业务代码; 自动配置原理? 这个场景SpringBoot帮我们配置了什么?能不能修改?能修改哪些配置?能不能扩展?xxx 12xxxxAutoConfiguration:帮我们给容器中自动配置组件;xxxxProperties:配置类来封装配置文件的内容; 2、SpringBoot对静态资源的映射规则;123@ConfigurationProperties(prefix = \"spring.resources\", ignoreUnknownFields = false)public class ResourceProperties implements ResourceLoaderAware { //可以设置和静态资源有关的参数,缓存时间等 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364WebMvcAuotConfiguration: @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { if (!this.resourceProperties.isAddMappings()) { logger.debug(\"Default resource handling disabled\"); return; } Integer cachePeriod = this.resourceProperties.getCachePeriod(); if (!registry.hasMappingForPattern(\"/webjars/**\")) { customizeResourceHandlerRegistration( registry.addResourceHandler(\"/webjars/**\") .addResourceLocations( \"classpath:/META-INF/resources/webjars/\") .setCachePeriod(cachePeriod)); } String staticPathPattern = this.mvcProperties.getStaticPathPattern(); //静态资源文件夹映射 if (!registry.hasMappingForPattern(staticPathPattern)) { customizeResourceHandlerRegistration( registry.addResourceHandler(staticPathPattern) .addResourceLocations( this.resourceProperties.getStaticLocations()) .setCachePeriod(cachePeriod)); } } //配置欢迎页映射 @Bean public WelcomePageHandlerMapping welcomePageHandlerMapping( ResourceProperties resourceProperties) { return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(), this.mvcProperties.getStaticPathPattern()); } //配置喜欢的图标 @Configuration @ConditionalOnProperty(value = \"spring.mvc.favicon.enabled\", matchIfMissing = true) public static class FaviconConfiguration { private final ResourceProperties resourceProperties; public FaviconConfiguration(ResourceProperties resourceProperties) { this.resourceProperties = resourceProperties; } @Bean public SimpleUrlHandlerMapping faviconHandlerMapping() { SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1); //所有 **/favicon.ico mapping.setUrlMap(Collections.singletonMap(\"**/favicon.ico\", faviconRequestHandler())); return mapping; } @Bean public ResourceHttpRequestHandler faviconRequestHandler() { ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler(); requestHandler .setLocations(this.resourceProperties.getFaviconLocations()); return requestHandler; } } ==1)、所有 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 找资源;== webjars:以jar包的方式引入静态资源; http://www.webjars.org/ ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180203181751.png) localhost:8080/webjars/jquery/3.3.1/jquery.js 123456<!--引入jquery-webjar-->在访问的时候只需要写webjars下面资源的名称即可 <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.3.1</version> </dependency> ==2)、”/**” 访问当前项目的任何资源,都去(静态资源的文件夹)找映射== 12345"classpath:/META-INF/resources/", "classpath:/resources/","classpath:/static/", "classpath:/public/" "/":当前项目的根路径 localhost:8080/abc === 去静态资源文件夹里面找abc ==3)、欢迎页; 静态资源文件夹下的所有index.html页面;被”/**”映射;== localhost:8080/ 找index页面 ==4)、所有的 **/favicon.ico 都是在静态资源文件下找;== 3、模板引擎JSP、Velocity、Freemarker、Thymeleaf ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/template-engine.png) SpringBoot推荐的Thymeleaf; 语法更简单,功能更强大; 1、引入thymeleaf;123456789101112 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> 2.1.6 </dependency>切换thymeleaf版本<properties> <thymeleaf.version>3.0.9.RELEASE</thymeleaf.version> <!-- 布局功能的支持程序 thymeleaf3主程序 layout2以上版本 --> <!-- thymeleaf2 layout1--> <thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version> </properties> 2、Thymeleaf使用1234567891011@ConfigurationProperties(prefix = \"spring.thymeleaf\")public class ThymeleafProperties { private static final Charset DEFAULT_ENCODING = Charset.forName(\"UTF-8\"); private static final MimeType DEFAULT_CONTENT_TYPE = MimeType.valueOf(\"text/html\"); public static final String DEFAULT_PREFIX = \"classpath:/templates/\"; public static final String DEFAULT_SUFFIX = \".html\"; // 只要我们把HTML页面放在classpath:/templates/,thymeleaf就能自动渲染; 使用: 1、导入thymeleaf的名称空间 1<html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\"> 2、使用thymeleaf语法; 123456789101112<!DOCTYPE html><html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\"><head> <meta charset=\"UTF-8\"> <title>Title</title></head><body> <h1>成功!</h1> <!--th:text 将div里面的文本内容设置为 --> <div th:text=\"${hello}\">这是显示欢迎信息</div></body></html> 3、语法规则1)、th:text;改变当前元素里面的文本内容; th:任意html属性;来替换原生属性的值 ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/2018-02-04_123955.png) 2)、表达式? 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869Simple expressions:(表达式语法) Variable Expressions: ${...}:获取变量值;OGNL; 1)、获取对象的属性、调用方法 2)、使用内置的基本对象: #ctx : the context object. #vars: the context variables. #locale : the context locale. #request : (only in Web Contexts) the HttpServletRequest object. #response : (only in Web Contexts) the HttpServletResponse object. #session : (only in Web Contexts) the HttpSession object. #servletContext : (only in Web Contexts) the ServletContext object. ${session.foo} 3)、内置的一些工具对象:#execInfo : information about the template being processed.#messages : methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.#uris : methods for escaping parts of URLs/URIs#conversions : methods for executing the configured conversion service (if any).#dates : methods for java.util.Date objects: formatting, component extraction, etc.#calendars : analogous to #dates , but for java.util.Calendar objects.#numbers : methods for formatting numeric objects.#strings : methods for String objects: contains, startsWith, prepending/appending, etc.#objects : methods for objects in general.#bools : methods for boolean evaluation.#arrays : methods for arrays.#lists : methods for lists.#sets : methods for sets.#maps : methods for maps.#aggregates : methods for creating aggregates on arrays or collections.#ids : methods for dealing with id attributes that might be repeated (for example, as a result of an iteration). Selection Variable Expressions: *{...}:选择表达式:和${}在功能上是一样; 补充:配合 th:object=\"${session.user}: <div th:object=\"${session.user}\"> <p>Name: <span th:text=\"*{firstName}\">Sebastian</span>.</p> <p>Surname: <span th:text=\"*{lastName}\">Pepper</span>.</p> <p>Nationality: <span th:text=\"*{nationality}\">Saturn</span>.</p> </div> Message Expressions: #{...}:获取国际化内容 Link URL Expressions: @{...}:定义URL; @{/order/process(execId=${execId},execType='FAST')} Fragment Expressions: ~{...}:片段引用表达式 <div th:insert=\"~{commons :: main}\">...</div> Literals(字面量) Text literals: 'one text' , 'Another one!' ,… Number literals: 0 , 34 , 3.0 , 12.3 ,… Boolean literals: true , false Null literal: null Literal tokens: one , sometext , main ,…Text operations:(文本操作) String concatenation: + Literal substitutions: |The name is ${name}|Arithmetic operations:(数学运算) Binary operators: + , - , * , / , % Minus sign (unary operator): -Boolean operations:(布尔运算) Binary operators: and , or Boolean negation (unary operator): ! , notComparisons and equality:(比较运算) Comparators: > , < , >= , <= ( gt , lt , ge , le ) Equality operators: == , != ( eq , ne )Conditional operators:条件运算(三元运算符) If-then: (if) ? (then) If-then-else: (if) ? (then) : (else) Default: (value) ?: (defaultvalue)Special tokens: No-Operation: _ 4、SpringMVC自动配置https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#boot-features-developing-web-applications 1. Spring MVC auto-configurationSpring Boot 自动配置好了SpringMVC 以下是SpringBoot对SpringMVC的默认配置:==(WebMvcAutoConfiguration)== Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans. 自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发?重定向?)) ContentNegotiatingViewResolver:组合所有的视图解析器的; ==如何定制:我们可以自己给容器中添加一个视图解析器;自动的将其组合进来;== Support for serving static resources, including support for WebJars (see below).静态资源文件夹路径,webjars Static index.html support. 静态首页访问 Custom Favicon support (see below). favicon.ico 自动注册了 of Converter, GenericConverter, Formatter beans. Converter:转换器; public String hello(User user):类型转换使用Converter Formatter 格式化器; 2017.12.17===Date; 12345@Bean@ConditionalOnProperty(prefix = \"spring.mvc\", name = \"date-format\")//在文件中配置日期格式化的规则public Formatter<Date> dateFormatter() { return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化组件} ==自己添加的格式化器转换器,我们只需要放在容器中即可== Support for HttpMessageConverters (see below). HttpMessageConverter:SpringMVC用来转换Http请求和响应的;User—Json; HttpMessageConverters 是从容器中确定;获取所有的HttpMessageConverter; ==自己给容器中添加HttpMessageConverter,只需要将自己的组件注册容器中(@Bean,@Component)== Automatic registration of MessageCodesResolver (see below).定义错误代码生成规则 Automatic use of a ConfigurableWebBindingInitializer bean (see below). ==我们可以配置一个ConfigurableWebBindingInitializer来替换默认的;(添加到容器)== 12初始化WebDataBinder;请求数据=====JavaBean; org.springframework.boot.autoconfigure.web:web的所有自动场景; If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration (interceptors, formatters, view controllers etc.) you can add your own @Configuration class of type WebMvcConfigurerAdapter, but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter or ExceptionHandlerExceptionResolver you can declare a WebMvcRegistrationsAdapter instance providing such components. If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc. 2、扩展SpringMVC1234567<mvc:view-controller path=\"/hello\" view-name=\"success\"/><mvc:interceptors> <mvc:interceptor> <mvc:mapping path=\"/hello\"/> <bean></bean> </mvc:interceptor></mvc:interceptors> ==编写一个配置类(@Configuration),是WebMvcConfigurerAdapter类型;不能标注@EnableWebMvc==; 既保留了所有的自动配置,也能用我们扩展的配置; 1234567891011//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能@Configurationpublic class MyMvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { // super.addViewControllers(registry); //浏览器发送 /atguigu 请求来到 success registry.addViewController(\"/atguigu\").setViewName(\"success\"); }} 原理: 1)、WebMvcAutoConfiguration是SpringMVC的自动配置类 2)、在做其他自动配置时会导入;@Import(EnableWebMvcConfiguration.class) 123456789101112131415161718 @Configurationpublic static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration { private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite(); //从容器中获取所有的WebMvcConfigurer @Autowired(required = false) public void setConfigurers(List<WebMvcConfigurer> configurers) { if (!CollectionUtils.isEmpty(configurers)) { this.configurers.addWebMvcConfigurers(configurers); //一个参考实现;将所有的WebMvcConfigurer相关配置都来一起调用; @Override // public void addViewControllers(ViewControllerRegistry registry) { // for (WebMvcConfigurer delegate : this.delegates) { // delegate.addViewControllers(registry); // } } }} 3)、容器中所有的WebMvcConfigurer都会一起起作用; 4)、我们的配置类也会被调用; 效果:SpringMVC的自动配置和我们的扩展配置都会起作用; 3、全面接管SpringMVC;SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置;所有的SpringMVC的自动配置都失效了 我们需要在配置类中添加@EnableWebMvc即可; 123456789101112//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能@EnableWebMvc@Configurationpublic class MyMvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { // super.addViewControllers(registry); //浏览器发送 /atguigu 请求来到 success registry.addViewController(\"/atguigu\").setViewName(\"success\"); }} 原理: 为什么@EnableWebMvc自动配置就失效了; 1)@EnableWebMvc的核心 12@Import(DelegatingWebMvcConfiguration.class)public @interface EnableWebMvc { 2)、 12@Configurationpublic class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { 3)、 12345678910@Configuration@ConditionalOnWebApplication@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurerAdapter.class })//容器中没有这个组件的时候,这个自动配置类才生效@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class })public class WebMvcAutoConfiguration { 4)、@EnableWebMvc将WebMvcConfigurationSupport组件导入进来; 5)、导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能; 5、如何修改SpringBoot的默认配置模式: 1)、SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(ViewResolver)将用户配置的和自己默认的组合起来; 2)、在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置 3)、在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置 6、RestfulCRUD1)、默认访问首页12345678910111213141516171819202122232425//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能//@EnableWebMvc 不要接管SpringMVC@Configurationpublic class MyMvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { // super.addViewControllers(registry); //浏览器发送 /atguigu 请求来到 success registry.addViewController(\"/atguigu\").setViewName(\"success\"); } //所有的WebMvcConfigurerAdapter组件都会一起起作用 @Bean //将组件注册在容器 public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){ WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController(\"/\").setViewName(\"login\"); registry.addViewController(\"/index.html\").setViewName(\"login\"); } }; return adapter; }} 2)、国际化1)、编写国际化配置文件; 2)、使用ResourceBundleMessageSource管理国际化资源文件 3)、在页面使用fmt:message取出国际化内容 步骤: 1)、编写国际化配置文件,抽取页面需要显示的国际化消息 ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180211130721.png) 2)、SpringBoot自动配置好了管理国际化资源文件的组件; 12345678910111213141516171819202122232425262728@ConfigurationProperties(prefix = \"spring.messages\")public class MessageSourceAutoConfiguration { /** * Comma-separated list of basenames (essentially a fully-qualified classpath * location), each following the ResourceBundle convention with relaxed support for * slash based locations. If it doesn't contain a package qualifier (such as * \"org.mypackage\"), it will be resolved from the classpath root. */ private String basename = \"messages\"; //我们的配置文件可以直接放在类路径下叫messages.properties; @Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); if (StringUtils.hasText(this.basename)) { //设置国际化资源文件的基础名(去掉语言国家代码的) messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray( StringUtils.trimAllWhitespace(this.basename))); } if (this.encoding != null) { messageSource.setDefaultEncoding(this.encoding.name()); } messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale); messageSource.setCacheSeconds(this.cacheSeconds); messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat); return messageSource; } 3)、去页面获取国际化的值; ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180211134506.png) 123456789101112131415161718192021222324252627282930313233343536<!DOCTYPE html><html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\"> <head> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"> <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"> <meta name=\"description\" content=\"\"> <meta name=\"author\" content=\"\"> <title>Signin Template for Bootstrap</title> <!-- Bootstrap core CSS --> <link href=\"asserts/css/bootstrap.min.css\" th:href=\"@{/webjars/bootstrap/4.0.0/css/bootstrap.css}\" rel=\"stylesheet\"> <!-- Custom styles for this template --> <link href=\"asserts/css/signin.css\" th:href=\"@{/asserts/css/signin.css}\" rel=\"stylesheet\"> </head> <body class=\"text-center\"> <form class=\"form-signin\" action=\"dashboard.html\"> <img class=\"mb-4\" th:src=\"@{/asserts/img/bootstrap-solid.svg}\" src=\"asserts/img/bootstrap-solid.svg\" alt=\"\" width=\"72\" height=\"72\"> <h1 class=\"h3 mb-3 font-weight-normal\" th:text=\"#{login.tip}\">Please sign in</h1> <label class=\"sr-only\" th:text=\"#{login.username}\">Username</label> <input type=\"text\" class=\"form-control\" placeholder=\"Username\" th:placeholder=\"#{login.username}\" required=\"\" autofocus=\"\"> <label class=\"sr-only\" th:text=\"#{login.password}\">Password</label> <input type=\"password\" class=\"form-control\" placeholder=\"Password\" th:placeholder=\"#{login.password}\" required=\"\"> <div class=\"checkbox mb-3\"> <label> <input type=\"checkbox\" value=\"remember-me\"/> [[#{login.remember}]] </label> </div> <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\" th:text=\"#{login.btn}\">Sign in</button> <p class=\"mt-5 mb-3 text-muted\">© 2017-2018</p> <a class=\"btn btn-sm\">中文</a> <a class=\"btn btn-sm\">English</a> </form> </body></html> 效果:根据浏览器语言设置的信息切换了国际化; 原理: 国际化Locale(区域信息对象);LocaleResolver(获取区域信息对象); 12345678910111213 @Bean @ConditionalOnMissingBean @ConditionalOnProperty(prefix = \"spring.mvc\", name = \"locale\") public LocaleResolver localeResolver() { if (this.mvcProperties .getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) { return new FixedLocaleResolver(this.mvcProperties.getLocale()); } AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver(); localeResolver.setDefaultLocale(this.mvcProperties.getLocale()); return localeResolver; }默认的就是根据请求头带来的区域信息获取Locale进行国际化 4)、点击链接切换国际化 12345678910111213141516171819202122232425262728/** * 可以在连接上携带区域信息 */public class MyLocaleResolver implements LocaleResolver { @Override public Locale resolveLocale(HttpServletRequest request) { String l = request.getParameter(\"l\"); Locale locale = Locale.getDefault(); if(!StringUtils.isEmpty(l)){ String[] split = l.split(\"_\"); locale = new Locale(split[0],split[1]); } return locale; } @Override public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { }} @Bean public LocaleResolver localeResolver(){ return new MyLocaleResolver(); }} 3)、登陆开发期间模板引擎页面修改以后,要实时生效 1)、禁用模板引擎的缓存 12# 禁用缓存spring.thymeleaf.cache=false 2)、页面修改完成以后ctrl+f9:重新编译; 登陆错误消息的显示 1<p style=\"color: red\" th:text=\"${msg}\" th:if=\"${not #strings.isEmpty(msg)}\"></p> 4)、拦截器进行登陆检查拦截器 123456789101112131415161718192021222324252627282930/** * 登陆检查, */public class LoginHandlerInterceptor implements HandlerInterceptor { //目标方法执行之前 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object user = request.getSession().getAttribute(\"loginUser\"); if(user == null){ //未登陆,返回登陆页面 request.setAttribute(\"msg\",\"没有权限请先登陆\"); request.getRequestDispatcher(\"/index.html\").forward(request,response); return false; }else{ //已登陆,放行请求 return true; } } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { }} 注册拦截器 1234567891011121314151617181920212223//所有的WebMvcConfigurerAdapter组件都会一起起作用 @Bean //将组件注册在容器 public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){ WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController(\"/\").setViewName(\"login\"); registry.addViewController(\"/index.html\").setViewName(\"login\"); registry.addViewController(\"/main.html\").setViewName(\"dashboard\"); } //注册拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { //super.addInterceptors(registry); //静态资源; *.css , *.js //SpringBoot已经做好了静态资源映射 registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns(\"/**\") .excludePathPatterns(\"/index.html\",\"/\",\"/user/login\"); } }; return adapter; } 5)、CRUD-员工列表实验要求: 1)、RestfulCRUD:CRUD满足Rest风格; URI: /资源名称/资源标识 HTTP请求方式区分对资源CRUD操作 普通CRUD(uri来区分操作) RestfulCRUD 查询 getEmp emp—GET 添加 addEmp?xxx emp—POST 修改 updateEmp?id=xxx&xxx=xx emp/{id}—PUT 删除 deleteEmp?id=1 emp/{id}—DELETE 2)、实验的请求架构; 实验功能 请求URI 请求方式 查询所有员工 emps GET 查询某个员工(来到修改页面) emp/1 GET 来到添加页面 emp GET 添加员工 emp POST 来到修改页面(查出员工进行信息回显) emp/1 GET 修改员工 emp PUT 删除员工 emp/1 DELETE 3)、员工列表: thymeleaf公共页面元素抽取12345678910111213141、抽取公共片段<div th:fragment=\"copy\">&copy; 2011 The Good Thymes Virtual Grocery</div>2、引入公共片段<div th:insert=\"~{footer :: copy}\"></div>~{templatename::selector}:模板名::选择器~{templatename::fragmentname}:模板名::片段名3、默认效果:insert的公共片段在div标签中如果使用th:insert等属性进行引入,可以不用写~{}:行内写法可以加上:[[~{}]];[(~{})]; 三种引入公共片段的th属性: th:insert:将公共片段整个插入到声明引入的元素中 th:replace:将声明引入的元素替换为公共片段 th:include:将被引入的片段的内容包含进这个标签中 1234567891011121314151617181920212223<footer th:fragment=\"copy\">&copy; 2011 The Good Thymes Virtual Grocery</footer>引入方式<div th:insert=\"footer :: copy\"></div><div th:replace=\"footer :: copy\"></div><div th:include=\"footer :: copy\"></div>效果<div> <footer> &copy; 2011 The Good Thymes Virtual Grocery </footer></div><footer>&copy; 2011 The Good Thymes Virtual Grocery</footer><div>&copy; 2011 The Good Thymes Virtual Grocery</div> 引入片段的时候传入参数: 1234567891011121314151617<nav class=\"col-md-2 d-none d-md-block bg-light sidebar\" id=\"sidebar\"> <div class=\"sidebar-sticky\"> <ul class=\"nav flex-column\"> <li class=\"nav-item\"> <a class=\"nav-link active\" th:class=\"${activeUri=='main.html'?'nav-link active':'nav-link'}\" href=\"#\" th:href=\"@{/main.html}\"> <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"feather feather-home\"> <path d=\"M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z\"></path> <polyline points=\"9 22 9 12 15 12 15 22\"></polyline> </svg> Dashboard <span class=\"sr-only\">(current)</span> </a> </li><!--引入侧边栏;传入参数--><div th:replace=\"commons/bar::#sidebar(activeUri='emps')\"></div> 6)、CRUD-员工添加添加页面 123456789101112131415161718192021222324252627282930313233343536<form> <div class=\"form-group\"> <label>LastName</label> <input type=\"text\" class=\"form-control\" placeholder=\"zhangsan\"> </div> <div class=\"form-group\"> <label>Email</label> <input type=\"email\" class=\"form-control\" placeholder=\"zhangsan@atguigu.com\"> </div> <div class=\"form-group\"> <label>Gender</label><br/> <div class=\"form-check form-check-inline\"> <input class=\"form-check-input\" type=\"radio\" name=\"gender\" value=\"1\"> <label class=\"form-check-label\">男</label> </div> <div class=\"form-check form-check-inline\"> <input class=\"form-check-input\" type=\"radio\" name=\"gender\" value=\"0\"> <label class=\"form-check-label\">女</label> </div> </div> <div class=\"form-group\"> <label>department</label> <select class=\"form-control\"> <option>1</option> <option>2</option> <option>3</option> <option>4</option> <option>5</option> </select> </div> <div class=\"form-group\"> <label>Birth</label> <input type=\"text\" class=\"form-control\" placeholder=\"zhangsan\"> </div> <button type=\"submit\" class=\"btn btn-primary\">添加</button></form> 提交的数据格式不对:生日:日期; 2017-12-12;2017/12/12;2017.12.12; 日期的格式化;SpringMVC将页面提交的值需要转换为指定的类型; 2017-12-12—Date; 类型转换,格式化; 默认日期是按照/的方式; 7)、CRUD-员工修改修改添加二合一表单 123456789101112131415161718192021222324252627282930313233343536373839404142<!--需要区分是员工修改还是添加;--><form th:action=\"@{/emp}\" method=\"post\"> <!--发送put请求修改员工数据--> <!--1、SpringMVC中配置HiddenHttpMethodFilter;(SpringBoot自动配置好的)2、页面创建一个post表单3、创建一个input项,name=\"_method\";值就是我们指定的请求方式--> <input type=\"hidden\" name=\"_method\" value=\"put\" th:if=\"${emp!=null}\"/> <input type=\"hidden\" name=\"id\" th:if=\"${emp!=null}\" th:value=\"${emp.id}\"> <div class=\"form-group\"> <label>LastName</label> <input name=\"lastName\" type=\"text\" class=\"form-control\" placeholder=\"zhangsan\" th:value=\"${emp!=null}?${emp.lastName}\"> </div> <div class=\"form-group\"> <label>Email</label> <input name=\"email\" type=\"email\" class=\"form-control\" placeholder=\"zhangsan@atguigu.com\" th:value=\"${emp!=null}?${emp.email}\"> </div> <div class=\"form-group\"> <label>Gender</label><br/> <div class=\"form-check form-check-inline\"> <input class=\"form-check-input\" type=\"radio\" name=\"gender\" value=\"1\" th:checked=\"${emp!=null}?${emp.gender==1}\"> <label class=\"form-check-label\">男</label> </div> <div class=\"form-check form-check-inline\"> <input class=\"form-check-input\" type=\"radio\" name=\"gender\" value=\"0\" th:checked=\"${emp!=null}?${emp.gender==0}\"> <label class=\"form-check-label\">女</label> </div> </div> <div class=\"form-group\"> <label>department</label> <!--提交的是部门的id--> <select class=\"form-control\" name=\"department.id\"> <option th:selected=\"${emp!=null}?${dept.id == emp.department.id}\" th:value=\"${dept.id}\" th:each=\"dept:${depts}\" th:text=\"${dept.departmentName}\">1</option> </select> </div> <div class=\"form-group\"> <label>Birth</label> <input name=\"birth\" type=\"text\" class=\"form-control\" placeholder=\"zhangsan\" th:value=\"${emp!=null}?${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}\"> </div> <button type=\"submit\" class=\"btn btn-primary\" th:text=\"${emp!=null}?'修改':'添加'\">添加</button></form> 8)、CRUD-员工删除123456789101112131415161718192021<tr th:each=\"emp:${emps}\"> <td th:text=\"${emp.id}\"></td> <td>[[${emp.lastName}]]</td> <td th:text=\"${emp.email}\"></td> <td th:text=\"${emp.gender}==0?'女':'男'\"></td> <td th:text=\"${emp.department.departmentName}\"></td> <td th:text=\"${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}\"></td> <td> <a class=\"btn btn-sm btn-primary\" th:href=\"@{/emp/}+${emp.id}\">编辑</a> <button th:attr=\"del_uri=@{/emp/}+${emp.id}\" class=\"btn btn-sm btn-danger deleteBtn\">删除</button> </td></tr><script> $(\".deleteBtn\").click(function(){ //删除当前员工的 $(\"#deleteEmpForm\").attr(\"action\",$(this).attr(\"del_uri\")).submit(); return false; });</script> 7、错误处理机制1)、SpringBoot默认的错误处理机制默认效果: 1)、浏览器,返回一个默认的错误页面 ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180226173408.png) 浏览器发送请求的请求头: ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180226180347.png) 2)、如果是其他客户端,默认响应一个json数据 ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180226173527.png) ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180226180504.png) 原理: 可以参照ErrorMvcAutoConfiguration;错误处理的自动配置; 给容器中添加了以下组件 1、DefaultErrorAttributes: 1234567891011帮我们在页面共享信息;@Override public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) { Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>(); errorAttributes.put(\"timestamp\", new Date()); addStatus(errorAttributes, requestAttributes); addErrorDetails(errorAttributes, requestAttributes, includeStackTrace); addPath(errorAttributes, requestAttributes); return errorAttributes; } 2、BasicErrorController:处理默认/error请求 12345678910111213141516171819202122232425@Controller@RequestMapping(\"${server.error.path:${error.path:/error}}\")public class BasicErrorController extends AbstractErrorController { @RequestMapping(produces = \"text/html\")//产生html类型的数据;浏览器发送的请求来到这个方法处理 public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = getStatus(request); Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes( request, isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); //去哪个页面作为错误页面;包含页面地址和页面内容 ModelAndView modelAndView = resolveErrorView(request, response, status, model); return (modelAndView == null ? new ModelAndView(\"error\", model) : modelAndView); } @RequestMapping @ResponseBody //产生json数据,其他客户端来到这个方法处理; public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); HttpStatus status = getStatus(request); return new ResponseEntity<Map<String, Object>>(body, status); } 3、ErrorPageCustomizer: 12@Value(\"${error.path:/error}\")private String path = \"/error\"; 系统出现错误以后来到error请求进行处理;(web.xml注册的错误页面规则) 4、DefaultErrorViewResolver: 123456789101112131415161718192021222324@Override public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) { ModelAndView modelAndView = resolve(String.valueOf(status), model); if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) { modelAndView = resolve(SERIES_VIEWS.get(status.series()), model); } return modelAndView; } private ModelAndView resolve(String viewName, Map<String, Object> model) { //默认SpringBoot可以去找到一个页面? error/404 String errorViewName = \"error/\" + viewName; //模板引擎可以解析这个页面地址就用模板引擎解析 TemplateAvailabilityProvider provider = this.templateAvailabilityProviders .getProvider(errorViewName, this.applicationContext); if (provider != null) { //模板引擎可用的情况下返回到errorViewName指定的视图地址 return new ModelAndView(errorViewName, model); } //模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面 error/404.html return resolveResource(errorViewName, model); } 步骤: 一但系统出现4xx或者5xx之类的错误;ErrorPageCustomizer就会生效(定制错误的响应规则);就会来到/error请求;就会被BasicErrorController处理; 1)响应页面;去哪个页面是由DefaultErrorViewResolver解析得到的; 1234567891011protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) { //所有的ErrorViewResolver得到ModelAndView for (ErrorViewResolver resolver : this.errorViewResolvers) { ModelAndView modelAndView = resolver.resolveErrorView(request, status, model); if (modelAndView != null) { return modelAndView; } } return null;} 2)、如果定制错误响应:1)、如何定制错误的页面; 1)、有模板引擎的情况下;error/状态码; 【将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的 error文件夹下】,发生此状态码的错误就会来到 对应的页面; 我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html); 页面能获取的信息; timestamp:时间戳 status:状态码 error:错误提示 exception:异常对象 message:异常消息 errors:JSR303数据校验的错误都在这里 2)、没有模板引擎(模板引擎找不到这个错误页面),静态资源文件夹下找; 3)、以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面; 2)、如何定制错误的json数据; 1)、自定义异常处理&返回定制json数据; 12345678910111213@ControllerAdvicepublic class MyExceptionHandler { @ResponseBody @ExceptionHandler(UserNotExistException.class) public Map<String,Object> handleException(Exception e){ Map<String,Object> map = new HashMap<>(); map.put(\"code\",\"user.notexist\"); map.put(\"message\",e.getMessage()); return map; }}//没有自适应效果... 2)、转发到/error进行自适应响应效果处理 1234567891011121314@ExceptionHandler(UserNotExistException.class) public String handleException(Exception e, HttpServletRequest request){ Map<String,Object> map = new HashMap<>(); //传入我们自己的错误状态码 4xx 5xx,否则就不会进入定制错误页面的解析流程 /** * Integer statusCode = (Integer) request .getAttribute(\"javax.servlet.error.status_code\"); */ request.setAttribute(\"javax.servlet.error.status_code\",500); map.put(\"code\",\"user.notexist\"); map.put(\"message\",e.getMessage()); //转发到/error return \"forward:/error\"; } 3)、将我们的定制数据携带出去;出现错误以后,会来到/error请求,会被BasicErrorController处理,响应出去可以获取的数据是由getErrorAttributes得到的(是AbstractErrorController(ErrorController)规定的方法); 1、完全来编写一个ErrorController的实现类【或者是编写AbstractErrorController的子类】,放在容器中; 2、页面上能用的数据,或者是json返回能用的数据都是通过errorAttributes.getErrorAttributes得到; 容器中DefaultErrorAttributes.getErrorAttributes();默认进行数据处理的; 自定义ErrorAttributes 1234567891011//给容器中加入我们自己定义的ErrorAttributes@Componentpublic class MyErrorAttributes extends DefaultErrorAttributes { @Override public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) { Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace); map.put(\"company\",\"atguigu\"); return map; }} 最终的效果:响应是自适应的,可以通过定制ErrorAttributes改变需要返回的内容, ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180228135513.png) 8、配置嵌入式Servlet容器SpringBoot默认使用Tomcat作为嵌入式的Servlet容器; ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180301142915.png) 问题? 1)、如何定制和修改Servlet容器的相关配置;1、修改和server有关的配置(ServerProperties【也是EmbeddedServletContainerCustomizer】); 123456789server.port=8081server.context-path=/crudserver.tomcat.uri-encoding=UTF-8//通用的Servlet容器设置server.xxx//Tomcat的设置server.tomcat.xxx 2、编写一个EmbeddedServletContainerCustomizer:嵌入式的Servlet容器的定制器;来修改Servlet容器的配置 1234567891011@Bean //一定要将这个定制器加入到容器中public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){ return new EmbeddedServletContainerCustomizer() { //定制嵌入式的Servlet容器相关的规则 @Override public void customize(ConfigurableEmbeddedServletContainer container) { container.setPort(8083); } };} 2)、注册Servlet三大组件【Servlet、Filter、Listener】由于SpringBoot默认是以jar包的方式启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml文件。 注册三大组件用以下方式 ServletRegistrationBean 123456//注册三大组件@Beanpublic ServletRegistrationBean myServlet(){ ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(),\"/myServlet\"); return registrationBean;} FilterRegistrationBean 1234567@Beanpublic FilterRegistrationBean myFilter(){ FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new MyFilter()); registrationBean.setUrlPatterns(Arrays.asList(\"/hello\",\"/myServlet\")); return registrationBean;} ServletListenerRegistrationBean 12345@Beanpublic ServletListenerRegistrationBean myListener(){ ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener()); return registrationBean;} SpringBoot帮我们自动SpringMVC的时候,自动的注册SpringMVC的前端控制器;DIspatcherServlet; DispatcherServletAutoConfiguration中: 1234567891011121314151617@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public ServletRegistrationBean dispatcherServletRegistration( DispatcherServlet dispatcherServlet) { ServletRegistrationBean registration = new ServletRegistrationBean( dispatcherServlet, this.serverProperties.getServletMapping()); //默认拦截: / 所有请求;包静态资源,但是不拦截jsp请求; /*会拦截jsp //可以通过server.servletPath来修改SpringMVC前端控制器默认拦截的请求路径 registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); registration.setLoadOnStartup( this.webMvcProperties.getServlet().getLoadOnStartup()); if (this.multipartConfig != null) { registration.setMultipartConfig(this.multipartConfig); } return registration;} 2)、SpringBoot能不能支持其他的Servlet容器; 3)、替换为其他嵌入式Servlet容器![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180302114401.png) 默认支持: Tomcat(默认使用) 12345<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> 引入web模块默认就是使用嵌入式的Tomcat作为Servlet容器;</dependency> Jetty 1234567891011121314151617<!-- 引入web模块 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <artifactId>spring-boot-starter-tomcat</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions></dependency><!--引入其他的Servlet容器--><dependency> <artifactId>spring-boot-starter-jetty</artifactId> <groupId>org.springframework.boot</groupId></dependency> Undertow 1234567891011121314151617<!-- 引入web模块 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <artifactId>spring-boot-starter-tomcat</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions></dependency><!--引入其他的Servlet容器--><dependency> <artifactId>spring-boot-starter-undertow</artifactId> <groupId>org.springframework.boot</groupId></dependency> 4)、嵌入式Servlet容器自动配置原理;EmbeddedServletContainerAutoConfiguration:嵌入式的Servlet容器自动配置? 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)@Configuration@ConditionalOnWebApplication@Import(BeanPostProcessorsRegistrar.class)//导入BeanPostProcessorsRegistrar:Spring注解版;给容器中导入一些组件//导入了EmbeddedServletContainerCustomizerBeanPostProcessor://后置处理器:bean初始化前后(创建完对象,还没赋值赋值)执行初始化工作public class EmbeddedServletContainerAutoConfiguration { @Configuration @ConditionalOnClass({ Servlet.class, Tomcat.class })//判断当前是否引入了Tomcat依赖; @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)//判断当前容器没有用户自己定义EmbeddedServletContainerFactory:嵌入式的Servlet容器工厂;作用:创建嵌入式的Servlet容器 public static class EmbeddedTomcat { @Bean public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() { return new TomcatEmbeddedServletContainerFactory(); } } /** * Nested configuration if Jetty is being used. */ @Configuration @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class }) @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedJetty { @Bean public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() { return new JettyEmbeddedServletContainerFactory(); } } /** * Nested configuration if Undertow is being used. */ @Configuration @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class }) @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT) public static class EmbeddedUndertow { @Bean public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() { return new UndertowEmbeddedServletContainerFactory(); } } 1)、EmbeddedServletContainerFactory(嵌入式Servlet容器工厂) 1234567public interface EmbeddedServletContainerFactory { //获取嵌入式的Servlet容器 EmbeddedServletContainer getEmbeddedServletContainer( ServletContextInitializer... initializers);} ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180302144835.png) 2)、EmbeddedServletContainer:(嵌入式的Servlet容器) ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180302144910.png) 3)、以TomcatEmbeddedServletContainerFactory为例 123456789101112131415161718192021222324@Overridepublic EmbeddedServletContainer getEmbeddedServletContainer( ServletContextInitializer... initializers) { //创建一个Tomcat Tomcat tomcat = new Tomcat(); //配置Tomcat的基本环节 File baseDir = (this.baseDirectory != null ? this.baseDirectory : createTempDir(\"tomcat\")); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); //将配置好的Tomcat传入进去,返回一个EmbeddedServletContainer;并且启动Tomcat服务器 return getTomcatEmbeddedServletContainer(tomcat);} 4)、我们对嵌入式容器的配置修改是怎么生效? 1ServerProperties、EmbeddedServletContainerCustomizer EmbeddedServletContainerCustomizer:定制器帮我们修改了Servlet容器的配置? 怎么修改的原理? 5)、容器中导入了EmbeddedServletContainerCustomizerBeanPostProcessor 12345678910111213141516171819202122232425262728293031323334353637//初始化之前@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { //如果当前初始化的是一个ConfigurableEmbeddedServletContainer类型的组件 if (bean instanceof ConfigurableEmbeddedServletContainer) { // postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean); } return bean;}private void postProcessBeforeInitialization( ConfigurableEmbeddedServletContainer bean) { //获取所有的定制器,调用每一个定制器的customize方法来给Servlet容器进行属性赋值; for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) { customizer.customize(bean); }}private Collection<EmbeddedServletContainerCustomizer> getCustomizers() { if (this.customizers == null) { // Look up does not include the parent context this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>( this.beanFactory //从容器中获取所有这葛类型的组件:EmbeddedServletContainerCustomizer //定制Servlet容器,给容器中可以添加一个EmbeddedServletContainerCustomizer类型的组件 .getBeansOfType(EmbeddedServletContainerCustomizer.class, false, false) .values()); Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE); this.customizers = Collections.unmodifiableList(this.customizers); } return this.customizers;}ServerProperties也是定制器 步骤: 1)、SpringBoot根据导入的依赖情况,给容器中添加相应的EmbeddedServletContainerFactory【TomcatEmbeddedServletContainerFactory】 2)、容器中某个组件要创建对象就会惊动后置处理器;EmbeddedServletContainerCustomizerBeanPostProcessor; 只要是嵌入式的Servlet容器工厂,后置处理器就工作; 3)、后置处理器,从容器中获取所有的EmbeddedServletContainerCustomizer,调用定制器的定制方法 ###5)、嵌入式Servlet容器启动原理; 什么时候创建嵌入式的Servlet容器工厂?什么时候获取嵌入式的Servlet容器并启动Tomcat; 获取嵌入式的Servlet容器工厂: 1)、SpringBoot应用启动运行run方法 2)、refreshContext(context);SpringBoot刷新IOC容器【创建IOC容器对象,并初始化容器,创建容器中的每一个组件】;如果是web应用创建AnnotationConfigEmbeddedWebApplicationContext,否则:AnnotationConfigApplicationContext 3)、refresh(context);刷新刚才创建好的ioc容器; 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn(\"Exception encountered during context initialization - \" + \"cancelling refresh attempt: \" + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } }} 4)、 onRefresh(); web的ioc容器重写了onRefresh方法 5)、webioc容器会创建嵌入式的Servlet容器;createEmbeddedServletContainer(); 6)、获取嵌入式的Servlet容器工厂: EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory(); 从ioc容器中获取EmbeddedServletContainerFactory 组件;TomcatEmbeddedServletContainerFactory创建对象,后置处理器一看是这个对象,就获取所有的定制器来先定制Servlet容器的相关配置; 7)、使用容器工厂获取嵌入式的Servlet容器:this.embeddedServletContainer = containerFactory .getEmbeddedServletContainer(getSelfInitializer()); 8)、嵌入式的Servlet容器创建对象并启动Servlet容器; 先启动嵌入式的Servlet容器,再将ioc容器中剩下没有创建出的对象获取出来; ==IOC容器启动创建嵌入式的Servlet容器== 9、使用外置的Servlet容器嵌入式Servlet容器:应用打成可执行的jar 优点:简单、便携; 缺点:默认不支持JSP、优化定制比较复杂(使用定制器【ServerProperties、自定义EmbeddedServletContainerCustomizer】,自己编写嵌入式Servlet容器的创建工厂【EmbeddedServletContainerFactory】); 外置的Servlet容器:外面安装Tomcat—应用war包的方式打包; 步骤1)、必须创建一个war项目;(利用idea创建好目录结构) 2)、将嵌入式的Tomcat指定为provided; 12345<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope></dependency> 3)、必须编写一个SpringBootServletInitializer的子类,并调用configure方法 123456789public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { //传入SpringBoot应用的主程序 return application.sources(SpringBoot04WebJspApplication.class); }} 4)、启动服务器就可以使用; 原理jar包:执行SpringBoot主类的main方法,启动ioc容器,创建嵌入式的Servlet容器; war包:启动服务器,服务器启动SpringBoot应用【SpringBootServletInitializer】,启动ioc容器; servlet3.0(Spring注解版): 8.2.4 Shared libraries / runtimes pluggability: 规则: 1)、服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例: 2)、ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名 3)、还可以使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类; 流程: 1)、启动Tomcat 2)、org\\springframework\\spring-web\\4.3.14.RELEASE\\spring-web-4.3.14.RELEASE.jar!\\META-INF\\services\\javax.servlet.ServletContainerInitializer: Spring的web模块里面有这个文件:org.springframework.web.SpringServletContainerInitializer 3)、SpringServletContainerInitializer将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都传入到onStartup方法的Set<Class<?>>;为这些WebApplicationInitializer类型的类创建实例; 4)、每一个WebApplicationInitializer都调用自己的onStartup; ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180302221835.png) 5)、相当于我们的SpringBootServletInitializer的类会被创建对象,并执行onStartup方法 6)、SpringBootServletInitializer实例执行onStartup的时候会createRootApplicationContext;创建容器 1234567891011121314151617181920212223242526272829303132333435363738protected WebApplicationContext createRootApplicationContext( ServletContext servletContext) { //1、创建SpringApplicationBuilder SpringApplicationBuilder builder = createSpringApplicationBuilder(); StandardServletEnvironment environment = new StandardServletEnvironment(); environment.initPropertySources(servletContext, null); builder.environment(environment); builder.main(getClass()); ApplicationContext parent = getExistingRootWebApplicationContext(servletContext); if (parent != null) { this.logger.info(\"Root context already created (using as parent).\"); servletContext.setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null); builder.initializers(new ParentContextApplicationContextInitializer(parent)); } builder.initializers( new ServletContextApplicationContextInitializer(servletContext)); builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class); //调用configure方法,子类重写了这个方法,将SpringBoot的主程序类传入了进来 builder = configure(builder); //使用builder创建一个Spring应用 SpringApplication application = builder.build(); if (application.getSources().isEmpty() && AnnotationUtils .findAnnotation(getClass(), Configuration.class) != null) { application.getSources().add(getClass()); } Assert.state(!application.getSources().isEmpty(), \"No SpringApplication sources have been defined. Either override the \" + \"configure method or add an @Configuration annotation\"); // Ensure error pages are registered if (this.registerErrorPageFilter) { application.getSources().add(ErrorPageFilterConfiguration.class); } //启动Spring应用 return run(application);} 7)、Spring的应用就启动并且创建IOC容器 1234567891011121314151617181920212223242526272829303132333435public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; FailureAnalyzers analyzers = null; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); Banner printedBanner = printBanner(environment); context = createApplicationContext(); analyzers = new FailureAnalyzers(context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); //刷新IOC容器 refreshContext(context); afterRefresh(context, applicationArguments); listeners.finished(context, null); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } return context; } catch (Throwable ex) { handleRunFailure(context, listeners, analyzers, ex); throw new IllegalStateException(ex); }} ==启动Servlet容器,再启动SpringBoot应用== 五、Docker1、简介Docker是一个开源的应用容器引擎;是一个轻量级容器技术; Docker支持将软件编译成一个镜像;然后在镜像中各种软件做好配置,将镜像发布出去,其他使用者可以直接使用这个镜像; 运行中的这个镜像称为容器,容器启动是非常快速的。 ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180303145450.png) ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180303145531.png) 2、核心概念docker主机(Host):安装了Docker程序的机器(Docker直接安装在操作系统之上); docker客户端(Client):连接docker主机进行操作; docker仓库(Registry):用来保存各种打包好的软件镜像; docker镜像(Images):软件打包好的镜像;放在docker仓库中; docker容器(Container):镜像启动后的实例称为一个容器;容器是独立运行的一个或一组应用 ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180303165113.png) 使用Docker的步骤: 1)、安装Docker 2)、去Docker仓库找到这个软件对应的镜像; 3)、使用Docker运行这个镜像,这个镜像就会生成一个Docker容器; 4)、对容器的启动停止就是对软件的启动停止; 3、安装Docker1)、安装linux虚拟机 1)、VMWare、VirtualBox(安装); 2)、导入虚拟机文件centos7-atguigu.ova; 3)、双击启动linux虚拟机;使用 root/ 123456登陆 4)、使用客户端连接linux服务器进行命令操作; 5)、设置虚拟机网络; 桥接网络===选好网卡====接入网线; 6)、设置好网络以后使用命令重启虚拟机的网络 1service network restart 7)、查看linux的ip地址 1ip addr 8)、使用客户端连接linux; 2)、在linux虚拟机上安装docker步骤: 12345678910111213141、检查内核版本,必须是3.10及以上uname -r2、安装dockeryum install docker3、输入y确认安装4、启动docker[root@localhost ~]# systemctl start docker[root@localhost ~]# docker -vDocker version 1.12.6, build 3e8e77d/1.12.65、开机启动docker[root@localhost ~]# systemctl enable dockerCreated symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.6、停止dockersystemctl stop docker 4、Docker常用命令&操作1)、镜像操作 操作 命令 说明 检索 docker search 关键字 eg:docker search redis 我们经常去docker hub上检索镜像的详细信息,如镜像的TAG。 拉取 docker pull 镜像名:tag :tag是可选的,tag表示标签,多为软件的版本,默认是latest 列表 docker images 查看所有本地镜像 删除 docker rmi image-id 删除指定的本地镜像 https://hub.docker.com/ 2)、容器操作软件镜像(QQ安装程序)—-运行镜像—-产生一个容器(正在运行的软件,运行的QQ); 步骤: 1234567891011121314151617181920212223242526272829301、搜索镜像[root@localhost ~]# docker search tomcat2、拉取镜像[root@localhost ~]# docker pull tomcat3、根据镜像启动容器docker run --name mytomcat -d tomcat:latest4、docker ps 查看运行中的容器5、 停止运行中的容器docker stop 容器的id6、查看所有的容器docker ps -a7、启动容器docker start 容器id8、删除一个容器 docker rm 容器id9、启动一个做了端口映射的tomcat[root@localhost ~]# docker run -d -p 8888:8080 tomcat-d:后台运行-p: 将主机的端口映射到容器的一个端口 主机端口:容器内部的端口10、为了演示简单关闭了linux的防火墙service firewalld status ;查看防火墙状态service firewalld stop:关闭防火墙11、查看容器的日志docker logs container-name/container-id更多命令参看https://docs.docker.com/engine/reference/commandline/docker/可以参考每一个镜像的文档 3)、安装MySQL示例1docker pull mysql 错误的启动 1234567891011121314151617[root@localhost ~]# docker run --name mysql01 -d mysql42f09819908bb72dd99ae19e792e0a5d03c48638421fa64cce5f8ba0f40f5846mysql退出了[root@localhost ~]# docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES42f09819908b mysql \"docker-entrypoint.sh\" 34 seconds ago Exited (1) 33 seconds ago mysql01538bde63e500 tomcat \"catalina.sh run\" About an hour ago Exited (143) About an hour ago compassionate_goldstinec4f1ac60b3fc tomcat \"catalina.sh run\" About an hour ago Exited (143) About an hour ago lonely_fermi81ec743a5271 tomcat \"catalina.sh run\" About an hour ago Exited (143) About an hour ago sick_ramanujan//错误日志[root@localhost ~]# docker logs 42f09819908berror: database is uninitialized and password option is not specified You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD;这个三个参数必须指定一个 正确的启动 12345[root@localhost ~]# docker run --name mysql01 -e MYSQL_ROOT_PASSWORD=123456 -d mysqlb874c56bec49fb43024b3805ab51e9097da779f2f572c22c695305dedd684c5f[root@localhost ~]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESb874c56bec49 mysql \"docker-entrypoint.sh\" 4 seconds ago Up 3 seconds 3306/tcp mysql01 做了端口映射 12345[root@localhost ~]# docker run -p 3306:3306 --name mysql02 -e MYSQL_ROOT_PASSWORD=123456 -d mysqlad10e4bc5c6a0f61cbad43898de71d366117d120e39db651844c0e73863b9434[root@localhost ~]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESad10e4bc5c6a mysql \"docker-entrypoint.sh\" 4 seconds ago Up 2 seconds 0.0.0.0:3306->3306/tcp mysql02 几个其他的高级操作 123456docker run --name mysql03 -v /conf/mysql:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag把主机的/conf/mysql文件夹挂载到 mysqldocker容器的/etc/mysql/conf.d文件夹里面改mysql的配置文件就只需要把mysql配置文件放在自定义的文件夹下(/conf/mysql)docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci指定mysql的一些配置参数 六、SpringBoot与数据访问1、JDBC123456789<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> 123456spring: datasource: username: root password: 123456 url: jdbc:mysql://192.168.15.22:3306/jdbc driver-class-name: com.mysql.jdbc.Driver 效果: 默认是用org.apache.tomcat.jdbc.pool.DataSource作为数据源; 数据源的相关配置都在DataSourceProperties里面; 自动配置原理: org.springframework.boot.autoconfigure.jdbc: 1、参考DataSourceConfiguration,根据配置创建数据源,默认使用Tomcat连接池;可以使用spring.datasource.type指定自定义的数据源类型; 2、SpringBoot默认可以支持; 1org.apache.tomcat.jdbc.pool.DataSource、HikariDataSource、BasicDataSource、 3、自定义数据源类型 1234567891011121314/** * Generic DataSource configuration. */@ConditionalOnMissingBean(DataSource.class)@ConditionalOnProperty(name = \"spring.datasource.type\")static class Generic { @Bean public DataSource dataSource(DataSourceProperties properties) { //使用DataSourceBuilder创建数据源,利用反射创建响应type的数据源,并且绑定相关属性 return properties.initializeDataSourceBuilder().build(); }} 4、DataSourceInitializer:ApplicationListener; 作用: 1)、runSchemaScripts();运行建表语句; 2)、runDataScripts();运行插入数据的sql语句; 默认只需要将文件命名为: 123456schema-*.sql、data-*.sql默认规则:schema.sql,schema-all.sql;可以使用 schema: - classpath:department.sql 指定位置 5、操作数据库:自动配置了JdbcTemplate操作数据库 2、整合Druid数据源12345678910111213141516171819202122232425262728293031323334353637383940414243导入druid数据源@Configurationpublic class DruidConfig { @ConfigurationProperties(prefix = \"spring.datasource\") @Bean public DataSource druid(){ return new DruidDataSource(); } //配置Druid的监控 //1、配置一个管理后台的Servlet @Bean public ServletRegistrationBean statViewServlet(){ ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), \"/druid/*\"); Map<String,String> initParams = new HashMap<>(); initParams.put(\"loginUsername\",\"admin\"); initParams.put(\"loginPassword\",\"123456\"); initParams.put(\"allow\",\"\");//默认就是允许所有访问 initParams.put(\"deny\",\"192.168.15.21\"); bean.setInitParameters(initParams); return bean; } //2、配置一个web监控的filter @Bean public FilterRegistrationBean webStatFilter(){ FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(new WebStatFilter()); Map<String,String> initParams = new HashMap<>(); initParams.put(\"exclusions\",\"*.js,*.css,/druid/*\"); bean.setInitParameters(initParams); bean.setUrlPatterns(Arrays.asList(\"/*\")); return bean; }} 3、整合MyBatis12345<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version></dependency> ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180305194443.png) 步骤: 1)、配置数据源相关属性(见上一节Druid) 2)、给数据库建表 3)、创建JavaBean 4)、注解版1234567891011121314151617//指定这是一个操作数据库的mapper@Mapperpublic interface DepartmentMapper { @Select(\"select * from department where id=#{id}\") public Department getDeptById(Integer id); @Delete(\"delete from department where id=#{id}\") public int deleteDeptById(Integer id); @Options(useGeneratedKeys = true,keyProperty = \"id\") @Insert(\"insert into department(departmentName) values(#{departmentName})\") public int insertDept(Department department); @Update(\"update department set departmentName=#{departmentName} where id=#{id}\") public int updateDept(Department department);} 问题: 自定义MyBatis的配置规则;给容器中添加一个ConfigurationCustomizer; 1234567891011121314@org.springframework.context.annotation.Configurationpublic class MyBatisConfig { @Bean public ConfigurationCustomizer configurationCustomizer(){ return new ConfigurationCustomizer(){ @Override public void customize(Configuration configuration) { configuration.setMapUnderscoreToCamelCase(true); } }; }} 123456789使用MapperScan批量扫描所有的Mapper接口;@MapperScan(value = \"com.atguigu.springboot.mapper\")@SpringBootApplicationpublic class SpringBoot06DataMybatisApplication { public static void main(String[] args) { SpringApplication.run(SpringBoot06DataMybatisApplication.class, args); }} 5)、配置文件版123mybatis: config-location: classpath:mybatis/mybatis-config.xml 指定全局配置文件的位置 mapper-locations: classpath:mybatis/mapper/*.xml 指定sql映射文件的位置 更多使用参照 http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/ 4、整合SpringData JPA1)、SpringData简介![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180306105412.png) 2)、整合SpringData JPAJPA:ORM(Object Relational Mapping); 1)、编写一个实体类(bean)和数据表进行映射,并且配置好映射关系; 12345678910111213//使用JPA注解配置映射关系@Entity //告诉JPA这是一个实体类(和数据表映射的类)@Table(name = \"tbl_user\") //@Table来指定和哪个数据表对应;如果省略默认表名就是user;public class User { @Id //这是一个主键 @GeneratedValue(strategy = GenerationType.IDENTITY)//自增主键 private Integer id; @Column(name = \"last_name\",length = 50) //这是和数据表对应的一个列 private String lastName; @Column //省略默认列名就是属性名 private String email; 2)、编写一个Dao接口来操作实体类对应的数据表(Repository) 123//继承JpaRepository来完成对数据库的操作public interface UserRepository extends JpaRepository<User,Integer> {} 3)、基本的配置JpaProperties 1234567spring: jpa: hibernate:# 更新或者创建数据表结构 ddl-auto: update# 控制台显示SQL show-sql: true 七、启动配置原理几个重要的事件回调机制 配置在META-INF/spring.factories ApplicationContextInitializer SpringApplicationRunListener 只需要放在ioc容器中 ApplicationRunner CommandLineRunner 启动流程: 1、创建SpringApplication对象12345678910111213141516initialize(sources);private void initialize(Object[] sources) { //保存主配置类 if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } //判断当前是否一个web应用 this.webEnvironment = deduceWebEnvironment(); //从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //从多个配置类中找到有main方法的主配置类 this.mainApplicationClass = deduceMainApplicationClass();} ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180306145727.png) ![](E:/BaiduNetdiskDownload/Spring Boot 笔记+课件/images/搜狗截图20180306145855.png) 2、运行run方法12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; FailureAnalyzers analyzers = null; configureHeadlessProperty(); //获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories SpringApplicationRunListeners listeners = getRunListeners(args); //回调所有的获取SpringApplicationRunListener.starting()方法 listeners.starting(); try { //封装命令行参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //准备环境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成 Banner printedBanner = printBanner(environment); //创建ApplicationContext;决定创建web的ioc还是普通的ioc context = createApplicationContext(); analyzers = new FailureAnalyzers(context); //准备上下文环境;将environment保存到ioc中;而且applyInitializers(); //applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法 //回调所有的SpringApplicationRunListener的contextPrepared(); // prepareContext(context, environment, listeners, applicationArguments, printedBanner); //prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded(); //s刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版 //扫描,创建,加载所有组件的地方;(配置类,组件,自动配置) refreshContext(context); //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调 //ApplicationRunner先回调,CommandLineRunner再回调 afterRefresh(context, applicationArguments); //所有的SpringApplicationRunListener回调finished方法 listeners.finished(context, null); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } //整个SpringBoot应用启动完成以后返回启动的ioc容器; return context; } catch (Throwable ex) { handleRunFailure(context, listeners, analyzers, ex); throw new IllegalStateException(ex); }} 3、事件监听机制配置在META-INF/spring.factories ApplicationContextInitializer 123456public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext applicationContext) { System.out.println(\"ApplicationContextInitializer...initialize...\"+applicationContext); }} SpringApplicationRunListener 123456789101112131415161718192021222324252627282930313233public class HelloSpringApplicationRunListener implements SpringApplicationRunListener { //必须有的构造器 public HelloSpringApplicationRunListener(SpringApplication application, String[] args){ } @Override public void starting() { System.out.println(\"SpringApplicationRunListener...starting...\"); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { Object o = environment.getSystemProperties().get(\"os.name\"); System.out.println(\"SpringApplicationRunListener...environmentPrepared..\"+o); } @Override public void contextPrepared(ConfigurableApplicationContext context) { System.out.println(\"SpringApplicationRunListener...contextPrepared...\"); } @Override public void contextLoaded(ConfigurableApplicationContext context) { System.out.println(\"SpringApplicationRunListener...contextLoaded...\"); } @Override public void finished(ConfigurableApplicationContext context, Throwable exception) { System.out.println(\"SpringApplicationRunListener...finished...\"); }} 配置(META-INF/spring.factories) 12345org.springframework.context.ApplicationContextInitializer=\\com.atguigu.springboot.listener.HelloApplicationContextInitializerorg.springframework.boot.SpringApplicationRunListener=\\com.atguigu.springboot.listener.HelloSpringApplicationRunListener 只需要放在ioc容器中 ApplicationRunner 1234567@Componentpublic class HelloApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println(\"ApplicationRunner...run....\"); }} CommandLineRunner 1234567@Componentpublic class HelloCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println(\"CommandLineRunner...run...\"+ Arrays.asList(args)); }} 八、自定义starterstarter: 1、这个场景需要使用到的依赖是什么? 2、如何编写自动配置 12345678910111213@Configuration //指定这个类是一个配置类@ConditionalOnXXX //在指定条件成立的情况下自动配置类生效@AutoConfigureAfter //指定自动配置类的顺序@Bean //给容器中添加组件@ConfigurationPropertie结合相关xxxProperties类来绑定相关的配置@EnableConfigurationProperties //让xxxProperties生效加入到容器中自动配置类要能加载将需要启动就加载的自动配置类,配置在META-INF/spring.factoriesorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\\org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\\org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\\ 3、模式: 启动器只用来做依赖导入; 专门来写一个自动配置模块; 启动器依赖自动配置;别人只需要引入启动器(starter) mybatis-spring-boot-starter;自定义启动器名-spring-boot-starter 步骤: 1)、启动器模块 12345678910111213141516171819202122<?xml version=\"1.0\" encoding=\"UTF-8\"?><project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"> <modelVersion>4.0.0</modelVersion> <groupId>com.atguigu.starter</groupId> <artifactId>atguigu-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> <!--启动器--> <dependencies> <!--引入自动配置模块--> <dependency> <groupId>com.atguigu.starter</groupId> <artifactId>atguigu-spring-boot-starter-autoconfigurer</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies></project> 2)、自动配置模块 123456789101112131415161718192021222324252627282930313233343536373839<?xml version=\"1.0\" encoding=\"UTF-8\"?><project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"> <modelVersion>4.0.0</modelVersion> <groupId>com.atguigu.starter</groupId> <artifactId>atguigu-spring-boot-starter-autoconfigurer</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>atguigu-spring-boot-starter-autoconfigurer</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.10.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <!--引入spring-boot-starter;所有starter的基本配置--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> </dependencies></project> 1234567891011121314151617181920212223242526package com.atguigu.starter;import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(prefix = \"atguigu.hello\")public class HelloProperties { private String prefix; private String suffix; public String getPrefix() { return prefix; } public void setPrefix(String prefix) { this.prefix = prefix; } public String getSuffix() { return suffix; } public void setSuffix(String suffix) { this.suffix = suffix; }} 123456789101112131415161718package com.atguigu.starter;public class HelloService { HelloProperties helloProperties; public HelloProperties getHelloProperties() { return helloProperties; } public void setHelloProperties(HelloProperties helloProperties) { this.helloProperties = helloProperties; } public String sayHellAtguigu(String name){ return helloProperties.getPrefix()+\"-\" +name + helloProperties.getSuffix(); }} 12345678910111213141516171819202122package com.atguigu.starter;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration@ConditionalOnWebApplication //web应用才生效@EnableConfigurationProperties(HelloProperties.class)public class HelloServiceAutoConfiguration { @Autowired HelloProperties helloProperties; @Bean public HelloService helloService(){ HelloService service = new HelloService(); service.setHelloProperties(helloProperties); return service; }} 更多SpringBoot整合示例https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples","link":"/2020/04/07/%E6%A1%86%E6%9E%B6/Springboot-quick-start/"},{"title":"maven介绍","text":"maven入门第一篇 前言Maven 这个词可以翻译为“知识的积累",也可以翻译为“专家”或“内行” 。简单来说,Maven 这一跨平台的项目管理丁具,在学习spring以及一些springboot框架之前都要先提前了解一下这个有用的工具。 maven 优于ant “Ant 还是 Maven?”这个大标题没什么建设性意义。如果你非要我们来回答这个问题,我们会很明确的说作为构建的基本技术,Maven 是 Ant 的更好选择;同时,Maven 的边界在持续的移动,Maven的社区也在持续的是试图找到新的方法,使其更通用,互操作性更好,更易协同工作。Maven 的核心财产是声明性构建,依赖管理,仓库管理,基于插件的高度和重用,但是当前,和开源社区相互协作以降低”企业级构建“的低效率这个目标来比,这些想法的特定实现没那么重要。 –《Maven权威指南》 下载到官网下载 找到自己电脑相应的版本,windows的找到最新的bin.zip后缀的压缩包进行下载。 然后配置好环境变量,就是当初jdk的配置一样,不懂可以百度,都学到了maven了,相信大家都可以完成这一步, 接下来可以在cmd中输入,查看是否安装配置好。 1mvn -v 找到官网中5分钟快速入门教学 按照步骤 12 初始用 maven的周期Validate: 验证所有的工程信息是否可用且正确 Compile: 编译源代码 Test: 在一套framework下运行但愿测试 Package: 以发布的格式打包编译的代码 Integration-test: 在集成测试环境中处理(部署)发布包 Verify: 检测发布包是否正确可用Install: 在本地的repository上安装发布包Deploy: 在远程的repository上安装发布包","link":"/2020/04/21/maven%E4%BB%8B%E7%BB%8D/"},{"title":"其它/数据库使用记录","text":"刚开始的设置 ALTER USER ‘root’@’localhost’ IDENTIFIED WITH mysql_native_password BY ‘YourPassword’;-设置密码格式,给远程连接排除问题 FLUSH PRIVILEGES;-刷新权限 source 文件 –mysql导入 mysqldump -h ip -uroot password 数据库 数据表 > 物理地址-mysql命令行导出 数据库的三大范式 主键-要求数据表的每一列都是不可分割的原子数据项 ,保证不可再分,比如说学习情况:不能存英语 良好 数学 优秀,这里是可以再分的 满足第一范式的前提下,属性要依赖主键,无关的不能设计在同一个表上-每一个表只描述一件事情 满足第二个范式,确保每一列都和主键直接相关,不能间接相关 关联查询的表不能超过第三张表。同时数据库设计最重要的是看需求跟性能,需求>性能>表结构。所以不能一味的去追求范式建立数据库。 JDBC的步骤 注册 获取连接DriverManager.getConnection 得到连接,执行sql-connection.createStatement jdbc的statement有三种execuateQuery和execuateUpdate和execuate Preparedstatement-防止SQL注入(本质是把传递的参数当作字符),并且效率更高 特点是预编译 connection.Preparedstatement 可以设置set方法来更新数据 获得Resultset(有查询和更新的sql语句方法) 关闭资源","link":"/2020/04/23/%E5%85%B6%E5%AE%83/%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BD%BF%E7%94%A8%E8%AE%B0%E5%BD%95/"},{"title":"mysql索引详细介绍","text":"索引前言总所周知,数据库查询是数据库的最主要功能之一。我们都希望查询数据的速度能尽可能的快。而支撑这一快速的背后就是索引;MySQL索引问题也是大家经常遇到的面试题模块,想想自己也没有去系统地总结过索引,所以记录这篇文章来讲下索引。下面还是按照是什么->有什么用->怎么用->来写 是什么往往大家第一时间提到索引,可能就会说到它是一种数据结构,来提高查询效率的数据结构,用在常用来查询的字段上。但是原理是什么呢?为什么它就可以加快查询? 首先,现如今,数据库系统大多的索引底层结构是B树或者B+树,在数据结构的学习中,大家可能都有了解过,我们先简单介绍下这两种结构。 B树 特点:每个结点都有数据,同时还有指向其下子树的指针域,单个结构和链表的基本单元相似。 每个结点一个数据,等于就命中,小于该数据走左边,大于走右边 B+树 是B树的变形,多路搜索树,是一种稠密索引 特点:真实的数据存储在叶子结点的链表中,其他非叶子结点并没有数据,而是作为叶子结点的索引;链表中的关键字是有序的。所有叶子结点都在同一层 B树和B+树结构上异同 同:都是平衡树,每个结点到叶子结点的高度都是相同的,也保证每个查询都是稳定,查询的时间复杂度是log2(n),利用平衡树的优势是可以很大程度加快查询的稳定性的。 异:关键字数量不同,存储的位置也不同,查询不同;B树在找到具体的数值以后,则结束,B+树通过索引找到叶子结点的数据才结束,也就是B+树一定都得找到叶子结点。 有什么用对于两种树结构的使用和应用总结,也就是重要作用。 B树的树内存储数据,因此查询单条数据的时候,B树的查询效率不固定,最好的情况是O(1)。我们可以认为在做单一数据查询的时候,使用B树平均性能更好。但是,由于B树中各节点之间没有指针相邻,因此B树不适合做一些数据遍历操作。 B+树的数据只出现在叶子节点上,因此在查询单条数据的时候,查询速度非常稳定。因此,在做单一数据的查询上,其平均性能并不如B树。但是,B+树的叶子节点上有指针进行相连,因此在做数据遍历的时候,只需要对叶子节点进行遍历即可,这个特性使得B+树非常适合做范围查询。 接下来重点讲的是MySQL的索引结构。 讲回索引,在MySQnL中,索引属于存储引擎级别的概念,而我们常常提到MySQL的引擎,就会提到MyISAM和InnoDB。这里插一下,MyISAM是非聚集(也叫非聚簇)索引,而InnoDB是聚集索引(也叫聚簇)。其实更简单通俗得讲,正文内容按照一个特定维度排序存储,这个特定的维度就是聚集索引; 聚集索引是指数据库表行中数据的物理顺序与键值的逻辑(索引)顺序相同。一个表只能有一个聚集索引,因为一个表的物理顺序只有一种情况,所以,对应的聚集索引只能有一个。如果某索引不是聚集索引,则表中的行物理顺序与索引顺序不匹配,与非聚集索引相比,聚集索引有着更快的检索速度。 –《百度百科》 MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址,想要获得数据,还得通过地址去获得;同时非聚集索引索引项顺序存储,但索引项对应的内容却是随机存储的; InnoDB表数据文件本身就是一个索引结构,树的叶节点data域保存了完整的数据记录,这种索引叫做聚集索引。这种索引特点是叶子结点完全包含了数据,同时InnoDB要求按主键聚集,所以也要求表要有主键,没有的话系统会自动选择一个唯一标识数据记录的列作为主键。因此,InnoDB的表也叫做索引表; 最后借用一个解释来帮助大家理解聚集索引和非聚集索引。同时这里有个很好的例子 1汉语字典提供了两类检索汉字的方式,第一类是拼音检索(前提是知道该汉字读音),比如拼音为cheng的汉字排在拼音chang的汉字后面,根据拼音找到对应汉字的页码(因为按拼音排序,二分查找很快就能定位),这就是我们通常所说的字典序;第二类是部首笔画检索,根据笔画找到对应汉字,查到汉字对应的页码。拼音检索就是聚集索引,因为存储的记录(数据库中是行数据、字典中是汉字的详情记录)是按照该索引排序的;笔画索引,虽然笔画相同的字在笔画索引中相邻,但是实际存储页码却不相邻。 怎么用首先讲下sql语句。 123456789101112# 主要记住加索引和删索引操作# 可以在一开始建表时候加,也可以后面加# ALTER TABLE用来创建普通索引、UNIQUE索引或PRIMARY KEY索引。ALTER TABLE table_name ADD INDEX index_name (column_list);# CREATE INDEX可对表增加普通索引或UNIQUE索引。CREATE INDEX index_name ON table_name (column_list)# 删除DROP INDEX index_name ON talbe_nameALTER TABLE table_name DROP INDEX index_name# 这个只在删除主键的时候使用 常常一张表只有一个主键ALTER TABLE table_name DROP PRIMARY KEY 下面的是简单使用情况以及结果分析(有索引和没索引的分析),先看一开始表结构的索引情况 执行以下语句,建立一个first_name_last_name索引。 12345678910111213141516171819USE myemployees;SHOW TABLES;DESC employees;# 建立了二级索引,是一个联合索引ALTER TABLE employees ADD INDEX first_name_last_name(first_name, last_name);# 为了明确看到查询性能,我们启用profiling并关闭query cache:SET profiling = 1;SET query_cache_type = 0;SET GLOBAL query_cache_size = 0;# 用EXPLAIN来查看sql语句执行的情况EXPLAIN SELECT * from employees WHERE first_name='Alyssa' AND last_name LIKE '%on';DESC employees;# 删除索引DROP INDEX first_name_last_name ON employees;# 查看无索引状态下的执行效率SELECT * from employees WHERE first_name='Alyssa' AND last_name LIKE '%on'; 查看此时的索引结构,以及有了索引 执行查询sql,看看有无索引的情况下的EXPLAIN语句情况 首先是无索引下的结果 再来是有索引的 这里解释下我标注出来的这三个参数,其实这里的数据量不是很大,看查询时间差距不大,所以查看rows的参数便可以参考下两个查询的区别,一个只需一行,另一个走了107行数据。所以说索引加快查询效率。之所以会有快速的效果,就是由于上面的B+树的数据结构在起作用。 就像十亿个数据,如果按照常规逻辑,可能最差的情况下,需要匹配十亿次才可以找到,加上这十亿个数据给内存带来了多少的负荷可想而知,所以要是转化为平衡树,可能只需要十层或者十几层之类的树结构,也就数据只需要花费很少的IO开销就可以找到了。这两个的差别就是天壤之别了。 type:表示MySQL在表中找到所需行的方式 ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行 ref::表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值 ROWS: 表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数 Extra:**该列包含MySQL解决查询的详细信息 ** 最后 借鉴1 借鉴2","link":"/2020/04/23/mysql/mysql-%E7%B4%A2%E5%BC%95/"},{"title":"“JavaScript笔记”","text":"学习js第一部分use strict-规范要求,写在第一行,用来检查 let-用来定义局部变量 - console.log() - 字符串不可变,长度也可以是length访问,大小写函数和java很像,substring、indexof也一样。 - 数组长度可以变,arr.length=…可以随意改变,只不过值是undefined;数组还有push、pop压入弹出(尾部);unshift\\shiift-压入弹出头部 第二部分- 对象 {}-表示一个对象,而且用键值对表示属性,最后一个属性不用逗号 var 对象名 = { 属性名:属性值, 属性名:属性值 } - 动态删除和增加属性 用delete删除,加上对象 直接用属性来添加也可以 - Map set-ES6才出来的 ``` JavaScript var map = new Map([“tom”,100],[“cks”,34]); var name = map.get(“tom”); map.set(“admin”,45);//新增 map.delete(“tom”)//删除 // set无序不重复的集合 var set = new Set([12,3,4,45]); set.add(6); set.delete(1); ``` - iterator遍历集合,用for of也可以遍历,for in也可以,还有foreach遍历 - arguments是在函数里可以传递所有参数的数组,fori循环来使用,包含所有的参数 - rest可以获取除了已经定义的参数 - js中var定义变量是由有作用域的,js中函数查找都是就近原则 - const关键字-表示只读变量 ## 第三部分 - Date get方法-年月日都可以获得,还有时分秒 - JSON 早期习惯是用XML来传输数据 是轻量级的数据交换格式,特点是清晰和简洁,任何js支持的类型都可以用json来表示 - 格式 对象使用{} 数组[] 所有键值对使用key-value格式 ```JavaScript //Converts a JavaScript value to a JavaScript Object Notation (JSON) string. //对象转化为JSON字符串 var jsonuser = JSON.stringify() //JSON字符串再转化为对象 参数是传入json字符串 var obj = JSON.parse(‘{“name”:”yhy”,”age”:3,”sex”:”男”}’); ``` - 面向对象 - 继承 一开始使用._proto_,后面ES6使用了class,模板和java一样,可以定义一个class,里面使用constructor用来定义构造函数的 ## 第四部分 - BOM-浏览器对象模型 window-浏览器窗口 Navigator-封装了浏览器的信息","link":"/2020/04/23/%E5%85%B6%E5%AE%83/JavaScript%E7%AC%94%E8%AE%B0/"},{"title":"TCP的点滴","text":"TCP连接与断开前言1相信面试过的小伙伴对这个话题应该不陌生,算是面试必问了,三次握手,四次挥手,以及其中的一些衍生问题。 TCP/IP(Transmission Control protocol / Internet Protocol)-传输控制协议,当前热门流行的网络传输控制协议,作用在传输层,同时TCP协议是面向连接的,是可靠的; 握手首先是握手连接图解, 一般回答的是也是分步骤来回答的; 1231、将三步具体回答出来,每一步做的是什么2、将标志位信息解释,同时还有序列号3、注意总结为什么需要三次 客户首先发送一个特殊的TCP报文段,服务器用另一个特殊的TCP报文段来响应. 最后客户再用第三个特殊报文段作为响应, 前两个报文段不承载 “ 有效载荷" ,也就是不包含应用层数据; 而第三个报文段可以承载有效载荷,由于在这两台主机之间发送了3个报文段,所以这种连接建立过程常被称为三次握手。 –《计算机网络-自顶向下方法》 三次握手建立连接(**步骤)** 1.在一开始的请求当中,先是客户端发送请求,同时将标志位SYN值为1,请求建立连接,并将seq值变为x发送 2.服务器接收到请求之后,表示同意建立请求,将ACK置1,并将seq变为x+1,同时发送一个数据位y过去 3.客户端收到请求确认之后,再将收到的心情返回给服务器 FLAG-SYN、ACK、FIN(重点关注) SYN是Synchronize Sequence Numbers用来建立连接时所发送的同步信号 ACK是Acknowledgement用于对收到的数据进行确认,确认的数据由确认序列号表示 FIN是finish,常常在断开连接时候来使用,表明自己不会发送数据了,要断开连接了,但在这个标志位下,是可以接收数据的 为什么需要三次? 首先是也是比较重要的目的,确认信息对等。若只有两次握手,其中被请求建立连接的一方并不能确认自己的发报能力和对方的收报能力。因为在这里没有收到反馈,相当于第三次也算是个反馈,这样两者的发报和收报都是可以保证正常与否才建立连接。 第二是防止超时,假设只有两次请求,常常会有无效的请求被当作有效的,这时会建立了脏连接,此时像B一样,确认同意了建立连接,而A并不会理会。 挥手其次是挥手的图解,和上面一样也是有步骤和注意事项的 步骤(很经典的一个实例) 男生:我们分手吧(FIN = 1,seq = x) 女生:好的,分吧,我收拾一下就走;等我收拾完告诉你(ACk = 1, seq = y, ack = x+1) 女生:我收拾完了,分吧,滚(说完这话,女生不能拥抱男生了)(FIN =1,ACk = 1,seq = w, ack = y+1) 男生:好的,再见(双方还约定了两个月的过渡时期,才可以分别找新的对象)(Ack = 1,seq = x+1,acl = w+1) TCP连接时是同步的,但结束时是不同步的,当挥手第二次后宣告的了主动关闭方不会再主动发送数据,但仍然可以接收数据,此时处于半关闭状态。这样被动关闭方有足够的时间去处理以前没有处理完的数据,它可能还有一部分数据没发送出去需要处理,在此之后提出主动关闭连接。所以4次挥手的设计为连接双方都提供了一定的处理扫尾工作的时间,从而显的是必要的。 细比一下,这很人性化,连接不是你想关就关的,就仿佛,你说要停电就立马把电停了。这样让我处于一个窘境,提前为手机充满电的时间都没有。 关键状态的分析 MSL:Maximum Segment LifeTime报文最大存活时间 TIME_WAIT:在客户端最后一次发送ack报文后,就会进入这个阶段,等待2MSL之后进入closed状态;2MSL是报文在网文上生存的最长时间,在超过这个值就会被丢弃。常常面试会问道,为什么需要这个阶段?不是很浪费资源吗? 121、可靠的终止TCP连接-确认被动关闭方能够顺利进入closed状态;常常有最后一个ack由于网络没法到达对方,和下面的2MSL问题回答相似2、保证让迟来的TCP报文段有足够的时间被识别并丢弃 为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态? 1虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。 CLOSE_WAIT:发生在被动关闭方收到关闭请求,并做出第一次应答后进入的状态,该状态是在等待关闭,并且通知各个程序线程发送剩余数据,处理后事,关掉一些资源。 最后借鉴了计算机网络书中的知识,还有码出高效的内容,有什么错误请大家及时指出,感谢阅读!!","link":"/2020/04/21/%E5%85%B6%E5%AE%83/TCP%E7%9A%84%E7%82%B9%E6%BB%B4/"},{"title":"排序算法","text":"前言为了准备面试,前阵子将排序算法认认真真得刷了一遍,通过看书看视频,实践打代码,还有一部分的leetcode题,自己感觉也有点进步,现在做个小记录。 冒泡排序 该排序就是一种像泡泡浮到水面以后,将其挑选,这种浮出来的前提是就是或者是小的/大的先露头,将/小的大的检索出来。(根据从大到小或者反过来) 1234567891011121314151617181920212223242526272829303132333435363738394041package src.datastructure;import java.util.Arrays;/** * @Author: yhy * @Time: 23:49 *///时间复杂度O(n^2)public class DubbleSort { public static void main(String[] args) { int[] abc = {1,9,-2,40,33}; int temp = 0; boolean flag = false; //第一轮for,n个数都要参与比较。 for (int i = 0; i <abc.length-1 ; i++) { //第1个和其他的数进行比较。。再到第二个数与其他的 for (int j = 0; j < abc.length-1 ; j++) { //这里是升序排序 if (abc[j] > abc[j+1]) { flag = true; temp = abc[j]; abc[j] = abc[j+1]; abc[j+1] = temp; } } //这里优化了一些,减少了比较的次数,若排好序了,就直接跳出循环,输出结果 if (!flag) { break; }else { flag = false; } } System.out.println(Arrays.toString(abc)); }} 选择排序 先找个值假设作为最小值,进行该值以后的数与该值比较,小的就放在前面 12345678910111213141516171819202122232425262728293031323334353637package src.datastructure;import java.util.Arrays;/** * @Author: yhy * @Time: 15:30 * 选择排序 */public class SelectSort { public static void main(String[] args) { int[] abc = {1,9,-2,40,33};// 选择排序:选择一开始为最小的,然后每一次开始排序// 也有最小值还有最小值的索引 for (int i = 0; i < abc.length; i++) { int min = abc[i]; int minIndex = i; for (int j = i+1; j < abc.length; j++) { if (min > abc[j]) { min = abc[j]; minIndex = j; abc[j] = abc[i]; }// 关键判断;要将原来的索引与min比较,若发生了交换,就将相对小的数放在前面去 if (minIndex != i) { abc[i] = min; } } } System.out.println(Arrays.toString(abc)); }} 插入排序插入意思就是从无序区找值插到有序区去,所以取第一个值为有序区,等到有序区长度为n(数组长度)时候就成功排序。 123456789101112131415161718192021222324252627282930313233343536373839package src.datastructure;import java.util.Arrays;/** * @Author: yhy * @Time: 15:30 */public class InsertSort { public static void main(String[] args) { int[] abc = {1,9,-2,40,33};// 插入排序,分为有序区,和无序区// 和选择排序有那么一点相似,// 这里是将一部分定义为有,然后比较再选择插入的位置等 for (int i = 1; i < abc.length ; i++) { int insertIndex = i -1; int insertValue = abc[i];// 这里的数组下标是可以等于0 while (insertIndex >= 0 && insertValue < abc[insertIndex]){// 将大的值往后移,直到找到合适的插入位置 abc[insertIndex+1] = abc[insertIndex]; insertIndex--; }// 退出循环后,就说明插入的位置找到了 加入判断,如果没有进去while的话,就不用赋值,插入位置不变 if (insertIndex + 1 != i) { abc[insertIndex+1] = insertValue; } } System.out.println(Arrays.toString(abc)); }} 快速排序 一种双指针移动的排序,找个基准值 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960package src.datastructure;import java.util.Arrays;/** * @Author: yhy * @Time: 12:05 * 采用指针交换来做 */public class QuickSort2 { public static void quickSort(int[] arr, int startIndex, int endIndex) {// 递归结束:startIndex 大于等于endIndex的时候 if (startIndex >= endIndex) { return; }// 得到基准元素位置 int pivotIndex = partition(arr, startIndex, endIndex);// 使用分治法递归数列的两部分 quickSort(arr, startIndex, pivotIndex - 1); quickSort(arr, pivotIndex + 1, endIndex); } public static int partition(int[] arr, int startIndex, int endIndex) {// 这里的交换次数更少了 int pivot = arr[startIndex]; int left = startIndex; int right = endIndex;// 退出循环的时候,说明已经检索到中间位置了 while (left != right){// 分别去找到左边和右边停止的指针 while (left < right && arr[right] > pivot){ right--; } while (left < right && arr[left] <= pivot){ left++; }// 然后进行交换 if (left < right) { int p = arr[left]; arr[left] = arr[right]; arr[right] = p; } }// 一轮交换已经结束// pivot和指针重合点交换// 将基准值放到所对应的位置 int p = arr[left]; arr[left] = arr[startIndex]; arr[startIndex] = p; return left; } public static void main(String[] args) { int[] arr = new int[]{4,2,3,6,15,7,1,9}; quickSort(arr,0,arr.length-1); System.out.println(Arrays.toString(arr)); }} 希尔排序 希尔排序就是分组排序,将取一个间隔gap作为每次分的组数。 123456789101112131415161718192021222324252627282930313233343536373839package src.datastructure;import java.util.Arrays;/** * @Author: yhy * @Time: 15:30 * 希尔排序 */public class ShellSort { public static void main(String[] args) { int[] abc = {1,9,-2,40,33};// 希尔排序,要进行分组排序,同时依据是gap长度除2,每一次进行交换// 用到了插入排序和冒泡排序的感觉// 这是分组 for (int gap = abc.length/2; gap > 0; gap /= 2) {// 这个for循环写法要注意 for (int i = gap; i < abc.length; i++) { int j = i; int temp = abc[j]; if (abc[j] < abc[j-gap]) { while (j - gap >= 0 && temp < abc[j-gap]){ abc[j] = abc[j-gap]; j -= gap; } //将相对小索引的值变为一开始的 abc[j] = temp; } } } System.out.println(Arrays.toString(abc)); }} 归并排序 利用了分治的思想,先分再合来排序。降低问题的难度,逐一突破。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667package src.datastructure;import java.util.Arrays;/** * @Author: yhy * @Time: 0:06 */public class MergeSort { public static void main(String[] args) {// 自顶向下的归并排序 int[] abc = {1, 9, -2, 40, 33, 6, 5}; int[] temp = new int[abc.length]; mergeSort(abc, 0, abc.length - 1, temp); System.out.println(Arrays.toString(abc)); } // 分加合 public static void mergeSort(int[] arr, int left, int right, int[] temp) { if (left < right) { int mid = (left + right) / 2; //中间索引 //向左递归进行分解 mergeSort(arr, left, mid, temp); //向右递归进行分解 mergeSort(arr, mid + 1, right, temp); //合并 merge(arr, left, mid, right, temp); } } // 合方法 public static void merge(int[] arr, int left, int mid, int right, int[] temp) { int leftStart = left; int rightStart = mid + 1; int i = 0;// 这里注意可以等于 while (leftStart <= mid && rightStart <= right) { if (arr[leftStart] >= arr[rightStart]) { temp[i++] = arr[rightStart++]; } else { temp[i++] = arr[leftStart++]; } } while (leftStart <= mid) { temp[i++] = arr[leftStart++]; } while (rightStart <= right) { temp[i++] = arr[rightStart++]; }// copy array i = 0; int tempLeft = left; while (tempLeft <= right) { arr[tempLeft++] = temp[i++]; } }} 基数排序 用到了十个桶,将每一个数据按照位数将其分割,百、十、个位数进行比较 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384package com.atguigu.sort;import java.text.SimpleDateFormat;import java.util.Arrays;import java.util.Date;public class RadixSort { public static void main(String[] args) { int arr[] = { 53, 3, 542, 748, 14, 214}; // 80000000 * 11 * 4 / 1024 / 1024 / 1024 =3.3G // int[] arr = new int[8000000];// for (int i = 0; i < 8000000; i++) {// arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数// } radixSort(arr); System.out.println(\"基数排序后 \" + Arrays.toString(arr)); } //基数排序方法 public static void radixSort(int[] arr) { //根据前面的推导过程,我们可以得到最终的基数排序代码 //1. 得到数组中最大的数的位数 int max = arr[0]; //假设第一数就是最大数 for(int i = 1; i < arr.length; i++) { if (arr[i] > max) { max = arr[i]; } } //得到最大数是几位数 int maxLength = (max + \"\").length(); //定义一个二维数组,表示10个桶, 每个桶就是一个一维数组 //说明 //1. 二维数组包含10个一维数组 //2. 为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.length //3. 名明确,基数排序是使用空间换时间的经典算法 int[][] bucket = new int[10][arr.length]; //为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数 //可以这里理解 //比如:bucketElementCounts[0] , 记录的就是 bucket[0] 桶的放入数据个数 int[] bucketElementCounts = new int[10]; //这里我们使用循环将代码处理 for(int i = 0 , n = 1; i < maxLength; i++, n *= 10) { //(针对每个元素的对应位进行排序处理), 第一次是个位,第二次是十位,第三次是百位.. for(int j = 0; j < arr.length; j++) { //取出每个元素的对应位的值 int digitOfElement = arr[j] / n % 10; //放入到对应的桶中 bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j]; bucketElementCounts[digitOfElement]++; } //按照这个桶的顺序(一维数组的下标依次取出数据,放入原来数组) int index = 0; //遍历每一桶,并将桶中是数据,放入到原数组 for(int k = 0; k < bucketElementCounts.length; k++) { //如果桶中,有数据,我们才放入到原数组 if(bucketElementCounts[k] != 0) { //循环该桶即第k个桶(即第k个一维数组), 放入 for(int l = 0; l < bucketElementCounts[k]; l++) { //取出元素放入到arr arr[index++] = bucket[k][l]; } } //第i+1轮处理后,需要将每个 bucketElementCounts[k] = 0 !!!! bucketElementCounts[k] = 0; } } }} leetcode 347题 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748package src.datastructure;import java.util.ArrayList;import java.util.Arrays;import java.util.List;/** * @Author: yhy * @Time: 14:37 * leetcode #347 * 用到了桶排序的思想 */public class BucketSort { // Given [1,1,1,2,2,3] and k = 2, return [1,2]. public static void main(String[] args) { int[] arr = new int[]{1, 1, 1, 2, 2, 3}; int[] a = bucketsort(arr); System.out.println(Arrays.toString(a)); System.out.println(maxReturn(a,2)); }// 传入数组,返回数组 public static int[] bucketsort(int[] arr){// 定义一个桶 int[] bucket = new int[10]; for (int val: arr ) { bucket[val]++; } return bucket; } private static List maxReturn(int[] arr,int k){ int max = arr[0]; List<Integer> list = new ArrayList<>(); while (k >0) { for (int i = 1; i < arr.length; i++) { if (max < arr[i]) { max = arr[i]; arr[i] = 0; list.add(i); } } max = 0; k--; } return list; }} 堆排序不稳定,线性复杂度,完全二叉树。 大顶堆结点大于两边子树,arr[i]>arr[2i+1]&&a[i]>a[2i+2],升序使用这个 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960package src.datastructure.sort;import java.util.Arrays;/** * @Author: yhy * @Date: 2020/3/19 * @Time: 12:30 */public class HeapSort { public static void main(String[] args) { int[] arr = {12, 3, 63, 6, 8}; heapsort(arr); } public static void heapsort(int[] arr) { int temp = 0; System.out.println(\"堆排序\");//找到第一个大顶堆结构,然后才可以进行下面的代码 for (int i = arr.length / 2 - 1; i >= 0; i--) { adjustHeap(arr, i, arr.length); } System.out.println(Arrays.toString(arr));//交换数据,数组排序 调整堆结构+交换堆顶元素与末尾元素 for (int i = arr.length - 1; i > 0; i--) { temp = arr[i]; arr[i] = arr[0]; arr[0] = temp; adjustHeap(arr, 0, i); } System.out.println(\"排序后的\" + Arrays.toString(arr)); } public static void adjustHeap(int[] arr, int i, int length) { //不断调整,弄个大顶堆 //后面要进行交换 int temp = arr[i]; //寻找非叶子节点 最后的条件表示左子节点还可以往下寻找 for (int j = i * 2 + 1; j < length; j = j * 2 + 1) { //左右节点的比较 if (j + 1 < length && arr[j] < arr[j + 1]) { j++; } if (arr[j] > temp) { arr[i] = arr[j]; i = j; } else { break; } } arr[i] = temp; }} 小顶堆结点小于两边子树 思想(借助树的结构完成排序) 将待排序序列构造成一个大顶堆 此时序列的最大值就是堆顶的根节点 将其与数组末尾元素交换,这样构造数组的最大值在右边 再将n-1个元素重新构造一个堆,这样就会得到n个元素的次小值,反复进行就可以得到一个有序的数组,完成排序","link":"/2020/04/23/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95/"},{"title":"DFS和BFS","text":"广度优先和深度优先搜索前言看着这两个搜索的前提的是读者具备图这一数据结构的基本知识,这些可以直接百度一波就了解了。图也像树一样,遍历具有很多的学问在里面,下面我将借用leetcode的题目讲解一下,虽然是图的遍历,但是借助树好像讲的更见浅白一点,不好的地方多指教。 广度优先搜索(BFS)-对于树而言,就是一种层层遍历的感觉,在实现的过程中,常常借助的是辅助队列来实现,也就是借助先进先出的特性来实现的。下图来看。用BFS的话,就是3-9-20-15-7的结果。 整体实现来说,就是遍历root再来遍历左右子树,不过与DFS区别的是,这里是借助先进先出的特点,也就是要将前面的先排列出来,不用走到叶子结点才输出。一句话简单来说,BFS就是队列,入队列,出队列; 下面是借助leetcode的题目来巩固这个知识点,上面的图也是这个题的。题目要求层层从左往右遍历结点。 123456789101112131415161718192021222324252627282930313233343536373839 class Solution { public int[] levelOrder(TreeNode root) { //特殊情况 if(root == null){ return new int[0]; } //用队列来实现广度优先搜索 ArrayList<Integer> list = new ArrayList<>(); Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); while(!queue.isEmpty()){ // 出列,这里的顺序就是先进先出,层层逐个遍历 TreeNode node = queue.poll(); list.add(node.val); // 逐个入列,辅助队列也是BFS的关键点 if(node.left != null){ queue.add(node.left); } if(node.right != null){ queue.add(node.right); } } // 这样转换会慢一点 // int[] res = list.stream().mapToInt(Integer::valueOf).toArray(); int[] res = new int[list.size()]; for(int i = 0; i < list.size();i++){ res[i] = list.get(i); } //题目要求返回的是int[] return res; } }} 上面这道可以变形成输出结果不一样,也就是剑指offer中的后面两道-面试题31 - II. 从上到下打印二叉树和面试32题。 31题是要求输出的结果是不同数组的集合,每层的结点作为一个数组,解决代码如下 12345678910111213141516171819202122232425262728293031323334353637class Solution { List<List<Integer>> res = new LinkedList<>(); public List<List<Integer>> levelOrder(TreeNode root) { if(root == null){ return res; } //用队列来实现广度优先搜索 Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); while(!queue.isEmpty()){ ArrayList<Integer> list = new ArrayList<>(); //用队列的长度来做,这里在for循环中,长度一直在变,所以要先将其取出来 //关键点:主要思想在于用每次的队列长度来 判定这一层的结点有多少 //正如一开始只有一个根结点,所以长度等于一,只需执行一次添加list int size = queue.size(); for(int i = 0; i < size; i++){ // 出列,这里的顺序就是先进先出,层层逐个遍历 TreeNode node = queue.poll(); //这道题要求每层出一个数组 list.add(node.val); // 逐个入列,辅助队列也是BFS的关键点 if(node.left != null){ queue.add(node.left);} if(node.right != null){queue.add(node.right);} } //每层加完就添加到结果里面 res.add(list); } //题目要求返回的是List<List<>> return res; } } 32题有和上面不一样的地方在于,第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。就是奇数偶数层不一样的遍历方式。可以通过借助一个布尔常量来实现。 12345678910111213141516171819202122232425262728293031323334353637383940414243class Solution { List<List<Integer>> res = new LinkedList<>(); public List<List<Integer>> levelOrder(TreeNode root) { if(root == null){ return res; } //用队列来实现广度优先搜索 Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); boolean flag = true; while(!queue.isEmpty()){ //这道题有实现头插法,为了高效,使用链表数组 List<Integer> list = new LinkedList<>(); //用队列的长度来做,这里在for循环中,长度一直在变,所以要先将其取出来 int size = queue.size(); for(int i = 0; i < size; i++){ // 出列,这里的顺序就是先进先出,层层逐个遍历 TreeNode node = queue.poll(); //关键点:这道题要求每层出一个数组,而且奇数行和偶数不一样 //奇数行是从左往右,偶数行是从右往左走 //借助一个布尔类型来完成 if(flag){ list.add(node.val); }else{ //前面开始插 list.add(0,node.val); } // 逐个入列,辅助队列也是BFS的关键点 if(node.left != null){ queue.add(node.left);} if(node.right != null){queue.add(node.right);} } //每次遍历完一行便开始更换布尔类型 flag = !flag; //每层加完就添加到结果里面 res.add(list); } //题目要求返回的是List<List<>>a return res; }} 深度优先搜索DFS讲到DFS,很容易想到递归,没错它就是借助了递归的思想。在图中的描述是:深度优先搜索在搜索过程中访问某个顶点后,需要递归地访问此顶点的所有未访问过的相邻顶点。 上面的图即是该题,题目要求输入一个目标sum,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。 12345比如给出22,则返回下面{[5,4,11,2],[5,8,4,5]} 1234567891011121314151617181920212223242526272829303132333435363738394041/**leetcode 二叉树中和为某一值的路径(剑指offer34题) * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */class Solution { LinkedList<List<Integer>> res = new LinkedList<>(); LinkedList<Integer> path = new LinkedList<>(); public List<List<Integer>> pathSum(TreeNode root, int sum) { // 有遍历 有递归 recur(root,sum); //返回的是链表的链表结果 return res; } public void recur(TreeNode root,int sum){// 递归的终止条件 if (root == null){ return; } path.add(root.val); sum -= root.val; //找到了最后叶子结点,且可以满足sum的和要求,便将该结果添加进去res if (sum == 0&& root.left ==null&&root.right == null){ //这里需要添加新的对象 res.add(new LinkedList<>(path)); }// 左子树递归 recur(root.left,sum);// 右子树递归 recur(root.right,sum);// 删掉上一个结点,这一步是比较难理解的,这一步有点回溯的感觉,就是你找到最后不符合要求的结点,你要返回到上一步,重新走下去。这一步是左右子树都递归完成后就会执行的 path.removeLast(); }} leetcode104-求深度这个题是要求求树的深度,可以很好得对比BFS和DFS的做法,实例如下。 直接上代码,格式和模板都和上面的差不多。 1234567891011121314151617181920212223242526272829303132333435 public int maxDepth(TreeNode root) {// bfs //时间复杂度也为O(n)if(root == null){ return 0;} Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); int num = 0; while(!queue.isEmpty()){ num++; //借助队列来完成 int size = queue.size(); for(int i = 0; i < size; i++){ TreeNode node = queue.poll(); if(node.left != null){ queue.add(node.left); } if(node.right != null){ queue.add(node.right); } } } return num; } //Dfs 只有这两行。// 时间复杂度为O(n), if(root == null){ return 0; }else{ return Math.max(maxDepth(root.left),maxDepth(root.right))+1; }","link":"/2020/04/26/DFS%E5%92%8CBFS/"},{"title":"leetcode-二进制","text":"leetcode经典二进制运算题目寻找只出现一次的两个数 输入:nums = [4,1,4,6]输出:[1,6] 或 [6,1] 123456789101112131415161718192021222324class Solution { public int[] singleNumbers(int[] nums) { //用到了异或运算和与运算 int res = 0; //遍历所有,最后的异或运算的结果就是这两个不同数的结果 for(int a : nums){ res ^= a; } //算出一个mask,二进制中的1的最低位 int flag = res & (-res); int result[] = new int[2]; for(int a: nums){ //与运算等于0的分到一组,另一个分到另一组去 if((a&flag )== 0){ result[0] ^= a; }else{ result[1] ^= a; } } return result; }} 50. Pow(x, n) (快速幂,二进制) 用到 向下整除 n // 2 等价于 右移一位 n >> 1 ; 取余数 n % 2 等价于 判断二进制最右位 n & 1 题目:实现函数求幂 1234567891011121314151617class Solution { public double myPow(double x, int n) { if(x == 0.0f) return 0.0d; long b = n; double res = 1.0; if(b < 0) { x = 1 / x; b = -b; } while(b > 0) { if((b & 1) == 1) res *= x; x *= x; b >>= 1; } return res; }} 这里需要注意的是int转为long,java中int类型的范围n∈[−2147483648,2147483647],如果n=−2147483648,执行-n就会出现越界,所以转为long来操作就安全了。一开始的报错。 这道题最核心的部分在于下面这三行,从二进制来看,比如2的9次幂。 123if((b & 1) == 1) res *= x; x *= x; b >>= 1; 那么上面的b就是9,我们需要转化为二进制为1001,又因为2的9次幂等于2的一次乘于2的八次;且由&运算可知,在1001中,也就是第一位1和第四位1会在if语句中生效,所以res就可以取得2^1*2^3=2^9;","link":"/2020/05/06/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/leetcode/"},{"title":"leetcode of String","text":"前言记录和归纳leetcode上的字符串题目 题目最长子回文串给定一个字符串,返回子串中的最长回文串 回文应该大家知道,aba、aa、abccba这样的 123456789101112131415161718192021222324252627282930313233//用的是中心扩散法class Solution { //往中间开始向周围找回文串 int lo; int max; public String longestPalindrome(String s) { int length = s.length(); if(length < 2){ return s; } for(int i = 0; i < length-1; i++){ findMiddle(s,i,i);//处理奇数长度 aba findMiddle(s,i,i+1);//处理偶数长度 abba } //截取 return s.substring(lo,lo+max); } public void findMiddle(String s,int left, int right){ //满足回文的条件,便开始循环 while(left >= 0&& right < s.length()&&left <= right && s.charAt(left) == s.charAt(right)){ left--; right++; } //每次更新的条件是满足大于之前的长度 if(max < right -left -1){ //这里的加一是因为在上面的while循环里面,假设条件是用到了left = 0了,执行完一次--,此时left会退到-1 lo = left +1; max = right -left -1; } }} 动态规划的做法,这个做法好像更容易一点,就是假设第一位和第四位相同字符,那么就要第二第三也是回文。所以借助一个布尔数组来判断 12345678910111213141516171819202122232425262728293031public String longestPalindrome(String s) { if (s == null || s.length() < 2) { return s; } int strLen = s.length(); int maxStart = 0; //最长回文串的起点 int maxEnd = 0; //最长回文串的终点 int maxLen = 1; //最长回文串的长度 boolean[][] dp = new boolean[strLen][strLen]; for (int r = 1; r < strLen; r++) { for (int l = 0; l < r; l++) { //判断中间是否是回文串 if (s.charAt(l) == s.charAt(r) && (r - l <= 2 || dp[l + 1][r - 1])) { dp[l][r] = true; //符合回文 if (r - l + 1 > maxLen) { maxLen = r - l + 1; maxStart = l; maxEnd = r; } } } } return s.substring(maxStart, maxEnd + 1); }","link":"/2020/04/28/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/leetcode-of-String/"},{"title":"leetcode-滑动窗口算法","text":"leetcode刷题——总结字符串滑动窗口思想解法做了一些字符串题目后,查看题解的时候看到了滑动窗口思想,之前都没有去了解过,看一些文章也比较模糊,想自己总结弄懂,然后能够讲接地气给你们看。 是什么【滑动窗口算法】(sliding window algorithm)–想必大家都有在平常生活中遇到过滑动窗口的场景,这个算法浅白来讲就是这样的感觉,滑动窗口(满足了连续的位置),改变长度或者位置,去获得不同要求的结果,很明显的是这个窗口滑动距离满足不超过这个窗户整体长度,所以在处理一些字符/数组的子部分的问题时候,可能就派上用场了。 简单的数组滑动实例: leetcode3-求无重复字符的最长子串长度 1234567输入: "abcabcbb"输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。输入: "pwwkew"输出: 3解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串 滑动窗口问题的解决一般都得设置好双指针。 leetcode76-最小覆盖子串 答案解析如下 模板在leetcode上有一位大佬总结出来了模板,可以参考下 大致逻辑如下 1234567891011121314int left = 0, right = 0;while (right < s.size()) {` // 增大窗口 window.add(s[right]); right++; while (window needs shrink) { // 缩小窗口 window.remove(s[left]); left++; }}//对应上面的例题,中间的while条件需要自己去修改,原理是一样的,遇到不符合的条件,就调整窗口大小 123456789101112131415161718192021222324252627282930313233343536373839public List<Integer> findAnagrams(String s, String p) { List<Integer> list = new ArrayList<>(); int left = 0 , right = 0; char[] needs = new char[128]; //相当于hashMap,用于记录每个字符的个数 char[] windows = new char[128]; for (int i = 0; i < p.length(); i++) { needs[p.charAt(i)]++; } //统计符合要求字符个数 int count =0; while(right < s.length()){ char ch = s.charAt(right); windows[ch]++; if (needs[ch] > 0 && needs[ch] >= windows[ch]){ count++; } //长度满足条件 while( count == p.length()){ //加入符合要求的结果 if (right + 1 - left == p.length()){ list.add(left); } //经过一轮的条件满足,要向下继续寻找,不符合下面要求,则滑动窗口,往右走 char ch1 = s.charAt(left); //这一步是有点难理解的,一开始我是结合例子,步步过才掌握了。很巧妙 if (needs[ch1] > 0){ //这里是通过剔除已经满足的窗边位置,这样才可以往下走,重新往右搜索 windows[ch1]--; if ( windows[ch1] < needs[ch1]){ count--; } } left++; } right++; } return list; } 作用 滑动窗口算法可以解决字符串或者数组的一些子部分问题,比如一些要求连续的子部分 同时可以提高效率,减低时间复杂度,将嵌套问题优化。 在有些字符串情况下,比kmp算法还要高效","link":"/2020/05/05/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3/"},{"title":"leetcode-动态规划","text":"动态规划1、前言动态规划(Dynamic Programming)本身是属于运筹学的一个分支。是解决决策过程最优解的数学方法, 常常做到动态规划问题,想记录下做过的dp题,遇到简单的还可以想出来一些,但是常常都是有点难度,而且动态规划问题很常见,在一些大厂的笔试面试题中,今天来总结下动态规划。 2、介绍动态规划有个很明显的特点就是可以解决重复的子问题,就是将多次重复计算的子部分省掉,只计算一次,将其记录起来,提高效率。还有一个很重要的特点是存在最优子结构-子部分中有个部分是符合题目的最优解。当然这个结果是在比较之后得出的。 3、分类在百度百科上有着详细的分类,遇到比较多的是背包问题,在下面的举例中的也大多是背包问题,背包问题就是简单来说,就一个背包,装东西,怎么装,在满足什么条件去装,从而得到不同的最优结果。 4、使用场景以及怎么使用使用场景首先是什么时候会用到动态规划,一开始我也会模糊这个使用。但总的来说就是遇到一些求最值问题,或者是最优解,往往动态规划都是可以用的。 同时记住要使用动态规划的前提是必须满足最优化原理(简单说这个解一定是最屌的解,最优解)和无后效性(不受前面影响)。 最优化原理(最优子结构性质):一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。 过程的历史只能通过当前的状态去影响它的未来的发展,不受之前的影响,当前的状态已经是之前的总结了,未来只能是通过现在去影响了。这个性质称为无后效性。 使用步骤概念动态规划中重要的三个概念有最优子结构、边界和状态转移方程。理解好这三个,任何动态规划问题就可以很好很容易解决的。 最优子结构-指的是不走最后一步或者走最后一步的最优子情况是怎么样的。 边界是每一个动态规划问题要想有解必须存在的,没有边界就没有答案,就是到某一个解的时候,其没有子解了,它自己已经是最小的了。 状态转移方程是最重要的,常常看到下面类似这种方程,就是状态转移方程。 步骤(当确定了使用动态规划之后) 1、找到状态(小规模问题的数学表示)状态表示阶段开始时的客观条件,当前处于什么的条件 2、最小状态是什么这个也是指边界(最小规模的问题)也是逆推的终点。 3、列出状态转移方程(大规模问题如何转化为更小的问题)确定过程由一个状态转移到另一个状态的演变过程。这个也是最重要的,最难的。 4、要求的返回值是什么题目要求返回什么,就将结果解答出来。因为有时最后的最优状态不是所需的,可能需要输出其他的结果,所以要看清楚题目要求。 5、动态规划解题思维有两种思维,简单的就是从头到尾,还有从尾到头。 自顶向下基本会用递归,大范围开始推算到小范围(后面的最优到最前的最优),注意不断保存着中间结果,避免了重复计算。这里是和运筹学中的逆推解法大同小异。意思是从最终状态开始,逆着实际过程的进展方向逐段求解。直到第一个阶段为止。 下面举例是熟悉的走楼梯 一个人每次只能走一层楼梯或者两层楼梯,问走到第100层楼梯一共有多少种方法。 12345678910111213int[] dp= new int[100];/*用于保存中间结果否则会重复计算很多重复的子问题*/int DP(int n){ //1和2是边界条件 if(n == 1) return 1; if(n == 2) return 2; //状态转移方程 dp[n] = DP(n-1) + DP(n-2); //返回的结果 return dp[n];} 自底向上自小推到大,从小范围到大范围,运筹学中的顺推法,与上面只是方向不一样。 格式如下,常常伴随着for遍历 123for(int i= 0; i < max; i++){ dp[i] = dp[i-1]+dp[i-2]} 6、例题题目有以下这些推荐— #背包问题背包问题也分为了0-1背包和完全背包和多重背包问题 0-1背包问题0-1背包就是每个物品只能用一次,重量价值都是一一对应,放的重量也是有限的。然后求出最大价值的放置方法 #416 完全背包完全背包问题是物品是无次数限制。其他和0-1一样 多重背包多重背包是物品有次数限制,比如2、3、4这样的 #5 #983 7、最后动态规划就是解决冗余的。 借鉴了一些文章如下,感谢,有什么不对的地方大家多多指教,多多留言,觉得写的还可以点个赞hh 程序员小灰 知乎","link":"/2020/05/05/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/leetcode%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"},{"title":"leetcode回文系列","text":"leetcdoe回文字系列这篇是记录在刷题时候遇到一些回文字符串或者回文数系列的题目,有着一定的共性,值得记录总结学习一下。 #5最长回文子串这个题之前面试的时候遇到过,题目如下,还是蛮重要的一道题 思路:这里不仅仅是验证是否是回文,还要你给回最长的子字符串,有种最优解的感觉,所以想必是可以使用动态规划来做的,验证状态方程的判断条件就使用以往的回文数的判定方法。借助双指针,这里的双指针是跟随双指针,不是之前的左右遍历双指针。 1234567891011121314151617181920212223242526272829303132333435363738class Solution { //动态规划 //关键在于找到状态方程以及转移方程 public String longestPalindrome(String s) { //先定义好一开始的一些变量 int size = s.length(); if(size < 2){ return s; } int start = 0; int end = 0; int max = 1;// 定义好布尔类型的二维数组 boolean[][] pali = new boolean[size][size]; //遍历 for(int r = 1; r < size; r++){ for(int l = 0; l < r; l++){ //想要判断当前的指针指针指向 if((s.charAt(l) == s.charAt(r)) && ((r - l <= 2) || pali[l+1][r-1]) ){ pali[l][r] = true; //符合回文 //这里有这一项是因为题目要求给出最长的子字符串,所以每次找到回文的就要 //判断一下它是否比之前的长 if(r-l+1 > max){ max = r- l+1; start = l; end = r; } } } } //返回结果 return s.substring(start,end+1); } } #9回文数题目如下,简单判断是否是回文数。顺着来和逆着来是一个数的意思。 思路: 首先是可以转换为字符串来做,暴力法或者是字符串双指针都是可以的 建议使用数学法,感觉更加高效一点,采用除余的方法来判断。 12345678910111213class Solution { public boolean isPalindrome(int x) { if(x < 0) return false; int cur = 0; int num = x; while(num != 0) { cur = cur * 10 + num % 10; num /= 10; } return cur == x; }} #680 验证回文字符串2题目如下,这道题有点特殊是,它可以允许删除一个字符来满足回文。 思路是用双指针来做,左右指针移动来完成条件的筛选,还借助了一个函数。 123456789101112131415161718192021222324252627class Solution { public boolean validPalindrome(String s) { int size = s.length(); //首先是总体的移动 for( int l = 0,r = size-1; l < r; l++,r--){ //找到不合适的地方,这里是允许删除一个字符来满足 if(s.charAt(l) != s.charAt(r)){ //选择删掉左边的一个字符或者是右边的字符来满足 return valid(s,l+1,r) || valid(s,l,r-1); } } return true; } //删除左边的是这种abcca //删除右边的是这种abbca public boolean valid(String s, int l, int r){ while(l < r){ //这里删完了之后。查找就像以前的回文字符串验证一样,遍历完看看是否全部符合就行了 if(s.charAt(l++) != s.charAt(r--)){ return false; } } return true; }}","link":"/2020/05/19/leetcode/"},{"title":"mybatis03-复杂查询","text":"MyBatis:持久层框架前言之前有看过和学习一些mybatis的文章和内容,但是没有去写过文章记录下,现在借鉴b站的狂神视频和官方文档看来重新撸一遍入门。有错误请多指教。 内容数据访问层-相当于之前web项目中dao层,数据库的交互,包括增删改查; 持久化就是将数据在持久状态和瞬时状态转化的过程。内存是断电即失。所以需要数据的持久化。 JDBC技术:Connection、PrepareStatement、ResultSet. 为什么要使用框架代替jdbc呢? 因为之前的jdbc的操作总是是重复单一的,在开发的时候要执行sql语句直接操作数据库,要经过加载驱动等操作,为了高效的开发,避免繁琐的操作,框架就诞生了。框架对jdbc进行封装,mybatis只需关注sql语句直接操作数据库,封装了操作的很多细节,这样可以将更多时间精力放在sql语句的编写上。 特点:灵活容易上手,用的多,封装jdbc;MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。 演示使用现在演示mybatis的一个helloworld程序,看看是怎么样去使用。 pom.xml, 整个项目的maven管理文件。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859<?xml version=\"1.0\" encoding=\"UTF-8\"?><project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"> <modelVersion>4.0.0</modelVersion> <groupId>com.yhy.learn</groupId> <artifactId>mybatisDemo</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>mybatis-01</module> </modules> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> </resources> </build></project> 工具类 123456789101112131415161718192021222324252627282930313233343536373839package com.yhy.utils;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.Configuration;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;import java.io.InputStream;/** * @Author: yhy * @Date: 2020/5/12 * @Time: 11:45 * 工具类 * 获取sqlsession工厂 */public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try {// 利用mybatis在一开始就获得了sqlsessionfactory String resource = \"mybatis-config.xml\"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } }// 有了工厂就可以获得实例来使用,sqlsession就可以面向数据库操作jdbc public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); }} pojo的user类 映射数据库的user表 1234567891011121314151617181920212223242526272829303132333435363738package com.yhy.pojo;/** * @Author: yhy * @Date: 2020/5/12 * @Time: 10:58 * 用户表的pojo层 */public class User { private int id; private String name; private String pwd; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; }} dao层的UserDao接口 123456789package com.yhy.dao;import com.yhy.pojo.User;import java.util.List;public interface UserDao { List<User> getUserList();} dao的接口实现xml 用这个文件代替了之前dao层中的接口实现类,之前的话需要编写jdbc的全部,查询编写sql,获取结果集,遍历结果集,关闭连接。现在的话就是简化了步骤。 12345678910111213<?xml version=\"1.0\" encoding=\"UTF-8\" ?><!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\"><!--namespace=绑定一个对应的Dao/Mapper接口 原本是写接口,但是现在不用怎么做--><mapper namespace=\"com.yhy.dao.UserDao\"> <!--select查询语句--> <select id=\"getUserList\" resultType=\"com.yhy.pojo.User\"> select * from mybatis.user </select></mapper> mybatis-config.xml 这个是关键的配置文件,决定了连接的对象以及设定了作用域 1234567891011121314151617181920212223242526<?xml version=\"1.0\" encoding=\"UTF-8\" ?><!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-config.dtd\"><configuration> <typeAliases> <package name=\"com.yhy.pojo\"/> </typeAliases> <environments default=\"development\"> <environment id=\"development\"> //事务管理 <transactionManager type=\"JDBC\"/> <dataSource type=\"POOLED\"> <property name=\"driver\" value=\"com.mysql.cj.jdbc.Driver\"/> <property name=\"url\" value=\"jdbc:mysql://localhost:3306/mybatis?characterEncoding=UTF-8\"/> <property name=\"username\" value=\"root\"/> <property name=\"password\" value=\"root\"/> </dataSource> </environment> </environments> //作用域,删掉就是全局 //这里的配置可以是直接放在resource文件下,就直接写上xml名称就行,放在dao层的话就需要写全路径 <mappers> <mapper resource=\"com/yhy/dao/UserMapper.xml\"/> </mappers></configuration> 测试 1234567891011121314151617181920212223242526272829303132333435package com.yhy.dao;import com.yhy.pojo.User;import com.yhy.utils.MybatisUtils;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import java.util.List;/** * @Author: yhy * @Date: 2020/5/12 * @Time: 12:15 */public class UserDaoTest {//借调工具类来使用测试 @Test public void test(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); List<User> userList = mapper.getUserList(); for (User u: userList){ System.out.println(u); } sqlSession.close(); }} 结果如下 演示两种mappers作用效果 1.将UserMapper.xml放在dao层下,跟着userdao配置就和我上面写的一样。最后效果如下,但是前提是你得在你项目的pom.xml设置好有效的作用域,要不然就疯狂报错,说找不到这个UserMapper.xml文件。 pom.xml需要配置如下,这里是配置能够得读取到src下的有效配置文件。 12345678910111213141516171819<build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> </resources></build> 2.是直接将UserMapper.xml放在src下的resource文件下,和总的mybatis-config.xml配置文件放置在一起。这样在mybatis-config.xml里面就直接如下这样写文件名就行了。 1<mapper resource=\"UserMapper.xml\"/> 最后效果和第一种方法是一样的,都能够读取到数据。 个人觉得这两种方法来说,第一种可以对项目的整体设计管理更加方便一点,就像接口和实现类都是靠近的,方便进一步的审查和优化,第二种在做小的demo的时候是很方便的,但是一旦多个配置文件时候就可以有点难找。 记录错误 maven创建项目一开始经常会有的错误,就是设定的jdk版本问题,一开始我都是手动去修改project的setting,比较麻烦。默认的版本一般是jdk1.4或者是jdk1.5,但大家往往不是这个版本,所以会报错不支持发行版本5什么的。 解决:现在记录下,永久得起配置好。到自己maven下载文件夹下找到配置文件,并在里面修改settings.xml 修改:如下,我的是jdk11。所以可以根据自己的版本来修改。 初步学习测试代码中的几个关键类,三者的关系都是从上到下生成。 SqlSessionFactoryBuilder这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例,这样也是开发的规范。 每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域,同时好的开发习惯,要在使用之后,将其关闭。SqlSession就相当于jdbc中的Connection对象 namespace:表示的作用的接口范围之类的,绑定的是包名。 resultType:sql语句执行的返回值 增删改查 编写接口 写mapper.xml中的sql语句 执行语句,获得结果(注意增删改提交事务) 处理结果集映射问题resultmap 这个处理数据库中和实体类的变量名不一样,导致结果出错。使用在主要配置文件中,为了解决列名不匹配的另外一种方式。 使用:property是实体类中的,另一个是数据库中的数据列 123456写在mybatis_config.xml中<resultMap id=\"userResultMap\" type=\"User\"> <id property=\"id\" column=\"user_id\" /> <result property=\"username\" column=\"user_name\"/> <result property=\"password\" column=\"hashed_password\"/></resultMap> 12345<select id=\"selectUsers\" resultMap=\"userResultMap\"> select user_id, user_name, hashed_password from some_table where id = #{id}</select> mybatis中的日志首先在setting中配置,在官网中有以下的选择。 用键值对的方式去设置各种参数,下面是使用标准日志输出。 1234<settings> <!-- 配置了日志输出 --> <setting name=\"logImpl\" value=\"STDOUT_LOGGING\" /> </settings> 选择了标准功能,开启日志功能之后,测试执行了一个方法,调用数据库查询某一个id的数据,输出如下 log4j 使用步骤 导包 配置log4j的文件-properties 实例化日志对象 使用对象的方法-info debug error等","link":"/2020/05/13/%E6%A1%86%E6%9E%B6/MyBatis01/"},{"title":"mybatis03-复杂查询","text":"mybatis-03学习复杂查询讲到复杂查询,在mybatis中就要用到resultMap结果映射,之前用到单表简单查询,用一个resultType就可以满足了,但一旦是一些连接查询等复杂查询时候,resultMap作用就体现出来了。下面重点讲下这个MyBatis中最强大的元素。 resultMap介绍显示resultMap的元素概念,下图是官网中的讲解图,重点讲下关联(association)和集合(collection) 关联(association) 关联(association)元素处理“有一个”类型的关系,同时MyBatis 有两种不同的方式加载关联: 嵌套的查询,和sql中的子查询类似 根据结果来映射 集合(collection)实例使用resultMap的关联查询正常的MySQL查询-连接查询,但是在mybatis怎么实现呢,接下来来讲下两种方式去实现。 方式一 嵌套查询StudentMapper.xml 1234567891011121314151617181920<mapper namespace=\"com.yhy.dao.StudentMapper\"> <select id=\"getStudent\" resultMap=\"StudentTeacher\"> select * from student; </select> <resultMap id=\"StudentTeacher\" type=\"Student\"> <result property=\"id\" column=\"id\" /> <result property=\"name\" column=\"name\" /><!-- 有时需要处理一些复杂的数据查询,多表查询等,就需要用到assoction--> <association property=\"teacher\" column=\"tid\" javaType=\"Teacher\" select=\"getTeacher\"/> </resultMap> <select id=\"getTeacher\" resultType=\"Teacher\"> select * from teacher where id = #{id}; </select></mapper> 结果,这个结果就类似sql中的子查询,一个结果中嵌套另一个结果 方式二 按照结果处理StudentMapper.xml修改成以下的形式,其实这样的形式更加容易理解。 1234567891011121314!--采用连接查询的方法--> <select id=\"getStudent2\" resultMap=\"StudentTeacher2\"> select s.id sid,s.name sname, t.name tname from student s , teacher t where s.tid = t.id; </select> <resultMap id=\"StudentTeacher2\" type=\"Student\"> <result property=\"id\" column=\"sid\"/> <result property=\"name\" column=\"sname\"/> <association property=\"teacher\" javaType=\"Teacher\"> <result property=\"name\" column=\"tname\"/> </association> </resultMap> 结果也一样 JavaType–用来指定实体类中的属性 ofType–用来指定映射到List或者集合中的pojo类型,泛型中的约束类型 最后无论使用什么的方式,保持代码的可读性和效率是很关键的。","link":"/2020/05/15/%E6%A1%86%E6%9E%B6/mybatis-03%E5%AD%A6%E4%B9%A0%E5%A4%8D%E6%9D%82%E6%9F%A5%E8%AF%A2/"},{"title":"mybatis02-分页","text":"mybatis学习系列第二篇分页在网页中常常用到,在查询数据库内容并想将其输出的时候,因为有时有多组数据,一页展示过于突兀,所以会用到分页操作。 在sql用limit来分页。首先是UserMapper.xml 1234567<!-- 实现分页的接口 --> <select id=\"getUserByLimit\" parameterType=\"map\" resultType=\"User\">select * from mybatis.user limit #{startIndex},#{pageSize}; </select> 然后是UserMapper.java 12// 实现分页查询操作 List<User> getUserByLimit(Map<String,Integer> map); 再到测试类中的代码 123456789101112131415161718public void getUserByLimit() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); Map<String,Integer> map = new HashMap<>(); //通过map来传参来实现数据的交互绑定 map.put(\"startIndex\", 1); map.put(\"pageSize\", 2); List<User> userlist = mapper.getUserByLimit(map); for (User user : userlist) { System.out.println(user); } sqlSession.close(); } 还可以使用RowBounds来实现分页UserMapper.java 12// 实现分页的方法二 List<User> getUserByLimit2(); UserMapper.xml 1234<!-- 实现分页的方法二 --> <select id=\"getUserByLimit2\" resultType=\"User\"> select * from mybatis.user ; </select> RowBounds构造方法,和limit相似。 test代码 12345678910111213141516public void getUserByLimit2() { SqlSession sqlSession = MybatisUtils.getSqlSession(); RowBounds rowBounds = new RowBounds(1,2); // 使用java代码来实现分页操作 List<User> selectList = sqlSession.selectList(\"com.yhy.dao.UserDao.getUserByLimit2\", null, rowBounds); for (User user : selectList) { System.out.println(user); } sqlSession.close(); } 输出结果 pageHelper分页插件了解 注解开发本质是使用反射,还有动态代理模式 在工具类MybatisUtils中,打开自动提交事务,这样在后续的编写代码中就不要在使用commit。 12345// 有了工厂就可以获得实例来使用,sqlsession就可以面向数据库操作jdbc// 打开自动提交事务 public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(true); } 注解开发流程-实例整体文件结构 使用注解开发的时候,接口里面定义的方法就是使用注解的地方,实例如下 1234//使用注解的方法,之前是需要写xml文件进行配置,这里就直接使用注解的方法//这个方法是查询所有用户信息@Select(\"select * from user\")List<User> getUsers(); 在主配置文件中,也是关键所在,绑定的有所不同。将文件资源标签改为class,并将所在的类位置标出。 测试类-使用的步骤和之前的没有使用注解的方法一样。 12345678910111213@Testpublic void test() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.getUsers(); for (User user : users) { System.out.println(user); } sqlSession.close();} 总的来说,注解是分散的数据配置,而xml是集中的数据配置。 使用注解可以很多程度来简化工作,省去了很多配置文件的编写,但有时注解过于分散不利于管理和维护,在一些通用配置上,像数据库连接等,还是比较建议xml文件进行配置,因为XML方式比注解的可扩展性和复杂性维护上好的多。所以注解有利有弊,看什么场景去使用,用对了就是事半功倍的效果! 了解LombokLombok是一个java开发插件,目的是简化代码,方便开发,通过注解省去了一些pojo中的get、set方法和构造方法,还有一些其他的tostring和equals等。有些介绍可以参考官网中的文档,当然对于这款插件,欢呼声和骂声一直都充斥在各个平台,各种分析文章网上也有很多,我就不再去记录太多了。但任何事情都有其两面性,理性看待,喜欢觉得有用大家就用,不喜欢就当了解一些也不为过。下面是记录使用的一些图片代码。 1、插件下载–setting里面的插件 2、maven导包,在官网找到信息进行导包,选择自己所需要的版本进行使用 我选择了最新的版本,有错再去解决 12345678910<dependencies> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>provided</scope> </dependency></dependencies> 3、使用实例在插件管理查看信息里面就可以看到Lombok的一些介绍吗,下面是一些注解的介绍 1234567891011121314151617181920212223@Getter and @Setter //生成get和set方法@FieldNameConstants @ToString //tostring方法@EqualsAndHashCode //equals和hashcode方法的生成@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor //全参构造和无参构造和一个自定义构造@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog@Data //这个最常见了,加一个可以默认生成了一系列的方法,可以见下图使用@Builder@SuperBuilder@Singular@Delegate@Value@Accessors@Wither@With@SneakyThrows@val@varexperimental @var@UtilityClassLombok config systemCode inspectionsRefactoring actions (lombok and delombok) 将变量定义留着,其他方法注释掉,就可以自动生成了 默认是生成了无参的构造器,加入AllArgsConstructor就可以了 最后留下的代码就剩得这么干净。自己看的确实很舒服,当用的多了,不懂的人可能够呛哈哈哈。 最后这是第二篇mybatis文章,谢谢阅读。有错误请多多指教,谅解!跟随b站狂神的视频记录的学习笔记","link":"/2020/05/14/%E6%A1%86%E6%9E%B6/MyBatis02/"},{"title":"ssm小结","text":"ssm框架学习记录学习SpringMVC框架这是表现层-相当于之前的servlet层和一些视图 Spring重点:控制反转(IOC)和AOP(面向切面编程) MyBatis:持久层框架数据访问层-相当于之前web项目中dao层,数据库的交互,包括增删改查; 持久化就是将数据在持久状态和瞬时状态转化的过程。内存是断电即失。所以需要数据的持久化。 JDBC技术:Connection、PrepareStatement、ResultSet. 为什么要使用框架代替jdbc呢? 因为之前的jdbc的操作总是是重复单一的,在开发的时候要执行sql语句直接操作数据库,要经过加载驱动等操作,为了高效的开发,避免繁琐的操作,框架就诞生了。框架对jdbc进行封装,mybatis只需关注sql语句直接操作数据库,封装了操作的很多细节,这样可以将更多时间精力放在sql语句的编写上。 特点:灵活容易上手,用的多,封装jdbc 第一个程序pojo的user类 映射数据库的user表 1234567891011121314151617181920212223242526272829303132333435363738package com.yhy.pojo;/** * @Author: yhy * @Date: 2020/5/12 * @Time: 10:58 * 用户表的pojo层 */public class User { private int id; private String name; private String pwd; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; }} pom.xml, 整个项目的maven管理文件。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859<?xml version=\"1.0\" encoding=\"UTF-8\"?><project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"> <modelVersion>4.0.0</modelVersion> <groupId>com.yhy.learn</groupId> <artifactId>mybatisDemo</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>mybatis-01</module> </modules> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> </resources> </build></project> 工具类 123456789101112131415161718192021222324252627282930313233343536373839package com.yhy.utils;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.Configuration;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;import java.io.InputStream;/** * @Author: yhy * @Date: 2020/5/12 * @Time: 11:45 * 工具类 * 获取sqlsession工厂 */public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try {// 利用mybatis在一开始就获得了sqlsessionfactory String resource = \"mybatis-config.xml\"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } }// 有了工厂就可以获得实例来使用,sqlsession就可以面向数据库操作jdbc public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); }} dao的接口实现xml 用这个文件代替了之前dao层中的接口实现类,之前的话需要编写jdbc的全部,查询编写sql,获取结果集,遍历结果集,关闭连接。现在的话就是简化了步骤。 12345678910111213<?xml version=\"1.0\" encoding=\"UTF-8\" ?><!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\"><!--namespace=绑定一个对应的Dao/Mapper接口 原本是写接口,但是现在不用怎么做--><mapper namespace=\"com.yhy.dao.UserDao\"> <!--select查询语句--> <select id=\"getUserList\" resultType=\"com.yhy.pojo.User\"> select * from mybatis.user </select></mapper> 测试 1234567891011121314151617181920212223242526272829303132333435package com.yhy.dao;import com.yhy.pojo.User;import com.yhy.utils.MybatisUtils;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import java.util.List;/** * @Author: yhy * @Date: 2020/5/12 * @Time: 12:15 */public class UserDaoTest {//借调工具类来使用测试 @Test public void test(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); List<User> userList = mapper.getUserList(); for (User u: userList){ System.out.println(u); } sqlSession.close(); }} mybatis-config.xml 这个是关键的配置文件,决定了连接的对象以及设定了作用域 1234567891011121314151617181920212223242526<?xml version=\"1.0\" encoding=\"UTF-8\" ?><!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-config.dtd\"><configuration> <typeAliases> <package name=\"com.yhy.pojo\"/> </typeAliases> <environments default=\"development\"> <environment id=\"development\"> //事务管理 <transactionManager type=\"JDBC\"/> <dataSource type=\"POOLED\"> <property name=\"driver\" value=\"com.mysql.cj.jdbc.Driver\"/> <property name=\"url\" value=\"jdbc:mysql://localhost:3306/mybatis?characterEncoding=UTF-8\"/> <property name=\"username\" value=\"root\"/> <property name=\"password\" value=\"root\"/> </dataSource> </environment> </environments> //作用域,删掉就是全局 //这里的配置可以是直接放在resource文件下,就直接写上xml名称就行,放在dao层的话就需要写全路径 <mappers> <mapper resource=\"com/yhy/dao/UserMapper.xml\"/> </mappers></configuration> 演示两种mappers作用效果 1.将UserMapper.xml放在dao层下,跟着userdao 配置就和我上面写的一样。最后效果如下,但是前提是你得在你项目的pom.xml设置好有效的作用域,要不然就疯狂报错,说找不到这个UserMapper.xml文件。 pom.xml需要配置如下,这里是配置能够得读取到src下的有效配置文件。 12345678910111213141516171819<build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> </resources></build> 2.是直接将UserMapper.xml放在src下的resource文件下,和总的mybatis-config.xml配置文件放置在一起。这样在mybatis-config.xml里面就直接如下这样写文件名就行了。 1<mapper resource=\"UserMapper.xml\"/> 最后效果和第一种方法是一样的,都能够读取到数据。 个人觉得这两种方法来说,第一种可以对项目的整体设计管理更加方便一点,就像接口和实现类都是靠近的,方便进一步的审查和优化,第二种在做小的demo的时候是很方便的,但是一旦多个配置文件时候就可以有点难找。 记录错误 maven创建项目一开始经常会有的错误,就是设定的jdk版本问题,一开始我都是手动去修改project的setting,比较麻烦。默认的版本一般是jdk1.4或者是jdk1.5,但大家往往不是这个版本,所以会报错不支持发行版本5什么的。 解决:现在记录下,永久得起配置好。到自己maven下载文件夹下找到配置文件,并在里面修改settings.xml 修改:如下,我的是jdk11。所以可以根据自己的版本来修改。 初步学习","link":"/2020/05/15/%E6%A1%86%E6%9E%B6/ssm%E6%A1%86%E6%9E%B6%E5%AD%A6%E4%B9%A0/"},{"title":"解决idea错误","text":"记录解决Intellij IDEA Tomcat启动项目加载页面的时候报错:java.lang.ClassNotFoundException-mysql数据库驱动问题一个javaweb项目,在编写登录页面跳转的时候,因为借助了mysql中的数据,所以调用了数据库驱动。 下面的是一开始的错误,登录无法跳转界面,通过debug,里面传到user是null对象,无法走到正确页面。 一开始我以为测试正确就可以使用mysql数据库的驱动,后面才知道是不一样的。 解决首先是File-project structure-Artifacts,这里是为了解决这是由于pom.xml中下载的jar包未被部署,也就是我的问题所在。 一开始我的左边是没有lib文件夹的,右边的依赖也没有加过来,所以要将右边的全部移动过来就可以解决了。 最后成功登录 最后希望自己能够从错误中不断学习,也很感谢那个大哥花时间帮我,感谢!! 参考1 参考2 参考3","link":"/2020/05/15/%E6%A1%86%E6%9E%B6/%E8%AE%B0%E5%BD%95%E8%A7%A3%E5%86%B3Intellij%20IDEA%20Tomcat%E5%90%AF%E5%8A%A8%E9%A1%B9%E7%9B%AE%E5%8A%A0%E8%BD%BD%E9%A1%B5%E9%9D%A2%E7%9A%84%E6%97%B6%E5%80%99%E6%8A%A5%E9%94%99%EF%BC%9Ajava.lang.ClassNotFoundException-mysql%E6%95%B0%E6%8D%AE%E5%BA%93%E9%A9%B1%E5%8A%A8%E9%97%AE%E9%A2%98/"},{"title":"spring系列-01","text":"springhello程序258eb28a50f8c2b5cf26bbcfa60bbffb 先写个实体类 配置pom.xml导入lombok纯粹是为了省一些写代码的操作,使用注解的方式来省下时间,但是初学者还是非常不建议使用这个 导的spring是下面这个mvc的web架构的包,可以使用 里面的包里面有这些分支,可以使用。 123456789101112131415161718192021<?xml version=\"1.0\" encoding=\"UTF-8\"?><beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd\"><!--这里的bean是一个对象 id是变量名 class是new的对象 property相当于给对象中的属性配置一个值 由spring创建的,原来是 对象由spring注册管理等--> <bean id=\"hello\" class=\"com.yhy.pojo.Hello\"> <property name=\"str\" value = \"hello world\"/> </bean> <!-- more bean definitions go here --></beans> 修改可以直接在xml文件中修改,其他的不用理了。所谓的IOC就是对象由spring创建、管理、装配。 核心查看DeaultListableBeanFactory的结构图,idea怎么使用结构图查看是怎么样的,可以参考下这个文章。 IOC(Inversion of Control) The org.springframework.beans and org.springframework.context packages are the basis for Spring Framework’s IoC container. –spinng官网上的一句华 直译过来就是这里两个包是spring框架中的IOC容器的基础,在上面的maven截图中,也是可以看到spring mvc里面有这两个包,要不然也不可能使用完整的spring框架。所以想学会这个IOC,这两个包的了解是必不可少的。 IOC创建对象 默认是无参构造方法 使用有参构造有几个方法 有三种 参数名 下标 名字","link":"/2020/05/23/%E6%A1%86%E6%9E%B6/spring%E7%B3%BB%E5%88%97-springhello/"},{"title":"Idea使用小结","text":"Idea使用记录小结主题配置-排错解决jdk发行版本问题在一些maven项目开启的时候,常常会有错误出现,因为idea里面当下的项目配置是因为maven的原本的配置给覆盖了。说先有可能是setting出了问题,可以先打开project-setting(在idea里面是左上角的file-project seetting) 解决发行版本问题 强制约束maven中的jdk版本。直接在pom.xml上设置 123456789101112<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>11</source> <target>11</target> </configuration> </plugin> </plugins></build> 其他功能打开UML结构图比较新的版本的Idea是支持UML结构类图的,找到一个类或者是接口,直接右键,看图操作就可以了。 最后的结果就是这样了。 Vscode问题总结 The compiler compliance specified is 11 but a JRE 12 is used","link":"/2020/05/25/Idea%E4%BD%BF%E7%94%A8%E5%B0%8F%E7%BB%93/"},{"title":"leetcode入门之数组系列","text":"前言之前看到刷leetcode的技巧相关文章的时候,看到一个大神建议说,在一开始可以先按数组、字符串、链表、树的tag来刷,同时切记一开始先刷easy的。这样可以很大程度上将基础巩固,因为在后面的中等题以上都是会用到这些基础知识,如果有了牢固基础,相信在学习其他复杂的知识点就可以很快解决。下面是记录一些解题的技巧和好的题目。 题目#leetcode-learn环节下面的例题是在英文版本的leetcode中的explore环节的学习。 1、找出连续1的数组长度 2、位数为偶数位的个数 3、数组的平方 做法一 做法二 4、复制零并且移动数组 5、合并数组 6、删除数组中的重复数字 7、在升序的数组删除重复的数字。6的变式 O(1)空间复杂度的数组复制 #66-加一操作 答案示例,这里的思路和清晰,这个题看似简单,但是里面的数组进位操作处理有点意思。 #88 合并两个有序数组 这里需要注意的是,这道题是直接在数组一上操作,不是开辟个新数组,这道题可以从后往前做,这样可以节省空间,更加高效,代码也很简洁。代码在下面 #287-寻找重复数题目如下,方框中是所需要注意的点 采用快慢指针的方法,这是一个环的入口问题 #974 子数组被K整除的个数 使用前缀和的方法-简单的讲是记录数组的前n项 有 N 个的正整数放到数组 A 里,现在要求一个新的数组 B,新数组的第 i 个数 B[i]是原数组 A 第 0 到第 i 个数的和。 #1464 数组中最大的两个数 暴力法 城市轨道交通与其它交通方式衔接的研究2017052397-颜华艺","link":"/2020/05/23/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/leetcode%E5%85%A5%E9%97%A8%E4%B9%8B%E6%95%B0%E7%BB%84%E7%B3%BB%E5%88%97/"},{"title":"阅读笔记","text":"记录下最近阅读到的一些读书笔记","link":"/2020/06/09/%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/"},{"title":"2020上半年总结","text":"","link":"/2020/06/10/2020%E4%B8%8A%E5%8D%8A%E5%B9%B4%E6%80%BB%E7%BB%93/"},{"title":"leetcode系列之剑指offer篇","text":"","link":"/2020/06/10/leetcode%E7%B3%BB%E5%88%97%E4%B9%8B%E5%89%91%E6%8C%87offer%E7%AF%87/"},{"title":"第一个springMVC","text":"前言开始学习springMVC整理的笔记,今天这一篇是回顾第一个springMVC程序。 环境大致文件结构 先是要创建好一个普通maven工程,加入一些servlet包以及mvc支持的jar包,如下,(我的这个项目设置是参考b站的狂神说Java,设置一些父工程这样,方便后期的扩展学习)。 先是整体父工程的配置-pom。xml(springMVC) 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879<?xml version=\"1.0\" encoding=\"UTF-8\"?><project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"> <modelVersion>4.0.0</modelVersion> <groupId>com.yhy</groupId> <artifactId>springMVC</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>springmvc-01-servlet</module> <module>springmvc-02-firstdemo</module> </modules> <!-- 引入的jar包等--> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.9.RELEASE</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency></dependencies> <!-- 设置maven的一些配置,要不然会报错jdk版本问题,这个是我自己设置maven的问题--> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>11</source> <target>11</target> </configuration> </plugin> </plugins> </build> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.release>11</maven.compiler.release> </properties> <profiles> <profile> <id>JDK-11</id> <activation> <activeByDefault>true</activeByDefault> <jdk>11</jdk> </activation> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <maven.compiler.compilerVersion>11</maven.compiler.compilerVersion> </properties> </profile> </profiles></project> 这里的第一个springMVC是第二个模块-springmvc-02-firstdemo 所以可以看下里面的具体文件结构,如下,这里的话,是使用了jsp去创建了,不懂jsp的可以稍微了解一下就行了,后面也不会怎么用。了解点到即可。 来看这个子模块的maven配置文件,都单独引入了什么包支持。然后发现这里是不用引入了什么。空空的,只有一天parent标签,表示是谁的子模块,可以继承其父亲的jar包支持 pom.xml(springmvc-02-firstdemo) 123456789101112131415<?xml version=\"1.0\" encoding=\"UTF-8\"?><project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"> <parent> <artifactId>springMVC</artifactId> <groupId>com.yhy</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>springmvc-02-firstdemo</artifactId></project> 主要代码先是controller, 12345678910111213141516171819202122232425262728package com.yhy.controller;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.mvc.Controller;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * @Author: yhy * @Date: 2020/6/15 * @Time: 0:26 */public class HelloController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { //ModelAndView 模型和视图 //当控制器处理完请求时,通常会将包含视图名称或视图对象以及一些模型属性的ModelAndView对象返回到DispatcherServlet。 //默认的bean风格构造方法,填充bean属性,不传递构造参数 ModelAndView view = new ModelAndView(); //封装对象 view.addObject(\"msg\",\"HelloSpringMVC!\"); //封装要跳转的对象 放在modelandview,将其返回的视图名称 view.setViewName(\"hello\"); return view; }} 页面hello.jsp-视图直接取到并且展示了从Controller带回的信息。 这个视图和其他视图一样,都放在/WEB-INF/目录下,保证了安全性和不可见性。 12345678910111213141516<%-- Created by IntelliJ IDEA. User: admin Date: 2020/6/15 Time: 0:36 To change this template use File | Settings | File Templates.--%><%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %><html><head> <title>yhy for springmvc</title></head><body>${msg}</body></html> 配置web.xml这里的web.xml与以往的web配置文件不同,不要注册servlet,而只是注册dispatchservlet ,这个是springMVC的关键之处,在后面会写到,所以就相当于全部servlet交给了spring去管理。 123456789101112131415161718192021222324<?xml version=\"1.0\" encoding=\"UTF-8\"?><web-app xmlns=\"http://xmlns.jcp.org/xml/ns/javaee\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd\" version=\"4.0\"> <!-- 注册dispatchservlet--> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 关联springmvc的配置文件:官方名字-(servletname)+servlet.xml--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> <!-- 设置启动级别 越大就越优先--> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></web-app> 主要的spring配置再来是resources下面的springmvc-servlet.xml的主要写法,这里添加了一些处理器和适配器。以及注册了自己定义的类。 12345678910111213141516171819<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 添加处理映射器--> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <!-- 添加处理适配器--> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <!-- 视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <!-- 后缀--> <property name="suffix" value=".jsp"/> </bean> <!-- 注册自己定义的类--> <bean id="/hello" class="com.yhy.controller.HelloController"/></beans> 结果显示 最后总结先讲下我一开始比较困惑的一个类ModelAndView,之前没接触过。借鉴,在上面的示例中,就使用了作用二,用于传递控制方法处理结果到结果页面中显示,上面是使用了jsp的${}来完成的。而且这个类是用户手动创建。类中的一些方法如下 ModelAndView对象有两个作用: 作用一 设置转向地址,如下所示(这也是ModelAndView和ModelMap的主要区别) ModelAndView view = new ModelAndView(“path:ok”); 作用二 用于传递控制方法处理结果数据到结果页面,也就是说我们把需要在结果页面上需要的数据放到ModelAndView对象中即可,他的作用类似于request对象的setAttribute方法的作用,用来在一个请求过程中传递处理的数据。通过以下方法向页面传递参数: addObject(String key,Object value); 在页面上可以通过el变量方式$key或者bboss的一系列数据展示标签获取并展示ModelAndView中的数据。 springmvc与以往的传统的Javaweb项目结构相比,主要体现在web的配置文件上,一个以往的servlet全部配置在web.xml,现在可以交给spring来完成,只需完成注册,到后面的注解实现,可以实现更加简洁方便。加上后面的前后端分离设计,代码结构更加清晰。","link":"/2020/06/15/%E7%AC%AC%E4%B8%80%E4%B8%AAspringMVC/"},{"title":"leetcode数组系列之搜索排序篇","text":"前言先是学习Expore中的二分搜索,写这篇文章记录下遇到的好的例题和好的代码题解。 首先这是二分搜索模块 Binary Search最简单的实现运用就最简单的实现二分搜索,一开始建议通过自己思考去完成,尽量不看题解去完成。 left+(right-left) / 2这个操作是防止越界。 求数的开方 答案-就简单的二分法去做 还有一种利用数学的方法去做 12345678910class Solution { public int mySqrt(int x) { //利用数学的方法去做 long r = x; while(r * r > x){ r = (r + x / r) / 2; } return (int)r; }}","link":"/2020/06/10/leetcode%E6%95%B0%E7%BB%84%E7%B3%BB%E5%88%97%E4%B9%8B%E6%90%9C%E7%B4%A2%E6%8E%92%E5%BA%8F%E7%AF%87/"},{"title":"计算机系统","text":"阅读《深入理解计算系统》","link":"/2020/06/11/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%B3%BB%E7%BB%9F/"},{"title":"随记及总结/cc++和java的区别","text":"区别Java没有的 首先c/c++在编译过程中都有一个预编译的过程,预处理器的作用;而java没有 c和c++是支持指针操作 c++支持多重继承, c/c++是编译性语言,需要编译链接等过后成为某文件,才可以执行,而且不支持跨平台操作 Java有的 java支持字符串 Java可以自动管理内存,有GC,可以自动进行无用内存回收操作,不需程序员手动操作 Java可以实现多个接口来达到这个目的 java属于一种解释性语言,需要一种解释器的感觉,在java就是jvm的存在 比喻今天听一个专家讲课是这样比喻c++和java的带参函数的区别 假设函数要处理一个房子,参数是房子类型的 在c++中,函数传入的是房子的房产证,就是指引房子在哪的房产证 而在java中,看似传的是整个房子,因为java没有指针的操作,但是在java中,早就将房子给屏蔽掉了,所以看得到,在使用的是房产证来着,房子已经给收起来了。","link":"/2020/08/12/%E9%9A%8F%E8%AE%B0%E5%8F%8A%E6%80%BB%E7%BB%93/cc++%E5%92%8Cjava%E7%9A%84%E5%8C%BA%E5%88%AB/"},{"title":"随记及总结/动态代理","text":"","link":"/2020/08/12/%E9%9A%8F%E8%AE%B0%E5%8F%8A%E6%80%BB%E7%BB%93/%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86/"},{"title":"随记及总结/红黑树是什么","text":"","link":"/2020/08/12/%E9%9A%8F%E8%AE%B0%E5%8F%8A%E6%80%BB%E7%BB%93/%E7%BA%A2%E9%BB%91%E6%A0%91%E6%98%AF%E4%BB%80%E4%B9%88/"},{"title":"随记及总结/shellVSc语言","text":"脚本语言和编程语言脚本语言是一种解释性语言,脚本语言极大得简化“开发、部署、测试和调试”的周期过程,容易部署,可以随时部署,不需要像c语言等需要编译 对于c语言来说,要想最终执行,要经过cpp文件/c程序 –.i文件预处理过后–.s文件是汇编文件– .o文件是目标文件 –可执行文件。","link":"/2020/08/12/%E9%9A%8F%E8%AE%B0%E5%8F%8A%E6%80%BB%E7%BB%93/shellVSc%E8%AF%AD%E8%A8%80/"},{"title":"JDBC","text":"JDBC[toc] JAVA Database Connectivity java 数据库连接 为什么会出现JDBC SUN公司提供的一种数据库访问规则、规范, 由于数据库种类较多,并且java语言使用比较广泛,sun公司就提供了一种规范,让其他的数据库提供商去实现底层的访问规则。 我们的java程序只要使用sun公司提供的jdbc驱动即可。 使用JDBC的基本步骤 注册驱动 DriverManager.registerDriver(new com.mysql.jdbc.Driver()); 建立连接 //DriverManager.getConnection("jdbc:mysql://localhost/test?user=monty&password=greatsqldb"); //2. 建立连接 参数一: 协议 + 访问的数据库 , 参数二: 用户名 , 参数三: 密码。 conn = DriverManager.getConnection("jdbc:mysql://localhost/student", "root", "root"); 创建statement //3. 创建statement , 跟数据库打交道,一定需要这个对象 st = conn.createStatement(); 执行sql ,得到ResultSet //4. 执行查询 , 得到结果集 String sql = "select * from t_stu"; rs = st.executeQuery(sql); 遍历结果集 //5. 遍历查询每一条记录 while(rs.next()){ int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println("id="+id + "===name="+name+"==age="+age); } 释放资源 12345678if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { } // ignore rs = null; }... JDBC 工具类构建 资源释放工作的整合 驱动防二次注册 DriverManager.registerDriver(new com.mysql.jdbc.Driver()); Driver 这个类里面有静态代码块,一上来就执行了,所以等同于我们注册了两次驱动。 其实没这个必要的。 //静态代码块 ---> 类加载了,就执行。 java.sql.DriverManager.registerDriver(new Driver()); 最后形成以下代码即可。 Class.forName("com.mysql.jdbc.Driver"); 使用properties配置文件 在src底下声明一个文件 xxx.properties ,里面的内容吐下: driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost/student name=root password=root 在工具类里面,使用静态代码块,读取属性 123456789101112131415161718192021static{ try { //1. 创建一个属性配置对象 Properties properties = new Properties(); InputStream is = new FileInputStream(\"jdbc.properties\"); //对应文件位于工程根目录 //使用类加载器,去读取src底下的资源文件。 后面在servlet //对应文件位于src目录底下 //InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream(\"jdbc.properties\"); //导入输入流。 properties.load(is); //读取属性 driverClass = properties.getProperty(\"driverClass\"); url = properties.getProperty(\"url\"); name = properties.getProperty(\"name\"); password = properties.getProperty(\"password\"); } catch (Exception e) { e.printStackTrace(); }} 将连接封装在一个util类中123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101public class JDBCUtil { static String driverClass = null; static String url = null; static String name = null; static String password= null; static{ try { //1. 创建一个属性配置对象 Properties properties = new Properties(); InputStream is = new FileInputStream(\"jdbc.properties\"); //使用类加载器,去读取src底下的资源文件。 后面在servlet// InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream(\"jdbc.properties\"); //导入输入流。 properties.load(is); //读取属性 driverClass = properties.getProperty(\"driverClass\"); url = properties.getProperty(\"url\"); name = properties.getProperty(\"name\"); password = properties.getProperty(\"password\"); } catch (Exception e) { e.printStackTrace(); } } /** * 获取连接对象 * @return */ public static Connection getConn(){ Connection conn = null; try { Class.forName(driverClass); //静态代码块 ---> 类加载了,就执行。 java.sql.DriverManager.registerDriver(new Driver()); //DriverManager.registerDriver(new com.mysql.jdbc.Driver()); //DriverManager.getConnection(\"jdbc:mysql://localhost/test?user=monty&password=greatsqldb\"); //2. 建立连接 参数一: 协议 + 访问的数据库 , 参数二: 用户名 , 参数三: 密码。 conn = DriverManager.getConnection(url, name, password); } catch (Exception e) { e.printStackTrace(); } return conn; } /** * 释放资源 * @param conn * @param st * @param rs */ public static void release(Connection conn , Statement st , ResultSet rs){ closeRs(rs); closeSt(st); closeConn(conn); } public static void release(Connection conn , Statement st ){ closeSt(st); closeConn(conn); } private static void closeRs(ResultSet rs){ try { if(rs != null){ rs.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ rs = null; } } private static void closeSt(Statement st){ try { if(st != null){ st.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ st = null; } } private static void closeConn(Connection conn){ try { if(conn != null){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ conn = null; } }} 数据库的CRUD sql insert INSERT INTO t_stu (NAME , age) VALUES ('wangqiang',28) 1INSERT INTO t_stu VALUES (NULL,'wangqiang2',28) 123456789101112131415// 1. 获取连接对象 conn = JDBCUtil.getConn(); // 2. 根据连接对象,得到statement st = conn.createStatement(); //3. 执行添加 String sql = \"insert into t_stu values(null , 'aobama' , 59)\"; //影响的行数, ,如果大于0 表明操作成功。 否则失败 int result = st.executeUpdate(sql); if(result >0 ){ System.out.println(\"添加成功\"); }else{ System.out.println(\"添加失败\"); } delete DELETE FROM t_stu WHERE id = 6 123456789101112131415// 1. 获取连接对象 conn = JDBCUtil.getConn(); // 2. 根据连接对象,得到statement st = conn.createStatement(); //3. 执行添加 String sql = \"delete from t_stu where name='aobama'\"; //影响的行数, ,如果大于0 表明操作成功。 否则失败 int result = st.executeUpdate(sql); if(result >0 ){ System.out.println(\"删除成功\"); }else{ System.out.println(\"删除失败\"); } query SELECT * FROM t_stu 12345678910111213141516// 1. 获取连接对象conn = JDBCUtil.getConn();// 2. 根据连接对象,得到statementst = conn.createStatement();// 3. 执行sql语句,返回ResultSetString sql = \"select * from t_stu\";rs = st.executeQuery(sql);// 4. 遍历结果集while (rs.next()) { String name = rs.getString(\"name\"); int age = rs.getInt(\"age\"); System.out.println(name + \" \" + age);} update UPDATE t_stu SET age = 38 WHERE id = 1; 123456789101112131415// 1. 获取连接对象 conn = JDBCUtil.getConn(); // 2. 根据连接对象,得到statement st = conn.createStatement(); //3. 执行添加 String sql = \"update t_stu set age = 26 where name ='qyq'\"; //影响的行数, ,如果大于0 表明操作成功。 否则失败 int result = st.executeUpdate(sql); if(result >0 ){ System.out.println(\"更新成功\"); }else{ System.out.println(\"更新失败\"); } 使用单元测试,测试代码 定义一个类, TestXXX , 里面定义方法 testXXX. 添加junit的支持。 右键工程 --- add Library --- Junit --- Junit4 在方法的上面加上注解 , 其实就是一个标记。 @Test public void testQuery() { ... } 光标选中方法名字,然后右键执行单元测试。 或者是打开outline视图, 然后选择方法右键执行。 Dao模式 Data Access Object 数据访问对象 新建一个dao的接口, 里面声明数据库访问规则 12345678910/** * 定义操作数据库的方法 */public interface UserDao { /** * 查询所有 */ void findAll();} 新建一个dao的实现类,具体实现早前定义的规则 123456789101112131415161718192021222324252627282930 public class UserDaoImpl implements UserDao{ @Override public void findAll() { Connection conn = null; Statement st = null; ResultSet rs = null; try { //1. 获取连接对象 conn = JDBCUtil.getConn(); //2. 创建statement对象 st = conn.createStatement(); String sql = \"select * from t_user\"; rs = st.executeQuery(sql); while(rs.next()){ String userName = rs.getString(\"username\"); String password = rs.getString(\"password\"); System.out.println(userName+\"=\"+password); } } catch (Exception e) { e.printStackTrace(); }finally { JDBCUtil.release(conn, st, rs); } }} 直接使用实现 @Test public void testFindAll(){ UserDao dao = new UserDaoImpl(); dao.findAll(); } ##Statement安全问题 Statement执行 ,其实是拼接sql语句的。 先拼接sql语句,然后在一起执行。 123456789String sql = \"select * from t_user where username='\"+ username +\"' and password='\"+ password +\"'\";UserDao dao = new UserDaoImpl();dao.login(\"admin\", \"100234khsdf88' or '1=1\");SELECT * FROM t_user WHERE username='admin' AND PASSWORD='100234khsdf88' or '1=1' 前面先拼接sql语句, 如果变量里面带有了 数据库的关键字,那么一并认为是关键字。 不认为是普通的字符串。 rs = st.executeQuery(sql); PrepareStatement 该对象就是替换前面的statement对象。 相比较以前的statement, 预先处理给定的sql语句,对其执行语法检查。 在sql语句里面使用 ? 占位符来替代后续要传递进来的变量。 后面进来的变量值,将会被看成是字符串,不会产生任何的关键字。 123456String sql = \"insert into t_user values(null , ? , ?)\"; ps = conn.prepareStatement(sql); //给占位符赋值 从左到右数过来,1 代表第一个问号, 永远你是1开始。 ps.setString(1, userName); ps.setString(2, password); 总结: JDBC入门 抽取工具类 ### Statement CRUD ### 演练crud Dao模式 ### 声明与实现分开 PrepareStament CRUD ### 预处理sql语句,解决上面statement出现的问题","link":"/2020/04/13/mysql/JDBC%E7%AC%94%E8%AE%B0/"},{"title":"mysql视图","text":"视图虚拟的表,只保持sql的逻辑,不保存查询结果 需要注意的使用规则 123456789101112# 创建CREATE VIEW view_nameAS SELECT statement...# 修改ALTER VIEW view_nameAS SELECT statement...# 删除 (可以删除多个 ,前提是有drop权限)DROP view [if exists] view_name,view_name …[restrict|cascade]# 查看视图SHOW CREATE VIEW view_name 以下视图不能更新 123456•包含以下关键字的sql语句:分组函数、distinct、group by、having、union或者union all•常量视图•Select中包含子查询•join•from一个不能更新的视图•where子句的子查询引用了from子句中的表 存储过程和函数事先编译并存储在数据库中的一段SQL语句; 好处: 简化工作 提高数据处理的效率 减少数据在数据库和应用服务器之间的传输 使用格式 12345678910111213141516171819202122# •创建存储过程:create procedure 存储过程名([proc_parameter[,…]])[characteristic…]routine_body# •创建函数:create function 函数名([func_parameter[,…]])returns type[characteristic…]routine_body# 调用存储过程:call 存储过程名(参数列表)# 调用函数:Select 函数名(参数列表)# 修改ALTER PROCEDURE/FUNCTION name# 删除DROP PROCEDURE/FUNCTION name# 查看1.查看存储过程或函数的状态:show {procedure|function} status like 存储过程或函数名2.查看存储过程或函数的定义:show create {procedure|function} 存储过程或函数名3.通过查看information_schema.routines了解存储过程和函数的信息(了解)select * from rountines where rounine_name =存储过程名|函数名","link":"/2020/04/17/mysql/mysql-%E8%A7%86%E5%9B%BE/"},{"title":"计算机网络小结","text":"计算机网络分层概述以及功能作用 不同之前的OSI分层,现在主流的分层都是偏向于TCP/IP的四层分层(五层的话就是最后的网络接口层给替换),OSI参考模型的抽象能力高,概念划分清除,但是过于复杂,不好理解。即由下面按四层协议的层次来组成计算机网络 1.应用层 2.传输层 3.网络层 4.网络接口层 具体的功能作用这下面的功能我是将第四层的网络接口层拆成了物理层和数据链路层来写的。 物理层:中继器和集线器;网卡(概括为了两器一卡) 数据链路层:为网络层提供可靠的数据传输,基本数据为帧;以太网协议,设备是网桥和交换机 网络层:对子网间的数据包进行路由选择,还可以实现拥塞控制,网络互连等功能;IP协议;ICMP协议-internet control message protocol因特网控制报文协议;ARP协议(将IP地址获取物理地址的):地址解析协议;路由器是重要设备。 路由选择和分组转发 异构网络互联 拥塞控制:若所有接点都来不及接受分组,而要丢弃大量分组的话,网络就除于拥塞状态,因此要采用一定措施来缓解这种拥塞 传输层:将上层数据分段并提供端到端的、可靠的或不可靠的传输以及端到端的差错控制和流量控制问题;主要用到的是TCP协议、UDP协议;网关是重要设备 应用层:为操作系统或网络应用程序提供访问网络服务的接口。 对应层所用的协议应用层这层的主要协议的话也是大家比较常见而且有一定了解的 SMTP:对邮件收发的一个协议,控制信件的中转方式。 HTTPS:具有安全性的文本传输控制协议。 HTTP:超文本传输协议,规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送数据的协议。 FTP:文件传输协议,因特网中使用最广泛的文件传输协议。 传输层对于应用层就是TCP和UDP协议-用来建立端到端的链接,这一层的数据单位是数据报 TCP三次握手:可靠连接,先建立连接(大数据包,数据要求安全) a. 发送请求,seq = x ,同步信号SYN b. 回请求,ack = x + 1; 双向连接,同步请求SYN seq = y c. 最后回建立请求,ack = y +1; 为什么握手不是两次的原因两次会造成资源浪费,当失效的请求再次到达,server以为是有效的,就建立了连接,就会造成浪费,使用三次主要是为了防止server端一直等待。 四次挥手,全双工的连接, a. 发送FIN请求断开 b. 接受FIN ,发送ack,表示已经接受到了 c. 再次发送FIN 请求断开连接 d. 最后发送ACK,最终断开连接 为什么要四次挥手?确保数据能够完成传输,收到FIN的时候,仅仅是代表对方没有数据发给你,你自己还没整理完数据发送给对方,所以这里还是需要你要确保自己整理好收拾好行李再发过去UDP:不可靠连接,不需建立连接(数据不安全,小数据包) 网络层路由的主要工作区域,用到的协议 1. ARP协议-地址解析协议 a. 已知对方IP去获取对方的MAC地址,用于同一子网内的寻址 b. 应用在局域网内 2. RARP-反向地址解析协议 a. 已知对端的MAC地址,获取其IP地址 。 3. ICMP协议 a. 检查网络通路对于网络层就是Ip\\ICMP\\ARP\\RARP协议-用来寻址和路由选择的。这一层的数据单位是数据包。 网络接口层包括了物理层和数据链路层 物理层:规定了通信设备的机械、电气的、功能的和过程的特性,用来简历和拆除物理链路连接。在一层的数据单位是比特。 数据链路层:在物理层之上,建立相邻接点之间的数据链路,通过差错控制提供数据帧在信道上无差错的传输。这一层的数据单位是帧。","link":"/2020/08/15/%E5%85%B6%E5%AE%83/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"}],"tags":[{"name":"JavaEE","slug":"JavaEE","link":"/tags/JavaEE/"},{"name":"随记","slug":"随记","link":"/tags/%E9%9A%8F%E8%AE%B0/"},{"name":"Dream","slug":"Dream","link":"/tags/Dream/"},{"name":"Java","slug":"Java","link":"/tags/Java/"},{"name":"springboot","slug":"springboot","link":"/tags/springboot/"},{"name":"Linux","slug":"Linux","link":"/tags/Linux/"},{"name":"算法","slug":"算法","link":"/tags/%E7%AE%97%E6%B3%95/"},{"name":"mysql","slug":"mysql","link":"/tags/mysql/"},{"name":"前端","slug":"前端","link":"/tags/%E5%89%8D%E7%AB%AF/"},{"name":"Javaweb","slug":"Javaweb","link":"/tags/Javaweb/"},{"name":"脚手架","slug":"脚手架","link":"/tags/%E8%84%9A%E6%89%8B%E6%9E%B6/"},{"name":"算法基础","slug":"算法基础","link":"/tags/%E7%AE%97%E6%B3%95%E5%9F%BA%E7%A1%80/"},{"name":"计算机网络","slug":"计算机网络","link":"/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"},{"name":"读书","slug":"读书","link":"/tags/%E8%AF%BB%E4%B9%A6/"},{"name":"-Java -mysql","slug":"Java-mysql","link":"/tags/Java-mysql/"},{"name":"Maven","slug":"Maven","link":"/tags/Maven/"},{"name":"排序","slug":"排序","link":"/tags/%E6%8E%92%E5%BA%8F/"},{"name":"JavaScript","slug":"JavaScript","link":"/tags/JavaScript/"},{"name":"leetcode","slug":"leetcode","link":"/tags/leetcode/"},{"name":"数据结构","slug":"数据结构","link":"/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"},{"name":"mybatis","slug":"mybatis","link":"/tags/mybatis/"},{"name":"spring","slug":"spring","link":"/tags/spring/"},{"name":"(驼峰)","slug":"(驼峰)","link":"/tags/%EF%BC%88%E9%A9%BC%E5%B3%B0%EF%BC%89/"},{"name":"springMVC","slug":"springMVC","link":"/tags/springMVC/"},{"name":"读书(驼峰)","slug":"读书(驼峰)","link":"/tags/%E8%AF%BB%E4%B9%A6%EF%BC%88%E9%A9%BC%E5%B3%B0%EF%BC%89/"}],"categories":[{"name":"java","slug":"java","link":"/categories/java/"},{"name":"Java学习","slug":"Java学习","link":"/categories/Java%E5%AD%A6%E4%B9%A0/"},{"name":"spring学习","slug":"spring学习","link":"/categories/spring%E5%AD%A6%E4%B9%A0/"},{"name":"Linux学习","slug":"Linux学习","link":"/categories/Linux%E5%AD%A6%E4%B9%A0/"},{"name":"MySQL学习","slug":"MySQL学习","link":"/categories/MySQL%E5%AD%A6%E4%B9%A0/"},{"name":"前端学习","slug":"前端学习","link":"/categories/%E5%89%8D%E7%AB%AF%E5%AD%A6%E4%B9%A0/"},{"name":"Javaweb学习","slug":"Javaweb学习","link":"/categories/Javaweb%E5%AD%A6%E4%B9%A0/"},{"name":"计算机","slug":"计算机","link":"/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"},{"name":"Reading","slug":"Reading","link":"/categories/Reading/"},{"name":"数据结构及算法","slug":"数据结构及算法","link":"/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%8F%8A%E7%AE%97%E6%B3%95/"},{"name":"数据结构与算法学习","slug":"数据结构与算法学习","link":"/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95%E5%AD%A6%E4%B9%A0/"},{"name":"错误记录","slug":"错误记录","link":"/categories/%E9%94%99%E8%AF%AF%E8%AE%B0%E5%BD%95/"},{"name":"随记","slug":"随记","link":"/categories/%E9%9A%8F%E8%AE%B0/"},{"name":"学习","slug":"学习","link":"/categories/%E5%AD%A6%E4%B9%A0/"}]}