首先,MySQL 解析查询语句,并将其分解成一个或多个子任务。
然后,MySQL 将子任务交给它的优化器来优化查询计划。
优化器根据数据库中的表结构和索引信息,选择最佳的执行方法来实现目标。
最后,MySQL 执行者将优化器生成的执行计划进行执行,并将最终的结果集返回给用户。
减少数据库服务器的压力,提高数据库性能。因为缓存中的结果集不需要重新执行 SQL 语句即可得到,所以大大减轻了数据库服务器的压力。
由于多次使用相同 SQL 语句时,MySQL 会将其结果集保存在缓存中,因此不用重复计算,这样也能够显著地减少 SQL 语句执行时间。
查询结果集会被共享,而不是仅对特定客户端而言。如果一个客户端执行了一条 SQL 语句并将其结果集保存在 MySQL 查询缓存中,那么其他客户端也可以使用这个相同的 SQL 结果集。
InnoDB:支持事务,具有行级锁定,外键等特性;MyISAM:不支持事务,具有表级锁定。
InnoDB 提供更好的并发性和安全性;MyISAM 提供更好的数据库压缩和速度优化。
InnoDB 在处理大量数据时能够保证一致性和正确性;MyISAM 在处理大量数据时可能会降低速度。
共享表空间指的是数据库的所有表数据,索引文件全部放在一个文件中
独立表空间:每一个表都将会生成以独立的文件方式来进行存储
区别在于如果把表放在共享表空间,即使表删除了空间也不会删除,因此表依然很大,而独立表空间如果删除表就会清除空间
A:delete from t
B:delete t
C:drop table t
D:truncate table t
答:D
对于查询来说两者都是从索引树进行查询,性能几乎没有任何区别;
对于更新操作来说,因为唯一索引需要先将数据读取到内存,然后需要判断是否有冲突,因此比唯一索引要多了判断操作,从而性能就比普通索引性能要低。
left join(左联结),返回左表全部记录和右表联结字段相等的记录;
right join(右联结),返回右表全部记录和左表联结字段相等的记录。
where a=1 只使用了索引 a;
where a=1 and b=2 只使用了索引 a,b;
where a=1 and b=2 and c=3 使用a,b,c;
where b=1 or where c=1 不使用索引;
where a=1 and c=3 只使用了索引 a;
where a=3 and b like ‘xx%’ and c=3 只使用了索引 a,b。
select * from t where num=10 or num=20;
答:如果使用 or 查询会使 MySQL 放弃索引而全表扫描,可以改为:
select * from t where num=10
union
select * from t where num=20;
事务是一种数据库处理机制,它用来保证数据的一致性和完整性。
事务具有以下几个特性:原子性(A)、一致性(C)、隔离性(I)、持久性(D)。
原子性表示事务是不可分割的最小工作单位,要么全部成功,要么全部失败;
一致性表示事务必须使所有数据从一个一致状态变更到另外一个一致状态;
隔离性表示在并发的情况下多个事务之间不能相互影响;
持久性表示即使在发生意外的情况下也能保证所做的更改永远不会丢失。
MySQL 默认使用 repetable read 的事务隔离级别。
行级锁和表级锁
行级锁是InnoDB存储引擎的最常用的锁定机制,它能够将冲突访问控制在单独的记录上。这样可以有效地避免因冲突而导致的性能问题。
表级锁则更加彻底,它会锁住整个表,使得其他事务无法对该表进行任何操作。
select * from t where id=1 for update
其中 id 字段必须有索引。
使用 MySQL 官方提供的数据库代理产品 MySql ProxySQL 搭建自动分配的数据库读写分离环境;
在程序层面配置多数据源使用代码实现读写分离。
检查网络带宽:可以检查两个数据库之间的带宽是否足够,如果不够则需要增加带宽。
数据库性能:可以优化数据库的相关性能参数,以便尽可能减少无用的IO开销。
采用合理的数据类型:尽量使用最小的字节来表示数据,从而减少存储空间的占用。
合理创建索引:根据查询需要选择合适的索引方式,这将有助于快速定位记录。
合理规划表与表之间的关系:正确规划数据库中表与表之间的关系,可以减少数据冗余。
优化SQL语句:使用合理、高效的SQL语句,避免不必要的运行时间浪费。
精确定义字段长度:如字符串字段可以根据实际情况来定义最大长度来减少存储量。
水平分片:基于某个表的某一个字段来进行分片,将数据库中同一类型的记录存储在不同的数据库实例中;
垂直分片:将一张表拆分成多张表,根据具体的业务需要将不同的字段存储在不同的数据库实例中;
分区分片:将数据库表根据时间或者其他规则划分成不同的区间,然后将这些区间映射到不同的数据库实例上。
使用索引:通过在表中创建索引来改善SQL查询的性能,特别是在执行复杂的查询时。
合并查询:尽量减少查询的数量,将多个查询合并成一个查询。
使用内联子查询:使用内联子查询可以减少代码量并改善性能。
限制检索记录数:限制检索的记录数可以减少SQL的运行时间。
简化条件:尽量减少where子句中的条件个数,避免复杂耗时的运算。
排序优化:选择最佳的字段进行升序或降序优化。
可能是积累的长连接导致内存占用太多,被系统强行杀掉导致的异常重启
因为在 MySQL 中长连接在执行过程中使用的临时内存对象,只有在连接断开的时候才会释放,这就会导致内存不断飙升
定期断开空闲的长连接;
如果是用的是 MySQL 5.7 以上的版本,可以定期执行 mysql_reset_connection 重新初始化连接资源
1)InputStream
InputStream:字节输入流基类,抽象类是表示字节输入流的所有类的超类。
1 | // 从输入流中读取数据的下一个字节 |
2)OutputStream
OutputStream:字节输出流基类,抽象类是表示输出字节流的所有类的超类。
1 | // 将 b.length 个字节从指定的 byte 数组写入此输出流 |
1)FileInputStream
FileInputStream:字节文件输入流,从文件系统中的某个文件中获得输入字节,用于读取诸如图像数据之类的原始字节流。
1 | //构造方法: |
1 | // 读取f盘下该文件f://hell/test.txt |
1 | // 读取f盘下该文件f://hell/test.txt |
注: 一次读取一个字节数组,提高了操作效率,IO流使用完毕一定要关闭。
2)FileOutputStream
FileOutputStream:字节文件输出流是用于将数据写入到File,从程序中写入到其他位置。
1 | //构造方法: |
1 | OutputStream outputStream = new FileOutputStream(new File("test.txt")); |
注;输出的目的地文件不存在,则会自动创建,不指定盘符的话,默认创建在项目目录下;输出换行符时一定要写\r\n不能只写\n,因为不同文本编辑器对换行符的识别存在差异性。
1)BufferedInputStream
BufferedInputStream:字节缓冲输入流,提高了读取效率。
1 | //构造方法: |
1 | InputStream in = new FileInputStream("test.txt"); |
2)BufferedOutputStream
BufferedOutputStream:字节缓冲输出流,提高了写出效率。
1 | //构造方法: |
1 | BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("test.txt", true)); |
1)Reader
Reader:读取字符流的抽象类
1 | //常用方法: |
2)Writer
Writer:写入字符流的抽象类.
1 | //常用方法: |
1)InputStreamReader
InputStreamReader:字节流转字符流,它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
1 | //构造方法: |
1 | //使用默认编码 |
注:Eclipse默认使用GBK编码,test.txt文件所以是GBK编码,当指定utf-8编码时所以会乱码。
2)OutputStreamWriter
OutputStreamWriter:字节流转字符流。
1 | //构造方法: |
1)BufferedReader
BufferedReader:字符缓冲流,从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
1 | //构造方法: |
1 | //生成字符缓冲流对象 |
2)BufferedWriter
BufferedWriter:字符缓冲流,将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
1 | //构造方法: |
1)FileReader:InputStreamReader类的直接子类,用来读取字符文件的便捷类,使用默认字符编码。
2)FileWriter:OutputStreamWriter类的直接子类,用来写入字符文件的便捷类,使用默认字符编码。
1)加载指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。
2)类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是前面所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过继承ClassLoader基类来创建自己的类加载器。
3)通过使用不同的类加载器,可以从不同来源加载类的二进制数据,通常有如下几种来源。
类加载器通常无须等到“首次使用”该类时才加载该类,Java虚拟机规范允许系统预先加载某些类。
当类被加载之后,系统为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。类连接又可分为如下3个阶段。
1)验证:验证阶段用于检验被加载的类是否有正确的内部结构,并和其他类协调一致。Java是相对C++语言是安全的语言,例如它有C++不具有的数组越界的检查。这本身就是对自身安全的一种保护。验证阶段是Java非常重要的一个阶段,它会直接的保证应用是否会被恶意入侵的一道重要的防线,越是严谨的验证机制越安全。验证的目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。其主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。
四种验证做进一步说明:
文件格式验证:主要验证字节流是否符合Class文件格式规范,并且能被当前的虚拟机加载处理。例如:主,次版本号是否在当前虚拟机处理的范围之内。常量池中是否有不被支持的常量类型。指向常量的中的索引值是否存在不存在的常量或不符合类型的常量。
元数据验证:对字节码描述的信息进行语义的分析,分析是否符合java的语言语法的规范。
字节码验证:最重要的验证环节,分析数据流和控制,确定语义是合法的,符合逻辑的。主要的针对元数据验证后对方法体的验证。保证类方法在运行时不会有危害出现。
符号引用验证:主要是针对符号引用转换为直接引用的时候,是会延伸到第三解析阶段,主要去确定访问类型等涉及到引用的情况,主要是要保证引用一定会被访问到,不会出现类等无法访问的问题。
2)准备:类准备阶段负责为类的静态变量分配内存,并设置默认初始值。
3)解析:将类的二进制数据中的符号引用替换成直接引用。说明一下:符号引用:符号引用是以一组符号来描述所引用的目标,符号可以是任何的字面形式的字面量,只要不会出现冲突能够定位到就行。布局和内存无关。直接引用:是指向目标的指针,偏移量或者能够直接定位的句柄。该引用是和内存中的布局有关的,并且一定加载进来的。
1)初始化是为类的静态变量赋予正确的初始值,准备阶段和初始化阶段看似有点矛盾,其实是不矛盾的,如果类中有语句:private static int a = 10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行链接的验证这一步骤,验证通过后准备阶段,给a分配内存,因为变量a是static的,所以此时a等于int类型的默认初始值0,即a=0,然后到解析(后面在说),到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10。
除此之外,下面几种情形需要特别指出:
对于一个final类型的静态变量,如果该变量的值在编译时就可以确定下来,那么这个变量相当于“宏变量”。Java编译器会在编译时直接把这个变量出现的地方替换成它的值,因此即使程序使用该静态变量,也不会导致该类的初始化。反之,如果final类型的静态Field的值不能在编译时确定下来,则必须等到运行时才可以确定该变量的值,如果通过该类来访问它的静态变量,则会导致该类被初始化。
类加载器负责加载所有的类,其为所有被载入内存中的类生成一个java.lang.Class实例对象。一旦一个类被加载如JVM中,同一个类就不会被再次载入了。正如一个对象有一个唯一的标识一样,一个载入JVM的类也有一个唯一的标识。在Java中,一个类用其全限定类名(包括包名和类名)作为标识;但在JVM中,一个类用其全限定类名和其类加载器作为其唯一标识。例如,如果在pg的包中有一个名为Person的类,被类加载器ClassLoader的实例kl负责加载,则该Person类对应的Class对象在JVM中表示为(Person.pg.kl)。这意味着两个类加载器加载的同名类:(Person.pg.kl)和(Person.pg.kl2)是不同的、它们所加载的类也是完全不同、互不兼容的。
JVM预定义有三种类加载器,当一个 JVM启动的时候,Java开始使用如下三种类加载器:
下面程序可以获得根类加载器所加载的核心类库,并会看到本机安装的Java环境变量指定的jdk中提供的核心jar包路径:
1 | public class ClassLoaderTest { |
扩展类加载器(extensions class loader):它负责加载JRE的扩展目录,lib/ext或者由java.ext.dirs系统属性指定的目录中的JAR包的类。由Java语言实现,父类加载器为null。
系统类加载器(system class loader):被称为系统(也称为应用)类加载器,它负责在JVM启动时加载来自Java命令的-classpath选项、java.class.path系统属性,或者CLASSPATH换将变量所指定的JAR包和类路径。程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。如果没有特别指定,则用户自定义的类加载器都以此类加载器作为父加载器。由Java语言实现,父类加载器为ExtClassLoader。
类加载器加载Class大致要经过如下8个步骤:
- 检测此Class是否载入过,即在缓冲区中是否有此Class,如果有直接进入第8步,否则进入第2步。
- 如果没有父类加载器,则要么Parent是根类加载器,要么本身就是根类加载器,则跳到第4步,如果父类加载器存在,则进入第3步。
- 请求使用父类加载器去载入目标类,如果载入成功则跳至第8步,否则接着执行第5步。
- 请求使用根类加载器去载入目标类,如果载入成功则跳至第8步,否则跳至第7步。
- 当前类加载器尝试寻找Class文件,如果找到则执行第6步,如果找不到则执行第7步。
- 从文件中载入Class,成功后跳至第8步。
- 抛出ClassNotFountException异常。
- 返回对应的java.lang.Class对象。
1)全盘负责:所谓全盘负责,就是当一个类加载器负责加载某个Class时,该Class所依赖和引用其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。
2)双亲委派:所谓的双亲委派,则是先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父加载器,依次递归,如果父加载器可以完成类加载任务,就成功返回;只有父加载器无法完成此加载任务时,才自己去加载。
3)缓存机制。缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓冲区中。这就是为很么修改了Class后,必须重新启动JVM,程序所做的修改才会生效的原因。
1)双亲委派机制,其工作原理的是,如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式,即每个儿子都很懒,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子自己才想办法去完成。
2)双亲委派机制的优势:采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。
]]>以 java.lang.annotation 中定义的 Target 注解来说明:
1 |
|
源码分析如下:
第一:元注解@Retention,成员value的值为RetentionPolicy.RUNTIME。
第二:元注解@Target,成员value是个数组,用{}形式赋值,值为ElementType.ANNOTATION_TYPE
第三:成员名称为value,类型为ElementType[]
另外,需要注意一下,如果成员名称是value,在赋值过程中可以简写。如果成员类型为数组,但是只赋值一个元素,则也可以简写。如上面的简写形式为:
1 |
|
注解的分类有两种分法:
1)基本内置注解,是指Java自带的几个Annotation,如@Override、Deprecated、@SuppressWarnings等;
2)元注解(meta-annotation),是指负责注解其他注解的注解,JDK 1.5及以后版本定义了4个标准的元注解类型,如下:
3)自定义注解,根据需要可以自定义注解,自定义注解需要用到上面的meta-annotation
1)源码时注解(RetentionPolicy.SOURCE)
2)编译时注解(RetentionPolicy.CLASS)
3)运行时注解(RetentionPolicy.RUNTIME)
注解需要标明注解的生命周期,这些信息是通过元注解 @Retention 实现,注解的值是 enum 类型的 RetentionPolicy,包括以下几种情况:
1 | public enum RetentionPolicy { |
如上所介绍的Java定义了4个标准的元注解:
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
1 | public class TestAnnoReflat { |
Java反射是Java被视为动态(或准动态)语言的一个关键性质。
这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。
Java反射机制容许程序在运行时加载、探知、使用编译期间完全未知的classes。
换言之,Java可以加载一个运行时才得知名称的class,获得其完整结构。
Java反射相关的API在包java.lang.reflect中,JDK 1.6.0的reflect包如下图
Member接口 | 该接口可以获取有关类成员(域或者方法)后者构造函数的信息。 |
---|---|
AccessibleObject类 | 该类是域(field)对象、方法(method)对象、构造函数(constructor)对象的基础类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。 |
Array类 | 该类提供动态地生成和访问JAVA数组的方法。 |
Constructor类 | 提供一个类的构造函数的信息以及访问类的构造函数的接口。 |
Field类 | 提供一个类的域的信息以及访问类的域的接口。 |
Method类 | 提供一个类的方法的信息以及访问类的方法的接口。 |
Modifier类 | 提供了 static 方法和常量,对类和成员访问修饰符进行解码。 |
Proxy类 | 提供动态地生成代理类和类实例的静态方法。 |
1 | class Type{ |
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。获取类的Class对象有多种方式:
方法调用 | 结果 |
---|---|
调用getClass | Boolean var1 = true;Class<?> classType2 = var1.getClass();System.out.println(classType2);输出:class java.lang.Boolean |
运用.class 语法 | Class<?> classType4 = Boolean.class;System.out.println(classType4);输出:class java.lang.Boolean |
运用static method Class.forName() | Class<?> classType5 = Class.forName(“java.lang.Boolean”);System.out.println(classType5);输出:class java.lang.Boolean |
运用primitive wrapper classes的TYPE 语法这里返回的是原生类型,和Boolean.class返回的不同 | Class<?> classType3 = Boolean.TYPE;System.out.println(classType3); 输出:boolean |
可以通过反射机制得到某个类的某个属性,然后改变对应于这个类的某个实例的该属性值。JAVA 的Class
类提供了几个方法获取类的属性。
方法调用 | 结果 |
---|---|
public Field getField(String name) | 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段 |
public Field[] getFields() | 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段 |
public Field getDeclaredField(String name) | 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段 |
public Field[] getDeclaredFields() | 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段 |
1 | Class<?> classType = ExtendType.class; |
可见getFields和getDeclaredFields区别:
getFields返回的是申明为public的属性,包括父类中定义,
getDeclaredFields返回的是指定类定义的所有定义的属性,不包括父类的。
通过反射机制得到某个类的某个方法,然后调用对应于这个类的某个实例的该方法
Class
类提供了几个方法获取类的方法。
方法调用 | 结果 |
---|---|
public Method getMethod(String name, Class<?>… parameterTypes) | 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法 |
public Method[] getMethods() | 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法 |
public Method getDeclaredMethod(String name,Class<?>… parameterTypes) | 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法 |
public Method[] getDeclaredMethods() | 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法 |
1 | // 使用getMethods获取函数 |
通过反射机制得到某个类的构造器,然后调用该构造器创建该类的一个实例
Class
类提供了几个方法获取类的构造器。
方法调用 | 结果 |
---|---|
public Constructor | 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法 |
public Constructor<?>[] getConstructors() | 返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法 |
public Constructor | 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法 |
public Constructor<?>[] getDeclaredConstructors() | 返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。它们是公共、保护、默认(包)访问和私有构造方法 |
1 | // 使用getConstructors获取构造器 |
通过反射机制创建新类的实例,有几种方法可以创建
方法调用 | 结果 |
---|---|
调用无自变量ctor | 1、调用类的Class对象的newInstance方法,该方法会调用对象的默认构造器,如果没有默认构造器,会调用失败.Class> classType = ExtendType.class;Object inst = classType.newInstance();System.out.println(inst);输出:Type:Default ConstructorExtendType:Default Constructorcom.quincy.ExtendType@d80be3 2、调用默认Constructor对象的newInstance方法Class> classType = ExtendType.class;Constructor<?> constructor1 = classType.getConstructor();Object inst = constructor1.newInstance();System.out.println(inst);输出:Type:Default ConstructorExtendType:Default Constructorcom.quincy.ExtendType@1006d75 |
调用带参数ctor | 3、调用带参数Constructor对象的newInstance方法Constructor<?> constructor2 =classType.getDeclaredConstructor(int.class, String.class);Object inst = constructor2.newInstance(1, “123”);System.out.println(inst);输出:Type:Default ConstructorExtendType:Constructor with parameterscom.quincy.ExtendType@15e83f9 |
通过反射获取类Method对象,调用Field的Invoke方法调用函数。
1 | Class<?> classType = ExtendType.class; |
通过反射获取类的Field对象,调用Field方法设置或获取值
1 | Class<?> classType = ExtendType.class; |
在spring boot中,摒弃了spring以往项目中大量繁琐的配置,遵循约定大于配置的原则,通过自身默认配置,极大的降低了项目搭建的复杂度。同样在spring boot中,大量注解的使用,使得代码看起来更加简洁,提高开发的效率。这些注解不光包括spring boot自有,也有一些是继承自spring的。
1)@SpringBootApplication 注解
@SpringBootApplication是一个复合注解,包含了@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan这三个注解。
@SpringBootConfiguration:标注当前类是配置类,这个注解继承自@Configuration。并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到srping容器中,并且实例名就是方法名。
@EnableAutoConfiguration:是自动配置的注解,这个注解会根据我们添加的组件jar来完成一些默认配置,我们做微服时会添加spring-boot-starter-web这个组件jar的pom依赖,这样配置会默认配置springmvc 和tomcat。
@ComponentScan:扫描当前包及其子包下被@Component,@Controller,@Service,@Repository注解标记的类并纳入到spring容器中进行管理。等价于context:component-scan的xml配置文件中的配置项。
大多数情况下,这3个注解会被同时使用,基于最佳实践,这三个注解就被做了包装,成为了@SpringBootApplication注解
2)@ServletComponentScan 注解
3)@MapperScan注解
4)资源导入注解
@ImportResource @Import @PropertySource 这三个注解都是用来导入自定义的一些配置文件。
@ImportResource(locations={}) 导入其他xml配置文件,需要标准在主配置类上。
导入property的配置文件 @PropertySource指定文件路径,这个相当于使用spring的
@import注解是一个可以将普通类导入到spring容器中做管理
1)@Controller注解
@Controller 表明这个类是一个控制器类,和@RequestMapping来配合使用拦截请求,如果不在method中注明请求的方式,默认是拦截get和post请求。这样请求会完成后转向一个视图解析器。但是在大多微服务搭建的时候,前后端会做分离。所以请求后端只关注数据处理,后端返回json数据的话,需要配合@ResponseBody注解来完成。
这样一个只需要返回数据的接口就需要3个注解来完成,大多情况我们都是需要返回数据。也是基于最佳实践,所以将这三个注解进一步整合。
@RestController 是@Controller 和@ResponseBody的结合,一个类被加上@RestController 注解,数据接口中就不再需要添加@ResponseBody。更加简洁。
同样的情况,@RequestMapping(value=””,method= RequestMethod.GET ),我们都需要明确请求方式。这样的写法又会显得比较繁琐,于是乎就有了如下的几个注解。
普通风格 | Rest风格 |
---|---|
@RequestMapping(value=“”,method = RequestMethod.GET) | @GetMapping(value =“”) |
@RequestMapping(value=“”,method = RequestMethod.POST) | @PostMapping(value =“”) |
@RequestMapping(value=“”,method = RequestMethod.PUT) | @PutMapping(value =“”) |
@RequestMapping(value=“”,method = RequestMethod.DELETE) | @DeleteMapping(value =“”) |
这几个注解是 @RequestMapping(value=””,method= RequestMethod.xxx )的最佳实践。为了代码的更加简洁。
2)@CrossOrigin 注解
@CrossOrigin(origins = “”, maxAge = 1000) 这个注解主要是为了解决跨域访问的问题。这个注解可以为整个controller配置启用跨域,也可以在方法级别启用。
我们在项目中使用这个注解是为了解决微服在做定时任务调度编排的时候,会访问不同的spider节点而出现跨域问题。
3)@Autowired 注解
4)@EnableCaching 注解
5)@PathVariable 注解
1)@Service
2)@Resource
@Resource和@Autowired一样都可以用来装配bean,都可以标注字段上,或者方法上。 @resource注解不是spring提供的,是属于J2EE规范的注解。
两个之前的区别就是匹配方式上有点不同,@Resource默认按照名称方式进行bean匹配,@Autowired默认按照类型方式进行bean匹配。
1)@Repository
@Repository注解类作为DAO对象,管理操作数据库的对象。
总得来看,@Component, @Service, @Controller, @Repository是spring注解,注解后可以被spring框架所扫描并注入到spring容器来进行管理
@Component是通用注解,其他三个注解是这个注解的拓展,并且具有了特定的功能。
通过这些注解的分层管理,就能将请求处理,义务逻辑处理,数据库操作处理分离出来,为代码解耦,也方便了以后项目的维护和开发。
所以我们在正常开发中,如果能用@Service, @Controller, @Repository其中一个标注这个类的定位的时候,就不要用@Component来标注。
2)@Transactional
通过这个注解可以声明事务,可以添加在类上或者方法上。
在spring boot中 不用再单独配置事务管理,一般情况是我们会在servcie层添加了事务注解,即可开启事务。要注意的是,事务的开启只能在public 方法上。并且主要事务切面的回滚条件。正常我们配置rollbackfor exception时 ,如果在方法里捕获了异常就会导致事务切面配置的失效。
@ControllerAdvice 和 @RestControllerAdvice:通常和@ExceptionHandler、@InitBinder、@ModelAttribute一起配合使用。
@ControllerAdvice 和 @ExceptionHandler 配合完成统一异常拦截处理。
@RestControllerAdvice 是 @ControllerAdvice 和 @ResponseBody的合集,可以将异常以json的格式返回数据。
序号 | 数据类型 | 位数 | 默认值 | 取值范围 | 举例说明 |
---|---|---|---|---|---|
1 | byte(位) | 8 | 0 | -2^7 ~ +2^7-1 | byte b = 10; |
2 | short(短整数) | 16 | 0 | -2^15 ~ +2^15-1 | short s = 10; |
3 | int(整数) | 32 | 0 | -2^31 ~ +2^31-1 | int i = 10; |
4 | long(长整数) | 64 | 0 | -2^63 ~ +2^63-1 | long l = 10l; |
5 | float(单精度) | 32 | 0.0 | -2^128 ~ +2^127 | float f = 10.0f; |
6 | double(双精度) | 64 | 0.0 | -2^1024 ~ +2^1023 | double d = 10.0d; |
7 | char(字符) | 16 | 空 | 0 ~ +2^16-1 | char c = ‘c’; |
8 | boolean(布尔值) | 8 | false | true、false | boolean b = true; |
1 | //自动装箱 |
1)StringBuilder > StringBuffer > String
1)当字符串相加操作或者改动较少的情况下,建议使用 String str=”hello”这种形式;
2)当字符串相加操作较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer。
1)传输数据的最基本单位是字节的流。
1)传输数据的最基本单位是字符的流。
1)字符类型一般包括:word、txt、文本类型。
2)字节类型一般包括:图片、声音、图像等。
3)因为一般字符流最终都要转换成字节流,所以为考虑到通用性,要用字节流。
1)编译错误:程序没有遵循语法规则,编译程序发现并提示错误的原因和位置。
2)运行时错误:程序在执行时,运行环境发现了不能执行的操作
3)逻辑错误:程序没有按照预期的逻辑顺序执行
4)异常:指程序运行时发生错误,而异常处理就是对这些错误进行处理和控制
1)Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。
2)Exception(异常):是程序本身可以处理的异常。
1)运行时异常:一般是由程序逻辑错误引起的,当程序中可能出现这类异常,即使没有用try-catch语句捕获它或throws子句声明抛出它,也会编译通过。
2)非运行时异常 (编译异常):从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。
1)抛出异常:throw,throws
2)捕获异常:try,catch,finally
1)原始类型: boolean,char,byte,short,int,long,float,double
2)包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
1)简洁。
2)非常容易并行计算。
3)可能代表未来的编程趋势。
1)若不用并行计算,很多时候计算速度没有比传统的 for 循环快。(并行计算有时需要预热才显示出效率优势)
2)不容易调试。
3)若其他程序员没有学过 lambda 表达式,代码不容易让其他语言的程序员看懂。
]]> 1)抽象类是用来捕捉子类的通用特性的
2)它不能被实例化,只能被用作子类的超类
3)抽象类是被用来创建继承层级里子类的模板
1)接口是抽象方法的集合
2)如果一个类实现了某个接口,那么它就继承了这个接口的抽象方法
3)如果实现了这个接口,那么就必须确保使用这些方法
4)接口只是一种形式,接口自身不能做任何事情
参数 | 抽象类 | 接口 |
---|---|---|
默认的方法实现 | 它可以有默认的方法实现 | 接口完全是抽象的。它根本不存在方法的实现 |
实现 | 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。 | 子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现 |
构造器 | 抽象类可以有构造器 | 接口不能有构造器 |
与正常Java类的区别 | 除了你不能实例化抽象类之外,它和普通Java类没有任何区别 | 接口是完全不同的类型 |
访问修饰符 | 抽象方法可以有public、protected和default这些修饰符 | 接口方法默认修饰符是public。你不可以使用其它修饰符。 |
main方法 | 抽象方法可以有main方法并且我们可以运行它 | 接口没有main方法,因此我们不能运行它。 |
多继承 | 抽象方法可以继承一个类和实现多个接口 | 接口只可以继承一个或多个其它接口 |
速度 | 它比接口速度要快 | 接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。 |
添加新方法 | 如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。 | 如果你往接口中添加方法,那么你必须改变实现该接口的类。 |
1)把类定义在另一个类的内部,该类就被称为内部类
2)把类Inner定义在类Outer中,类Inner就被称为内部类
1)可以直接访问外部类的成员,包括私有
2)外部类要想访问内部类成员,必须创建对象
1)成员内部类:位于外部类成员位置的类,可以使用外部类中所有的成员变量和成员方法(包括private的)
2)局部内部类:定义在一个方法或者一个作用域里面的类,主要是作用域发生了变化,只能在自身所在方法和属性中被使用
3)静态内部类:用static修饰的内部类,不能使用外部类的非static成员变量和成员方法
4)匿名内部类:一个没有名字的类,是内部类的简化写法,其实是继承该类或者实现接口的子类匿名对象
1)同一个类中的多个方法具有相同的名字,这些方法具有不同的参数列表
2)参数类型和个数不一样,返回值类型可以相同也可以不相同
3)无法以返回型别作为重载函数的区分标准
4)重载Overloading是一个类中多态性的一种表现。
1)子类定义的方法与父类中的方法具有相同的方法名字,相同的参数表和相同的返回类型
2)子类中不能重写父类中的final方法
3)子类中必须重写父类中的abstract方法
相等(相同)的对象必须具有相等的哈希码(或者散列码)。
如果两个对象的hashCode相同,它们并不一定相同。
1)上界<? extend Fruit>
2)下界<? super Apple>
1)上界的list只能get,不能add(确切地说不能add出除null之外的对象,包括Object)
3)下界的list只能add,不能get
1 | import java.util.ArrayList; |
1 | public class GenericTest { |
所谓的单例设计指的是一个类只允许产生一个实例化对象。
最好理解的一种设计模式,分为懒汉式和饿汉式。
1)构造方法私有化,外部无法产生新的实例化对象
2)只能通过static方法取得实例化对象
1 | class Singleton { |
1)当第一次去使用Singleton对象的时候才会为其产生实例化对象的操作
1 | class Singleton { |
工厂方法模式分为三种:
(1)普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
(2)多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。
(3)静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。
1)建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
1 | interface Sender { |
1)该模式是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。
1 | interface Sender { |
1)将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。
1 | interface Sender { |
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要扩展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?
那么这就用到了抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
1 | interface Provider { |
工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性。
1 | import java.util.ArrayList; |
适配器模式是将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的的类的兼容性问题。主要分三类:类的适配器模式、对象的适配器模式、接口的适配器模式。
1 | class Source { |
1)基本思路和类的适配器模式相同,只是将Adapter 类作修改,这次不继承Source 类,而是持有Source 类的实例,以达到解决兼容性的问题。
1 | class Source { |
1)有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。
1 | /** |
顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
1 | interface Shape { |
代理模式指给一个对象提供一个代理对象,并由代理对象控制对原对象的引用。代理可以分为静态代理和动态代理。
通过代理模式,可以利用代理对象为被代理对象添加额外的功能,以此来拓展被代理对象的功能。可以用于计算某个方法执行时间,在某个方法执行前后记录日志等操作。
1)静态代理需要我们写出代理类和被代理类,而且一个代理类和一个被代理类一一对应。代理类和被代理类需要实现同一个接口,通过聚合使得代理对象中有被代理对象的引用,以此实现代理对象控制被代理对象的目的。
1 | /** |
JDK 1.3 之后,Java通过java.lang.reflect包中的三个类Proxy、InvocationHandler、Method来支持动态代理。动态代理常用于有若干个被代理的对象,且为每个被代理对象添加的功能是相同的(例如在每个方法运行前后记录日志)。
动态代理的代理类不需要我们编写,由Java自动产生代理类源代码并进行编译最后生成代理对象。
创建动态代理对象的步骤:
(1)指明一系列的接口来创建一个代理对象
(2) 创建一个调用处理器(InvocationHandler)对象
(3)将这个代理指定为某个其他对象的代理对象
(4)在调用处理器的invoke()方法中采取代理,一方面将调用传递给真实对象,另一方面执行各种需要的操作
1 | import java.lang.reflect.InvocationHandler; |
策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。
需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数。策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。
因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。
1 | /** |
名称 | 模型 | 并发模式 | 历史模式 | 变更范围 | 网络协议 | 原子提交性 |
---|---|---|---|---|---|---|
CVS | Client-server | Merge | Changeset | File | Pserver,ssh | No |
SVN | Client-server | 3-way merge, recursive merge, octopus merge | Changeset and Snapshot | Tree | custom (svn), custom (svn) over ssh, HTTP and SSL (usingWebDAV) | Yes |
Git | Distributed | Merge or lock | Snapshot | Tree | custom, custom over ssh, rsync, HTTP/HTTPS, email, bundles | Yes |
简而言之,各有优缺点,git要配合hub,可以避免分布式损坏。svn有权限控制,避免全被clone走。git适合纯代码,svn适合综合性文档管理,结合起来就完美。显然最大的不同在于git是分布式的。
1)git是世界上目前最先进的分布式版本控制系统,致力于团队、个人进行项目版本管理,完美的解决难以比较代码、难以合并代码、难以取消修改、难以在写当前代码的过程中保存未完成的修改去修改线上版本的bug等的痛点。
2)git是一个非常强大的工具,但作为一个git使用者来说,不用完全学习Git的知识点与命令,因为有的命令的使用频率非常的低甚至数年都不会用到。
1)Linus在1991年创建了开源的Linux,从此,Linux系统不断发展,已经成为最大的服务器系统软件了。Linus虽然创建了Linux的核心,但Linux的壮大是靠全世界热心的志愿者参与的,这么多人在世界各地为Linux编写代码,那Linux的代码是如何管理的呢?
2)事实是,在2002年以前,世界各地的志愿者把源代码文件通过diff的方式发给Linus,然后由Linus本人通过手工方式合并代码!你也许会想,为什么Linus不把Linux代码放到版本控制系统里呢?不是有CVS、SVN这些免费的版本控制系统吗?因为Linus坚定地反对CVS和SVN,这些集中式的版本控制系统不但速度慢,而且必须联网才能使用。有一些商用的版本控制系统,虽然比CVS、SVN好用,但那是付费的,和Linux的开源精神不符。不过,到了2002年,Linux系统已经发展了十年了,代码库之大让Linus很难继续通过手工方式管理了,社区的弟兄们也对这种方式表达了强烈不满,于是Linus选择了一个商业的版本控制系统BitKeeper,BitKeeper的东家BitMover公司出于人道主义精神,授权Linux社区免费使用这个版本控制系统。
3)安定团结的大好局面在2005年就被打破了,原因是Linux社区牛人聚集,不免沾染了一些梁山好汉的江湖习气。开发Samba的Andrew试图破解BitKeeper的协议(这么干的其实也不只他一个),被BitMover公司发现了(监控工作做得不错!),于是BitMover公司怒了,要收回Linux社区的免费使用权。Linus可以向BitMover公司道个歉,保证以后严格管教弟兄们,嗯,这是不可能的。实际情况是这样的:Linus花了两周时间自己用C写了一个分布式版本控制系统,这就是Git!一个月之内,Linux系统的源码已经由Git管理了!牛是怎么定义的呢?大家可以体会一下。
4)Git迅速成为最流行的分布式版本控制系统,尤其是2008年,GitHub网站上线了,它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub,包括jQuery,PHP,Ruby等等。历史就是这么偶然,如果不是当年BitMover公司威胁Linux社区,可能现在我们就没有免费而超级好用的Git了。
git是linux的创始人linus,在付费版本控制工具BitMover收回对Linux社区免费使用权利的时候,一怒之下花费两个星期的时间写出来的。(不要逼牛笔的人)
1)工作区:用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,拷贝的就是这里的数据。
2)暂存区:保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。有时候也被称作“索引”,不过一般说法还是叫暂存区域。
3)版本库:也叫本地版本库,之所以说git 快,大部分提交都是对本地仓库而言的,不依赖网络,最后一次会推送的到远程仓库。
4)远程仓库:可以看做是github,它是一个远程仓库,它提供web服务的 供大家方便下载、查看、提交、存储。文件的状态
1)新建文件状态为untracked
2)add命令执行后状态变为staged
3)已存在的文件状态为unmodified
4)修改文件内容,文件状态变为modified
5)commit提交,文件状态编程unmodifed。
1 | git config --global user.email "dxj1718874198@gmail.com" |
1 | git init |
1 | git status |
1)新添加的未跟踪文件前面有 ?? 标记;
2)新添加到暂存区中的文件前面有 A 标记;
3)修改过的文件前面有 M 标记。 M 有两个可以出现的位置:
4)出现在右边的 M 表示该文件被修改了但是还没放入暂存区,
5)出现在靠左边的 M 表示该文件被修改了并放入了暂存区。
6)加到暂存区之后,又修改了一次,修改之后,并没有添加到暂存区,前面有MM标记。
7)以此类推,如果加到暂存区之后,被修改了两次,修改之后,并没有添加到暂存区,前面有MMM标记。…
1 | git add file1 |
1 | git commit -m "add file1" file1 |
1 | git diff file1 |
1 | git log |
1 | git reflog # 包括已经被删除的 commit 记录和 reset 的操作 |
1 | .DS_Store |
1)创建一个名为 .gitignore 的文件,列出要忽略的文件模式
2)星号(*)匹配零个或多个任意字符;
3)[abc] 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个c);
4)问号(?)只匹配一个任意字符;
5)如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9]表示匹配所有 0 到 9 的数字)。
6)使用两个星号(*) 表示匹配任意中间目录,比如a/**/z 可以匹配 a/z, a/b/z 或a/b/c/z等
1)工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。
2)Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
3)第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;
4)第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。
1 | git checkout -- file1 |
1 | git reset HEAD |
1 | git reflog |
1 | git checkout file1 |
1 | git rm file1 |
1 | git branch <branch_name> |
1 | git branch # 查看所有分支 |
1 | git checkout <branch_name> # 切换到指定分支 |
1 | git branch -d <branch_name> # 删除一个干净的分支(即相对当前分支而言该分支没有新的提交记录) |
注意:删除分支前都需要先切换到其他分支才能进行删除操作
1 | git reflog # 查找该分支指向的commitId |
1 | git branch -m <branch_name> newname |
1 | git merge <branch_name> # 将指定分支合并到当前分支,如果两个分支没有产生分叉情况,那么会进行快速合并,即fast-forward方式 |
1 | git merge --no-ff -m "msg" <branch_name> # 合并分支时禁用Fast forward模式 |
1)当对分叉分支进行合并时,如果两个分支都对同一文件进行了修改,那么合并时就有可能会产生冲突情况。
2)如果两个分支对同一文件的修改是有规律的,比如对不同地方的修改,那么git工具可以实现自动合并
3)如果无法自动合并,则需要对冲突文件进行手动修改,修改完成后使用git add表示冲突已经解决,然后使用git commit进行提交
1 | git stash # 将工作暂存 |
1)先恢复,而后再删除暂存
1 | git stash apply |
2)恢复的同时也将stash内容删除
1 | git stash pop |
1 | git remote add origin 远程仓库地址 # 将本地仓库和远程仓库进行关联 |
注意有坑: 新建远程仓库的时候如果你勾选了
Initialize this repository with a README
(就是创建仓库的时候自动给你创建一个README文件),那么到了第7步你将本地仓库内容推送到远程仓库的时候就会报一个failed to push some refs to 远程仓库地址
的错。原因: 由于你新创建的那个仓库里面的
README.md
文件不在本地仓库目录中,这时我们可以通过git pull --rebase origin master
命令先将内容合并,此时再push就能成功了。
1 | git clone 远程仓库地址 # 从远程服务器克隆一个一模一样的版本库到本地,复制的是整个版本库 |
1 | git fetch |
1 | 由于现在的.git文件夹里累积了太多辣鸡数据,甚至已经超过博客本身的大小了,于是打算从头开始重新部署一下博客的所有静态资源。 |
1)数据库事务是构成单一逻辑工作单元的操作集合,包含一个或多个数据库操作,这些操作构成一个逻辑上的整体。
2)一个典型的数据库事务如下所示
1 | BEGIN TRANSACTION //事务开始 |
1)**原子性(Atomicity)**:事务中的所有操作作为一个整体像原子一样不可分割,要么全部成功,要么全部失败。
2)**一致性(Consistency)**:事务的执行结果必须使数据库从一个一致性状态到另一个一致性状态。
3)**隔离性(Isolation)**:并发执行的事务不会相互影响,其对数据库的影响和它们串行执行时一样。比如多个用户同时往一个账户转账,最后账户的结果应该和他们按先后次序转账的结果一样。
4)**持久性(Durability)**:事务一旦提交,其对数据库的更新就是持久的。任何事务或系统故障都不会导致数据丢失。
一致性状态:
系统的状态满足数据的完整性约束(主码,参照完整性,check约束等)
系统的状态反应数据库本应描述的现实世界的真实状态,比如转账前后两个账户的金额总和应该保持不变。
1)指一个事务A正在访问数据,并且对该数据进行了修改,但是这种修改还没有提交到数据库中(也可能因为某些原因Rollback了)。这时候另外一个事务B也访问这个数据,然后使用了这个被A修改的数据,那么这个数据就是脏的,并不是数据库中真实的数据。这就被称作脏读。
2)解决办法:把数据库事务隔离级别调整到READ_COMMITTED
3)即让用户在更新时锁定数据库,阻止其他用户读取,直到更新全部完成才让你读取。
1)指一个事务A对一个表中的数据进行了修改,而且该修改涉及到表中所有的数据行;同时另一个事务B也在修改表中的数据,该修改是向表中插入一行新数据。那么经过这一番操作之后,操作事务A的用户就会发现表中还有没修改的数据行,就像发生了幻觉一样。这就被称作幻读。
2)解决办法:把数据库事务隔离级别调整到SERIALIZABLE_READ
1)指在一个事务A内,多次读同一个数据,但是事务A没有结束时,另外一个事务B也访问该同一数据。那么在事务A的两次读数据之间,由于事务B的修改导致事务A两次读到的数据可能是不一样的。这就发生了在一个事务内两次读到的数据不一样,这就被称作不可重复读。
2)解决办法:把数据库事务隔离级别调整到REPEATABLE_READ
注:
级别高低:脏读 < 不可重复读 < 幻读
所以设置了最高级别的SERIALIZABLE_READ就不需要设置其他的了,即解决了幻读问题那么脏度和不可重复读自然就都解决了。
1)索引其实是一种数据结构,能够帮助我们快速的检索数据库中的数据,解决效率低问题
1)可以通过建立唯一索引或者主键索引,保证数据库表中每一行数据的唯一性
2)建立索引可以大大提高检索的数据,以及减少表的检索行数
3)在表连接的连接条件,可以加速表与表直接的相连
4)在分组和排序字句进行数据检索,可以减少查询时间中分组和排序时所消耗的时间(数据库的记录会重新排序)
5)建立索引,在查询中使用索引 可以提高性能
1)hash(哈希)
2)二叉树
3)红黑树
4)B+树
1 | SELECT * FROM py_user u, py_paths p where u.userUUID = p.userUUID; |
1 | SELECT * FROM py_user u INNER JOIN py_paths p ON u.userUUID = p.userUUID; |
1 | SELECT * FROM py_user u LEFT JOIN py_paths p ON u.userUUID = p.userUUID; |
1 | SELECT * FROM py_user u RIGHT JOIN py_paths p ON u.userUUID = p.userUUID; |
1 | SELECT * FROM py_user u LEFT JOIN py_paths p ON u.userUUID = p.userUUID; |
1)第一次的查询结果可以作为第二次的查询的条件或者表名使用.
2)子查询中可以包含:IN、NOT IN、ANY、ALL、EXISTS 和 NOT EXISTS等关键字. 还可以包含比较运算符:= 、 !=、> 、< 等
1 | select * from (select * from person) as 表名; |
1 | SELECT * FROM table_name ORDER BY column_name1, column_name2 DESC; |
1 | SELECT u.userName, COUNT(*) FROM py_user u, py_paths p WHERE u.userUUID = p.userUUID GROUP BY regEmail; |
1 | SELECT AVG(salary) FROM person; |
1 | SELECT MAX(salary) FROM person; |
1 | SELECT MIN(salary) FROM person; |
1)作用的对象不同。WHERE 子句作用于表和视图,HAVING 子句作用于组。
2)WHERE 在分组和聚集计算之前选取输入行(因此,它控制哪些行进入聚集计算), 而 HAVING 在分组和聚集之后选取分组的行。
3)WHERE 子句不能包含聚集函数,HAVING 子句总是包含聚集函数。
4)HAVING 一般跟在 GROUP BY 之后,执行记录组选择的一部分来工作的。WHERE 则是执行所有数据来工作的
5)HAVING 可以用聚合函数,如 HAVING SUM(salary) > 1000
1 | SELECT city FROM weather WHERE temp_lo = (SELECT MAX(temp_lo) FROM weather); |
1)创建一个线程子类继承Thread类
2)重写run() 方法,把需要线程执行的程序放入run方法,线程启动后方法里的程序就会运行
3)创建该类的实例,并调用对象的start()方法启动线程
1 | public class ThreadDemo extends Thread { |
1)定义一个线程类实现Runnable接口,并重写该接口的run()方法,方法中依然是包含指定执行的程序
2)创建一个Runnable实现类实例,将其作为target参数传入,并创建Thread类实例
3)调用Thread类实例的start()方法启动线程
1 | public class RunnableDemo implements Runnable{ |
1)创建Callable接口的实现类,实现call() 方法
2)创建Callable实现类实例,通过FutureTask类来包装Callable对象,该对象封装了Callable对象的call()方法的返回值
3)将创建的FutureTask对象作为target参数传入,创建Thread线程实例并启动新线程
4)调用FutureTask对象的get方法获取返回值。
1 | class ThreadUtil implements Callable { |
volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住
volatile仅能使用在变量级别,synchronized则可以使用在变量、方法
volatile仅能实现变量的修改可见性,而synchronized则可以保证变量的修改可见性和原子性
volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞
当一个域的值依赖于它之前的值时,volatile就无法工作了,如n=n+1,n++等。如果某个域的值受到其他域的值的限制,那么volatile也无法工作,如Range类的lower和upper边界,必须遵循lower<=upper的限制。
使用volatile而不是synchronized的唯一安全的情况是类中只有一个可变的域。
1)线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。
1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能执行。
3)提高线程的可管理性,线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。
1)newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
2)newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
3)newScheduledThreadPool:创建一个可定期或者延时执行任务的定长线程池,支持定时及周期性任务执行。
4)newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
1)Running:能接受新任务以及处理已添加的任务
2)Shutdown:不接受新任务,可以处理已添加的任务
3)Stop:不接受新任务,不处理已添加的任务,并且中断正在处理的任务
4)Tidying:所有任务已终止,ctl记录为”任务数量”为0,ctl负责记录线程池的运行状态与活动线程数量
5)Terminated:线程池彻底终止
]]>1 | Collection 接口的接口 对象的集合(单列集合) |
List 以特定索引来存取元素,可以有重复元素。
Set 不能存放重复元素(用对象的equals()方法来区分元素是否重复)。
Map 保存键值对(key-value pair)映射,映射关系可以是一对一或多对一。
HashMap 是线程不安全的,效率高;HashTable 线程安全,效率低。
HashMap允许键和值是null,而Hashtable不允许键或者值是null
Chinese families attach great importance to their children’s education. Many parents believe that they should work hard to ensure that their children are well educated. Not noly are they perfectly willing to invest in their children’s education, but they also spend a lot of time urging their children to learn. Most parents hope that their children will attend a prestigious university. Due to the reform and opening up, more and more parents can send their children abroad to study or participate in international exchange programs to broaden their horizons. Through these efforts, they expect their children to grow healthily and contribute to the development and prosperity of the country.
中国家庭十分重视孩子的教育。许多父母认为应该努力工作,确保孩子受到良好教育。他们不仅非常情愿为孩子的教育投资,而且花很多时间督促他们学习。多数家长希望孩子能上名牌大学。由于改革开放,越来越多的家长能送孩子到国外学习或参与国际交流项目,以拓宽其视野。通过这些努力,他们期望孩子健康成长,为国家的发展和繁荣作出贡献。
]]>Dear Tom, Yours sincerely,
On hearing that you are planning to teach English in China and inquire which city to work in, I’d like to recommend our capital city Beijing to you, which is an international metropolis.
The reasons why I recommend Beijing can be listed as follows. First of all, there are a lot of English-speaking foreigners in Beijing, which could help you adapt to life here very quickly. Furthermore, as the capital of several dynasties, Beijing has a profound cultural background, so you can better experience the extensive and profound traditional Chinese culture in Beijing. Most importantly, parents in Beijing attach great importance to their children’s English learning and many people who work in multinational companies alse need to learn English.
I truly hope that you can come to Beijing to start your teaching life and I’m looking forward to your arrival. If you have any question about the city, please feel free to contact me for further information.
Li Ming
In recent years, more and more cities in China have begun to build subways. The development of subways can help reduce traffic congestion and air pollution in cities. The subway has the advantages of safety, speed and comfort. More and more people choose the subway as the main means of transportation to work or school every day. Nowadays, it is becoming more and more convenient to take the subway in China. In some cities, passengers can use a card or a mobile phone to take the subway. Many local elderly citizens can also take the subway for free.
近年来,中国有越来越多的城市开始建设地铁。发展地铁有助于减少城市的交通拥挤和空气污染。地铁具有安全、快捷和舒适的优点。越来越多的人选择地铁作为每天上班或上学的主要交通工具。如今,在中国乘坐地铁正变得越来越方便。在有些城市里,乘客只需用卡或手机就可以乘坐地铁。许多当地老年市民还可以免费乘坐地铁。
]]> As we all know, proficiency in speaking is necessary for us to become well-rounded communicators. However, the capacity to put words together in a meaningful way to reflect thoughts, opinions, and feelings is not something we’re born with but needs some techniques and practice.
Firstly, build confidence and concentrate on getting our message across, which help us gain the attention of the audience in return. Secondly, experiment with the things we know well instead of challenging ourselves with difficult words since fluency appears more important during oral communication. Lastly, create some opportunities to practice like narrating our daily life to ourselves or maintaining a regular chat with friends.
To sum up, only by being confident enough and using efficient methods can we enhance our speaking ability. Follow the steps to improve our speaking skills in order to achieve a higher standard in communication.
Buses used to be the main means of transportation for the Chinese people. In recent years, with the number of private cars increasing, cities have been facing increasingly severe traffic problems. To encourage more people to travel by bus, many cities have been making efforts to improve bus services. Bus facilities have been continuously renovated, and bus speed has also increased dramatically. However, bus fares are still cheap. Now, in most cities, many local elderly citizens can take a bus for free.
公交车曾是中国人出行的主要交通工具。近年来,由于私家车数量不断增多,城市的交通问题越来越严重。许多城市为了鼓励更多人乘坐公交车出行,一直在努力改善公交车的服务质量。车辆的设施不断更新,车速也有了显著提高。然而,公交车的票价却依然相当低廉。现在,在大多数城市,许多当地老年市民都可以免费乘坐公交车。
]]> As the most productive and communicative way to express ourselves, writing is attached great importance to in all ages. Whether we want to improve our writing skills as a creative writer or simply perfect our skills for schoolwork, we can take some steps to learn how to be a better writer.
Firstly, in order to make our writing creative and imaginative, brainstorming is one of the key elements to build up a unique topic. Don’t hesitate to take down all the ideas that come into our mind. Secondly, a good development of our writing is based on a clear structure or paragraph organization. Even a simple outline will help us see the big picture and save us hours of rewriting. Finally, diversity of vocabulary and grammar used in writing is highly recommended for the reason that one of the most common manifestations of bad writing is overuse or reuse of simple language.
To sum up, we should take practice and expand our knowledge to become a great writer. With ecough hard work and scientific techniques, we will amaze not only ourselves but also anybody else.
In the past, traveling by plane was unimaginable for most Chinese people. Today, with development of China’s economy and the improvement of people’s living standards, more and more Chinese people, including many farmers and migrant workers, can travel by air. They can fly to all major cities, and many other cities are also planning to build airports. Air services continue to improve, and there are often cheap flights. In recent years, the number of people choosing to travel by air during holidays has been increasing.
过去,乘飞机出行对大多数中国人来说是难以想象的。如今,随着经济的发展和生活水平的提高,越来越多的中国人包括许多农民和外出务工人员都能乘飞机出行。他们可以乘飞机到达所有大城市,还有很多城市也在筹建机场。航空服务不断改进,而且经常会有廉价机票。近年来,节假日期间选择乘飞机外出旅游的人数在不断增加。
]]>