反射机制的实现 主要通过 操作java.lang.Class类
目录
下面将主要讲解 java.lang.Class
类
1.1 Class类 简介
Class
类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。
每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class
对象。
基本的 Java 类型(boolean
、byte
、char
、short
、int
、long
、float
和 double
)和关键字 void
也表示为 Class
对象。
1.2 class类 特点
优点:
1.反射提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提高硬编码目标类
2.反射是其他一些常用语言,如C、C++、Fortran或者Pascal等不具备的
3.Java反射技术应用领域很广,如软件测试、JavaBean
4.许多流行的开源框架例如Struts、Hibernate、Spring在实现过程中都采用了该技术
缺点:
1.性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此Java反射机制只要应用在对;灵活性和扩展性要求很高的系统框架上,普通程序不建议使用
2.使用反射会模糊程序内部逻辑:程序员希望在代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂
1.3 class类 方法简介
/**
* 返回与带有给定字符串名的类或接口相关联的 Class 对象。调用此方法等效于:
* Class.forName(className, true, currentLoader)
* 其中 currentLoader 表示当前类的定义类加载器。
* 例如,以下代码片段返回命名为 java.lang.Thread 的类的运行时 Class 描述符。
*
* Class t = Class.forName("java.lang.Thread")
* 调用 forName("X") 将导致命名为 X 的类被初始化
*
*
*
*
* @param className className - 所需类的完全限定名。
* @return 具有指定名的类的 Class 对象。
* @throws ClassNotFoundException
*/
public static Class<?> forName(String className)
throws ClassNotFoundException
/**
*
* 使用给定的类加载器,返回与带有给定字符串名的类或接口相关联的 Class 对象。
*(以getName 所返回的格式)给定一个类或接口的完全限定名,此方法会试图定位、加载和链接该类或接口。
* 指定的类加载器用于加载该类或接口。
* 如果参数 loader 为 null,则该类通过引导类加载器加载。
* 只有 initialize 参数为 true 且以前未被初始化时,才初始化该类。
* 如果 name 表示一个基本类型或 void,则会尝试在未命名的包中定位用户定义的名为 name 的类。
* 因此,该方法不能用于获得表示基本类型或 void 的任何 Class 对象。
*
* 如果 name 表示一个数组类,则会加载但不初始化该数组类的组件类型。
*
*
* @param name - 所需类的完全限定名
* @param initialize - 是否必须初始化类
* @param loader - 用于加载类的类加载器
* @return 表示所需类的类对象
* @throws ClassNotFoundException
*
* LinkageError - 如果链接失败
* ExceptionInInitializerError - 如果该方法激发的初始化失败
* ClassNotFoundException - 如果指定的类加载器无法定位该类
*
*/
public static Class<?> forName(String name,
boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
/**
*
* 强制转换该 Class 对象,以表示指定的 class 对象所表示的类的一个子类。
* 检查强制转换的有效性,如果无效*则抛出 ClassCastException。如果此方法成功了,
* 它将始终返回对此 class 对象的一个引用
*
* @param clazz
* @param <U>
* @return 此 Class 对象,它被强制转换以表示指定类对象的子类。
*/
public <U> Class<? extends U> asSubclass(Class<U> clazz)
/**
*
* 创建此 Class 对象所表示的类的一个新实例。
* 如同用一个带有一个空参数列表的 new 表达式实例化该类。
* 如果该类尚未初始化,则初始化这个类。
*
* @return 此对象所表示的类的一个新分配的实例。
* @throws InstantiationException
* @throws IllegalAccessException
*/
public T newInstance()
throws InstantiationException,
IllegalAccessException
/**
*
* 从接口 AnnotatedElement 复制的描述
* 如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。
*
* @param annotationClass - 对应于注释类型的 Class 对象
* @param <A>
* @return 如果该元素的指定注释类型的注释存在于此对象上,则返回这些注释,否则返回 null
*/
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
....
1.4 class类 简单调用
1.4.1 定义Bean类
public class Person {
// 姓名 私有化:只支持本类调用
private String name;
// 性别 保护 :只支持本类,子类以及本包调用
protected String sex;
// 电话号码 默认:只支持本类,本包调用
Integer phone;
// 年龄 公共化:支持所有调用
public int age;
// 私有化 空参构造方法
private Person() {
System.out.println("私有化 空参构造方法执行了 ...");
}
// 公共 带参构造方法
public Person(String name){
this.name = name;
System.out.println("公共 带参构造方法 ... name="+this.name);
}
// 默认 带参构造方法
Person(Integer phone){
this.phone = phone;
System.out.println("默认 带参构造方法 ... phone="+this.phone);
}
// 受保护的 带参构造方法
protected Person(int age){
this.age = age;
System.out.println("受保护的 带参构造方法 ... age="+this.age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", phone=" + phone +
", age=" + age +
'}';
}
/**
* 用于Class类反射调用...
*/
public static void main(String[] args) {
System.out.println("Person类的main方法执行了 ~~~");
}
}
1.4.2 class反射字节码获取对象
// ---------------- 通过反射获取对象 -------------------
System.out.println("------- getClass() 获取对象 --------");
Person person = new Person("旺财");
Class<? extends Person> personC = person.getClass();
System.out.println(personC.getName());
System.out.println("-------- 利用Class属性 获取对象 --------");
Class<Person> personClass = Person.class;
System.out.println(personClass.getName());
try {
System.out.println("-------- 带包名的类路径,包名.类名 获取对象 --------");
Class<?> aClass = Class.forName("com.czxy.reflection.pojo.Person");
System.out.println(aClass.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
打印结果如下:
在上面的三个通过反射获取对象的方法中,我们最常用的是第三种方法。
第一种方法: 我们已经有了对象,再利用反射来获取对象,岂不是 画蛇添足---多此一举。
第二种方法:我们需要导入类,导入包 依赖性太强
第三种方法:我们可以通过传入字符串,也可以在文件中配置等多种方法,低耦合
1.4.3 constructors方法反射构造方法并调用
System.out.println("-------- 带包名的类路径,包名.类名 获取对象 --------");
Class<?> aClass = Class.forName("com.czxy.reflection.pojo.Person");
System.out.println(aClass.getName());
// ---------------- 通过反射获取构造方法 -------------------
System.out.println(" ---------------- 通过反射获取构造方法 -------------------");
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
System.out.println("getDeclaredConstructors -> 返回此类的所有构造方法:");
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
System.out.println("--------------------------------------------");
// 表示此类公共构造方法的 Constructor 对象数组
Constructor<?>[] constructors = aClass.getConstructors();
System.out.println("getConstructors() -> 返回此类的所有公共构造方法:");
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println("--------------------------------------------");
/**
*
* 通过输入的参数来返回相应的构造方法
* 无论该构造方法是被什么修饰的
*
* null:表示空参构造
* 如果参数输入 Object.class 或者 其他构造方法内没有的类型 会抛出 :NoSuchMethodException
*/
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(null);
System.out.println("getDeclaredConstructor(parameter) -> 通过输入的参数来返回相应的构造方法 无论该构造方法是被什么修饰符修饰的");
System.out.println(declaredConstructor);
System.out.println();
/**
*
* 通过输入的参数来返回相应的构造方法
* 该构造方法必须是公共的
*
*/
Constructor<?> constructor = aClass.getConstructor(String.class);
System.out.println("getConstructor(parameter) -> 通过输入的参数来返回相应的构造方法(该构造方法必须是公共的)");
System.out.println(constructor);
System.out.println("----------------------------");
/**
* 调用私有的构造方法
*
* 强转&非强转 :IllegalAccessException
*/
// Person person = (Person) declaredConstructor.newInstance();
// Object o = declaredConstructor.newInstance();
// System.out.println(o);
// private com.czxy.reflection.pojo.Person()
System.out.println("当前构造方法:"+declaredConstructor);
// 暴力调用构造方法 true:是 false:否
System.out.println("setAccessible(Boolean) -> 暴力调用构造方法 true:是 false:否");
declaredConstructor.setAccessible(true);
Person person = (Person) declaredConstructor.newInstance();
System.out.println(person);
/**
*
* 私有化构造方法不能调用?
* 多半是惯的 用一下暴力就好了 setAccessible(Boolean)
* ps: 我们都是文明人
*/
打印结果如下:
1.4.4 field反射对象属性并调用
System.out.println("================= 桀骜的分割线 ===================");
/**
*
* 返回所有公共的属性
*/
Field[] fields = aClass.getFields();
System.out.println("getFields() -> 返回所有公共的属性");
for (Field field : fields) {
System.out.println(field);
}
System.out.println();
/**
*
* 返回所有属性 无论公私
*/
Field[] declaredFields = aClass.getDeclaredFields();
System.out.println("getDeclaredFields() -> 返回所有属性 无论公私");
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println();
/**
* parameter: name -> private -> NoSuchFieldException
* sex -> protected -> ...
* phone -> deflaut -> ...
*/
Field name = aClass.getField("age");
System.out.println("getField(parameter) -> 获取名字为parameter的公有属性");
System.out.println(name);
System.out.println();
Field name1 = aClass.getDeclaredField("name");
System.out.println("getDeclaredField -> 获取名字为parameter的属性 无论公私");
System.out.println(name1);
打印结果如下:
1.4.5 method反射成员方法并调用
System.out.println("==================== 傲慢的分割线 ==============");
/**
*
* 返回所有公共的成员方法 (包括父类)
*/
Method[] methods = aClass.getMethods();
System.out.println("getMethods() -> 返回所有公共的成员方法 (包括父类)");
for (Method method : methods) {
System.out.println(method);
}
System.out.println("-------------------");
/**
*
* 返回所有本类和被重写之后父类的成员方法
*/
Method[] declaredMethods = aClass.getDeclaredMethods();
System.out.println("getDeclaredMethods() -> 返回所有本类和被重写之后父类的成员方法");
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println("------------------");
/**
*
* parameter1: 方法名
* parameter2:参数类型
* 多个参数用 , 分割
*/
Method method4 = aClass.getMethod("method4", String.class,String.class,Integer.class);
System.out.println("getMethod(parameter1,parameter2) -> 返回名字为parameter1,参数为parameter2的公共成员方法");
System.out.println(method4);
System.out.println("-----------------------");
/**
*
* 返回名字为 Parameter1 的成员方法 无论公私
*/
Method method1 = aClass.getDeclaredMethod("method1", null);
System.out.println("getDeclaredMethod(parameter1,parameter2) -> 返回名字为parameter1,参数为parameter2的成员方法 无论公私");
System.out.println(method1);
// 实例化Person对象
Constructor<?> constructor1 = aClass.getDeclaredConstructor(null);
// 因为我调用的是私有化构造方法 所以这里要使用暴力
constructor1.setAccessible(true);
// 获取实例
Object o1 = constructor1.newInstance();
// 此方法为 protected 修饰
Method method2 = aClass.getDeclaredMethod("method2", String.class);
// 此方法为 deflaut 修饰
// Method method3 = aClass.getDeclaredMethod("method3", null);
// 所以 这里同样要使用暴力
method2.setAccessible(true);
// method3.setAccessible(true);
/**
*
* parameter1:对象
* parameter2:参数
* return:返回的数据
*
* 如果当前方法没有返回值则返回:null
*/
Object result = method2.invoke(o1, "泡芙");
// Object result = method3.invoke(o1, null);
System.out.println("返回值:"+result);
打印结果如下:
1.5 class类 其他调用
1.5.1 反射main方法
System.out.println("================== 昂然的分割线 ===============");
/**
* 反射main方法
*
* parameter1: 方法是static静态的,所以为null可以,
* parameter2:
* jdk的语法为了向上兼容,我们在把一个字符串数组传递过去的时候,系统会默认认为我们是把参数包裹成一个数组传递过去,
* 得到该数组后,它会把数组按内容进行分割,这样说来,我们代码中传递过去得其实就是三个字符串了,所以会报错。
* 解决办法:
* (Object) new String[]{"a", "b", "c"} -> 将String字符串强转为Object
*
*/
Method main = aClass.getMethod("main", String[].class);
Object invoke = main.invoke(o1, (Object) new String[]{"a", "b", "c"});
System.out.println(invoke);
打印结果如下:
1.5.2 反射运行配置文件内容
// 配置文件
className = com.czxy.reflection.pojo.Person
methodName = method1
System.out.println("================== 饕餮的分割线 ==============");
// 获取类路径
String className = Reflection.getValue("className");
// 通过类路径反射class字节码文件
Class<?> clazz = Class.forName(className);
// 获取方法名
String methodName = Reflection.getValue("methodName");
// 该方法不是 公共的 所以需要使用getDeclaredMethod(parameter)
Method method = clazz.getDeclaredMethod(methodName);
// 使用暴力
method.setAccessible(true);
// 该构造方法不是 公共的 所以需要使用getDeclaredConstructor()
Constructor<?> declaredConstructor1 = clazz.getDeclaredConstructor();
// 暴力
declaredConstructor1.setAccessible(true);
// 调用方法 当前方法没有参数 也没有返回值 -> null 可以不写
method.invoke(declaredConstructor1.newInstance(),null);
// 定义读取配置文件方法
public static String getValue(String key){
try {
// 获取配置文件对象
Properties properties = new Properties();
// 文件输入流
FileReader fileReader = new FileReader("D:\\code\\JAVASE\\Reflection\\src\\com\\czxy\\reflection\\per.txt");
// 读取文件内容 放入获取配置文件对象
properties.load(fileReader);
// 关闭流
fileReader.close();
// 根据输入的键 获取对应的值
return properties.getProperty(key);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
打印结果如下:
1.6 所有代码
public class Reflection {
public static void main(String[] args) {
// ---------------- 通过反射获取对象 -------------------
// System.out.println("------- getClass() 获取对象 --------");
// Person person = new Person("旺财");
// Class<? extends Person> personC = person.getClass();
// System.out.println(personC.getName());
//
// System.out.println("-------- 利用Class属性 获取对象 --------");
// Class<Person> personClass = Person.class;
// System.out.println(personClass.getName());
try {
System.out.println("-------- 带包名的类路径,包名.类名 获取对象 --------");
Class<?> aClass = Class.forName("com.czxy.reflection.pojo.Person");
System.out.println(aClass.getName());
// ---------------- 通过反射获取构造方法 -------------------
System.out.println(" ---------------- 通过反射获取构造方法 -------------------");
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
System.out.println("getDeclaredConstructors -> 返回此类的所有构造方法:");
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
System.out.println("--------------------------------------------");
// 表示此类公共构造方法的 Constructor 对象数组
Constructor<?>[] constructors = aClass.getConstructors();
System.out.println("getConstructors() -> 返回此类的所有公共构造方法:");
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println("--------------------------------------------");
/**
*
* 通过输入的参数来返回相应的构造方法
* 无论该构造方法是被什么修饰的
*
* null:表示空参构造
* 如果参数输入 Object.class 或者 其他构造方法内没有的类型 会抛出 :NoSuchMethodException
*/
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(null);
System.out.println("getDeclaredConstructor(parameter) -> 通过输入的参数来返回相应的构造方法 无论该构造方法是被什么修饰符修饰的");
System.out.println(declaredConstructor);
System.out.println();
/**
*
* 通过输入的参数来返回相应的构造方法
* 该构造方法必须是公共的
*
*/
Constructor<?> constructor = aClass.getConstructor(String.class);
System.out.println("getConstructor(parameter) -> 通过输入的参数来返回相应的构造方法(该构造方法必须是公共的)");
System.out.println(constructor);
System.out.println("----------------------------");
/**
* 调用私有的构造方法
*
* 强转&非强转 :IllegalAccessException
*/
// Person person = (Person) declaredConstructor.newInstance();
// Object o = declaredConstructor.newInstance();
// System.out.println(o);
// private com.czxy.reflection.pojo.Person()
System.out.println("当前构造方法:"+declaredConstructor);
// 暴力调用构造方法 true:是 false:否
System.out.println("setAccessible(Boolean) -> 暴力调用构造方法 true:是 false:否");
declaredConstructor.setAccessible(true);
Person person = (Person) declaredConstructor.newInstance();
System.out.println(person);
/**
*
* 私有化构造方法不能调用?
* 多半是惯的 用一下暴力就好了 setAccessible(Boolean)
* ps: 我们都是文明人
*/
System.out.println("================= 桀骜的分割线 ===================");
/**
*
* 返回所有公共的属性
*/
Field[] fields = aClass.getFields();
System.out.println("getFields() -> 返回所有公共的属性");
for (Field field : fields) {
System.out.println(field);
}
System.out.println();
/**
*
* 返回所有属性 无论公私
*/
Field[] declaredFields = aClass.getDeclaredFields();
System.out.println("getDeclaredFields() -> 返回所有属性 无论公私");
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println();
/**
* parameter: name -> private -> NoSuchFieldException
* sex -> protected -> ...
* phone -> deflaut -> ...
*/
Field name = aClass.getField("age");
System.out.println("getField(parameter) -> 获取名字为parameter的公有属性");
System.out.println(name);
System.out.println();
Field name1 = aClass.getDeclaredField("name");
System.out.println("getDeclaredField -> 获取名字为parameter的属性 无论公私");
System.out.println(name1);
System.out.println("==================== 傲慢的分割线 ==============");
/**
*
* 返回所有公共的成员方法 (包括父类)
*/
Method[] methods = aClass.getMethods();
System.out.println("getMethods() -> 返回所有公共的成员方法 (包括父类)");
for (Method method : methods) {
System.out.println(method);
}
System.out.println("-------------------");
/**
*
* 返回所有本类和被重写之后父类的成员方法
*/
Method[] declaredMethods = aClass.getDeclaredMethods();
System.out.println("getDeclaredMethods() -> 返回所有本类和被重写之后父类的成员方法");
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println("------------------");
/**
*
* parameter1: 方法名
* parameter2:参数类型
* 多个参数用 , 分割
*/
Method method4 = aClass.getMethod("method4", String.class,String.class,Integer.class);
System.out.println("getMethod(parameter1,parameter2) -> 返回名字为parameter1,参数为parameter2的公共成员方法");
System.out.println(method4);
System.out.println("-----------------------");
/**
*
* 返回名字为 Parameter1 的成员方法 无论公私
*/
Method method1 = aClass.getDeclaredMethod("method1", null);
System.out.println("getDeclaredMethod(parameter1,parameter2) -> 返回名字为parameter1,参数为parameter2的成员方法 无论公私");
System.out.println(method1);
// 实例化Person对象
Constructor<?> constructor1 = aClass.getDeclaredConstructor(null);
// 因为我调用的是私有化构造方法 所以这里要使用暴力
constructor1.setAccessible(true);
Object o1 = constructor1.newInstance();
// 此方法为 protected 修饰
Method method2 = aClass.getDeclaredMethod("method2", String.class);
// 此方法为 deflaut 修饰
// Method method3 = aClass.getDeclaredMethod("method3", null);
// 所以 这里同样要使用暴力
method2.setAccessible(true);
// method3.setAccessible(true);
/**
*
* parameter1:对象
* parameter2:参数
* return:返回的数据
*
* 如果当前方法没有返回值则返回:null
*/
Object result = method2.invoke(o1, "泡芙");
// Object result = method3.invoke(o1, null);
System.out.println("返回值:"+result);
System.out.println("================== 昂然的分割线 ===============");
/**
* 反射main方法
*
* parameter1: 方法是static静态的,所以为null可以,
* parameter2:
* jdk的语法为了向上兼容,我们在把一个字符串数组传递过去的时候,系统会默认认为我们是把参数包裹成一个数组传递过去,
* 得到该数组后,它会把数组按内容进行分割,这样说来,我们代码中传递过去得其实就是三个字符串了,所以会报错。
* 解决办法:
* (Object) new String[]{"a", "b", "c"} -> 将String字符串强转为Object
*
*/
Method main = aClass.getMethod("main", String[].class);
Object invoke = main.invoke(o1, (Object) new String[]{"a", "b", "c"});
System.out.println(invoke);
System.out.println("================== 饕餮的分割线 ==============");
// 获取类路径
String className = Reflection.getValue("className");
// 通过类路径反射class字节码文件
Class<?> clazz = Class.forName(className);
// 获取方法名
String methodName = Reflection.getValue("methodName");
// 该方法不是 公共的 所以需要使用getDeclaredMethod(parameter)
Method method = clazz.getDeclaredMethod(methodName);
// 使用暴力
method.setAccessible(true);
// 该构造方法不是 公共的 所以需要使用getDeclaredConstructor()
Constructor<?> declaredConstructor1 = clazz.getDeclaredConstructor();
// 暴力
declaredConstructor1.setAccessible(true);
// 调用方法 当前方法没有参数 也没有返回值 -> null 可以不写
method.invoke(declaredConstructor1.newInstance(),null);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getValue(String key){
try {
// 获取配置文件对象
Properties properties = new Properties();
// 文件输入流
FileReader fileReader = new FileReader("D:\\code\\JAVASE\\Reflection\\src\\com\\czxy\\reflection\\per.txt");
// 读取文件内容 放入获取配置文件对象
properties.load(fileReader);
// 关闭流
fileReader.close();
// 根据输入的键 获取对应的值
return properties.getProperty(key);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
至此 java反射 就简单介绍完毕了 ...
{\__/} {\__/}
( ·-·) (·-· )
/ >------------------------------------------------< \
| ☆ |
| ☆ |
| ★ |
| ☆ |
| ☆ |
| |
-------------------------------------
有兴趣可以关注我的公众号: