反射机制介绍

(1)反射初识

反射指的是对象的反向处理操作,既然是反向处理。

怎么称为对象的反向操作呢?那对象正向操作是如何进行的?

我们先来观察一下"正"的操作。在默认情况下,必须要先导入一个包,而后才能产⽣类的实例化对象

观察正常处理:

import java.util.Date;
public class Test {
public static void main(String[] args) {
Date date = new Date() ;
     }
}

以上是我们正常的关于对象的处理流程:根据包名.类名找到类.这就是我们一般创建实例的方法.

而这个的"反"指的是根据对象来取得对象的来源信息,那么这个"反"的操作核心的处理就在于Object类的一个方法:

取得Class对象:

public final native Class<?> getClass();

该方法返回的是一个Class类对象,这个Class描述的就是类。

调用getClass()方法:

import java.util.Date;
public class Test {
public static void main(String[] args) {
Date date = new Date() ;
System.out.println(date.getClass());
   }
}

此时通过对象取得了对象的来源,这就是"反"的本质。
在反射的世界里面,看重的不再是一个对象,而是对象身后的组成(类、构造、普通、成员等)

(2)三种实例化对象方法

那么上面看到反射的源头就是类,那么只要抓住这个类对象如何产生,那么就知道怎么去用了,下面介绍一下三种实例化对象方法

  1. 任何类的实例化对象可以通过Object类中的getClass()方法取得Class类对象。
  2. “类.class”:直接根据某个具体的类来取得Class类的实例化对象。
  3. 使用Class类提供的方法:public static Class<?> forName(String className) throwsClassNotFoundException

看一下怎么使用这三种方法 :

//1.   
         Date date = new Date() ;
         date.getClass()
         
//2.Duck是我随便写的类,获取格式都是这样的         
       Class<Duck> clazz = Duck.class;
       
//3.       
       Class<?> clazz1 = Class.forName("frank.ioc.Duck");

在以上给出的三个方法中我们可以发现,除了第一种方法会产生Date类的实例化对象之外,其他的两种都不会产生Date类的实例化对象。于是取得了Class类对象有一个最直接的好处:可以通过反射实例化对象,在Class类中定义有如下方法:

public T newInstance()
throws InstantiationException, IllegalAccessException

反射实例化对象:

通过类对象,生成对象的实例: 通过构造方法生成

  1. 无参数构造方法创建
Class<?> clazz = Class.forName("frank.ioc.Duck");
Duck duck = (Duck) clazz.newInstance();
        System.out.println(duck);

2.有参数构造方法创建

Class<?> clazz = Class.forName("frank.ioc.Duck");
Duck duck2 = (Duck) clazz.getConstructor(String.class, Integer.class).newInstance("小黄鸭", 12);
        System.out.println(duck2);

现在发现除了关键字new之外,对于对象的实例化模式有了第二种做法,通过反射进行。
取得了Class对象就意味着取得了一个指定类的操作权

(3)反射与类操作

利用反射可以做出一个对象具备的所有操作行为,最为关键的是这一切的操作都可以基于Object进行。

下面来列举一些例子

<1>.取得父类信息

在java中任何的程序类都一定会有父类,在Class类中就可以通过如下方法来取得父类或者实现的父接口:

取得类的包名称: public Package getPackage()
取得⽗类的Class对象: public native Class<? super T> getSuperclass()
取得实现的父接口:public Class<?>[] getInterfaces()

package reflect;
interface IFruit {}
interface IMessage{}
class CLS implements IFruit,IMessage{}
public class Test {
public static void main(String[] args) {
Class<?> cls = CLS.class ; // 取得Class类对象
// 取得Package名称
System.out.println(cls.getPackage().getName());
// 取得父类名称
System.out.println(cls.getSuperclass().getName());
// 取得实现的父接口对象
Class<?>[] iClass = cls.getInterfaces() ;
for (Class<?> class1 : iClass) {
System.out.println(class1.getName());
       }
   }
}

<2>.反射调用构造

一个类中可以存在多个构造方法,如果要想取得类中构造的调用,就可以使用Class类中提供的两个方法:

取得指定参数类型的构造:

