java基礎增強(泛型,反射, 註解,日誌)
阿新 • • 發佈:2019-02-05
泛型
作用:異常提前到編譯期
// 執行時期異常
@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("異常");
}
}