Java反射 笔记

Java的反射机制是指在运行状态中,对于任意一个类都能够知道这个类的所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法。
Java对象中许多对象运行时都会出现两种类型:编译时类型和运行时类型。编译时类型由声明对象时使用的类型决定,运行时的类型由实际赋值给对象的类型决定。如:

Person p=new Student;

其中编译时类型为Person,运行时类型为Student。

但是编译时类型无法获取具体方法。程序在运行时还可能接收到外部传入的对象,该对象的编译时类型为Object,但是程序有时需要调用该对象运行时类型的方法,因此程序需要在运行时发现对象和类的真实信息,既然编译时类型根本无法预知该对象和类的具体类(是说Object类只是传入对象的最高父类,不知道传入的这个对象到底是个啥类吧?),程序只能依靠运行时类型来发现该对象和类的真实信息,此时就需要反射

Java反射API

  • Class类:反射的核心类,可以获取类的属性,方法等信息;
  • Field类:表示类的成员变量,可以用来获取和设置类之中的属性值;
  • Method类:表示类的方法,它可以用来获取类中的方法信息或者执行方法;
  • Constructor:表示类的构造方法。

反射的使用步骤

  • 获取想要操作类的Class对象(这是反射的核心),通过Class对象我们可以任意调用类的方法。
  • 调用Class类中的方法(即反射的使用阶段)
  • 使用反射API来操作这些信息(不知道说的啥)

获取Class对象的3种方法
调用某个对象的getClass()方法:

Person p=new Person();\
Class clazz=p.getClass();

调用某个类的class属性来获取该类对应得Class对象:

Class clazz=Person.class;

使用Class类中的forName()静态方法(最安全且性能最好)

Class clazz=Class.forName("reflection.Person");	//输入的是类的全路径

当我们获得了想要操作的类的Class对象后,可以通过Class类中的方法获取并查看该类中方法和属性:

//获取Person类的Class对象
Class clazz=Class.forName("reflection.Person");
//获取Person类的所有方法信息
Method[] methods=clazz.getMethods();
for (Method method : methods) {
	System.out.println("方法:"+method);
}
//获取所有成员属性信息
Field[] fields=clazz.getFields();
for (Field field : fields) {
	System.out.println("属性:"+field);
}
//获取构造器信息(是获取的构造器还是只是信息?)
Constructor[] constructors=clazz.getConstructors();
for (Constructor constructor : constructors) {
    System.out.println("构造器:"+constructor);
}

创建对象的两种方法
Class对象的newInstance(): 使用Class对象的new Instance()方法来创建该Class对象对应类的实例,这种方法要求该Class对象对应的类有默认的空构造器。
调用Constructor对象的newInstance(): 先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建Class对象对应类的实例,通过这种方法可以选定构造方法创建实例。

//获取Person类的Class对象
Class clazz=Class.forName("reflection.Person");
//使用newInstance方法创建对象
Person p=(Person) clazz.newInstance();
//获取构造方法并创建对象
Constructor c=clazz.getDeclaredConstructor(String.class,String.class,int.class);
//创建对象并设置属性
Person p1=(Person) c.newInstance("李四""男"20)

获取构造函数
一个类会有多个构造函数,getConstructors()返回的是Constructor[]数组,包含了所有声明的用public修饰的构造函数。
如果你已经知道了某个构造的参数,可以通过下面的方法获取到回应的构造函数对象:其中new Class[]{String.class}通过参数类型列表指定了获得的构造器(应该是用的重载的原理吧?)

public class Alunbar {
    public static void  main(String arts[]){

        Class birdClass = Bird.class;
        try{
            Constructor constructors = birdClass.getConstructor(new Class[]{String.class});
        }catch(NoSuchMethodException  e){

        }
    }

    private class Bird {
        public Bird(){

        }

        public Bird(String eat){

        }
    }
}

获取Methods方法信息
无参的getMethods()获取到所有public修饰的方法,返回的是Method[]数组。 无参的getDeclaredMethods()方法到的是所有的成员方法,和修饰符无关。对于有参的getMethods()方法,必须提供要获取的方法名以及方法名的参数。

Method[] metchods = birdClass.getMethods();
Method[] metchods1 = birdClass.getDeclaredMethods();
Method eatMetchod = birdClass.getMethod("eat", new Class[]{int.class});
Method eatMetchod1 = birdClass.getDeclaredMethod("eat", new Class[]{int.class});

如果要获取的方法没有参数,则用null替代:

Method eatMetchod = birdClass.getMethod("eat", null);

注意无参的getMethods()和getDeclaredMethods()都只能获取到类声明的成员方法,不能获取到继承父类的方法。

invoke()方法
java反射提供invoke()方法,在运行时根据业务需要调用相应的方法,这种情况在运行时非常常见,只要通过反射获取到方法名之后,就可以调用对应的方法。invoke方法有两个参数,第一个参数是要调用方法的对象,上面的代码中就是Bird的对象,第二个参数是调用方法要传入的参数。如果有多个参数,则用数组。

Class birdClass = Bird.class;
Constructor constructors1 = birdClass.getConstructor();
Method eatMetchod = birdClass.getMethod("eat", new Class[]{int.class});	//获得了eat方法
System.out.println(eatMetchod.invoke(constructors1.newInstance(), 2));	//invoke调用eat方法,传入对应参数,相当于new Bird().eat(2)

如果调用的是static方法,invoke()方法第一个参数就用null代替:

public class Alunbar {
    public static void  main(String arts[]){
        try{
            Class birdClass = Bird.class;
            Constructor constructors1 = birdClass.getConstructor();
            Method eatMetchod = birdClass.getMethod("eat", new Class[]{int.class});
            System.out.println(eatMetchod.invoke(null, 2));
        }catch(Exception  e){
            e.printStackTrace();
            System.out.println("没有对应的构造函数");
        }
    }

}

class Bird{
    public static int eat(int eat){
        return eat;
    }
    public Bird(){

    }

    public Bird(String eat){

    }

    private void talk(){}
 }

使用反射可以在运行时检查和调用类声明的成员方法,可以用来检测某个类是否有getter和setter方法。getter和setter是java bean必须有的方法。getter和setter方法有下面的一些规律: getter方法以get为前缀(那看来是用字符串比较来检验了),无参,有返回值 setter方法以set为前缀,有一个参数,返回值可有可无, 下面的代码提供了检测一个类是否有getter和setter方法:

public static void printGettersSetters(Class aClass){
  Method[] methods = aClass.getMethods();

  for(Method method : methods){
    if(isGetter(method)) System.out.println("getter: " + method);
    if(isSetter(method)) System.out.println("setter: " + method);
  }
}

public static boolean isGetter(Method method){
  if(!method.getName().startsWith("get"))      return false;	//函数名不以get开头的不是
  if(method.getParameterTypes().length != 0)   return false;	//参数列表长度不为0的不是
  if(void.class.equals(method.getReturnType()) return false;	//返回值类型为void的不是
  return true;
}

public static boolean isSetter(Method method){
  if(!method.getName().startsWith("set")) return false;	//函数名不以set开头的不是
  if(method.getParameterTypes().length != 1) return false;	//参数列表长度不为1的不是
  return true;
}

获取成员变量
getFields()方法获取所有public修饰的成员变量,getField()方法需要传入变量名,并且变量必须是public修饰符修饰。getDeclaredFields方法获取所有生命的成员变量,不管是public还是private。

Class birdClass = Bird.class;
Field[] fields1 = birdClass.getFields();
Field[] fields2 = birdClass.getDeclaredFields();
Field fields3 = birdClass.getField("age");
Field fields4 = birdClass.getDeclaredField("age");

一旦获取到成员变量的Field引用,就可以获取通过get()方法获取变量值,通过set()方法给变量赋值:

Class birdClass = Bird.class;
Field fields3 = birdClass.getField("age");
Bird bird = new Bird();
Object value = fields3.get(bird);	//获取了bird实例的field3代表的成员变量的值,等价于调用bird.getAge()
fields3.set(bird, value);	//将bird实例的age值设为value,等价于调用bird.setAge(value),注意fields3.set(bird,value)的fields3就是指定了set方法中使用的是age的set方法即setAge()

访问私有变量
使用 Class.getDeclaredField(String name)或者Class.getDeclaredFields()才能获取到私有变量。必须调用setAccessible(true)方法,这是针对私有变量而言,public和protected等都不需要。这个方法是允许通过反射访问类的私有变量。

public class PrivateField {
    private String name;

    public PrivateField(String name){
        this.name = name;
    }
}

public class PrivateFieldTest {
    public static void main(String args[])throws Exception{
        Class privateFieldClass = PrivateField.class;
        Field privateName = privateFieldClass.getDeclaredField("name");
        privateName.setAccessible(true);
        PrivateField privateField = new PrivateField("Alunbar");
        String privateFieldValue = (String) privateName.get(privateField);
        System.out.println("私有变量值:" + privateFieldValue);
    }
}

访问私有方法
和私有变量一样,私有方法也是不允许其他的类随意调用的,但是通过反射可以饶过这一限制。 使用Class.getDeclaredMethod(String name, Class[] parameterTypes)或者Class.getDeclaredMethods()方法获取到私有方法。

public class PrivateMethod {
    private String accesPrivateMethod(){
        return "成功访问私有方法";
    }
}

public class PrivateMethodTest {
    public static void main(String args[])throws Exception{
        Class privateMethodClass = PrivateMethod.class;

        Method privateStringMethod = privateMethodClass.getDeclaredMethod("accesPrivateMethod", null);
        privateStringMethod.setAccessible(true);
        String returnValue = (String)privateStringMethod.invoke(new PrivateMethod(), null);

        System.out.println("returnValue = " + returnValue);
    }
}

访问类注解信息
访问类的所有注解信息:

Class aClass = TheClass.class;
Annotation[] annotations = aClass.getAnnotations();

for(Annotation annotation : annotations){
    if(annotation instanceof MyAnnotation){
        MyAnnotation myAnnotation = (MyAnnotation) annotation;
        System.out.println("name: " + myAnnotation.name());
        System.out.println("value: " + myAnnotation.value());
    }
}

泛型方法返回类型
下面的代码使用反射检测getStringList()方法返回的是List而不是List。通过getActualTypeArguments()获取泛型参数列表,最后打印出java.lang.String

public class MyClass {
    protected List<String> stringList = Arrays.asList("zhf","unknown");

    public List<String> getStringList(){
        return this.stringList;
    }

    public static void main(String[] args) throws Exception {
        Method method = MyClass.class.getMethod("getStringList", null);
        Type returnType = method.getGenericReturnType();
        if (returnType instanceof ParameterizedType){
            ParameterizedType type=(ParameterizedType) returnType;
            Type[] types=type.getActualTypeArguments();
            for (Type type1 : types) {
                Class typeClass= (Class) type1;
                System.out.println(typeClass.getName());
            }
        }
    }
}

详细参考

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值