public Constructor getConstructor(Class<?>… paramete
throws NoSuchMethodException, SecurityException

取得类中的所有构造:

public Constructor<?>[] getConstructors() throws SecurityException

取得类中所有构造信息:

package reflect;

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

public reflect.Person()
public reflect.Person(java.lang.String)
public reflect.Person(java.lang.String,int)

上的操作是直接利用了Constructor类中的toString()⽅法取得了构造方法的完整信息(包含方法权限,
参数列表),而如果你只使用了getName()方法,只会返回构造方法的包名.类名。

<3>反射调用普通方法(核心)

类中普通方法的反射调用你在开发之中一定会使用到,并且使用好了可以节省大量的重复编码。在
Class类中有如下两种取得类中普通⽅法的函数:

取得全部普通方法:

public Method[] getMethods() throws SecurityException

取得指定普通方法:

public Method getMethod(String name, Class<?>… parameterTypes)

以上两个方法返回的类型是 java.lang.reflect.Method 类的对象,在此类中提供有一个调用方法的
支持:

调用:

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

取得一个类中的全部普通方法:

package reflect;

import java.lang.reflect.Method;

class Person1 {
    private String name ;
    private int age ;
    public Person1() {}
    public Person1(String name, int age) {
        this.name = name ;
        this.age = age ;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", 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;
    }
}
public class Test1 {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Person1.class ;
        Method[] methods = cls.getMethods() ;
        for (Method method : methods) {
            System.out.println(method);
        }
    }
}

现在有了反射机制是不是可以搞一点不一样的了

之前程序编写的简单java类中的getter、 setter方法采用的都是明确的对象调用。
而现在有了反射机制处理之后,即使你没有明确的Person类型对象(依然需要实例化对象, Object对象
描述,所有的普通方法必须在有实例化对象之后才可以进行调用)。就可以通过反射调用。

通过反射调用setter、 getter方法

package reflect;

import java.lang.reflect.Method;

class Person2 {
    private String name;
    private int age;

    public Person2() {
    }

    public Person2(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", 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;
    }
}

public class Test2 {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("reflect.Person2");
        // 任何时候调⽤类中的普通⽅法都必须有实例化对象
        Object obj = cls.newInstance();
// 取得setName这个⽅法的实例化对象,设置⽅法名称与参数类型
        Method setMethod = cls.getMethod("setName", String.class);
// 随后需要通过Method类对象调⽤指定的⽅法,调⽤⽅法需要有实例化对象
// 同时传⼊参数
        setMethod.invoke(obj, "yuisama"); // 相当于Person对象.setName("yuisama") ;
        Method getMethod = cls.getMethod("getName");
        Object result = getMethod.invoke(obj); // 相当于Person对象.getName() ;
        System.out.println(result);
    }
}

此类操作的好处是:不再局限于某一具体类型的对象,而是可以通过Object类型进行所有类的方法调用。

<4>.反射调用类中属性

在之前已经成功的实现了类的构造调用、方法调用,除了这两种模式之外还有类中属性调用。

前提:类中的所有属性一定在类对象实例化之后才会进行空间分配,所以此时如果要想调用类的属性,必须保证有实例化对象。通过反射的newInstance()可以直接取得实例化对象(Object类型)

在Class类中提供有两组取得属性的方法:

  1. 第一组(父类中) -取得类中全部属性: public Field[] getFields() throws SecurityException
  2. 第二组(父类中)-取得类中指定名称属性: public Field getField(String name) throws
    NoSuchFieldException, SecurityException
  3. 第三组(本类中)-取得类中全部属性: public Field[] getDeclaredFields() throws
    SecurityException
  4. 第四组(本类中)-取得类中指定名称属性 : public Method getDeclaredMethod(String name,
    Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException

取得类中全部属性:

package reflect;

import java.lang.reflect.Field;

class Person3 {
    public String name ;
    public int age ;
}
class Student extends Person3 {
    private String school ;
}
public class Test3 {
    public static void main(String[] args) throws Exception {
        Class<?> cls =
                Class.forName("reflect.Student") ;
        { // 普通代码块
// 第⼀组-取得类中全部属性
            Field[] fields = cls.getFields() ;
            for (Field field : fields) {
                System.out.println(field) ;
            }
        }
        System.out.println("------------------------");
        {
// 第⼆组-取得类中全部属性
            Field[] fields = cls.getDeclaredFields() ;
            for (Field field : fields) {
                System.out.println(field);
            }
        }
    }
}

//输出结果:
public java.lang.String reflect.Person3.name
public int reflect.Person3.age
------------------------
private java.lang.String reflect.Student.school

因为在实际开发之中,属性基本上都会进行封装处理,所以没有必要去关注本类中的属性。也就是说以后所取得的属性都以本类属性为主。

先介绍到这吧!!! 下期再见

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值