20145236 《Java程序设计》第九周学习总结
教材学习内容总结
第十六章 整合数据库
JDBC简介
1.JDBC是java联机数据库的标准规范。它定义了一组标准类与接口,标准API中的接口会有数据库厂商操作,称为JDBC驱动程序。
2.JDBC标准主要分为两个部分:JDBC应用程序开发者接口和JDBC驱动程序开发者接口。应用程序需要联机数据库,其相关API主要在java.sql和javax.sql两个包中。
3.应用程序使用JDBC联机数据库的通用语法:
Connection conn = DriverManager.getConnection(……);
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM T_USER");
4.JDBC希望达到一个“写一个java程序,操作所有数据库”的目的。
5.驱动器按照操作方式可分为四种类型:
(1)JDBC-ODBC Bridge Driver :其在Microsoft系统上最为成熟。
缺点:ODBC在平台上先设定好,弹性不足;本身也有跨平台限制。
(2)Native API Driver:以原生方式,调用数据库提供的原生链接库。
缺点:有跨平台限制
优点:速度最快
(3)JDBC-Net Driver :将JDBC方法调用转换为特定的网络协议调用
优点:跨平台
缺点:速度慢,获得架构弹性是使用这种类型驱动程序的目的
(4)Native Protocol Driver :由数据库厂商直接提供
优点:跨平台 ——是最常见的驱动程序类型。
链接数据库
1.要连接数据库,必须在CLASSPATH中设定驱动程序JAR文档。
2.基本数据库操作相关JDBC接口或类是为位于java.sql包中。要取得数据库联机,需要进行几个动作:
(1)注册Driver对象
(2)取得Connection操作对象
(3)关闭Connection操作对象
3.注册Driver对象
使用JDBC要求加载.class文档的4种方式:
(1)使用Class.forName()
(2)自行建立Driver接口操作类的实例
(3)启动JVM时指定jdbc.drivers属性
(4)设定JAR中/services/java.sql.Driver文档
4.取得Connection操作对象
Connection接口的操作对象是数据库联机代表对象。其定义了数据库连接时的协议、子协议、数据源识别:
协议:子协议:数据源识别
协议:jdbc
子协议:桥接的驱动程序
数据源识别:标出数据库的地址、端口号、名称、用户、密码等信息。
注意:要使用中文存取必须给定参数useUnicode和characterEncoding。表明是否使用Unicode,并指定字符编码方式。
在XML配置文件中,不能直接写&符号,要写成&。
使用Statement、ResultSet
1.Statement是SQL描述的代表对象,可以使用executeUpdate()、executeQuery()等方法来执行SQL。
2.Statement的executeUpdate()方法用于SELECT等查询数据库的SQL,返回int结果,表示数据变动的笔数
3.Statement的executeQuery()方法返回java.sql.ResultSet对象,代表查询结果。
4.Statement的execute()方法用来执行SQL,返回true表示SQL执行将返回ResultSet作为查询结果。
使用PreparedStatement、CallableStatement
1.如果有些操作只是SQL语句当中某些参数会有所不同,其余的SQL子句皆相同,则可以使用java.sql.PreparedStatement。调用clearParametere()清除设置的参数,之后就能再次使用这个PreparedStatement实例。
2.使用PreparedStatement的好处:
(1)可以将SQL描述预编译为数据库的执行指令,执行速度可以快很多。
(2)防止SQL注入
3.如果撰写数据库的预存程序,并想要使用JDBC来调用,则可使用java.sql.CallableStatement。必须调用prepareCall()建立CallableStatement异常,可以使用registerOutParameter()注册输出参数。
使用DataSource取得联机
1.让MessageDAO依赖于java.sql.DataSource接口,可以通过其定义的getConnection()方法取得Connection。
2.日后要修改数据库服务器主机位置,为了打算重复利用Connection对象而想要加入联机池机制等情况,这个MessageDAO都不用修改。
3.联机相关信息可以使用.properties设定。
使用ResultSet卷动、更新数据
1.建立Statement或PreparedStatement实例时,可以指定结果集类型与并行方式。
createStatement(int resultSetType,int resultSetConcurrency) prepareStatement(String sql,int resultSetType,int resultSetConcurrency)
2.结果集类型resultSetType可以指定3种:
ResultSet.TYPE _ FORWARD _ ONLY(默认)
ResultSet.TYPE _ SCROLL _ INSENSITIVE
ResultSet.TYPE _ SCROLL _ SENSITIVE
3.更新设定resultSetConcurrency可以指定2种:
ResultSet.CONCURREADONLY(默认)
ResultSet.CONCUR_UPDATABLE
4.ResultSet进行数据修改的条件限制
必须选取单一表格
必须选取主键
必须选取所有NOT NULL的值
批次更新
1.批次更新的限制是,SQL不能使SELECT,否则会抛出异常。
2.要支持批次更新,必须在JDBC URL 上附加rewriteBatchedStatements = true 参数才有实际的作用。
Blob与Clob
1.BLOB用于存储大量的二进制数据,像是图档、影音档等;CLOB用于存储大量的文字数据。
2.可以把BLOB字段对应byte[]或输入输出串流。
交易简介
1.交易的四个基本原则:
原子性:若有一个步骤失败,必须撤回曾经执行过的动作,回到交易前的状态
一致性:交易作用的数据集合在交易前后必须一致
隔离行为:交易与交易之间,必须互不干扰
持续性:即使系统挂了,交易的结果也不能遗失
2.在交易管理时,仅想要撤回某个SQL执行点,则可以设定存储点。
3.数据库可设定指定的隔离行为,可设定常数为:
TRANSACTION _ UNCOMMITTED: 更新遗失
最低隔离层级,读取错误数据的几率太高,一般不会采用这种隔离层级。
TRANSACTION _ COMMITTED: 更新遗失+脏读
交易读取的数据必须是其他交易已确认的数据
TRANSACTION _ REPEATABLE _ READ: 更新遗失+脏读+无法重复读取
读取交易在确认前不阻止其他读取交易,,但会阻止其他更新交易。
TRANSACTION _ SERIALIZABLE: 更新遗失+脏读+无法重复读取+幻读 交易时若有数据不一致的疑虑,交易必须可以照顺序逐一进行。
4.通过JDBC得知数据库是否支持某个隔离行为设定,可以通过Connection的getMetaData()取得DatabaseMetadata对象,通过DatabaseMetadata的supportTransaction-IsolationLevel()得知是否支持某个隔离行为。
metadata简介
Metadata即“诠读数据的数据”,可以通过connection的getMetaData()方法取得DatabaseMetaData对象,可以取得数据库整体信息,而ResultSet表示查询到的数据,可以通过ResultSet的getMetaData()方法,取得ResultSetMetaData对象。
RowSet简介
1.javax.sql.RowSet接口,代表数据的列集合。可以对列集合进行增删改查。通过setCommand()设定查询指令,通过execute()执行查询指令以填充数据。
2.RowSet定义了五个标准列集合子接口:
(1)jdbcRowSet
是联机式的RowSet,保持与数据库的联机,可视为取得、操作RowSet的行为封装。
(2)CacheRowSet
是脱机式的RowSet,在查询并填充完数据后,会断开与数据源的联机
(3)FilteredRowSet
可以对列集合进行过滤,实现类似SQL中WHERE等条件式的功能。
(4)JoinRowSet
可以让你结合两个RowSet对象,可以通过setMatchColumn()指定要结合的行,然后使用addRowSet()来加入RowSet进行结合。
(5)WebRowSet
是CachedRowSet的子接口,不仅具备脱机操作,还能进行XM读写。
第十七章 反射与类加载器
运用反射
反射:.class文档反映了类基本信息,从Class等API取得类信息的方式称为反射。
Class与.class文档
1.java.lang.Class的实例代表Java应用程序运行时加载的.class文档,类、接口、Enum等编译过后,都会生成.class文档。Class类没有公开构造函数,实例时候JVM自动产生,每个.class文档加载时,JVM会自动生成对应的Class对象。
2.取得Class对象的方式:
(1)通过Object的getClass方法
(2)通过.class常量取得每个对象对应的Class对象
(3)如果是基本类型,可以使用对应的打包类加上.TYPE取得Class对象
3.默认首次加载.class文档时会执行静态区块。
4.类信息是在编译时期存储在.class文档,这是java支持执行运行时类型识别的方式。编译时期若使用到相关类,编译程序会检查对应的.class文档中记载的信息,以确定是否可完成编译。执行时期使用某类是,会先检查是否有对应的Class对象,如果没有,会加载对应的.class文档并生成对应的Class实例。
5.默认使用getClass()或.class取得的Class实例会是同一个对象。
使用Class.forName()
1.Class.forName()方法实现动态加载类,可用字符串指定类名称来获得类相关信息。
2.Class.forName()另一个版本可以让指定类名称、加载类时是否执行静态区块与类加载器。
static Class forName(String name,boolean initialize,ClassLoader loader)
从Class获得信息
Class对象代表加载的.class文档,取得Class对象后,就可以取得.class文档中加载的信息,像是包、构造函数、方法成员、数据成员等类型。
从Class建立对象
1.事先不知道类名称时,可以利用Class.forName()动态加载.class文档,取得Class对象之后,利用其newInstance()方法建立类实例。
Class clz = Class.forName(args[0]); Object obj = clz.newInstance();
2.若类定义有多个构造函数,也可以指定使用哪个构造函数生成对象,这必须在调用Class的getConstructor()方法时指定参数类型,取得代表构造函数的Constructor对象,利用Constructor的newInstance()指定创建时的参数值来建立对象。
3.生成数组:数组的Class实例是由JVM生成,也可以通过.class或getClass()取得Class实例。生成动态生成长度为10的java.util.ArrayList数组:
Class clz = java.util.ArrayList.class; Object obj = Array.newInstance(clz,10);
4.从第一个索引取得被手机对象实际的Class实例,此时就可以用它配合Array.newInstance()建立数组实例。
操作对象方法与成员
1.可以使用invoke()方法来动态调用指定的方法。
2.调用受把保护或私有方法,可以使用Class的getDeclaredMethod()取得方法,并在调用Method的setAccessible()时指定为true。
动态代理
1.在反射API中有个Proxy类,可动态建立接口的操作对象。
2.使用代理机制有两种代理方式:静态代理和动态代理。
3.静态代理
在静态代理实现中,代理对象与被代理对象必须实现同一接口,在代理对象中可以实现日志服务,必要时调用被代理对象,,可以这样使用代理对象:
Hello proxy = new HelloProxy(new HelloSpeaker()); proxy.hello("Justin");
创建代理对象HelloProxy时必须指定被代理对象HelloSpeaker,代理对象代理HelloSpeaker执行hello方法。
静态代理必须为个别接口操作出个别代理类,在应用程序行为复杂时,多个接口必须定义多个代理对象,操作与维护代理对象会有不少的负担。
4.动态代理
使用动态代理机制,可使用一个处理者代理多个接口的操作对象,处理者类必须操作java.lang.reflect.InvocationHandler接口。
使用Proxy.newPrxyInstance()方法建立代理对象,调用时必须指定类加载器,告知要代理的接口,以及接口上定义方法被调用时的处理者,Proxy.newPrxyInstance()方法底层会使用原生方式生成代理对象的Class实例,并利用它来生成代理对象。
了解类加载器
类加载器实际的职责就载入.class文档,JDK本身有默认的类加载器。
类加载器层级架构
1.Bootstrap Loader:产生Exrtended Loader和System Loader
Exrtended Loader:父加载器为Bootstrap Loader
System Loader:父加载器为Exrtended Loader
2.Bootstrap Loader通常由C撰写而成。若是Oracle的JDK,Bootstrap Loader会搜索系统参数sun.boot.class.path中指定位置的类,默认是JRE目录的classes中的.class文档,或lib目录中.jar文档里的类。
3.Exrtended Loader由Java撰写而成,会搜索系统参数java.ext.dirs中指定位置的类,默认是JRE目录libextclasses中的.class文档。
4.System Loader由Java撰写而成,会搜索系统参数java.class.path中指定位置的类,也就是CLASSPATH路径,默认是当前工作路径下的.class文档。
5.加载类是会以Bootstrap Loader->Exrtended Loader->System Loader的顺序寻找类,如果所有类加载器都找不到指定类,就会抛出java.lang.NoClassDefFoundError.
6.对null调用getParent()方法会抛出NullPointedException异常。
7.ClassLoader可以使用loadClass()方法加载类,使用localClass方法加载类时,默认不会执行静态区块,真正使用类建立实例时才会执行静态区块。
建立ClassLoader实例
1.使用URLClassLoader来产生新的类加载器,需要java.net.URL作为其参数来指定类加载的搜索路径。使用URLClassLoader的loadClass()方法加载指定类时,会先委托父加载器代为搜索。
2.由同一类加载器载入的.class文档,只会有一个Class实例。如果同一.class文档由两个不同的类加载器载入,则会有两份不同的Class实例。
3.path可以输入不在System Loader以上层级类加载器搜索路径的其他路径。
第十八章 自定义泛型、枚举与注释
自定义泛型
泛型定义:
(1)仅定义在方法上的泛型语法
(2)用来限制泛型可用类型的extends与super关键字
(3)?类型通配字符的使用
使用extends与?
1.若extends之后指定了类与接口,想再指定其他接口,可以使用&连接。
2.如果B是A的子类,而Node< B>可视为一种Node< A>,则称Node具有共变性或有弹性的。Java泛型不具有共变性,可以使用类型通配字符?与extends来声明变量,使其达到类似的共变性。
3.若声明?不搭配extends,则默认为? extends Object。
Node<?> node = null;//相当于Node<? extends Object>
4.java的泛型语法在执行时期实际上只会知道是Object类型,由于无法在执行时期获得类型信息,编译程序只能就编译时期看到的类型来做检查,因而造成以上谈及的限制。
使用super与?
1.如果B是A的子类,而Node< A>可视为一种Node< B>,则称为Node具有逆变性。Java泛型不具有逆变性,可以使用类型通配字符?与super来声明,使其达到类似的逆变性的效果。
2.若泛型类或接口不具共变性或逆变性,则称为不可变的或严谨的。
自定义枚举
了解java.lang.Enum类
1.直接撰写程序继承Enum类会被编译程序拒绝。
2.Enum是个抽象类,无法直接实例化,它操作了Comparable接口。Action的构造函数被声明为private,因此只能在Action类中调用。
3.在JDK1.4之前撰写的API,仍是使用interface定义常数作为枚举值。
4.Enum的equals()方法与hashCode()方法基本上继承了Object的行为,但被标示为final。由于标示为final,所以定义枚举是,不能重新操作equals()与hashCode(),这是因为枚举成员,在JVM中智慧存在单一实例,Object定义的equals()与hashCode()作为对象相等性比较是适当的定义。
enum高级运用
1.values()方法,将内部维护Action枚举实例的数据复制后返回。如果想要知道有哪些枚举成员,就可以使用这个方法。
2.Enum类可以自行定义构造函数,但不得为公开构造函数,也不可以在构造函数中调用super()。
3.在enum中调用构造函数比较特别,直接在枚举成员后加上括号,就可以指定构造函数需要的自变量。
4.在static区块中,编译程序仍自行维护name与ordinal的值,接着才是调用自定义构造函数时传入的value值。
5.特定值类本体语法:在枚举成员后,直接加上{}操作Command的execute()方法,这代表着每个枚举实例都会有不同的execute()曹组欧,在职责分配上,比switch的方式清楚许多。特定值类本体语法不仅在操作接口时可以使用,,也可以运用在重新定义父类方法。
关于注释
在原始码中使用注释,对编译程序提供额外编译提示,或提供应用程序执行时期可读取的组态信息。注释可以仅用于原始码,编译后留在.class文档仅供编译程序读取或开放执行时期读取。
常用标准注释
1.@Override
就是标准注释,被注释的方法必须是父类或接口中已定义的方法,请编译程序协助是否真的为重新定义方法。
2.@Deprecated
如果某个方法原先存在与API中,后来不建议再使用,可以在该方法上注释。若有用户后续想调用或重新定义这个方法,编译程序会提出警告。对于支持泛型的API,建议明确指定泛型真正类型,如果没有指定,编译程序会提出警告。
3.@SuppressWarnings
指定抑制unchecked的警告产生:
@SuppressWarnings(value={"unchecked"})
4.@SafeVarargs
表明开发人员确定避免了heap pollution问题。heap pollution问题就是编译程序无法检查执行时期的类型错误,无法具体确认自变量类型。
5.@FunctionalInterface
让编译程序可协助检查interface是否可做为lambda的目标类型
自定义注释类型
1.标示注释:就是注释名称本身就是信息,对编译程序或应用程序来说,主要是检查是否有注释出现,并作出对应的动作。
2.相关规则:
(1)如果注释名称本身无法提供足够信息,设置单值注释
(2)注释属性也可以用数组形式指定。
(3)在定义注释属性时,如果属性名称为value,则可以省略属性名称,直接指定值。
(4)对成员设定默认值,使用default关键字即可。
(5)要设定数组默认值,可以在default之后加上{},必要时{}中可放置元素值。
3.定义注释时,可使用java.lang.annotation.Target限定时可指定java.lang.annotation.ElementType的枚举值。
4.在制作JavaDoc文件时,默认不会将注释数据加入文件中,如果想要将注释数据加入文件,可以使用java.lang.annotation.Documented。
5.默认父类设定的注释,不会被继承至子类,在定义注释时,设定java.lang.annotation.Inherited注释,就可以让注释被子类继承。
JDK8标注增强功能
1.ElementType的枚举成员是用来限定哪个声明位置可以进行标注。在JDK8中,增加了两个枚举成员TYPE _PARAMETER、TYPE _USE。
2.ElementType.TYPE _ USE可用于标注在各式类型,一个标注如果被设定为ElementType.TYPE_USE,只要是类型名称,都可以进行标注。
3.@Repeatable
可以让你在同一个位置重复相同标注
4.@Filters
作为收集重复标注信息的容器,而每个@Filters储存各自指定的字符串值。
执行时期读取注释信息
1.自定义注释,默认会将注释信息存储于.class文档,可被编译程序或位码分析工具读取,但执行时期无法读取注释信息,在执行时期读取注释信息,可以使用java.lang.annotation.Retention搭配java.lang.annotation.RetentionPolicy枚举指定。
2.RetentionPolicy为RUNTIME的时机,在于让注释在执行时期提供应用程序信息,可使用java.lang.reflect.AnnotatedElement接口操作对象取得注释信息。
3.JDK 8中新增了getDeclaredAnnotation()、getDeclaredAnnotationsByType()、getAnnotationsByType()三个方法。
getDeclaredAnnotation()可以让你取回指定的标注,在指定@Repeatable的标注时,会寻找收集重复标注的容器。
getDeclaredAnnotationsByType()、getAnnotationsByType()就不会处理@Repeatable的标记。
代码调试中的问题和解决过程
import java.util.logging.*;
public class LoggerTest {
public static void main(String[] args) {
Logger log = Logger.getLogger("lavasoft" );
log.setLevel(Level.INFO);
Logger log1 = Logger.getLogger("lavasoft" );
System.out.println(log==log1); //true
Logger log2 = Logger.getLogger("lavasoft.blog" );
log2.setLevel(Level.WARNING);
log.info("aaa" );
log2.info("bbb" );
log2.fine("fine" );
}
}
- 运行结果:
当把log2.setLevel(Level.WARNING);这一行注释掉之后,发现运行结果变成了:
本周代码托管截图
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 200/200 | 2/2 | 20/20 | |
第二周 | 300/500 | 2/4 | 18/38 | |
第三周 | 500/1000 | 1/5 | 22/60 | |
第四周 | 500/1500 | 1/6 | 30/90 | |
第五周 | 500/2000 | 1/7 | 20/110 | |
第六周 | 500/2500 | 2/9 | 20/130 | |
第七周 | 500/3000 | 2/11 | 20/150 | |
第八周 | 300/3300 | 2/13 | 20/170 | |
第九周 | 300/3600 | 2/15 | 20/190 |