Java进阶学习第十四天(泛型、反射泛型、注解、Log4J日志组件)

版权声明:使用本品须Jack Gao同意,违者必究 https://blog.csdn.net/Mr_GaoYang/article/details/82686754

一、泛型

1、概述
泛型是JDK1.5以后才有的,可以在编译时期进行类型检查,且可以避免频繁类型转化!

	// 运行时期异常 
	@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); 
	}

2、泛型擦除
泛型只在编译时期有效,编译后的字节码文件中不存在有泛型信息!

	//泛型擦除实例  
	public void save(List<Person> p){
	}
	public void save(List<Dept> d){    // 报错:与上面方法编译后一样
	}

3、泛型写法

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

4、泛型方法、泛型类、泛型接口
作用:设计公用的类、方法,对公用的业务实现进行抽取, 使程序更灵活!
① 泛型方法

public class GenericDemo {
	// 定义泛型方法
	public <K,T> T save(T t,K k) {
		return null;
	}
	// 测试方法
	@Test
	public void testMethod() throws Exception {
		// 使用泛型方法:在使用泛型方法的时候,确定泛型类型
		save(1.0f,1);
	}
}

② 泛型类

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 interface IBaseDao<T> {
	void save(T t );
	void update(T t );
}

泛型接口类型确定: 实现泛型接口的类也是抽象,那么类型在具体的实现中确定或创建泛型类的时候确定

public class BaseDaoImpl<T> implements BaseDao<T> {
}

泛型接口类型确定: 在业务实现类中直接确定接口的类型

public class PersonDao implements BaseDao<Person>{
}

5、泛型关键字
① ? :指定只是接收值

/**
 * Ctrl + shift + R   查看当前项目中类
 * Ctrl + shift + T   查看源码jar包中的类
 * Ctrl + shift + L   查看方法返回值
 */
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("");// 报错
	}
}

② extends【上限】 :元素的类型必须继承自指定的类

public class App_extends_super {
	/**
	 * 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【下限】:元素的类型必须是指定的类的父类

扫描二维码关注公众号,回复: 4094062 查看本文章
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);
	}
}

二、、泛型的反射(反射泛型)

1、反射泛型涉及API
① Type:接口,任何类型默认的接口!包括: 引用类型、原始类型、参数化类型
② 参数化类型: ParameterizedType
List<String> list = new ArrayList<String>();
即:ArrayList<String> 为参数化类型
③ 反射泛型案例

public class AdminDao extends BaseDao<Admin> {}
public class AccountDao extends BaseDao<Account> {}
/**
 * 所有dao的公用的方法,都在这里实现
 */
public class BaseDao<T>{
	private Class clazz;	// 保存当前运行类的参数化类型中的实际的类型
	private String tableName;	// 表名
	
	// 构造函数: 通过获取当前运行类的参数化类型,来获取参数化类型中实际类型的定义(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 T    返回封装后的对象
	 */
	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);
		}
	}
	
	/**
	 * 查询全部
	 */
	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);
		}
	}
}

2、反射复习
① 反射:可以在运行时期动态创建对象以及获取对象的属性、方法

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、注解作用
① 告诉编译器如何运行程序
② 简化(取代)配置文件

3、常用的注解:@Override、@SuppressWarnings、@Deprecated

	// 重写父类的方法
	@Override
	public String toString() {
		return super.toString();
	}
	
	// 抑制编译器警告
	@SuppressWarnings({"unused","unchecked"})
	private void save() {
		List list = null;
	}
	
	// 标记方法以及过时
	@Deprecated
	private void save1() {
	}

4、自定义注解
通过自定义注解,可以给类、字段、方法上添加描述信息!
① 注解基本写法

/**
 * 自定义注解(描述一个作者)
 */
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;// 带默认值的注解,使用的时候就可以不写此属性值
}

③ 默认名称的注解
注解属性名称为value,这就是默认名称

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   局部变量
})

◆ 指定注解的声明周期:

@Retention(RetentionPolicy.SOURCE)     注解只在源码级别有效
@Retention(RetentionPolicy.CLASS)      注解在字节码即别有效:默认值
@Retention(RetentionPolicy.RUNTIME)    注解在运行时期有效

◆ 注解反射

@Target({TYPE,FIELD , METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
//@Target({METHOD,FIELD,TYPE})   指定只能在方法、字段、类上用;
@Retention(RetentionPolicy.RUNTIME)   // 字节码级别有效
public @interface Author {
	String authorName() default "Jet";
	int age() default 30;	
	String remark();
}
public class Demo {
	private String test;
	@Id
	@Author(remark = "保存信息!!!", age = 19)
	public void save() throws Exception {
		// 获取注解信息: name/age/remark
		// 1. 先获取代表方法的Method类型;
		Class clazz = Demo.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());
	}
	
	@Test
	public void testMain() throws Exception {
		save();
	}
}

5、注解:优化BaseDao的代码
当表名与数据库名称不一致、 字段与属性不一样、主键不叫id, 上面的BaseDao不能用!一方面,可以通过配置文件(XML) 解决(便于维护,但是需要读取代码)!

@Table(tableName="a_admin")
public class Admin {
	@Id
	@Column(columnName = "a_id")
	private int id;
	@Column(columnName = "a_userName")
	private String userName;
	@Column(columnName = "a_pwd")
	private String pwd;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getPwd() {
		return pwd;
	}
	public void setPwd(String pwd) {
		this.pwd = pwd;
	}
	@Override
	public String toString() {
		return "Admin [id=" + id + ", pwd=" + pwd + ", userName=" + userName
				+ "]";
	}
}
/**
 * 解决优化的问题:
 * 	  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);
		}
	}
}

注解:简化XML配置, 程序处理非常方便,但是不便于维护: 例如修改字段名,要重新编译!

四、Log4J日志组件

1、程序中为什么用日志组件?
简单来说,为了项目后期部署上线后的维护、错误排查!

2、Log4j:log for java,开源的日志组件!

3、使用步骤
① 下载组件,引入jar文件:log4j-1.2.11.jar
② 配置: src/log4j.properties
③ 使用

public class Demo {
	Log log = LogFactory.getLog(Demo.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("异常");	
	}
}
# 通过根元素指定日志输出的级别、目的地: 
# 日志输出优先级: 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

猜你喜欢

转载自blog.csdn.net/Mr_GaoYang/article/details/82686754