Java枚举和反射
枚举类型概述
- Java中的枚举类型属于自定义引用类型,定义枚举类型使用enum关键字修饰类。
- 枚举类型通常包含一组常量数据值,适合某种事物固定的构成结构(如季节由’’四季组成)。
- 枚举类型可以实现某个接口,但是枚举类型不能继承其他类同时枚举类型也不能被其他类所继承。
- 枚举类型可以定义非抽象方法,方法必须在所有枚举常量之后;枚举类型中也可以定义内部类及内部接口。
枚举类型的定义
public enum SignalLamp{
/* 枚举中的常量 */
RED,
GREEN,
BLUE;
}
枚举常用方法
public final int compareTo(E o) 比较枚举值大小,小于参数返回负数,大于参数返回正数,相等返回0
public final String name() 获取枚举的名称字符串形式
public final int ordinal() 获取枚举所在枚举集合中位置的索引,索引默认从0开始
public static E[ ] values() 获取所有枚举数组形式
public static E valueOf(String name)根据参数获取枚举常量对象
总结,想定义一组常量表示某种特定事物时,可以使用枚举。
反射概述
Java中的反射编程是通过在JVM运行时获取某个类型的字节码(Class)对象,从而利用其反向获取此类型定义的内部信息实现编程的一种机制。
反射编程通常属于非常规操作,存在一定的安全问题。
反射编程在设计开发工具框架时使用较多,诸如spring,hibernate等java应用框架技术中使用比较频繁。
反射编程比较普通的编程方式执行效率相对要低。
反射编程常用组件
javalang.Class 字节码类型
java.lang.reflect.Constructor 构造器类型
java.lang.reflect.Method 方法类型
java.lang.reflect.Field 属性类型
java.lang.reflect.Modifier 访问修饰符类型
Class类
Class类的实例表示正在运行的Java应用程序中的类和接口,是字节码的实例。Class没有公共构造方法。Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass方法自动构造的。任何一个java对象都可以获得一个和其相关的Class对象。利用Class对象可以获得此类的信息;所有同一类型的实例共享一个字节码对象
public static Class<?> forName(String className) //表示给定一个字符串,这个字符串是某个引用类的字符串描述,它包含整个类的绝对路径包名,加载此类会返回一个字节码对象
public Field getField(String name) //返回一个Field实例,返回一个字段
public Field[] getField() //返回所有字段
public Field getDeclaredField(String name) //按照给定名称获取一个私有字段
public Method getMethod(String name,Class<?>... parameterTypes) //获取类中的方法,第一个参数是名称,第二个是字节码类型,第三个是可变数组
public Method[] getMethods() //获取类中的所有方法
public Constructor<T> getConstructor(Class<?>... parameterTypes) //获取类中的构造器
public Constructor<?>[ ] getConstructors() //获取所有构造器
public native int getModifiers() //获取当前类的访问标识符,native表示本地方法
public ClassLoader getClassLoader() //获取类加载器
Constructor类
Constructor类表示某个类的构造方法实例类型。利用Constructor可以创建Constructor所在类的实例并对此类进行初始化等操作。
Constructorm没有公有的构造方法,所以不能实例化它,可以通过某个类的Class实例获得一个Constructor实例
public T newInstance(Object...initargs) //获取某一个对象类型的实例
public void setAccessible(boolean flag) //设置构造器可访问或者不可访问
public int getModifiers() //获取访问级别
public Parameter[] getParameters() //获取当前构造器的参数列表
public int getParameterCount() //返回参数的个数
Field类
Field提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
Field提供了对字段的反射操作动作,使编程中访问属性字段更加灵活。
读取配置文件的方法,参考了这篇博文:https://www.cnblogs.com/jona-test/p/11399218.html
/** * 将reflectTask 应用程序导入Eclipse 集成开发工具中,调整导入可能产生的错误(jar 文件导入路径可能发生错误); * 按照以下所述完成实践任务.程序中Env 类为读取../conf/dbconf.properties 属性文件的组件, 应在Env中编写 * 读取dbconf.properties 文件的功能实现代码. * * 程序中DataSourceSupport 类是为程序提供数据源的组件,其中私有方法getDataSource 返回BasicDataSource * 类型的数据源对象; DataSourceSupport 类中的成员属性,构造方法及getDataSource 皆是私有的访问级别,不能在 * 此类之外为私有属性赋值, 创建此类的对象和调用私有的getDataSource 方法. * 为了能够实现对DataSourceSupport 的创建,为私有属性赋值并实现调用getDataSource私有方法. 定义了 * ReflectSupport 类, 此类是本任务最核心的编码任务, 在ReflectSupport类中应利用java反射编程机制, 实现 * 以下操作: * 1 获取ReflectSupport 字节码(Class 实例)对象; * 2 获取ReflectSupport 的构造器, 应在运行时动态改变ReflectSupport构造器的访问级别; * 3 利用构造器创建ReflectSupport 实例; * 4 为ReflectSupport实例的私有属性赋值(属性值来源于Env读取的dbconf.properties文件配置) * 5 获取getDataSource 方法实例,并修改其访问级别 * 6 实现动态调用getDataSource私有方法返回BasicDataSource对象 * * 在TestReflect类中的main方法中编写测试代码,完成上述任务的测试. */ package com.xzit.db; import org.apache.commons.dbcp2.BasicDataSource; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; /** * 此类实现以反射编程方式提供获取DataSourceSupport实例 * 为私有属性赋值(属性值来自dbconf.properties 属性文件定义) * 实现调用私有方法(方法名称来自dbconf.properties 属性文件定义) * @author Administrator * */ public final class ReflectSupport { /* * 请定义一个合适的方法实现功能 * */ public static void main(String[] args) { try { //使用Class类通过反射的方式new一个已有的DataSourceSupport类的对象stuCla /** * 1 获取ReflectSupport 字节码(Class 实例)对象; */ Class<?> c = Class.forName("com.xzit.db.DataSourceSupport"); /** * 2 获取ReflectSupport 的构造器, 应在运行时动态改变ReflectSupport构造器的访问级别; */ //如果目标类构造器是私有的,那就动态的改变它的访问级别 Constructor con = c.getDeclaredConstructor(); con.setAccessible(true); //通过newInstance方法new一个DataSourceSupport类的默认构造器,强转成DataSourceSupport类型的对象data DataSourceSupport data = (DataSourceSupport) con.newInstance(); /** * 4 为ReflectSupport实例的私有属性赋值(属性值来源于Env读取的dbconf.properties文件配置) */ Env env = new Env(); //实例化Env类 env.read(); //调用读取配置文件的方法read //获取不可见的字段属性 Field jdbcUrl= c.getDeclaredField("jdbcUrl"); Field jdbcDriverClass= c.getDeclaredField("jdbcDriverClass"); Field jdbcUserName= c.getDeclaredField("jdbcUserName"); Field jdbcPassword= c.getDeclaredField("jdbcPassword"); /* 运行时动态改变私有字段的访问级别 */ jdbcUrl.setAccessible(true); jdbcDriverClass.setAccessible(true); jdbcUserName.setAccessible(true); jdbcPassword.setAccessible(true); //为私有字段赋值,值取自Env读取的dbconf.properties文件配置 jdbcUrl.set(data,env.getJdbcUrl()); jdbcDriverClass.set(data,env.getJdbcDriverClass()); jdbcUserName.set(data,env.getJdbcUserName()); jdbcPassword.set(data,env.getJdbcPassword()); //输出值检验一下: System.out.println("jdbcUrl= "+jdbcUrl.get(data)); System.out.println("jdbcDriverClass= "+jdbcDriverClass.get(data)); System.out.println("jdbcUserName= "+jdbcUserName.get(data)); System.out.println("jdbcPassword= "+jdbcPassword.get(data)); /** * 5 获取getDataSource 方法实例,并修改其访问级别 */ Method getData = c.getDeclaredMethod("getDataSource"); getData.setAccessible(true);//运行时更改此方法的访问级别 /** * 6 实现动态调用getDataSource私有方法返回BasicDataSource对象 */ BasicDataSource big = (BasicDataSource)getData.invoke(data); //返回一个MaxIdle值检验一下确实是调用成功了 System.out.println(big.getMaxIdle()); } catch (Exception e) { e.printStackTrace(); } } }
/** * 程序中Env 类为读取../conf/dbconf.properties 属性文件的组件, * 应在Env中编写读取dbconf.properties 文件的功能实现代码. */ package com.xzit.db; import java.util.Properties; import java.util.ResourceBundle; /** * 此类实现对dbconf.properties 文件信息的读取 * @author Administrator * */ public final class Env extends Properties { private String className; private String jdbcUrl; private String jdbcDriverClass; private String jdbcUserName; private String jdbcPassword; private String actionMethod; /* * 请编写实现代码 * */ public void read(){ //使用getBundle()方法,读取指定路径下的配置文件 ResourceBundle resourceBundle = ResourceBundle.getBundle("com.xzit.conf.dbconf"); //获取资源dbconf中的className字段的值 className = resourceBundle.getString("className"); //获取资源dbconf中的jdbcUrl字段的值 jdbcUrl = resourceBundle.getString("jdbcUrl"); //获取资源dbconf中的jdbcDriverClass字段的值 jdbcDriverClass = resourceBundle.getString("jdbcDriverClass"); //获取资源dbconf中的jdbcUserName字段的值 jdbcUserName = resourceBundle.getString("jdbcUserName"); //获取资源dbconf中的jdbcDriverClass字段的值 jdbcPassword= resourceBundle.getString("jdbcPassword"); //获取资源dbconf中的actionMethod字段的值 actionMethod = resourceBundle.getString("jdbcDriverClass"); } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public String getJdbcUrl() { return jdbcUrl; } public void setJdbcUrl(String jdbcUrl) { this.jdbcUrl = jdbcUrl; } public String getJdbcDriverClass() { return jdbcDriverClass; } public void setJdbcDriverClass(String jdbcDriverClass) { this.jdbcDriverClass = jdbcDriverClass; } public String getJdbcUserName() { return jdbcUserName; } public void setJdbcUserName(String jdbcUserName) { this.jdbcUserName = jdbcUserName; } public String getJdbcPassword() { return jdbcPassword; } public void setJdbcPassword(String jdbcPassword) { this.jdbcPassword = jdbcPassword; } public String getActionMethod() { return actionMethod; } public void setActionMethod(String actionMethod) { this.actionMethod = actionMethod; } }
package com.xzit.db; import org.apache.commons.dbcp2.BasicDataSource; /** * 提供获取目标数据源的通用组件 * @author Administrator * */ public class DataSourceSupport{ private String jdbcUrl;// 连接目标数据源的url private String jdbcDriverClass;// 数据源jdbc驱动类名称 private String jdbcUserName;// 登录数据库的用户名称 private String jdbcPassword;// 登录数据库用户的密码 /** * 提供返回BasicDataSource对象的方法 * @return BasicDataSource */ private BasicDataSource getDataSource(){ BasicDataSource ds = new BasicDataSource(); ds.setUrl(jdbcUrl); ds.setDriverClassName(jdbcDriverClass); ds.setUsername(jdbcUserName); ds.setPassword(jdbcPassword); return ds; } /** * 私有构造器 */ private DataSourceSupport(){ } }