Java反射机制是在运行状态中,对于任意一个类(class文件),都能够知道这个类的
所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能就称为Java语言的反射机制。
动态获取类中的信息就是Java的反射机制。可以理解为对类的解剖。
反射大大的提高了程序的扩展性。
反射是程序为外部提供了接口和配置文件,通过对接口的实现和对配置文件的配置,程序通过反射会自动加载实现了接口的类,从而读取类中内容,实现程序的扩展。
通过一个类的字节码文件获得该类的属性和方法。
通过class文件对象去使用该文件中的成员变量,构造方法,成员对象
要想这样使用,必须先得到class文件的对象,其实就是的到class类的对象
class类包括:
成员变量
构造方法
成员方法
获取class文件对象的方式
A:object类的getClass()
--Person p = new Person();
Class c = p.getClass();
B: 数据类型的静态属性class
--Class c = Person.class;或
--Class c = int.class;或
--Class c = String.class;
C: class类中的静态方法
--public static Class forName(String className)
--括号内的部分写的是类的带包全路径,即使是在同一个包下也要写全路径,否则会报找不到异常
--Class c = Class.forName("com.qfxa.Person");
一般我们使用哪一种呢?
A:自己玩 可以任选一种,第二种比较方便
B: 开发 选第三种
因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中。
通过反射获取构造方法并使用
1)获取字节码文件对象
Class c = Class.forName("com.qfxa.Person");
2)获取构造方法
public Constructor[] getConstructors(); --所有public的构造方法
--返回一个包含某些Constructor对象的数组,这些对象反映此Class对象所表示的类的所有**公共**构造方法。
Constructor[] cons = c.getConstructors();
//遍历获取的构造方法
for(Constructor con : cons){
System.out.println(con);
}
2)想获取所有的构造方法,可以用
public Constructor[] getDeclaredConstructors();
这个方法,返回Constructor对象的一个数组,这些对象反映此Class对象表示的类声明的**所有**构造方法。
--Constructor[] cons = c.getDeclaredConstructors();
想获取单个构造方法,可以用(但是只能获得public修饰的构造方法)
public Constructor getConstructor(Class>...parameterTypes)
--...代表可变参数,有参数也行没有参数也行
--因为构造方法的参数可能有很多个也可能没有,所以用可变参数来代替所有的参数。
--参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象。
--Constructor con = c.getConstructor(); //返回的是空参构造方法对象。
--Constructor con = c.getConstructor(String.class,int.class,String.class);//返回的是例子中的三参构造方法对象。
通过反射获取私有的构造方法
public Constructor getDeclaredConstructor(Class>...parameterTypes)
--Constructor con = c.getDeclaredConstructor(String.class);
--这样会获得私有的构造方法,但是不能访问,因为私有的构造方法在外界不能访问。
--con.setAccessible(true); --通过这样设置,值为true代表反射的对象在使用时应取消java语言访问检查,这时候就可以访问私有构造方法了。
3)根据获取的构造方法创建新实例
--用的方法:public T newInstance(Object... initargs)
--使用此Constructor对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
--T代表泛型
--Object obj = con.newInstance(); --obj表示的是Person类的对象
--Object obj = con.newInstance("tom",12,"haha"); --表示是通过三参构造方法获得的新实例。
通过反射获取成员变量并使用
--获取所有成员
getFields.getDeclaredFields
--获取单个成员
getFields.getDeclaredField
--修改成员的值
set(Object obj,Object value) //将指定对象上此Field对象表示的字段设置成指定的新值。
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
//通过反射获取成员变量并使用
public class ReflectDmeo {
public static void main(String[] args) throws Exception {
//获取字节码文件对象
Class c = Class.forName("com.qfxa.Person");
//获取所有的public成员变量
Field[] fields1 = c.getFields();
for(Field field : fields1){
System.out.println(field);
}
//获取所有的成员变量
Field[] fields = c.getDeclaredFields();
for(Field f : fields){
System.out.println(f);
}
//获取单个成员变量
//获取address并对其赋值
/**
* Person p = new Person();
* p.address = "Beijing";
* System.out.println(p);
*/
Field addressField = c.getField("address");
//通过无参构造创建对象
Constructor constructor = c.getConstructor();
Object object = constructor.newInstance();
//System.out.println(object);
//public void set(Object obj,Object value)
//将指定对象变量上此Field对象表示的字段设置为指定的新值。
addressField.set(object, "beijing"); //给object对象的addressField字段设置值为北京
/**
* 需求:通过反射获取成员变量并使用
* 1.获取类的字节码文件对象
* --Class c = Class.forName("com.qfxa.Person");
* 2.获取单个成员变量address
* --Field addressField = c.getField("address");
* 3.通过无参构造创建对象(原因是给成员变量设置新值时需要指定是哪个对象,所以需要创建一个对象)
* --Constructor constructor = c.getConstructor();
* --Object object = constructor.newInstance();
* 4.将指定对象变量上此Field对象表示的字段设置为指定的新值
* --addressField.set(object, "beijing");
*/
//获取成员变量name并对其赋值
//Field nameField = c.getField("name"); --会报下面错误
//Exception in thread "main" java.lang.NoSuchFieldException: name
//报错是因为name这个成员变量是私有的,所以要用另一个方法getDeclaredField
Field nameField = c.getDeclaredField("name");
//Exception in thread "main" java.lang.IllegalAccessException: Class com.qfxa03.ReflectDmeo can not access a member of class com.qfxa.Person with modifiers "private"
//还报错是因为获取的成员变量name是私有的,不能访问,所以要设置setAccessible为true
nameField.setAccessible(true);
//修改成员变量name的值
nameField.set(object, "linqingxia");
System.out.println(object);
//获取成员变量age并赋值
//管他的,直接用getDeclaredField方法调用,以便于能获得私有的成员变量
Field ageField = c.getDeclaredField("age");
//同样,管他的,直接设置了取消java的访问检查,以便于能对私有成员变量进行访问
ageField.setAccessible(true);
//设置新建的object对象的age为指定值
ageField.set(object, 10);
System.out.println(object);
}
}
获取所有方法
--getMethods
--getDeclaredMethods
获取单个方法
--getMethod
--getDeclaredMethod
暴力访问
--method.setAccessible(true);
package com.qfxa04;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectDemo {
public static void main(String[] args) throws Exception{
//获取字节码文件
Class c = Class.forName("com.qfxa.Person");
//获取所有的方法
--Method[] methods = c.getMethods(); //--获取自己的和父亲的public方法
--Method[] methods = c.getDeclaredMethods(); //--获取自己的所有方法不包括父亲的
//获取单个方法并使用
/**
* Person p = new Person();
* p.show();
*/
//获得构造方法
Constructor con = c.getConstructor();
//根据构造方法创建对象
Object object = con.newInstance();
//要拿到public void show()方法
//获取单个方法需要用到这个方法
--public Method getMethod(String name,Class>...ParameterTypes)
--第一个参数表示的参数名,第二个参数表示的是方法的参数的class类型
Method m1 = c.getMethod("show");
//public Object invoke(Object obj,Object... args)
--返回的是object接收,第一个参数表示对象是谁,第二个参数表示调用该方法的参数
m1.invoke(object); //其本质是调用objec的m1方法
--因为show 方法没有返回值也没有参数,所以这样写。
//获得public void method(String s)这个方法
Method m2 = c.getMethod("method", String.class);
m2.invoke(object, "hahaha");
//获取public String getString(String s,int i)这个方法
Method m3 = c.getMethod("getString", String.class, int.class);
Object objString = m3.invoke(object, "haha", 10);或者
String string = (String)m3.invoke(object, "hahah", 10);
--当你知道返回的具体是什么类型的时候可以直接强制转成对应类型,否则用object接收,因为object可以接收所有类型
System.out.println(objString);
System.out.println(string);
//获得private void function()这个方法
Method m4 = c.getDeclaredMethod("function");
m4.setAccessible(true);
m4.invoke(object);
}
}