一.反射的概念:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。 对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。另一种解释,反射就是把Java类中的各种成分映射成相应的java类。
字节码文件(class文件):java文件经过编译器预处理后生成的二进制的文件就是字节码文件。
2.获取字节码文件对应的class类型的对象的三种方法:
(1)源文件阶段(.java):
Class c = class.forName(“包名.类名”);
//作用:读取配置文件
例子(通过读取配置文件创建class类的方法):
1).创建一个配置文件(例如配置文件名为“config.properties”),配置文件中添加需要调用的”全包名.类名”(如:com.reflect.类名)。
2).类名的代码如下:
class Cat implements Animal{
public void eat(){
System.out.println(“吃鱼”);
}
}
3).类名的父类(接口)代码,使用父类(接口)功能的调用类:
interface Animal{
public void eat();
}
class Using{
Public void use(Animal a){ //传入动物对象
A.eat();
}
}
4).使用配置文件获取字节码的代码:
public class ReflectTest{
/**
* 动物吃肉的案例
* 分别有动物(Animal)猫(CAt)狗(Dog) * @throws Exception
*/
public static void main(String[]args)throws Exception{
BufferedReader bufr = new BufferedReader(new FileReader("config.properties")); //创建输入流对象,关联配置文件
Class c = Class.forName(bufr.readLiine());
//读取配置文件一行内容,获取该类的字节码对象
Animal a = (Animal)c.newInstance();
//通过字节码对象创建实例对象,同时利用多态
Using u = new Using();
U.use(a);
}
}
(2)java文件编译成class文件后:
Class c = 类名.class;
//作用:当作静态方法的锁对象
(3)创建实例(对象)后:
Object obj = new Object();
Class c = obj(对象名).getClass();//利用的是Object的getClass方法。
//作用:判断是否是同个字节码文件
二.反射常用的:Constructor、Field、Method
1.Constructor:Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:
例子:Constructor [] constructors= Class.forName("java.lang.Integer").getConstructors();
得到某一个构造方法:
例子: Constructor constructor = Class.forName(“java.lang.Integer”).getConstructor(String.class);
//获得方法时要用到类型,(构造一个新分配的 Integer 对象,它表示 String 参数所指示的 int 值。)
创建实例对象:
通常方式:Integer in= new Integer("5"));
反射方式:
Constructor constructor = Class.forName(“java.lang.Integer”).getConstructor(String.class);
nteger in = constructor.newInstance("5");
//调用获得的方法时要用到上面相同类型的实例对象
2.Field:提供有关类或接口的单个字段的信息,可以代表一个成员变量。
Class.getField(String)方法可以获取类中的指定字段(可见的), 如果是私有的可以用getDeclaedField("name")方法获取,通过set(obj, "李四")方法可以设置指定对象上该字段的值, 如果是私有的需要先调用setAccessible(true)设置访问权限,用获取的指定的字段调用get(obj)可以获取指定对象中该字段的值
例子1(将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。
):
class Xxx
{
String name="abc";
String email="abd";
int x = 5;
}
func(Object obj)
{
Field [] fields = obj.getClass().getDeclaredFields();
for(Field field : fields)
{
if(field.getType()==java.lang.String.class)
{
field.setAccesible(true);
String original = (String)field.get(obj);
field.set(obj,original.replaceAll("b","a");
}
}
}
例子2(修改字段的值):
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class FieldDemo {
/**
* Class.getField(String)方法可以获取类中的指定字段(可见的),
* 如果是私有的可以用getDeclaedField("name")方法获取,通过set(obj, "李四")方法可以设置指定对象上该字段的值,
* 如果是私有的需要先调用setAccessible(true)设置访问权限,用获取的指定的字段调用get(obj)可以获取指定对象中该字段的值
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Class clazz = Class.forName("Person"); //Person类和本类在同个包中
Constructor c = clazz.getConstructor(String.class,int.class); //获取有参构造
Person p = (Person) c.newInstance("张三",23); //通过有参构造创建对象
//Field f = clazz.getField("name"); //获取姓名字段
//f.set(p, "李四"); //修改姓名的值
Field f = clazz.getDeclaredField("name"); //暴力反射获取字段
f.setAccessible(true); //去除私有权限
f.set(p, "李四");
System.out.println(p);
}
}
class Person{
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
3.Method:Method类代表某个类中的一个成员方法
Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String, Class...)方法可以获取类中的指定方法,调用invoke(Object, Object...)可以调用该方法,Class.getMethod("eat") invoke(obj) Class.getMethod("eat",int.class) invoke(obj,10)
例子1: Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
调用方法:
String str = “sdfsfsg”;
通常方式:System.out.println(str.charAt(1));
反射方式: System.out.println(charAt.invoke(str, 1));
例子2:
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import com.heima.bean.Person;
public class DemoMethod {
/**
* Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String,
* Class...)方法可以获取类中的指定方法,调用invoke(Object,
* Object...)可以调用该方法,Class.getMethod("eat") invoke(obj)
* Class.getMethod("eat",int.class) invoke(obj,10)
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("com.heima.bean.Person");
Constructor c = clazz.getConstructor(String.class,int.class); //获取有参构造
Person p = (Person) c.newInstance("张三",23); //通过有参构造创建对象
Method m = clazz.getMethod("eat"); //获取eat方法
m.invoke(p);
Method m2 = clazz.getMethod("eat", int.class); //获取有参的eat方法
m2.invoke(p, 10);
}
}
例子3(将obj对象中名为propertyName的属性的值设置为value):
public class Test3 {
/**
* * A:案例演示
* public void setProperty(Object obj, String propertyName, Object value){},
* 此方法可将obj对象中名为propertyName的属性的值设置为value。
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Student s = new Student("张三", 23);
System.out.println(s);
Tool t = new Tool();
t.setProperty(s, "name", "李四");
System.out.println(s);
}
}
class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
import java.lang.reflect.Field;
public class Tool {
//此方法可将obj对象中名为propertyName的属性的值设置为value。
public void setProperty(Object obj, String propertyName, Object value) throws Exception {
Class clazz = obj.getClass(); //获取字节码对象
Field f = clazz.getDeclaredField(propertyName); //暴力反射获取字段
f.setAccessible(true); //去除权限
f.set(obj, value);
}
}
三.数组的反射的特征:
1.具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象(此处比较与值无关)。
2.代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。(Object[] 与String[]没有父子关系,Object与String有父子关系,所以new Object[]{“aaa”,”bb”}不能强制转换成new String[]{“aaa”,”bb”};,Object x = “abc”能强制转换成String x = “abc”。
)
3.基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
四:利用反射避开泛型的限制的实例:
import java.lang.reflect.Method;
import java.util.ArrayList;
public class Test {
/**
* @param args
* ArrayList<Integer>的一个对象,在这个集合中添加一个字符串数据,如何实现呢?
* 泛型只在编译期有效,在运行期会被擦除掉
* @throws Exception
*/
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<>();
list.add(111);
list.add(222);
Class clazz = Class.forName("java.util.ArrayList"); //获取字节码对象
Method m = clazz.getMethod("add", Object.class); //获取add方法
m.invoke(list, "abc");
System.out.println(list);
}
}
五:动态代理概述
* 代理:本来应该自己做的事情,请了别人来做,被请的人就是代理对象。
* 举例:春节回家买票让人代买
* 动态代理:在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理
* 在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib,Proxy类中的方法创建动态代理类对象
* public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
* 最终会调用InvocationHandler的方法
* InvocationHandler Object invoke(Object proxy,Method method,Object[] args)
以下为接口的代理例子:
public interface User {
public void add();
public void delete();
}
public class UserImp implements User {
@Override
public void add() {
//System.out.println("权限校验");
System.out.println("添加功能");
//System.out.println("日志记录");
}
@Override
public void delete() {
//System.out.println("权限校验");
System.out.println("删除功能");
//System.out.println("日志记录");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
接口的动态代理功能的类
*/
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("权限校验");
method.invoke(target, args); //执行被代理target对象的方法
System.out.println("日志记录");
return null;
}
}
import java.lang.reflect.Proxy;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
UserImp ui = new UserImp();
ui.add();
ui.delete();
System.out.println("-------------------------------"); /*
* public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
* InvocationHandler h)
*/
MyInvocationHandler m = new MyInvocationHandler(ui);
User u = (User)Proxy.newProxyInstance(ui.getClass().getClassLoader(), ui.getClass().getInterfaces(), m);
u.add();
u.delete();
}
}