1. 程式人生 > >java基礎增強(泛型,反射, 註解,日誌)

java基礎增強(泛型,反射, 註解,日誌)

泛型

作用:異常提前到編譯期

    // 執行時期異常 
    @Test
    public void testGeneric() throws Exception {
        // 集合的宣告
        List list = new ArrayList();
        list.add("China");
        list.add(1);

        // 集合的使用
        String str = (String) list.get(1);

    }

    // 使用泛型
    @Test
    public void testGeneric2() throws Exception {
        // 宣告泛型集合的時候指定元素的型別
List<String> list = new ArrayList<String>(); list.add("China"); // list.add(1);// 編譯時期報錯 String str = list.get(1); }

泛型擦除,

泛型只在編譯時期有效,編譯後的位元組碼檔案中不存在有泛型資訊!
    public void save(List<Person> p){
    }
    public void save(List<Dept> d){    // 報錯: 與上面方法編譯後一樣
}

寫法

// 泛型寫法
    @Test
    public void testGeneric3() throws Exception {
        // 宣告泛型集合,集合兩端型別必須一致
        List<Object> list = new ArrayList<Object>();
        List<String> list1 = new ArrayList<String>();
        List list2 = new ArrayList<String>();
        List
<Integer> list3 = new ArrayList(); // 錯誤 //List<Object> list4 = new ArrayList<String>(); // 錯誤: 泛型型別必須是引用型別,不能為基本型別 List<int> list5 = new ArrayList<int>(); }

泛型類

public class GenericDemo<T> {

    // 定義泛型方法
    public <K> T save(T t,K k) {
        return null;
    }

    public void update(T t) {

    }

    // 測試方法
    @Test
    public void testMethod() throws Exception {

        // 泛型類:  在建立愛泛型類物件的時候,確定型別
        GenericDemo<String> demo = new GenericDemo<String>();
        demo.save("test", 1);
    }
}

泛型方法

public class GenericDemo {

    // 定義泛型方法
    public <K,T> T save(T t,K k) {
        return null;
    }

    // 測試方法
    @Test
    public void testMethod() throws Exception {
        // 使用泛型方法:  在使用泛型方法的時候,確定泛型型別
        save(1.0f, 1);
    }
}

泛型介面

/**
 * 泛型介面
 * @author Jie.Yuan
 *
 * @param <T>
 */
public interface IBaseDao<T> {
    void save(T t );
    void update(T t );
}


//泛型介面型別確定: 實現泛型介面的類也是抽象,那麼型別在具體的實現中確定或建立泛型類的時候確定
public class BaseDao<T> implements IBaseDao<T> {

//泛型介面型別確定: 在業務實現類中直接確定介面的型別
public class PersonDao implements IBaseDao<Person>{

泛型關鍵字

泛型中:
? 指定只是接收值
extends 元素的型別必須繼承自指定的類
super 元素的型別必須是指定的類的父類

/**
 *?
 * 泛型, 涉及到一些關鍵字
 * 
 * Ctrl + shift + R   檢視當前專案中類
 * Ctrl + shift + T   檢視原始碼jar包中的類
 * @author Jie.Yuan
 *
 */
public class App_extends_super {

    //只帶泛型特徵的方法
    public void save(List<?> list) {
        // 只能獲取、迭代list;  不能編輯list
    }

    @Test
    public void testGeneric() throws Exception {

        // ?  可以接收任何泛型集合, 但是不能編輯集合值; 所以一般在方法引數中用
        List<?> list = new ArrayList<String>();
        //list.add("");// 報錯
    }
}
public class App_extends_super {


    /**
     * extends
     * list集合只能處理 Double/Float/Integer等型別
     * 限定元素範圍:元素的型別要繼承自Number類  (上限)
     * @param list
     */
    public void save(List<? extends Number> list) {
    }

    @Test
    public void testGeneric() throws Exception {
        List<Double> list_1 = new ArrayList<Double>();
        List<Float> list_2 = new ArrayList<Float>();
        List<Integer> list_3 = new ArrayList<Integer>();

        List<String> list_4 = new ArrayList<String>();

        // 呼叫
        save(list_1);
        save(list_2);
        save(list_3);
        //save(list_4);
    }
}
/**
 * super
 * 泛型, 涉及到一些關鍵字
 * 
 * Ctrl + shift + R   檢視當前專案中類
 * Ctrl + shift + T   檢視原始碼jar包中的類
 * @author Jie.Yuan
 *
 */
public class App_super {


    /**
     * super限定元素範圍:必須是String父類   【下限】
     * @param list
     */
    public void save(List<? super String> list) {
    }

    @Test
    public void testGeneric() throws Exception {
        // 呼叫上面方法,必須傳入String的父類
        List<Object> list1 = new ArrayList<Object>();
        List<String> list2 = new ArrayList<String>();

        List<Integer> list3 = new ArrayList<Integer>();
        //save(list3);
    }
}

泛型反射的應用

Type 介面:
任何型別預設的介面!
包括: 引用型別、原始型別、引數化型別

引數化型別: ParameterizedType
如:“ArrayList ” 為引數化型別

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);
        }
    }
}

