Java-反射机制

一、反射:

1、什么是java反射机制;

Java反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息及动态调用对象方法的机制就是反射。

2、反射的作用

反射机制允许在运行时取得任何一个已知名称的类的内部信息,包括属性、方法等。运用反射可以使编写的代码更加灵活,同时还可以降低代码的耦合度。有利就有弊,反射可以获取任意一个类的属性和方法,破坏了类的封装性。

3、Class类对象的三种产生方式:

  • 任何类的实例化对象,都可通过Object类的 getClass() 方法取得Class对象:

public final native Class<?> getClass();
  • “类 . class“:直接根据某个具体的类来取得 Class 类的实例化对象。

  • 使用Class类提供的 forName() 静态方法:

public static Class<?> forName(String className) throws ClassNotFoundException;

来看具体的例子:

import java.util.Date;
public class Test {
    public static void main(String[] args) {
        Date date = new Date() ;
        //1、getClass()获取对象的类信息:如传过来的是Object对象不知道具体类型就可通过这种方法获取
        Object obj1 = date.getClass();

        //2、类.class:这种方法获取对象方便可靠
        Object obj2 = Date.class;

        //3、Class.forName():知道一个类的全限定名,就可获得该类对象,可能抛出ClassNotFoundException异常
        try {
            Object obj3 = Class.forName("java.util.Date");
            //三者的关系
            System.out.println(obj1.equals(obj2) && obj2.equals(obj3));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

那么上面三种方式产生的对象之间有什么关系呢?程序运行结果如下:

结果是true,说明上面obj1、obj2、obj3都是同一个对象。因此需要注意:在JVM中一个类只有一个Class实例。


二、反射与类操作

(1)取得父类信息

java中的任何类都一定有父类,通过以下方法可以得到父类的信息:

  • 取得类的包名称: public Package getPackage()
  • 取得父类的Class对象: public native Class<? super T> getSuperclass();
class Message{...}
class MessageImpl extends Message{...}
public class Demo{
    public static void main(String[] args) {
        //取得Class对象
        Class<?> cls = MessageImpl.class;
        System.out.println(cls.getName());
        //取得包名
        System.out.println(cls.getPackage().getName());
        //取得父类对象
        Class superclass = cls.getSuperclass();
        System.out.println(superclass.getName());
    }
}

(2)反射调用构造

  • 取得指定参数类型的构造:
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException;
  • 取得类中的所有构造:
public Constructor<?>[] getConstructors() throws SecurityException;

这两个方法返回的都是java.lang.reflect.Constructor类的实例化对象,在Constructor类中重点关注newInstance()这个方法:

public T newInstance(Object ... initargs) throws InstantiationException, IllegalAccessException,IllegalArgumentException, InvocationTargetException;

举个例子:

package com.mytest;
import java.lang.reflect.Constructor;

class Person {
    public Person() {}
    public Person(String name) {}
    public Person(String name,int age) {}
}
public class Demo {
    public static void main(String[] args) {
        Class<?> cls = Person.class ;
        // 取得类中的全部构造
        Constructor<?>[] constructors = cls.getConstructors() ;
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
    }
}

运行后成功拿到了Person类的所有构造方法:

上面信息打印调用了Constructor类中的toString()方法,因此取得了构造方法的完整信息(包含权限方法权限和参数列表),如果使用getName()方法,只能拿到构造方法的包名.类名。

另外需要注意:在定义简单java类的时候一定要有无参构造,Class类通过反射实例化对象的时候,只能调用无参构造,如果没有无参构造会出现InstantiationException(实例化异常)。

出现这个问题的解决的办法有两种:

  • 一种就是加一个无参构造
  • 另一种方法就是明确调用指定参数的构造方法
class Person {
    private String name ;
    private int age ;
    //方法一:在这里添加无参构造
    public Person(String name,int age) {
        this.name = name ;
        this.age = age ;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}
public class Demo {
    public static void main(String[] args) throws InstantiationException,
            IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class<?> cls = Person.class ;
        //方法二:调用具体的构造方法
        Constructor<?> constructor = cls.getConstructor(String.class,int.class);
        System.out.println(constructor.newInstance("Tony",20));
    }
}

(3)反射调用普通方法

  • 取得全部普通方法
public Method[] getMethods() throws SecurityException;
  • 取得指定普通方法
public Method getMethod(String name, Class<?>... parameterTypes);

这两个方法返回类型都是 java.lang.reflect.Method 类的对象,在Method类中有一个支持调用的方法invoke()

public Object invoke(Object obj, Object... args)throws IllegalAccessException,IllegalArgumentException,InvocationTargetException;

还是以上面的例子来说明:

public class Demo {
    public static void main(String[] args) throws InstantiationException,
            IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class<?> cls = Person.class ;
        //调用类中的普通方法必须有实例化对象
        Object obj = cls.newInstance();
        //取得setName()方法的实例化对象
        Method setNameMethod = cls.getMethod("setName",String.class);
        Method setAgeMethod = cls.getMethod("setAge",int.class);
        //Method类对象调用指定方法
        setNameMethod.invoke(obj,"Jack");
        setAgeMethod.invoke(obj,20);
        Method getNameMethod = cls.getMethod("getName");
        Method getAgeMethod = cls.getMethod("getAge");
        //获取结果
        Object nameResult = getNameMethod.invoke(obj);
        Object ageResult = getAgeMethod.invoke(obj);
        System.out.println(nameResult + " " + ageResult);
    }
}

上面这种调用方式,可以通过Object类型实现所有类的方法调用,而不局限于某一具体类型对象。

(4)反射调用类中的属性

类中的所有属性是在类对象实例化之后才会分配空间,因此想要取得类中的属性,就必须有类的实例化对象。通过反射的newInstance()可以直接取得实例化对象(Object类型)。

  • 取得本类中的全部属性:
public Field[] getDeclaredFields() throws SecurityException;
  • 取得本类中的指定属性:
public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException;

上面两个方法返回的是java.lang.reflect.Field类型,在Filed类中有两个重要的方法set()和get():

//1、设置属性内容 : 
public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException;

//2、取得属性内容 : 
public Object get(Object obj) throws IllegalArgumentException,IllegalAccessException;

如果要修改类中的私有属性,则要使用到一个AccessibleObject类的setAccessible()方法,来动态设置是否封装:

public void setAccessible(boolean flag) throws SecurityException;

来看例子:

public class Demo {
    public static void main(String[] args) throws InstantiationException,
            IllegalAccessException, NoSuchFieldException, ClassNotFoundException {
        Class<?> cls = Class.forName("com.mytest.Person") ;
        //调用类中的普通方法必须有实例化对象
        Object obj = cls.newInstance();
        Field nameField = cls.getDeclaredField("name");
        Field ageField = cls.getDeclaredField("age");
        //取消封装:取消后可以修改私有属性
        nameField.setAccessible(true);
        ageField.setAccessible(true);
        //设置属性值
        nameField.set(obj,"Marry");
        ageField.set(obj,18);
        //取出
        System.out.println(nameField.get(obj) + " " + ageField.get(obj));
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值