反射允许对成员变量,成员方法和构造方法的信息进行编程访问。 1、获取Class对象(字节码文件对象)的三种方式 ①、Class.forName(“全类名”) (在源代码阶段使用) ②、类名.class(在
反射允许对成员变量,成员方法和构造方法的信息进行编程访问。
1、获取Class对象(字节码文件对象)的三种方式
①、Class.forName(“全类名”) (在源代码阶段使用)
②、类名.class (在加载阶段中使用)
③、对象.getClass() (在运行阶段使用)
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
//最为常用
final Class<?> aClass = Class.forName("Test05.Student");
System.out.println(aClass);
//一般更多的时当做参数进行传递
final Class<Student> studentClass = Student.class;
System.out.println(studentClass);
//需要有对象才可以使用
Student s = new Student();
final Class<? extends Student> aClass1 = s.getClass();
System.out.println(aClass1 == aClass); //true
System.out.println(aClass == studentClass); true
}
}
2、反射获取构造方法
如果要获取指定的构造方法,可以给方法中添加和构造相同的参数,如下所示:
import java.lang.reflect.Constructor;
public class Test1 {
public static void main(String[] args) throws NoSuchMethodException {
//获取Student的字节对象
final Class<Student> studentClass = Student.class;
//获取指定参数的构造方法
final Constructor<Student> declaredConstructor = studentClass.getDeclaredConstructor(String.class);
//int类型的形参
studentClass.getDeclaredConstructor(int.class);
System.out.println(declaredConstructor);
}
}
3、利用反射获取成员方法
4、利用反射获取成员方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test1 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//创建调用方法的对象
Student stu = new Student();
//获取字节码对象
final Class<Student> clazz = Student.class;
//获取方法:第一个参数为方法名,第二个参数为方法中的形参
final Method method1 = clazz.getDeclaredMethod("show", String.class);
//参数一:表示调用方法的对象
//参数二:表示给调用方法中传递的形参
method1.invoke(stu,"是酋长呀!");
}
}
5、反射的作用
①、获取一个类里面所有的信息,获取到了之后,再执行其它的业务逻辑。
②、结合配置文件,动态的创建对象并调用方法。
5.1、案例一:对于任意一个对象,都可以把对象所有的字段名和值保存到文件中。
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
public class Test1 {
public static void main(String[] args) {
Student stu = new Student("张三", 121);
try {
saveObject(stu);
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
//封装成一个方法
public static void saveObject(Object obj) throws IOException, IllegalAccessException {
//获取字节码对象
final Class<?> clazz = obj.getClass();
//创建io流
BufferedWriter bw = new BufferedWriter(new FileWriter("F:\\Java基础\\Test02\\bb\\Information.txt"));
//获取所有的成员变量
final Field[] fields = clazz.getDeclaredFields();
//遍历上面的成员变量
for (Field field : fields) {
//先暂时取消访问权限
field.setAccessible(true);
//获取成员变量的名称
final String name = field.getName();
//获取成员变量的值,参数为一个对象
final Object value = field.get(obj);
//写出数据
bw.write(name + " = " + value);
//换行
bw.newLine();
}
//关闭io流的资源
bw.close();
}
}
5.2、案例二:反射可以跟配置文件(后缀为properties的文件)结合的方式,动态的创建对象,并调用方法。
配置文件:
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class Test1 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//读取配置文件的信息
Properties prop = new Properties();
//创建io流
FileInputStream fis = new FileInputStream("F:\\Java基础\\Test02\\prop.properties");
//加载
prop.load(fis);
//关闭资源
fis.close();
//获取全类名和方法
final String className = (String) prop.get("classname");
final String methodName = (String) prop.get("method");
//利用反射创建对象并且运行方法
final Class<?> clazz = Class.forName(className);
//获取构造方法
final Constructor<?> con = clazz.getDeclaredConstructor();
//通过构造方法创建对象
final Object o = con.newInstance();
//获取成员方法并运行
final Method method = clazz.getDeclaredMethod(methodName);
//设置临时访问权限
method.setAccessible(true);
//调用方法,对象为上面通过构造方法创建的那个
method.invoke(o);
}
}
6、动态代理
步骤一:创建类
public class BigStar implements Star {
private String name;
private int age;
//实现唱歌的方法
@Override
public void sing(String name) {
System.out.println(this.name + "正在唱" + name);
}
//实现跳舞的方法
@Override
public void dance() {
System.out.println(this.name + "正在跳舞");
}
public BigStar() {
}
public BigStar(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
*
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
*
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
*
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "BigStar{name = " + name + ", age = " + age + "}";
}
}
步骤二:把类中需要代理的方法添加封装成一个接口,所以类中需要实现接口中的抽象方法。
/**
* 定义接口
* */
public interface Star {
//唱歌的抽象方法
void sing(String name);
//跳舞的抽象方法
void dance();
}
步骤三:封装生成代理的类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtil {
//私有化构造方法
private ProxyUtil() {
}
//产生代理
public static Star creatProxy(BigStar bigStar) {
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(), //参数一:用于指定用那个类加载器,去加载生成的代理类
new Class[]{Star.class}, //参数二:指定接口,这些接口用于指定生成的代理长什么样,也就是有那些方法,这里是数组的形式,可以添加多个接口,但是多个接口都需要被实现
//参数三:用来指定生成的代理对象要做什么事情
new InvocationHandler() {
@Override
/**
* 参数一:代理的对象,一般不用管
* 参数二:要运行的方法
* 参数三:调用要运行的方法时传递的实参
* */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("sing".equals(method.getName())) {
System.out.println("准备话筒,布置舞台!");
} else if ("dance".equals(method.getName())) {
System.out.println("准备舞伴,布置舞台!");
}
//利用反射调用BigStar中的方法
return method.invoke(bigStar, args);
}
}
);
return star;
}
}
步骤四:通过生成代理的类生成一个代理对象,然后通过这个对象调用相应的方法
public class Test {
public static void main(String[] args) {
//创建对象
BigStar bigStar = new BigStar("邓紫棋", 28);
//获取代理的对象
final Star star = ProxyUtil.creatProxy(bigStar);
//通过代理调用里面的方法
star.sing("倒数");
star.dance();
}
}
最后输出结果:
准备话筒,布置舞台! 邓紫棋正在唱倒数 准备舞伴,布置舞台! 邓紫棋正在跳舞