反射


public class Admin {

    // Field
    private int id = 1000;
    private String name = "匿名";

    // Constructor
    public Admin(){
        System.out.println("Admin.Admin()");
    }
    public Admin(String name){
        System.out.println("Admin.Admin()" + name);
    }

    // Method
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

}


// 反射技術
public class App {

    // 1. 建立物件
    @Test
    public void testInfo() throws Exception {
        // 類全名
        String className = "cn.itcast.c_reflect.Admin";
        // 得到類位元組碼
        Class<?> clazz = Class.forName(className);

        // 建立物件1: 預設建構函式簡寫
        //Admin admin = (Admin) clazz.newInstance();

        // 建立物件2: 通過帶引數構造器建立物件
        Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
        Admin admin = (Admin) constructor.newInstance("Jack");

    }
    @Test
    //2. 獲取屬性名稱、值
    public void testField() throws Exception {

        // 類全名
        String className = "cn.itcast.c_reflect.Admin";
        // 得到類位元組碼
        Class<?> clazz = Class.forName(className);
        // 物件
        Admin admin =  (Admin) clazz.newInstance();

        // 獲取所有的屬性名稱
        Field[]  fs =  clazz.getDeclaredFields();
        // 遍歷:輸出每一個屬性名稱、值
        for (Field f : fs) {
            // 設定強制訪問
            f.setAccessible(true);
            // 名稱
            String name = f.getName();
            // 值
            Object value = f.get(admin);

            System.out.println(name + value);
        }
    }

    @Test
    //3. 反射獲取方法
    public void testMethod() throws Exception {

        // 類全名
        String className = "cn.itcast.c_reflect.Admin";
        // 得到類位元組碼
        Class<?> clazz = Class.forName(className);
        // 物件
        Admin admin =  (Admin) clazz.newInstance();

        // 獲取方法物件    public int getId() {
        Method m = clazz.getDeclaredMethod("getId");
        // 呼叫方法
        Object r_value = m.invoke(admin);

        System.out.println(r_value);
    }

}

註解

註解與註釋,
註解,告訴編譯器如何執行程式!
註釋, 給程式設計師閱讀,對編譯、執行沒有影響;

註解作用,
1. 告訴編譯器如何執行程式;
2. 簡化(取代)配置檔案 【案例後再看】

常用註解

// 重寫父類的方法
    @Override
    public String toString() {
        return super.toString();
    }

    // 抑制編譯器警告
    @SuppressWarnings({"unused","unchecked"})
    private void save() {
        List list = null;
    }

    // 標記方法以及過時
    @Deprecated
    private void save1() {
    }

自定義註解

通過自定義註解,可以給類、欄位、方法上新增描述資訊!
基本寫法:

/**
 * 自定義註解  (描述一個作者)
 * @author Jie.Yuan
 *
 */
