(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基本概念

为什么使用枚举类

一些方法在运行的过程中所需要的值不是任意的,而是在一个范围中。在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 }
View Code

上述定义的枚举为默认构造函数枚举, 也可以这样

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 }
View Code

给我的体验忒儿的

---恢复内容结束---

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基本概念

为什么使用枚举类

一些方法在运行的过程中所需要的值不是任意的,而是在一个范围中。在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 }
View Code

上述定义的枚举为默认构造函数枚举, 也可以这样

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 }
View Code

 6.5枚举API

l Java中声明的枚举类,均是java.lang.Enum类的孩子,它继承了Enum类的所有方法。常用方法:

  • Stirng name()
  • Int ordinal()
  • Enum valueof(Class enumClass, String name)

自定义的枚举类

  • 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         }
原文地址:https://www.cnblogs.com/OliverZhang/p/6003178.html