反射
Posted 日积跬步
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了反射相关的知识,希望对你有一定的参考价值。
概念
Java反射机制是在运行状态中
- 对于任意一个
类
,都能够知道这个类的所有属性
和方法
; - 对于任意一个
对象
,都能够调用这个对象的所有属性
和方法
。
这种动态获取信息以及动态调用对象的功能称为Java语言的反射机制。即,通过反射,该类对我们来说是完全透明的,可以获取任何有关它的信息。
使用反射的意义
- 增加程序的灵活性,避免将程序写死到代码里。
- 定义了一个接口,实现这个接口的类有20个,程序里用到了这个实现类的地方有很多地方。如果不使用配置文件,而是手写的话,但需要修改代码时,代码的改动量很大,因为每个地方都要改而且不容易定位。
- 但是,如果在编写之前先将接口与实现类写在配置文件里,下次只需改配置文件,利用反射(Java API已经封装好了,直接用就可以用
Class.newInstance()
)就可完成。
- 代码简洁,提高代码的复用率,外部调用方便。
- 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,获取其属性。
实例:
package reflection.testdemo;
interface Fruit { // 水果接口
public void eat() ; // 吃水果
}
class Apple implements Fruit{ // 定义苹果
public void eat() {
System.out.println("吃苹果。");
}
}
class Orange implements Fruit{
public void eat() {
System.out.println("吃橘子。");
}
}
class Factory{
public static Fruit getInstance(String className){
Fruit fruit = null ;
try{
fruit = (Fruit) Class.forName(className).newInstance() ;
}catch(Exception e ){
e.printStackTrace() ;
}
return fruit ;
}
}
public class FactoryDemo{
public static void main(String args[]){
// 通过工厂类取得接口实例,传入完整的包.类名称
Fruit fruit = Factory.getInstance("reflection.testdemo.Apple") ;
if(fruit != null){ // 判断是否取得接口实例
fruit.eat() ;
}
}
}
如果不用反射,那么如果再加一个西瓜类,就得在Factory里判断,每添加一个类都要修改一次Factory。但用了反射,只用在调用的时候传入完整的类名就可完成。
结果:用反射,修改一处代码;不用反射,修改两处代码。
动态获取类的信息
Java有3种方法可以获取Class信息:
类名.class
获取类信息(静态)- 直接通过一个
类
的静态变量class
获取:Class cls = String.class;
。
- 直接通过一个
Class.forName("类名")
获取类信息 (动态)- 如果知道一个
Class类
的完整类名,可以通过静态方法Class.forName()
获取:Class cls = Class.forName("类名");
。
- 如果知道一个
obj.getClass()
获取类信息。运行期间,通过当前对象获取类信息- 如果有一个实例变量,可以通过该实例变量提供的
getClass()
方法获取:String s = "Hello"; Class cls = s.getClass();
。
- 如果有一个实例变量,可以通过该实例变量提供的
获得Class后,可以调用具体的方法:
- 通过
Method[] all = cls.getDeclaredMethods()
动态获取类的方法
信息; - 通过
Field[] all = cls.getDeclaredFields()
动态获取类声明的属性
信息; - 通过
Constructor[] constructors = cls.getDeclaredConstructors()
动态获取类的构造器
信息;
动态获取类的方法信息并调用类的方法
动态获取类的方法信息
获取方法:
// 动态加载类
Class cls = Class.forName(className);
// 动态获取类的方法信息
Method[] all = cls.getDeclaredMethods();
实例:
package class_information;
import java.lang.reflect.Method;
/**
* 动态获取类的方法信息
* getDeclaredMethods
*
* 类的全名:class_information.Foo
* 编译后:class_information.Foo.class
* @author chenzufeng
*/
public class Foo {
public int test() {
return 5;
}
public double test1() {
return 5d;
}
public static void main(String[] args) {
getClassMethod();
}
/**
* 动态的加载类信息到方法区,并且返回对应的Class对象!
* Class对象可以访问类的全部信息!
*
* 将className对应的类文件,从磁盘中加载到内存方法区,返回这个类的信息
*/
public static void getClassMethod() {
String className = "class_information.Foo";
try {
// 动态加载类
Class cls = Class.forName(className);
// 动态获取类的方法信息
Method[] all = cls.getDeclaredMethods();
for (Method method : all) {
System.out.println(method.getName());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出结果:
main
test
getClassMethod
test1
动态调用类的方法
实例:
package class_method;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* 动态调用类的方法
* @author chenzufeng
*/
public class InvokeClassMethod {
public static void main(String[] args) throws
NoSuchMethodException, InvocationTargetException, IllegalAccessException {
List<String> list = new ArrayList<>();
list.add("Tom");
list.add("Jack");
// 1. 动态获取类信息
Class cls = list.getClass();
// 2. 通过[方法名]和[参数类型]找到对应的方法:remove
Method method = cls.getDeclaredMethod("remove", new Class[] {int.class});
// 3. 调用方法,传递对象和具体参数
Object value = method.invoke(list, new Object[] {0});
System.out.println(value); // Tom
}
}
动态获取类的属性信息和属性值
动态获取类的属性信息
获取方法:
// 动态加载类
Class cls = Class.forName(className);
// 动态获取类声明的属性信息
Field[] all = cls.getDeclaredFields();
实例:
package class_information;
import java.lang.reflect.Field;
/**
* 动态获取类的属性信息
*
* @author chenzufeng
*/
public class Eoo {
int id;
String name;
double salary;
public Eoo() {}
public Eoo(int id, String name, double salary) {
super();
this.id = id;
this.name = name;
this.salary = salary;
}
/**
* 动态获取一个类的全部属性信息
* 1. 动态加载一个类到方法区
* 2. 动态获取类的属性信息
*/
public static void getClassFields() throws ClassNotFoundException {
String className = "class_information.Eoo";
// 动态加载类
Class cls = Class.forName(className);
// 动态获取类声明的属性信息
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
// getName获取属性的名字
System.out.println(field.getName());
}
}
public static void main(String[] args) throws ClassNotFoundException {
getClassFields();
}
}
输出结果:
id
name
salary
动态获取类的属性值
实现过程(利用反射API实现动态属性访问):
- 找到对象的类型信息(方法区)
- 在信息中找属性信息(Field)
- 在对象上获取属性的值!
实例:
package attribute_value;
import java.lang.reflect.Field;
/**
* 动态获取类的属性值
* @author chenzufeng
*/
public class Goo {
public int id;
public String name;
public Goo() {}
public Goo(int id, String name) {
super();
this.id = id;
this.name = name;
}
/**
* 获取obj对象的 fieldName对应属性的值
* @param object 对象
* @param filedName 属性
* @return 属性值
*/
public Object getAttributeValue(Object object, String filedName) {
try {
/*
* 1. 获取类信息:找到对象的类型信息
* Java有3种方法可以获取Class信息
* a) 类名.class获取类信息(静态)
* b) Class.forName("类名") 获取类信息 (动态)
* c) obj.getClass()获取类信息。运行期间,通过当前对象获取类信息
*/
Class cls = object.getClass();
/*
* 2. 找到属性:在信息中找属性信息
* getDeclaredField按照属性名在cls中查找类信息。当属性没有找到时候,抛出异常!
*/
Field field = cls.getDeclaredField(filedName);
/*
* 3. 在对象上获取属性的值
* get方法:在一个对象上获取属性的值,对象上没有对应的属性,抛出异常
*/
Object value = field.get(object);
return value;
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public static void testGetAttributeValue() {
Goo goo = new Goo(1, "Tom");
Object id = goo.getAttributeValue(goo, "id");
Object name = goo.getAttributeValue(goo, "name");
System.out.println(id + " " + name);
}
public static void main(String[] args) {
testGetAttributeValue(); // 1 Tom
}
}
动态获取类的构造器信息
获取方法:
// 动态加载类
Class cls = Class.forName(className);
// 动态获取类的构造器信息
Constructor[] constructors = cls.getDeclaredConstructors();
实例:
/**
* 动态获取类的构造器信息
*/
public static void getAllConstructors() throws ClassNotFoundException {
String className = "class_information.Eoo";
// 动态加载类
Class cls = Class.forName(className);
// 动态获取类的构造器信息
Constructor[] constructors = cls.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.print(constructor.getName());
/*
* 获取构造器的参数类型列表
* Parameter参数;Type类型
* Class[] 代表所有参数的类型列表
*/
Class[] types = constructor.getParameterTypes();
System.out.println(Arrays.toString(types));
}
}
输出结果:
class_information.Eoo[]
class_information.Eoo[int, class java.lang.String, double]
动态创建对象
调用无参构造器创建对象
// 动态加载类会遇见必检异常:java.lang.ClassNotFoundException
Class cls = Class.forName(className);
// cls.newInstance()调用无参数构造器创建对象
Object obj = cls.newInstance();
- 如果没有
显式地提供无参数构造器,将发生异常! - Class提供了方法newInstance()。
实例:
public class Date {
public Date() {
this(System.currentTimeMillis());
}
public Date(long date) {
fastTime = date;
}
}
package create_object;
import java.util.Date;
/**
* 调用无参构造器创建对象
* @author chenzufeng
*/
public class UseConstructorWithoutParameter {
public static void main(String[] args) throws Exception {
createObject();
}
/**
* 动态调用无参数构造器创建对象
* 1.动态加载类
* 2.利用class的方法newInstance创建对象
*
* 注意:对象所属类必须有无参数构造器,否则出异常
*/
public static void createObject() throws
ClassNotFoundException, IllegalAccessException, InstantiationException {
String className = "java.util.Date";
Class cls = Class.forName(className);
// cls.newInstance()调用无参数构造器创建对象
Object object = cls.newInstance();
System.out.println("动态的创建对象:");
System.out.println(object);
// 静态的创建对象!编译已经就固定了!
Date date = new Date();
System.out.println("静态的创建对象:");
System.out.println(date);
}
}
输出结果:
动态的创建对象:
Sun Feb 07 21:18:50 CST 2021
静态的创建对象:
Sun Feb 07 21:18:50 CST 2021
调用有参构造器创建对象
// 动态加载类
Class cls = Class.forName(className);
// 动态获取指定参数类型的构造器
Constructor constructor = cls.getDeclaredConstructor(paramTypes);
// 执行构造器constructor.newInstance()方法,创建对象
Object obj = constructor.newInstance(params);
实例:
package create_object;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 调用有参构造器创建对象
* @author chenzufeng
*/
public class UseConstructorWithParameter {
public static void main(String[] args) {
testCreateObject();
}
/**
* 调用有参构造器创建对象:className + paramTypes共同决定调用哪个构造器!
* @param className 调用className对应类的有参数构造器
* @param paramTypes 代表对应构造器的参数列表
* @param params 执行构造器还需要具体的参数params,为构造器参数列表赋值
* @return 创建的对象
*/
public static Object createObject(String className, Class[] paramTypes, Object[] params) {
try {
// 1.动态加载类
Class cls = Class.forName(className);
// 2.动态获取指定参数类型的构造器
Constructor constructor = cls.getDeclaredConstructor(paramTypes);
// 3.执行构造器newInstance()方法,创建对象
Object object = constructor.newInstance(params);
return object;
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
/**
* 调用有参构造器创建对象
* new Date(long date)
* new String("Hello")
* new String(byte[],"utf-8");
*/
public static void testCreateObject() {
/*
* 动态调用 new Date(long date);
*/
String className = "java.util.Date";
// paramTypes类型列表:Class类型的数组
Class[] paramTypes = {long.class};
// 实际参数列表:params
Object[] params = {1000L*60*60*24*365};
Object object = createObject(className, paramTypes, params);
System.out.println(object);
/*
* 动态调用 new String("Hello");
* {} 只能用在声明变量时候直接初始化,不能用于赋值语句!
* 赋值语句可以使用 new Object[]{"Hello"}
*/
className = "java.lang.String";
paramTypes = new Class[] {String.class};
params = new Object[] {"Hello"};
object = createObject(className, paramTypes, params);
System.out.println(object);
/*
* 动态调用 new String(byte[],"utf-8");
*/
object = createObject("java.lang.String",
new Class[] {byte[].class, String.class},
new Object[] {new byte[] {65, 66, 67, 68}, "UTF-8"});
System.out.println(object);
}
}
输出结果:
Fri Jan 01 08:00:00 CST 1971
Hello
ABCD
参考资料
以上是关于反射的主要内容,如果未能解决你的问题,请参考以下文章