public @interface Author {

    /**
     * 註解屬性
     *    1. 修飾為預設或public
     *    2. 不能有主體
     */
    String name();
    int age();
}

//使用
@Author(name = "Jet", age = 30)
    public void save() {

    }

帶預設值的註解:

public @interface Author {

    /**
     * 註解屬性
     *    1. 修飾為預設或public
     *    2. 不能有主體
     */
    String name();
    int age() default 30;   // 帶預設值的註解;  使用的時候就可以不寫此屬性值
}

預設名稱:

public @interface Author {
    // 如果註解名稱為value,使用時候可以省略名稱,直接給值
    // (且註解只有一個屬性時候才可以省略名稱)
    String value();
}

//使用
@Author("Jet")
@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) 註解在執行時期有效

註解反射:

    @Author(remark = "儲存資訊!!!", age = 19)
    public void save() throws Exception {
        // 獲取註解資訊: name/age/remark


        // 1. 先獲取代表方法的Method型別;
        Class clazz = App_2.class;
        Method m = clazz.getMethod("save");

        // 2. 再獲取方法上的註解
        Author author = m.getAnnotation(Author.class);
        // 獲取輸出註解資訊
        System.out.println(author.authorName());
        System.out.println(author.age());
        System.out.println(author.remark());
    }

註解的應用:

import static java.lang.annotation.ElementType.TYPE;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 註解,描述表名稱
 * @author Jie.Yuan
 *
 */
@Target({TYPE})
@Retention(RetentionPolicy.RUNTIME)  // 指定註解在執行時期有效
public @interface Table {

    String tableName();
}
import static java.lang.annotation.ElementType.FIELD;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 描述一個主鍵欄位
 * @author Jie.Yuan
 *
 */
@Target({FIELD})
@Retention(RetentionPolicy.RUNTIME)  // 指定註解在執行時期有效
public @interface Id {

}
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;

/**
 * 描述一個欄位
 * @author Jie.Yuan
 *
 */
@Target({FIELD})
@Retention(RetentionPolicy.RUNTIME)  // 指定註解在執行時期有效
public @interface Column {

    String columnName();
}
/**
 * 解決優化的問題:
 *    1. 當資料庫表名與類名不一致、 
 *    2. 欄位與屬性不一樣、
 *    3. 主鍵不叫id 
 *    
 */
public class BaseDao<T> {

    // 當前執行類的型別
    private Class<T> clazz;
    // 表名
    private String tableName;
    // 主鍵
    private String id_primary;

    // 拿到當前執行類的引數化型別中實際的型別  ( BaseDao<Admin> ,  Admin.class)
    public BaseDao(){
        Type type = this.getClass().getGenericSuperclass();
        ParameterizedType pt = (ParameterizedType) type;
        Type[] types = pt.getActualTypeArguments();
        clazz = (Class<T>) types[0];

        //已經拿到:  Admin.class

        /*******1. 獲取表名*******/
        Table table = clazz.getAnnotation(Table.class);
        tableName = table.tableName();

        /*******2. 獲取主鍵欄位*******/
        //獲取當前執行類的所有欄位、遍歷、獲取每一個欄位上的id註解
        Field[] fs = clazz.getDeclaredFields();
        for (Field f : fs) {

            // 設定強制訪問
            f.setAccessible(true);

            // 獲取每一個欄位上的id註解
            Id anno_id = f.getAnnotation(Id.class);

            // 判斷
            if (anno_id != null) {
                // 如果欄位上有id註解,當前欄位(field)是主鍵; 再獲取欄位名稱
                Column column = f.getAnnotation(Column.class);
                // 主鍵
                id_primary = column.columnName();
                // 跳出迴圈
                break;
            }
        }

        System.out.println("表:" + tableName);
        System.out.println("主鍵:" + id_primary);
    }


