java反射这个知识点,是我一直想要掌握的。但是却一直因为其他种种问题没有时间学习,昨晚浅略的学习了一下,现在写篇博文来记录学习的成果。
什么是反射?
在运行的时候(只有在运行的时候.java文件才会被编译成.clss文件),可以知道某个类的所有构造器、public修饰的成员变量、所有方法(有参、无参、继承的、实现的),动态的获取某个类的方法和属性就是反射。
反射能干嘛?
通过获取到反射对象,用这个反射对象去获取被反射类的成员变量、构造器、方法并且使用。
反射有什么用?
反射创建对象啊,当然这是最基本的,最重要的用途就是开发各种通用的框架。
比如Spring框架,由Spring容器创建Bean对象,实际上是我们把类的配置信息给到了Spring容器,Spring容器反射创建Bean
反射的原理?
JAVA语言编译之后会生成一个.class文件,反射就是通过这些个字节码文件找到某一个类、类的构造器、类实现的接口、类中的方法以及属性。
并且还能通过这个字节码文件调用被反射的方法和构造器。
反射常用对象
对象 | 对应关系 |
---|---|
Class | 被反射类对象和被反射对象实现的接口对象 |
Constructor | 被反射类的构造器 |
Field | 对应的是被反射类的成员变量 |
Method | 调用被反射类对象的某个方法 |
反射的精髓: invoke方法:调用反射类的某个方法。
数据准备
创建一个要被反射的类
public class Person implements PersonService{
public String name ;
private Integer id ;
public String password;
public Person() {
System.out.println("Person类初始化了");
}
static{
System.out.println("Person类静态代码块");
}
public Person(String name) {
this.name = name;
}
public Person(Integer id) {
this.id = id;
}
public Person(Integer id,String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Class
获取到某个类的class对象
方式一: 通过被反射类的全限定名
Class<?> aClass= Class.forName("com.neu.domain.Person");
这个方法传入的参数是被反射类的全限定名,此方 法 会加载 被放射类的静态方法或静态成员变量,因为这个方法是将被反射的类加载到内存并初始化该类。
Class<?>aClass2=Class.forName("com.neu.domain.Person",false,RJava.class.getClassLoader());
这个方法是上一个方法的重载,但是该方法需要多传入两个参数,第二参数时一个布尔类型,默认是true,如果是true是将被反射类加载到内存并初始化,false的话只是将被反射类加载到内存不初始化。第三个参数是当前类的类加载器。
如果同时存在这个两个方法个,只生效第一个方法。(但是谁会无聊用两个办法去创建同一个被反射类的class对象呢)
方式二:通过被反射类的类名
Class<Person> personClass = Person.class;
这个方法并不会加载被反射类的静态方法和静态成员变量,只是将被反射类加载到内存并不执行初始化操作。
方式三:通过被反射类的对象
Person pp = new Person();
Class<?> ppClass= pp.getClass();
获取到某个类的所实现的接口
Class<?>[] interfaces = personClass.getInterfaces();
该方法的返回结果是一个数组,如果被反射类没有实现接口,则数组为空
Constructor
获取到某个类的构造器
Constructor<?> constructor = personClass.getConstructor(Integer.class,String.class);
通过传入的类型的简单类名,获取到被反射类的相应类型的构造器,简单类名的书写顺序必须与被反射类的构造器参数类型的顺序一致
就是说,被反射的那个类,有多种构造器,但是那么多种的构造器都是通过重载来实现的,这就根据getConstructor传入的参数类型来获取到对应的构造器
getConstructor方法的参数时可变参数
调用获得的构造器方法:newInstance
Person person = (Person)constructor.newInstance(1,"张三");
System.out.println("id是:"+person.getId()+"名字是:"+person.getName());
Field
获取到某个类指定的成员变量
Field field = personClass.getField("name");
取到被反射类所有的public修饰的成员变量
Field[] fields = personClass.getFields();
注意: 严重注意!!!如果反射类的成员变量不是public修饰的,getField方法就不能拿到该指定的成员变量,会报错
Method
获取到某个类的方法
获取到被反射的所有方法,无参方法、有参方法、继承父类的方法,除了构造器方法不能获取
Method[] methods = personClass.getMethods();
获得被反射类的指定方法
Method method = personClass.getMethod("setName",String.class);
获取无参方法只需要传入方法名即可
获取有参方法传入方法名,还必须传入参数类型的简单类名
invoke
反射精髓!!!!!
反射调用类的方法
method.invoke(person,"");
invoke方法的第一个参数是一个对象,什么对象呢,就是被反射类的对象
调用这个对象的method方法(调用什么方法还得看需要获取被反射类的什么方法),传入某某参数
这个参数时可变参数,但调用无参方法时,参数就直接不写
我对反射的理解
反射嘛,就是获得到一个类的反射对象,然后通过这个反射对象,获取到被反射类的字段、方法、构造器等,并且通过反射对象去调用被反射类的字段、方法、构造器。但是只是一晚上的学习,并没有太深入的去了解反射的原理和底层,以后还是需要时间去专研反射的源码,看看里面的秘密。
最后附上昨晚学习的源代码:
public class RJava {
public static void main(String[] args) {
/**
* 反射含义:
* 在运行的时候(只有在运行的时候.java文件才会被编译成.clss文件),可以知道某个类的所有构造器、public修饰的成员变量、所有方法(有参、无参、继承的、实现的)
* 动态的获取某个类的方法和属性就是反射。
*
*
* 反射原理:
* JAVA语言编译之后会生成一个.class文件,反射就是通过这些个字节码文件找到某一个类、类的构造器、类实现的接口、类中的方法以及属性。
* 并且还能通过这个字节码文件调用被反射的方法和构造器
*
*
* 反射的对象:
* Class :对应的是被反射类对象和被反射对象实现的接口对象
*
* Constructor:对应的是被反射类的构造器
*
* Field:对应的是被反射类的成员变量
*
* Method:对应的是被反射类的方法
*
* 精髓:invoke方法:调用被反射类对象的某个方法
*
*
* 反射能干什么:
* 通过获取到反射对象,用这个反射对象去获取被反射类的成员变量、构造器、方法并且使用。
*
*
* 反射有什么用:
* 反射创建对象啊,当然这是最基本的,最重要的用途就是开发各种通用的框架
* 比如Spring框架,由Spring容器创建Bean对象,实际上是我们把类的配置信息给到了Spring容器,Spring容器反射创建Bean
*
*/
try {
//获取到某个类的class对象
//方式一:
//传入的参数是类的权限定名
//这个方式会 加载 被反射的类的静态方法或静态成员变量
//这个方法是把类加载到内存,同时初始化该类,所以调用这个方法会加载类的静态代码块
Class<?> aClass= Class.forName("com.neu.domain.Person");
//这个方法传入的后面两个参数:
//第一个:true -加载类到内存并且初始化类
//第二个:当前类的类加载器
Class<?> aClass2= Class.forName("com.neu.domain.Person",false,RJava.class.getClassLoader());
//当这两个方法 同时存在的时候,以第一个方法优先。(但是谁会无聊用两个办法去创建同一个被反射类的class对象呢)
//方式二:
//这个方式并 不加载 被反射的类的静态方法或静态成员变量
//这个方法是把类加载到内存,但是却不执行初始化操作,只是单纯的返回Class对象,所以这个调用这个方法并没有加载类的静态代码块
Class<Person> personClass = Person.class;
//方式三:
//通过类的对象来获取被反射类的class对象(new一个对象当然必须会调用这个类的构造器)
/* Person pp = new Person();
Class<?> ppClass= pp.getClass();*/
System.out.println(aClass2);
System.out.println("************");
//获取被反射类实现的所有接口
//如果没有实现接口则获取到空
Class<?>[] interfaces = personClass.getInterfaces();
for (Class i:interfaces) {
System.out.println("实现的接口"+i);
}
System.out.println("************");
//获取到对应的构造器
//通过传入的类型的简单类名,获取到被反射类的相应类型的构造器,简单类名的书写顺序必须与被反射类的构造器参数类型的顺序一致
//就是说,被反射的那个类,有多种构造器,但是那么多种的构造器都是通过重载来实现的,这就根据getConstructor传入的参数类型来获取到对应的构造器
//getConstructor方法的参数时可变参数
Constructor<?> constructor = personClass.getConstructor(Integer.class,String.class);
System.out.println(constructor);
//调用获得的构造器方法(由于是调用有参构造器,所以必须先执行无参构造器)
//newInstance方法传入的参数时根据该构造器需要的参数而确定的
Person person = (Person)constructor.newInstance(1,"张三");
System.out.println("id是:"+person.getId()+"名字是:"+person.getName());
System.out.println("************");
//获取到被反射类指定的成员变量
//注意:严重注意!!!如果反射类的成员变量不是public修饰的,getField方法就不能拿到该指定的成员变量,会报错
Field field = personClass.getField("name");
System.out.println(field);
//获取到被反射类所有的public修饰的成员变量
Field[] fields = personClass.getFields();
for (Field f:fields) {
System.out.println(f);
}
System.out.println("************");
//获取到被反射的所有方法,无参方法、有参方法、继承父类的方法,除了构造器方法不能获取
Method[] methods = personClass.getMethods();
for (Method m:methods) {
System.out.println(m);
}
System.out.println("************");
//获得被反射类的指定方法
//获取无参方法只需要传入方法名即可
//获取有参方法传入方法名,还必须传入参数类型的简单类名
Method method = personClass.getMethod("setName",String.class);
System.out.println(method);
System.out.println("************");
Person p = new Person();
//反射精髓!!!!!invoke
//invoke方法的第一个参数是一个对象,什么对象呢,就是被反射类的对象,
// 调用这个对象的method方法(调用什么方法还得看需要获取被反射类的什么方法),传入某某参数
// 这个参数时可变参数,但调用无参方法时,参数就直接不写
//因为需要这个被反射类的对象,所以这个被反射的类就被构造出来,调用了无参的构造方法
method.invoke(person,"");
System.out.println(person.getName());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
最后附上一点额外的知识点:
泛型:
E : Element (指定一般用于集合)
T : Type(指定Java的某个类)
K : Key(指定键)
V : Value(指定值)
N :Number(指定数值类型)
? : 表示不确定的java类型
Class<?> :表示不确定类型的类
这些泛型的存在是为了约束java类型,也简化了开发
日后还需要深入去了解这些泛型的奥妙。
*
初步探索java反射的奥妙,入门小白,还请大家指点一二。欢迎各位留言讨论,一起学习鸭。