(27)回复泛型,注解、日志组件、枚举在实际项目中的使用
---恢复内容开始---
1、泛型
掌握的知识:基本用法、泛型擦除、泛型类/泛型方法/泛型接口、泛型关键字、反射泛型(案例)!
a、概述:泛型是JDK1.5以后才有的, 可以在编译时期进行类型检查,且可以避免频繁类型转化!
1 // 运行时期异常
2 @Test
3 public void testGeneric() throws Exception {
4 // 集合的声明
5 List list = new ArrayList();
6 list.add("China");
7 list.add(1);
8
9 // 集合的使用
10 String str = (String) list.get(1);
11
12 }
13
14 // 使用泛型
15 @Test
16 public void testGeneric2() throws Exception {
17 // 声明泛型集合的时候指定元素的类型
18 List<String> list = new ArrayList<String>();
19 list.add("China");
20 // list.add(1);// 编译时期报错
21
22 String str = list.get(1);
23 }
泛型擦除,泛型只在编译时期有效,编译后的字节码文件中不存在有泛型信息!
1 /*
2 * 泛型擦除实例
3
4 public void save(List<Person> p){
5 }
6 public void save(List<Dept> d){ // 报错: 与上面方法编译后一样
7 }
8 */
泛型的写法
1 // 泛型写法
2 @Test
3 public void testGeneric3() throws Exception {
4 // 声明泛型集合,集合两端类型必须一致
5 List<Object> list = new ArrayList<Object>();
6 List<String> list1 = new ArrayList<String>();
7 List list2 = new ArrayList<String>();
8 List<Integer> list3 = new ArrayList();
9
10 // 错误
11 //List<Object> list4 = new ArrayList<String>();
12 // 错误: 泛型类型必须是引用类型,不能为基本类型
13 List<int> list5 = new ArrayList<int>();
14 }
b. 泛型方法/泛型类/泛型接口
作用:
设计公用的类、方法,对公用的业务实现进行抽取!
使程序更灵活!
1. 泛型方法:
1 public class GenericDemo {
2
3 // 定义泛型方法
4 public <K,T> T save(T t,K k) {
5 return null;
6 }
7
8 // 测试方法
9 @Test
10 public void testMethod() throws Exception {
11 // 使用泛型方法: 在使用泛型方法的时候,确定泛型类型
12 save(1.0f, 1);
13 }
14 }
2. 泛型类:
1 public class GenericDemo<T> {
2
3 // 定义泛型方法
4 public <K> T save(T t,K k) {
5 return null;
6 }
7
8 public void update(T t) {
9
10 }
11
12 // 测试方法
13 @Test
14 public void testMethod() throws Exception {
15
16 // 泛型类: 在创建爱泛型类对象的时候,确定类型
17 GenericDemo<String> demo = new GenericDemo<String>();
18 demo.save("test", 1);
19 }
20 }
3. 泛型接口:
1 /**
2 * 泛型接口
3 * @author Jie.Yuan
4 *
5 * @param <T>
6 */
7 public interface IBaseDao<T> {
8 void save(T t );
9 void update(T t );
10 }
11
12 =======================================
13 泛型接口类型确定: 实现泛型接口的类也是抽象,那么类型在具体的实现中确定或创建泛型类的时候确定
14 public class BaseDao<T> implements IBaseDao<T> {
15 ======================================
16 泛型=接口类型确定: 在业务实现类中直接确定接口的类型
17 public class PersonDao implements IBaseDao<Person>{
18 }
c. 泛型关键字
泛型中:
? 指定只是接收值
extends 元素的类型必须继承自指定的类
super 元素的类型必须是指定的类的父类
关键字 : ?
1 /**
2 * 泛型, 涉及到一些关键字
3 *
4 * Ctrl + shift + R 查看当前项目中类
5 * Ctrl + shift + T 查看源码jar包中的类
6 * @author Jie.Yuan
7 *
8 */
9 public class App_extends_super {
10
11 //只带泛型特征的方法
12 public void save(List<?> list) {
13 // 只能获取、迭代list; 不能编辑list
14 }
15
16 @Test
17 public void testGeneric() throws Exception {
18
19 // ? 可以接收任何泛型集合, 但是不能编辑集合值; 所以一般在方法参数中用
20 List<?> list = new ArrayList<String>();
21 //list.add("");// 报错
22 }
23 }
关键字 : extends 【上限】
1 public class App_extends_super {
2
3
4 /**
5 * list集合只能处理 Double/Float/Integer等类型
6 * 限定元素范围:元素的类型要继承自Number类 (上限)
7 * @param list
8 */
9 public void save(List<? extends Number> list) {
10 }
11
12 @Test
13 public void testGeneric() throws Exception {
14 List<Double> list_1 = new ArrayList<Double>();
15 List<Float> list_2 = new ArrayList<Float>();
16 List<Integer> list_3 = new ArrayList<Integer>();
17
18 List<String> list_4 = new ArrayList<String>();
19
20 // 调用
21 save(list_1);
22 save(list_2);
23 save(list_3);
24 //save(list_4);
25 }
26 }
关键字 : super 【下限】
1 /**
2 * 泛型, 涉及到一些关键字
3 *
4 * Ctrl + shift + R 查看当前项目中类
5 * Ctrl + shift + T 查看源码jar包中的类
6 * @author Jie.Yuan
7 *
8 */
9 public class App_super {
10
11
12 /**
13 * super限定元素范围:必须是String父类 【下限】
14 * @param list
15 */
16 public void save(List<? super String> list) {
17 }
18
19 @Test
20 public void testGeneric() throws Exception {
21 // 调用上面方法,必须传入String的父类
22 List<Object> list1 = new ArrayList<Object>();
23 List<String> list2 = new ArrayList<String>();
24
25 List<Integer> list3 = new ArrayList<Integer>();
26 //save(list3);
27 }
28 }
d. 泛型的反射
案例,设置通用方法,会用到反射泛型!下面有简单代码
步骤:
1. 案例分析 / 实现
2. 涉及知识点(jdk api)
3. 优化 / 反射泛型
反射泛型涉及API:
Student 类型的表示
Id name
ParameterizedType 参数化类型的表示
ArrayList<String>();
Type 接口,任何类型默认的接口!
包括: 引用类型、原始类型、参数化类型
List<String> list = new ArrayList<String>();
泛型集合: list
集合元素定义:new ArrayList<String>(); 中的String
参数化类型: ParameterizedType
即:“ArrayList<String> ” 为参数化类型
public class AdminDao extends BaseDao<Admin> {}
public class AccountDao extends BaseDao<Account> {}
/**
* 所有dao的公用的方法,都在这里实现
* @author Jie.Yuan
*
*/
public class BaseDao<T>{
// 保存当前运行类的参数化类型中的实际的类型
private Class clazz;
// 表名
private String tableName;
// 构造函数: 1. 获取当前运行类的参数化类型; 2. 获取参数化类型中实际类型的定义(class)
public BaseDao(){
// this 表示当前运行类 (AccountDao/AdminDao)
// this.getClass() 当前运行类的字节码(AccountDao.class/AdminDao.class)
// this.getClass().getGenericSuperclass(); 当前运行类的父类,即为BaseDao<Account>
// 其实就是“参数化类型”, ParameterizedType
Type type = this.getClass().getGenericSuperclass();
// 强制转换为“参数化类型” 【BaseDao<Account>】
ParameterizedType pt = (ParameterizedType) type;
// 获取参数化类型中,实际类型的定义 【new Type[]{Account.class}】
Type types[] = pt.getActualTypeArguments();
// 获取数据的第一个元素:Accout.class
clazz = (Class) types[0];
// 表名 (与类名一样,只要获取类名就可以)
tableName = clazz.getSimpleName();
}
/**
* 主键查询
* @param id 主键值
* @return 返回封装后的对象
*/
public T findById(int id){
/*
* 1. 知道封装的对象的类型
* 2. 表名【表名与对象名称一样, 且主键都为id】
*
* 即,
* ---》得到当前运行类继承的父类 BaseDao<Account>
* ----》 得到Account.class
*/
String sql = "select * from " + tableName + " where id=? ";
try {
return JdbcUtils.getQuerrRunner().query(sql, new BeanHandler<T>(clazz), id);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/**
* 查询全部
* @return
*/
public List<T> getAll(){
String sql = "select * from " + tableName ;
try {
return JdbcUtils.getQuerrRunner().query(sql, new BeanListHandler<T>(clazz));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
这里额外回顾反射,直接看代码:
1 public class Admin {
2
3 // Field
4 private int id = 1000;
5 private String name = "匿名";
6
7 // Constructor
8 public Admin(){
9 System.out.println("Admin.Admin()");
10 }
11 public Admin(String name){
12 System.out.println("Admin.Admin()" + name);
13 }
14
15 // Method
16 public int getId() {
17 return id;
18 }
19 public void setId(int id) {
20 this.id = id;
21 }
22 public String getName() {
23 return name;
24 }
25 public void setName(String name) {
26 this.name = name;
27 }
28
29 }
30
31
32 // 反射技术
33 public class App {
34
35 // 1. 创建对象
36 @Test
37 public void testInfo() throws Exception {
38 // 类全名
39 String className = "cn.itcast.c_reflect.Admin";
40 // 得到类字节码
41 Class<?> clazz = Class.forName(className);
42
43 // 创建对象1: 默认构造函数简写
44 //Admin admin = (Admin) clazz.newInstance();
45
46 // 创建对象2: 通过带参数构造器创建对象
47 Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
48 Admin admin = (Admin) constructor.newInstance("Jack");
49
50 }
51 @Test
52 //2. 获取属性名称、值
53 public void testField() throws Exception {
54
55 // 类全名
56 String className = "cn.itcast.c_reflect.Admin";
57 // 得到类字节码
58 Class<?> clazz = Class.forName(className);
59 // 对象
60 Admin admin = (Admin) clazz.newInstance();
61
62 // 获取所有的属性名称
63 Field[] fs = clazz.getDeclaredFields();
64 // 遍历:输出每一个属性名称、值
65 for (Field f : fs) {
66 // 设置强制访问
67 f.setAccessible(true);
68 // 名称
69 String name = f.getName();
70 // 值
71 Object value = f.get(admin);
72
73 System.out.println(name + value);
74 }
75 }
76
77 @Test
78 //3. 反射获取方法
79 public void testMethod() throws Exception {
80
81 // 类全名
82 String className = "cn.itcast.c_reflect.Admin";
83 // 得到类字节码
84 Class<?> clazz = Class.forName(className);
85 // 对象
86 Admin admin = (Admin) clazz.newInstance();
87
88 // 获取方法对象 public int getId() {
89 Method m = clazz.getDeclaredMethod("getId");
90 // 调用方法
91 Object r_value = m.invoke(admin);
92
93 System.out.println(r_value);
94 }
95
96 }
3. 注解
概述
注解与注释,
注解,告诉编译器如何运行程序!
注释, 给程序员阅读,对编译、运行没有影响;
注解作用,
1. 告诉编译器如何运行程序;
2. 简化(取代)配置文件 【案例后再看】
常用的注解,
// 重写父类的方法 @Override public String toString() { return super.toString(); } // 抑制编译器警告 @SuppressWarnings({"unused","unchecked"}) private void save() { List list = null; } // 标记方法以及过时 @Deprecated private void save1() { }
自定义注解
通过自定义注解,可以给类、字段、方法上添加描述信息!
a、注解基本写法
/**
* 自定义注解 (描述一个作者)
* @author Jie.Yuan
*
*/
public @interface Author {
/**
* 注解属性
* 1. 修饰为默认或public
* 2. 不能有主体
*/
String name();
int age();
}
//使用自定义注解
@Author(name = "Jet", age = 30)
public void save() {
}
b.带默认值的注解
1 public @interface Author {
2
3 /**
4 * 注解属性
5 * 1. 修饰为默认或public
6 * 2. 不能有主体
7 */
8 String name();
9 int age() default 30; // 带默认值的注解; 使用的时候就可以不写此属性值
10 }
c、默认名臣的注解
注解属性的默认默认名称是value
1 public @interface Author { 2 // 如果注解名称为value,使用时候可以省略名称,直接给值 3 // (且注解只有一个属性时候才可以省略名称) 4 String value(); 5 } 6 7 使用 8 @Author("Jet") 9 @Author(value = "Jet")
注解属性类型为数组:
public @interface Author {
String[] value() default {"test1","test2"};
}
使用:
@Author({“”,“”})
public void save() {
}
元注解
元注解,表示注解的注解!
指定注解的可用范围:
@Target({
TYPE, 类
FIELD, 字段
METHOD, 方法
PARAMETER, 参数
CONSTRUCTOR, 构造器
LOCAL_VARIABLE 局部变量
})
// 元注解 - 2. 指定注解的声明周期
@Retention(RetentionPolicy.SOURCE) 注解只在源码级别有效
@Retention(RetentionPolicy.CLASS) 注解在字节码即别有效 默认值
@Retention(RetentionPolicy.RUNTIME) 注解在运行时期有效
注解的反射
1 @Id
2 @Author(remark = "保存信息!!!", age = 19)
3 public void save() throws Exception {
4 // 获取注解信息: name/age/remark
5
6
7 // 1. 先获取代表方法的Method类型;
8 Class clazz = App_2.class;
9 Method m = clazz.getMethod("save");
10
11 // 2. 再获取方法上的注解
12 Author author = m.getAnnotation(Author.class);
13 // 获取输出注解信息
14 System.out.println(author.authorName());
15 System.out.println(author.age());
16 System.out.println(author.remark());
17 }
4. 注解,优化BaseDao的代码
当表名与数据库名称不一致、 字段与属性不一样、主键不叫id, 上面的BaseDao不能用!
这是,
可以通过配置文件(XML) 解决!
注解:
简化XML配置, 程序处理非常方便!
(不便于维护: 例如修改字段名,要重新编译!)
XML
便于维护! 需要些读取代码!
1 当表名与数据库名称不一致、 字段与属性不一样、主键不叫id, 上面的BaseDao不能用! 2 这是, 3 可以通过配置文件(XML) 解决! 4 5 6 注解: 7 简化XML配置, 程序处理非常方便! 8 (不便于维护: 例如修改字段名,要重新编译!) 9 10 XML 11 便于维护! 需要些读取代通过反射注解=ViewCode】
5. Log4J日志组件
程序中为什么用日志组件?
简单来说,为了项目后期部署上线后的维护、错误排查!
Log4j, log for java, 开源的日志组件!
使用步骤:
1. 下载组件,引入jar文件;
log4j-1.2.11.jar
2. 配置 : src/log4j.properties
3. 使用
# 通过根元素指定日志输出的级别、目的地(目的地可以同时指定多个~): # 日志输出优先级: debug < info < warn < error log4j.rootLogger=info,console,file ############# 日志输出到控制台 ############# # 日志输出到控制台使用的api类 log4j.appender.console=org.apache.log4j.ConsoleAppender # 指定日志输出的格式: 灵活的格式 log4j.appender.console.layout=org.apache.log4j.PatternLayout # 具体格式内容 log4j.appender.console.layout.ConversionPattern=%d %p %c.%M()-%m%n ############# 日志输出到文件 ############# log4j.appender.file=org.apache.log4j.RollingFileAppender # 文件参数: 指定日志文件路径 log4j.appender.file.File=../logs/MyLog.log # 文件参数: 指定日志文件最大大小 log4j.appender.file.MaxFileSize=5kb # 文件参数: 指定产生日志文件的最大数目 log4j.appender.file.MaxBackupIndex=100 # 日志格式 log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d %c.%M()-%m%n
下面是使用log4j的示例代码
1 public class App {
2
3 Log log = LogFactory.getLog(App.class);
4
5 @Test
6 public void save() {
7 try {
8 log.info("保存: 开始进入保存方法");
9
10 int i = 1/0;
11
12 log.info("保存: 执行保存结束,成功");
13 } catch (Exception e) {
14
15 log.error("执行App类Save()方法出现异常!"); // 异常
16
17 e.printStackTrace();
18 }
19 }
20
21 /*
22 * 思考: 日志的输出级别作用?
23 * ----> 控制日志输出的内容。
24 */
25 @Test
26 public void testLog() throws Exception {
27 // 输出不同级别的提示
28 log.debug("调试信息");
29 log.info("信息提示");
30 log.warn("警告");
31 log.error("异常");
32
33 }
34 }
35 public class Index extends HttpServlet {
36
37
38 Log log = LogFactory.getLog(Index.class);
39
40 public void doGet(HttpServletRequest request, HttpServletResponse response)
41 throws ServletException, IOException {
42 try {
43 log.info("进入servlet");
44 int i = 1/0;
45 log.info("进入servlet结束");
46 } catch (Exception e) {
47 log.error("计算异常!",e);
48 }
49 }
50 }
6、枚举完全讲解
6.1基本概念
l 为什么使用枚举类
n 一些方法在运行的过程中所需要的值不是任意的,而是在一个范围中。在jdk1.5出现之前解决的方案,就是自己实现一个带有枚举功能的类。
l Jdk1.5新增的enum关键字用于定义一个枚举类,一旦定义一个枚举类之后,这个枚举类及自动继承了,java类库中的Enum
自定义枚举类
/**
* 自己手动的创建一个 枚举类
* 1、私有化,private,隐藏构造方法不让别人使用
* 2、蒂尼几个public static final的实例,给外界使用
*
* 手动实现的一个枚举类就是 Grade
* 为Student类中定义一个,Grade,当为这个grade复制的时候,只能是Grade中定义好的几个public static final型的值,否则就会出现变异问题
* 一旦定义其他值就会出错,于是就达到了,枚举的作用,但是这样做还是比较麻烦的,于是,java中退出了一个枚举类
* @author YUCHEN
*
*/
class Grade{
private Grade(){
}
public static final Grade A = new Grade();
public static final Grade B = new Grade();
public static final Grade C = new Grade();
public static final Grade D = new Grade();
public static final Grade E = new Grade();
}
class Student{
private Grade grade; //考试等级
public Grade getGrade()
{
return this.grade;
}
public void setGrade(Grade grade){
this.grade = grade;
}
}
6.2默认构造函数枚举
按下面步骤,读下面案例:
1 // 1. 枚举类定义 2 enum Grade{ 3 A,B,C,D,E; 4 } 5 class Student{ 6 private String name; 7 8 // 2. 使用枚举类型 9 private Grade grade; //ABCDE 10 public Grade getGrade() { 11 return grade; 12 } 13 public void setGrade(Grade grade) { 14 this.grade = grade; 15 } 16 } 17 public class Demo1 { 18 public static void main(String[] args) { 19 Student stu = new Student(); 20 // 3. 给枚举类型赋值,只能是枚举类定义的值(第1步中所定义) 21 stu.setGrade(Grade.A); 22 23 System.out.println(stu.getGrade()); 24 } 25 }
上述定义的枚举为默认构造函数枚举, 也可以这样
1 // 1. 枚举类定义
2 enum Grade{
3 A(),B(),C(),D(),E();
4 // 必须为私有
5 private Grade(){
6 }
7 }
此时,Grade类中有一个默认无参数构造函数
6.3有参构造函数
1 // 1. 带参数构造函数的枚举定义
2 enum Grade{
3 A("100-90"),B("90-80"),C("80-70"),D("70-60"),E("60-0");
4
5 private String value;
6 // 定义get方法返回数据
7 public String getValue() {
8 return value;
9 }
10
11 private Grade(String value) {
12 this.value = value;
13 }
14 }
15 class Student{
16 // 2. 使用枚举类型
17 private Grade grade; //ABCDE
18 public Grade getGrade() {
19 return grade;
20 }
21 public void setGrade(Grade grade) {
22 this.grade = grade;
23 }
24 }
25 public class Demo1 {
26 public static void main(String[] args) {
27 Student stu = new Student();
28 // 3. 给枚举类型赋值
29 stu.setGrade(Grade.A);
30 // 输出对应的“分数”
31 System.out.println(stu.getGrade().getValue());
32 }
33 }
枚举类中抽象方法的定义
1 // 1. 带参数构造函数的枚举定义 2 // 并且需要返回更多的信息, 优秀,良好,好,一般,差 3 enum Grade{ 4 A("100-90"){ 5 public String getLocalStr() { 6 return "优秀"; 7 } 8 } 9 10 ,B("90-80"){ 11 public String getLocalStr() { 12 return "良好"; 13 } 14 } 15 16 ,C("80-70"){ 17 public String getLocalStr() { 18 return "好"; 19 } 20 } 21 22 ,D("70-60"){ 23 public String getLocalStr() { 24 return "一般"; 25 } 26 } 27 28 ,E("60-0"){ 29 public String getLocalStr() { 30 return "差"; 31 } 32 }; 33 34 private String value; 35 // 定义get方法返回数据 36 public String getValue() { 37 return value; 38 } 39 40 private Grade(String value) { 41 this.value = value; 42 } 43 44 // 返回成绩段对应的“描述”, 需要每个对象重新实现次方法 45 public abstract String getLocalStr(); 46 } 47 class Student{ 48 // 2. 使用枚举类型 49 private Grade grade; //ABCDE 50 public Grade getGrade() { 51 return grade; 52 } 53 public void setGrade(Grade grade) { 54 this.grade = grade; 55 } 56 } 57 public class Demo1 { 58 public static void main(String[] args) { 59 Student stu = new Student(); 60 // 3. 给枚举类型赋值 61 stu.setGrade(Grade.A); 62 // 输出对应的“分数” 63 System.out.println(stu.getGrade().getValue()); 64 65 // 输出描述 66 System.out.println(stu.getGrade().getLocalStr()); 67 } 68 }
给我的体验忒儿的
---恢复内容结束---
1、泛型
掌握的知识:基本用法、泛型擦除、泛型类/泛型方法/泛型接口、泛型关键字、反射泛型(案例)!
a、概述:泛型是JDK1.5以后才有的, 可以在编译时期进行类型检查,且可以避免频繁类型转化!
1 // 运行时期异常
2 @Test
3 public void testGeneric() throws Exception {
4 // 集合的声明
5 List list = new ArrayList();
6 list.add("China");
7 list.add(1);
8
9 // 集合的使用
10 String str = (String) list.get(1);
11
12 }
13
14 // 使用泛型
15 @Test
16 public void testGeneric2() throws Exception {
17 // 声明泛型集合的时候指定元素的类型
18 List<String> list = new ArrayList<String>();
19 list.add("China");
20 // list.add(1);// 编译时期报错
21
22 String str = list.get(1);
23 }
泛型擦除,泛型只在编译时期有效,编译后的字节码文件中不存在有泛型信息!
1 /*
2 * 泛型擦除实例
3
4 public void save(List<Person> p){
5 }
6 public void save(List<Dept> d){ // 报错: 与上面方法编译后一样
7 }
8 */
泛型的写法:
1 // 泛型写法
2 @Test
3 public void testGeneric3() throws Exception {
4 // 声明泛型集合,集合两端类型必须一致
5 List<Object> list = new ArrayList<Object>();
6 List<String> list1 = new ArrayList<String>();
7 List list2 = new ArrayList<String>();
8 List<Integer> list3 = new ArrayList();
9
10 // 错误
11 //List<Object> list4 = new ArrayList<String>();
12 // 错误: 泛型类型必须是引用类型,不能为基本类型
13 List<int> list5 = new ArrayList<int>();
14 }
b. 泛型方法/泛型类/泛型接口
作用:
设计公用的类、方法,对公用的业务实现进行抽取!
使程序更灵活!
1. 泛型方法:
1 public class GenericDemo {
2
3 // 定义泛型方法
4 public <K,T> T save(T t,K k) {
5 return null;
6 }
7
8 // 测试方法
9 @Test
10 public void testMethod() throws Exception {
11 // 使用泛型方法: 在使用泛型方法的时候,确定泛型类型
12 save(1.0f, 1);
13 }
14 }
2. 泛型类:
1 public class GenericDemo<T> {
2
3 // 定义泛型方法
4 public <K> T save(T t,K k) {
5 return null;
6 }
7
8 public void update(T t) {
9
10 }
11
12 // 测试方法
13 @Test
14 public void testMethod() throws Exception {
15
16 // 泛型类: 在创建爱泛型类对象的时候,确定类型
17 GenericDemo<String> demo = new GenericDemo<String>();
18 demo.save("test", 1);
19 }
20 }
3. 泛型接口:
1 /**
2 * 泛型接口
3 * @author Jie.Yuan
4 *
5 * @param <T>
6 */
7 public interface IBaseDao<T> {
8 void save(T t );
9 void update(T t );
10 }
11
12 =======================================
13 泛型接口类型确定: 实现泛型接口的类也是抽象,那么类型在具体的实现中确定或创建泛型类的时候确定
14 public class BaseDao<T> implements IBaseDao<T> {
15 ======================================
16 泛型=接口类型确定: 在业务实现类中直接确定接口的类型
17 public class PersonDao implements IBaseDao<Person>{
18 }
c. 泛型关键字
泛型中:
? 指定只是接收值
extends 元素的类型必须继承自指定的类
super 元素的类型必须是指定的类的父类
关键字 : ?
1 /**
2 * 泛型, 涉及到一些关键字
3 *
4 * Ctrl + shift + R 查看当前项目中类
5 * Ctrl + shift + T 查看源码jar包中的类
6 * @author Jie.Yuan
7 *
8 */
9 public class App_extends_super {
10
11 //只带泛型特征的方法
12 public void save(List<?> list) {
13 // 只能获取、迭代list; 不能编辑list
14 }
15
16 @Test
17 public void testGeneric() throws Exception {
18
19 // ? 可以接收任何泛型集合, 但是不能编辑集合值; 所以一般在方法参数中用
20 List<?> list = new ArrayList<String>();
21 //list.add("");// 报错
22 }
23 }
关键字 : extends 【上限】
1 public class App_extends_super {
2
3
4 /**
5 * list集合只能处理 Double/Float/Integer等类型
6 * 限定元素范围:元素的类型要继承自Number类 (上限)
7 * @param list
8 */
9 public void save(List<? extends Number> list) {
10 }
11
12 @Test
13 public void testGeneric() throws Exception {
14 List<Double> list_1 = new ArrayList<Double>();
15 List<Float> list_2 = new ArrayList<Float>();
16 List<Integer> list_3 = new ArrayList<Integer>();
17
18 List<String> list_4 = new ArrayList<String>();
19
20 // 调用
21 save(list_1);
22 save(list_2);
23 save(list_3);
24 //save(list_4);
25 }
26 }
关键字 : super 【下限】
1 /**
2 * 泛型, 涉及到一些关键字
3 *
4 * Ctrl + shift + R 查看当前项目中类
5 * Ctrl + shift + T 查看源码jar包中的类
6 * @author Jie.Yuan
7 *
8 */
9 public class App_super {
10
11
12 /**
13 * super限定元素范围:必须是String父类 【下限】
14 * @param list
15 */
16 public void save(List<? super String> list) {
17 }
18
19 @Test
20 public void testGeneric() throws Exception {
21 // 调用上面方法,必须传入String的父类
22 List<Object> list1 = new ArrayList<Object>();
23 List<String> list2 = new ArrayList<String>();
24
25 List<Integer> list3 = new ArrayList<Integer>();
26 //save(list3);
27 }
28 }
d. 泛型的反射
案例,设置通用方法,会用到反射泛型!下面有简单代码
步骤:
1. 案例分析 / 实现
2. 涉及知识点(jdk api)
3. 优化 / 反射泛型
反射泛型涉及API:
Student 类型的表示
Id name
ParameterizedType 参数化类型的表示
ArrayList<String>();
Type 接口,任何类型默认的接口!
包括: 引用类型、原始类型、参数化类型
List<String> list = new ArrayList<String>();
泛型集合: list
集合元素定义:new ArrayList<String>(); 中的String
参数化类型: ParameterizedType
即:“ArrayList<String> ” 为参数化类型
public class AdminDao extends BaseDao<Admin> {}
public class AccountDao extends BaseDao<Account> {}
/**
* 所有dao的公用的方法,都在这里实现
* @author Jie.Yuan
*
*/
public class BaseDao<T>{
// 保存当前运行类的参数化类型中的实际的类型
private Class clazz;
// 表名
private String tableName;
// 构造函数: 1. 获取当前运行类的参数化类型; 2. 获取参数化类型中实际类型的定义(class)
public BaseDao(){
// this 表示当前运行类 (AccountDao/AdminDao)
// this.getClass() 当前运行类的字节码(AccountDao.class/AdminDao.class)
// this.getClass().getGenericSuperclass(); 当前运行类的父类,即为BaseDao<Account>
// 其实就是“参数化类型”, ParameterizedType
Type type = this.getClass().getGenericSuperclass();
// 强制转换为“参数化类型” 【BaseDao<Account>】
ParameterizedType pt = (ParameterizedType) type;
// 获取参数化类型中,实际类型的定义 【new Type[]{Account.class}】
Type types[] = pt.getActualTypeArguments();
// 获取数据的第一个元素:Accout.class
clazz = (Class) types[0];
// 表名 (与类名一样,只要获取类名就可以)
tableName = clazz.getSimpleName();
}
/**
* 主键查询
* @param id 主键值
* @return 返回封装后的对象
*/
public T findById(int id){
/*
* 1. 知道封装的对象的类型
* 2. 表名【表名与对象名称一样, 且主键都为id】
*
* 即,
* ---》得到当前运行类继承的父类 BaseDao<Account>
* ----》 得到Account.class
*/
String sql = "select * from " + tableName + " where id=? ";
try {
return JdbcUtils.getQuerrRunner().query(sql, new BeanHandler<T>(clazz), id);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/**
* 查询全部
* @return
*/
public List<T> getAll(){
String sql = "select * from " + tableName ;
try {
return JdbcUtils.getQuerrRunner().query(sql, new BeanListHandler<T>(clazz));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
这里额外回顾反射,直接看代码:
1 public class Admin {
2
3 // Field
4 private int id = 1000;
5 private String name = "匿名";
6
7 // Constructor
8 public Admin(){
9 System.out.println("Admin.Admin()");
10 }
11 public Admin(String name){
12 System.out.println("Admin.Admin()" + name);
13 }
14
15 // Method
16 public int getId() {
17 return id;
18 }
19 public void setId(int id) {
20 this.id = id;
21 }
22 public String getName() {
23 return name;
24 }
25 public void setName(String name) {
26 this.name = name;
27 }
28
29 }
30
31
32 // 反射技术
33 public class App {
34
35 // 1. 创建对象
36 @Test
37 public void testInfo() throws Exception {
38 // 类全名
39 String className = "cn.itcast.c_reflect.Admin";
40 // 得到类字节码
41 Class<?> clazz = Class.forName(className);
42
43 // 创建对象1: 默认构造函数简写
44 //Admin admin = (Admin) clazz.newInstance();
45
46 // 创建对象2: 通过带参数构造器创建对象
47 Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
48 Admin admin = (Admin) constructor.newInstance("Jack");
49
50 }
51 @Test
52 //2. 获取属性名称、值
53 public void testField() throws Exception {
54
55 // 类全名
56 String className = "cn.itcast.c_reflect.Admin";
57 // 得到类字节码
58 Class<?> clazz = Class.forName(className);
59 // 对象
60 Admin admin = (Admin) clazz.newInstance();
61
62 // 获取所有的属性名称
63 Field[] fs = clazz.getDeclaredFields();
64 // 遍历:输出每一个属性名称、值
65 for (Field f : fs) {
66 // 设置强制访问
67 f.setAccessible(true);
68 // 名称
69 String name = f.getName();
70 // 值
71 Object value = f.get(admin);
72
73 System.out.println(name + value);
74 }
75 }
76
77 @Test
78 //3. 反射获取方法
79 public void testMethod() throws Exception {
80
81 // 类全名
82 String className = "cn.itcast.c_reflect.Admin";
83 // 得到类字节码
84 Class<?> clazz = Class.forName(className);
85 // 对象
86 Admin admin = (Admin) clazz.newInstance();
87
88 // 获取方法对象 public int getId() {
89 Method m = clazz.getDeclaredMethod("getId");
90 // 调用方法
91 Object r_value = m.invoke(admin);
92
93 System.out.println(r_value);
94 }
95
96 }
3. 注解
概述
注解与注释,
注解,告诉编译器如何运行程序!
注释, 给程序员阅读,对编译、运行没有影响;
注解作用,
1. 告诉编译器如何运行程序;
2. 简化(取代)配置文件 【案例后再看】
常用的注解,
// 重写父类的方法 @Override public String toString() { return super.toString(); } // 抑制编译器警告 @SuppressWarnings({"unused","unchecked"}) private void save() { List list = null; } // 标记方法以及过时 @Deprecated private void save1() { }
自定义注解
通过自定义注解,可以给类、字段、方法上添加描述信息!
a、注解基本写法
/**
* 自定义注解 (描述一个作者)
* @author Jie.Yuan
*
*/
public @interface Author {
/**
* 注解属性
* 1. 修饰为默认或public
* 2. 不能有主体
*/
String name();
int age();
}
//使用自定义注解
@Author(name = "Jet", age = 30)
public void save() {
}
b.带默认值的注解
1 public @interface Author {
2
3 /**
4 * 注解属性
5 * 1. 修饰为默认或public
6 * 2. 不能有主体
7 */
8 String name();
9 int age() default 30; // 带默认值的注解; 使用的时候就可以不写此属性值
10 }
c、默认名臣的注解
注解属性的默认默认名称是value
1 public @interface Author { 2 // 如果注解名称为value,使用时候可以省略名称,直接给值 3 // (且注解只有一个属性时候才可以省略名称) 4 String value(); 5 } 6 7 使用 8 @Author("Jet") 9 @Author(value = "Jet")
注解属性类型为数组:
public @interface Author {
String[] value() default {"test1","test2"};
}
使用:
@Author({“”,“”})
public void save() {
}
元注解
元注解,表示注解的注解!
指定注解的可用范围:
@Target({
TYPE, 类
FIELD, 字段
METHOD, 方法
PARAMETER, 参数
CONSTRUCTOR, 构造器
LOCAL_VARIABLE 局部变量
})
// 元注解 - 2. 指定注解的声明周期
@Retention(RetentionPolicy.SOURCE) 注解只在源码级别有效
@Retention(RetentionPolicy.CLASS) 注解在字节码即别有效 默认值
@Retention(RetentionPolicy.RUNTIME) 注解在运行时期有效
注解的反射
1 @Id
2 @Author(remark = "保存信息!!!", age = 19)
3 public void save() throws Exception {
4 // 获取注解信息: name/age/remark
5
6
7 // 1. 先获取代表方法的Method类型;
8 Class clazz = App_2.class;
9 Method m = clazz.getMethod("save");
10
11 // 2. 再获取方法上的注解
12 Author author = m.getAnnotation(Author.class);
13 // 获取输出注解信息
14 System.out.println(author.authorName());
15 System.out.println(author.age());
16 System.out.println(author.remark());
17 }
4. 注解,优化BaseDao的代码
当表名与数据库名称不一致、 字段与属性不一样、主键不叫id, 上面的BaseDao不能用!
这是,
可以通过配置文件(XML) 解决!
注解:
简化XML配置, 程序处理非常方便!
(不便于维护: 例如修改字段名,要重新编译!)
XML
便于维护! 需要些读取代码!
1 当表名与数据库名称不一致、 字段与属性不一样、主键不叫id, 上面的BaseDao不能用! 2 这是, 3 可以通过配置文件(XML) 解决! 4 5 6 注解: 7 简化XML配置, 程序处理非常方便! 8 (不便于维护: 例如修改字段名,要重新编译!) 9 10 XML 11 便于维护! 需要些读取代通过反射注解=ViewCode】
5. Log4J日志组件
程序中为什么用日志组件?
简单来说,为了项目后期部署上线后的维护、错误排查!
Log4j, log for java, 开源的日志组件!
使用步骤:
1. 下载组件,引入jar文件;
log4j-1.2.11.jar
2. 配置 : src/log4j.properties
3. 使用
# 通过根元素指定日志输出的级别、目的地(目的地可以同时指定多个~): # 日志输出优先级: debug < info < warn < error log4j.rootLogger=info,console,file ############# 日志输出到控制台 ############# # 日志输出到控制台使用的api类 log4j.appender.console=org.apache.log4j.ConsoleAppender # 指定日志输出的格式: 灵活的格式 log4j.appender.console.layout=org.apache.log4j.PatternLayout # 具体格式内容 log4j.appender.console.layout.ConversionPattern=%d %p %c.%M()-%m%n ############# 日志输出到文件 ############# log4j.appender.file=org.apache.log4j.RollingFileAppender # 文件参数: 指定日志文件路径 log4j.appender.file.File=../logs/MyLog.log # 文件参数: 指定日志文件最大大小 log4j.appender.file.MaxFileSize=5kb # 文件参数: 指定产生日志文件的最大数目 log4j.appender.file.MaxBackupIndex=100 # 日志格式 log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d %c.%M()-%m%n
下面是使用log4j的示例代码
1 public class App {
2
3 Log log = LogFactory.getLog(App.class);
4
5 @Test
6 public void save() {
7 try {
8 log.info("保存: 开始进入保存方法");
9
10 int i = 1/0;
11
12 log.info("保存: 执行保存结束,成功");
13 } catch (Exception e) {
14
15 log.error("执行App类Save()方法出现异常!"); // 异常
16
17 e.printStackTrace();
18 }
19 }
20
21 /*
22 * 思考: 日志的输出级别作用?
23 * ----> 控制日志输出的内容。
24 */
25 @Test
26 public void testLog() throws Exception {
27 // 输出不同级别的提示
28 log.debug("调试信息");
29 log.info("信息提示");
30 log.warn("警告");
31 log.error("异常");
32
33 }
34 }
35 public class Index extends HttpServlet {
36
37
38 Log log = LogFactory.getLog(Index.class);
39
40 public void doGet(HttpServletRequest request, HttpServletResponse response)
41 throws ServletException, IOException {
42 try {
43 log.info("进入servlet");
44 int i = 1/0;
45 log.info("进入servlet结束");
46 } catch (Exception e) {
47 log.error("计算异常!",e);
48 }
49 }
50 }
6、枚举完全讲解
6.1基本概念
l 为什么使用枚举类
n 一些方法在运行的过程中所需要的值不是任意的,而是在一个范围中。在jdk1.5出现之前解决的方案,就是自己实现一个带有枚举功能的类。
l Jdk1.5新增的enum关键字用于定义一个枚举类,一旦定义一个枚举类之后,这个枚举类及自动继承了,java类库中的Enum
自定义枚举类
/**
* 自己手动的创建一个 枚举类
* 1、私有化,private,隐藏构造方法不让别人使用
* 2、蒂尼几个public static final的实例,给外界使用
*
* 手动实现的一个枚举类就是 Grade
* 为Student类中定义一个,Grade,当为这个grade复制的时候,只能是Grade中定义好的几个public static final型的值,否则就会出现变异问题
* 一旦定义其他值就会出错,于是就达到了,枚举的作用,但是这样做还是比较麻烦的,于是,java中退出了一个枚举类
* @author YUCHEN
*
*/
class Grade{
private Grade(){
}
public static final Grade A = new Grade();
public static final Grade B = new Grade();
public static final Grade C = new Grade();
public static final Grade D = new Grade();
public static final Grade E = new Grade();
}
class Student{
private Grade grade; //考试等级
public Grade getGrade()
{
return this.grade;
}
public void setGrade(Grade grade){
this.grade = grade;
}
}
6.2默认构造函数枚举
按下面步骤,读下面案例:
1 // 1. 枚举类定义 2 enum Grade{ 3 A,B,C,D,E; 4 } 5 class Student{ 6 private String name; 7 8 // 2. 使用枚举类型 9 private Grade grade; //ABCDE 10 public Grade getGrade() { 11 return grade; 12 } 13 public void setGrade(Grade grade) { 14 this.grade = grade; 15 } 16 } 17 public class Demo1 { 18 public static void main(String[] args) { 19 Student stu = new Student(); 20 // 3. 给枚举类型赋值,只能是枚举类定义的值(第1步中所定义) 21 stu.setGrade(Grade.A); 22 23 System.out.println(stu.getGrade()); 24 } 25 }
上述定义的枚举为默认构造函数枚举, 也可以这样
1 // 1. 枚举类定义
2 enum Grade{
3 A(),B(),C(),D(),E();
4 // 必须为私有
5 private Grade(){
6 }
7 }
此时,Grade类中有一个默认无参数构造函数
6.3有参构造函数
1 // 1. 带参数构造函数的枚举定义
2 enum Grade{
3 A("100-90"),B("90-80"),C("80-70"),D("70-60"),E("60-0");
4
5 private String value;
6 // 定义get方法返回数据
7 public String getValue() {
8 return value;
9 }
10
11 private Grade(String value) {
12 this.value = value;
13 }
14 }
15 class Student{
16 // 2. 使用枚举类型
17 private Grade grade; //ABCDE
18 public Grade getGrade() {
19 return grade;
20 }
21 public void setGrade(Grade grade) {
22 this.grade = grade;
23 }
24 }
25 public class Demo1 {
26 public static void main(String[] args) {
27 Student stu = new Student();
28 // 3. 给枚举类型赋值
29 stu.setGrade(Grade.A);
30 // 输出对应的“分数”
31 System.out.println(stu.getGrade().getValue());
32 }
33 }
6.4枚举类中抽象方法的定义
1 // 1. 带参数构造函数的枚举定义 2 // 并且需要返回更多的信息, 优秀,良好,好,一般,差 3 enum Grade{ 4 A("100-90"){ 5 public String getLocalStr() { 6 return "优秀"; 7 } 8 } 9 10 ,B("90-80"){ 11 public String getLocalStr() { 12 return "良好"; 13 } 14 } 15 16 ,C("80-70"){ 17 public String getLocalStr() { 18 return "好"; 19 } 20 } 21 22 ,D("70-60"){ 23 public String getLocalStr() { 24 return "一般"; 25 } 26 } 27 28 ,E("60-0"){ 29 public String getLocalStr() { 30 return "差"; 31 } 32 }; 33 34 private String value; 35 // 定义get方法返回数据 36 public String getValue() { 37 return value; 38 } 39 40 private Grade(String value) { 41 this.value = value; 42 } 43 44 // 返回成绩段对应的“描述”, 需要每个对象重新实现次方法 45 public abstract String getLocalStr(); 46 } 47 class Student{ 48 // 2. 使用枚举类型 49 private Grade grade; //ABCDE 50 public Grade getGrade() { 51 return grade; 52 } 53 public void setGrade(Grade grade) { 54 this.grade = grade; 55 } 56 } 57 public class Demo1 { 58 public static void main(String[] args) { 59 Student stu = new Student(); 60 // 3. 给枚举类型赋值 61 stu.setGrade(Grade.A); 62 // 输出对应的“分数” 63 System.out.println(stu.getGrade().getValue()); 64 65 // 输出描述 66 System.out.println(stu.getGrade().getLocalStr()); 67 } 68 }
6.5枚举API
l Java中声明的枚举类,均是java.lang.Enum类的孩子,它继承了Enum类的所有方法。常用方法:
- Stirng name()
- Int ordinal()
- Enum valueof(Class enumClass, String name)
l 自定义的枚举类
- Enum valueof(String name)
- Enum[] values() 此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便
掌握枚举对象、枚举对象下标、枚举字符串:
1 // 1. 输出枚举名称
2 System.out.println(Grade.B.name());// -->B
3
4 // 2. 获取枚举的位置(下标)
5 System.out.println(Grade.B.ordinal());
6
7 // 3. 字符串转换为枚举类型
8 Grade g = Grade.valueOf("B");
9 // Grade g = Grade.valueOf(Grade.class, "B");// 用另外一个重载方法,也可以
10 System.out.println(g.getLocalStr());
11
12 // 字符串转换为枚举类型
13 Grade grade = Enum.valueOf(Grade.class, "B");
14 System.out.println(grade.getLocalStr());
15
16 // 4. 遍历所有的枚举值
17 Grade[] gs = Grade.values();
18 for (Grade myGrade : gs) {
19 System.out.println("myGrade-->" + myGrade);
20 }