    public T findById(int id){
        try {
            String sql = "select * from " + tableName + " where " + id_primary +"=?";
            /*
             * DbUtils的已經封裝好的工具類:BeanHandler?   屬性=欄位
             */
            return JdbcUtils.getQuerrRunner().query(sql, new BeanHandler<T>(clazz), id);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    public List<T> getAll(){
        try {
            String sql = "select * from " + tableName;
            return JdbcUtils.getQuerrRunner().query(sql, new BeanListHandler<T>(clazz));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

/**
 * 自定義結果集:封裝單個Bean物件
 */
class BeanHandler<T> implements ResultSetHandler<T>{
    // 儲存傳入的要封裝的類的位元組碼
    private Class<T> clazz;
    public BeanHandler(Class<T> clazz) {
        this.clazz = clazz;
    }

    // 封裝結果集的方法
    @Override
    public T handle(ResultSet rs) throws SQLException {
        try {
            // 建立要封裝的物件  ‘1’
            T t = clazz.newInstance(); 
            // 向下讀一行
            if (rs.next()) {

                // a. 獲取類的所有的Field欄位陣列
                Field[] fs = clazz.getDeclaredFields();

                // b. 遍歷, 得到每一個欄位型別:Field
                for (Field f : fs) {

                    // c. 獲取”屬性名稱“
                    String fieldName = f.getName();

                    // e. 獲取Field欄位上註解   【@Column(columnName = "a_userName")】
                    Column column =  f.getAnnotation(Column.class);

                    // f. ”欄位名“
                    String columnName = column.columnName();        // 資料庫中欄位 a_userName

                    // g. 欄位值
                    Object columnValue = rs.getObject(columnName);

                    // 設定(BeanUtils元件)
                    BeanUtils.copyProperty(t, fieldName, columnValue);
                }
            }
            return t;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}


/**
 * 自定義結果集:封裝多個Bean物件到List集合
 */
class BeanListHandler<T> implements ResultSetHandler<List<T>>{

    // 要封裝的單個物件
    private Class<T> clazz;
    public BeanListHandler(Class<T> clazz){
        this.clazz = clazz;
    }

    // 把從資料庫查詢到的沒一行記錄,封裝為一個物件,再提交到list集合, 返回List<T>
    @Override
    public List<T> handle(ResultSet rs) throws SQLException {
        List<T> list = new ArrayList<T>();
        try {
            // 向下讀一行
            while (rs.next()) {

                // 建立要封裝的物件  ‘1’
                T t = clazz.newInstance(); 

                // a. 獲取類的所有的Field欄位陣列
                Field[] fs = clazz.getDeclaredFields();

                // b. 遍歷, 得到每一個欄位型別:Field
                for (Field f : fs) {

                    // c. 獲取”屬性名稱“
                    String fieldName = f.getName();

                    // e. 獲取Field欄位上註解   【@Column(columnName = "a_userName")】
                    Column column =  f.getAnnotation(Column.class);

                    // f. ”欄位名“
                    String columnName = column.columnName();        // 資料庫中欄位 a_userName

                    // g. 欄位值
                    Object columnValue = rs.getObject(columnName);

                    // 設定(BeanUtils元件)
                    BeanUtils.copyProperty(t, fieldName, columnValue);
                }
                // 物件新增到集合
                list.add(t);
            }
            return list;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

Log4j

配置檔案:

# 通過根元素指定日誌輸出的級別、目的地: 
#  日誌輸出優先順序: 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
public class App {

    Log log = LogFactory.getLog(App.class);

    @Test
    public void save() {
        try {
            log.info("儲存: 開始進入儲存方法");

            int i = 1/0;

            log.info("儲存: 執行儲存結束,成功");
        } catch (Exception e) {

            log.error("執行App類Save()方法出現異常!");  // 異常

            e.printStackTrace();
        }
    }

    /*
     * 思考: 日誌的輸出級別作用?
     *   ----> 控制日誌輸出的內容。
     */
    @Test
    public void testLog() throws Exception {
        // 輸出不同級別的提示
        log.debug("除錯資訊");
        log.info("資訊提示");
        log.warn("警告");
        log.error("異常");

    }
}