JAVA快速入门-反射

版权声明:欢迎转载,转载请注明出处 http://blog.csdn.net/itdo_just https://blog.csdn.net/itdo_just/article/details/79330932

Tips:

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
JAVA反射(放射)机制:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。


简单来说就是你不用具体实例化某个对象只通过一个类的名字(这个类名你可能事先不知道,具体运行的时候才能确定)来调用类的方法和修改类的属性。比如以前都是通过 import 某个类,然后 new 后取得实例化对象去调用类的方法和设置类的属性,先做个简单的测试,看能不能先通过实例化后的对象来得到类的完整名字,如以下程序:

package a.b.c.d;

class Person {
    private String name;

    void setName(String name) { this.name  = name; }
    String getName(){ return this.name; }
}

public class Reflect {
    public static void main(String args[]) {
        Person p = new Person();
        /* 根据实例化对象获取类并获得类名打印出来 */
        System.out.println(p.getClass().getName()); 
    }
}

上面的程序直接结果是打印出了:a.b.c.d.Person
既然可以根据实例化对象来获得类名,那有没有办法通过 Person 这个类的名字来获得类对象,从而调用 Person 类里面的方法呢,是可以的,这也就是反射的概念,可以看下面的程序实现。

package a.b.c.d;

class Person {
    private String name;

    void setName(String name) { this.name  = name; }
    String getName(){ return this.name; }
}

public class Reflect {
    public static void main(String args[]) {
        Person p = new Person();

        /* class用来描述类的 */
        /* 下面用三种方法来获得class */

        /* 一:通过类名 */
        Class<?> c1 = null;
        try {
            c1 = Class.forName("a.b.c.d.Person");
        } catch (ClassNotFoundException e) {
            System.out.println(e);
        }

        /* 二:通过实例对象 */
        Class<?> c2 = p.getClass();
        /* 三:通过实例对象 */
        Class<?> c3 = Person.class;

        System.out.println(c1.getName());
        System.out.println(c2.getName());
        System.out.println(c3.getName());
    }
}

上面输出的结果如下:
a.b.c.d.Person
a.b.c.d.Person
a.b.c.d.Person
输出的都是一样的,所以以上三种获得 class 的方法都是可以的。上面做了 Person 类的测试,那基本数据类型是否也能获得它的类名呢,可以使用下面的代码来进行测试。

public class Reflect {
    public static void main(String args[]) {        
        /* 打印数组的类 */
        int arr[] = {1,2,3};
        int arr2[] = {1,2,3,4};
        int arr3[][] = {{1,2,3,4}, {2}};

        Class<?> c4 = arr.getClass();
        Class<?> c5 = arr2.getClass();
        Class<?> c6 = arr3.getClass();

        System.out.println(c4.getName()); 
        System.out.println(c5.getName());
        System.out.println(c6.getName());
        /* 判断是否一致 */
        System.out.println((c4 == c5)); /* 一样 */
        System.out.println((c4 == c6)); /* 不一样 */

        /* 基本数据类型 int 是否也有类 */
        Class<?> c7 = int.class;
        System.out.println(c7.getName());/* 类类型是int */
    }
}

输出结果如下:
[I
[I
[[I
true
false
int
可以看到一维数组和二维数组的类名不同,int 的类名是 int 。当然使用反射肯定不仅仅是为了获得类的名字,而是想通过类名来正常访问使用一个类,下面我们把 Person 类写到一个包里面,然后通过反射去访问他的有参和无参的构造函数,使用反射就不需要去 import Person 这个类了,代码如下:

Person类:

package a.b.c.d;

public class Person {
    private String name;

    void setName(String name) { this.name  = name; }
    String getName(){ return this.name; }

    public Person(){
        System.out.println("Constructor of Person");
    }

    public Person(String name){
        this.name = name;
        System.out.println("Constructor of Person, name is "+this.name);
    }
}

下面使用反射来访问这个 Person 类,代码如下:

import java.lang.reflect.Constructor;

public class Reflect {
    public static void main(String args[]) throws Exception{
        /* 通过类名字获得一个类 */
        Class<?> c = null;
        try {
            /* Class使用名字来获得Person object */
            c = Class.forName("a.b.c.d.Person");//类名
        } catch (ClassNotFoundException e) {
            System.out.println(e);
        }

        /* 不能这么写,会找不到Person这个类 */
        //Person p = c.newInstance();

        /* 可以使用Object,Object是所有类的父类 */
        Object p = null;
        try {
            p = c.newInstance();/* 调用类的无参构造方法 */
        } catch(InstantiationException e) {
            System.out.println(e);
        }

        /* 调用有参构造函数首先需要获得有参构造方法 */
        Constructor<?> con = c.getConstructor(String.class);
        Object p2 = con.newInstance("call the person");
    }
}

上面仅通过类名就访问到了类的有参和无参构造方法,能否访问类的其它方法呢,也是可以的,下面先是调用设置的方法(setName),然后再调用获取的方法(getName)来获取设置值,看如下代码:

/* 调用 Constructor 需要import */
import java.lang.reflect.Constructor;
/* 调用 Method 需要import*/
import java.lang.reflect.Method;

public class Reflect {
    public static void main(String args[]) throws Exception{
        Class<?> c = null;

        try {
            /* Class使用名字来获得Person object */
            c = Class.forName("a.b.c.d.Person");
        } catch (ClassNotFoundException e) {
            System.out.println(e);
        }
        /* Object是所有类的父类 */
        Object p = null;

        try {
            p = c.newInstance();/* 调用类的无参构造方法 */
        } catch(InstantiationException e) {
            System.out.println(e);
        }

        /* 调用有参构造函数首先需要获得构造方法 */
        Constructor<?> con = c.getConstructor(String.class);
        Object p2 = con.newInstance("call the person");

        /* 获得类里面的方法,方法需要传入参数String.class */
        Method set = c.getMethod("setName", String.class);
        /* invoke调用,这个方法是用来设置这个对象传入的参数,传入字符串 */
        set.invoke(p2,"123");
        set.invoke(p,"abc");

        Method get = c.getMethod("getName");

        /* 调用 */
        System.out.println(get.invoke(p));
        System.out.println(get.invoke(p2));
    }
}

上面通过类名调用到类里面方法的形式去修改了类的成员变量,那能不能直接去设置这些类里面的变量呢,还有一种方式,就是使用 Field 直接去设置,如下代码:

/* 调用 Constructor 需要import */
import java.lang.reflect.Constructor;

/* 调用 Field 需要import*/
import java.lang.reflect.Field;

public class Reflect {
    public static void main(String args[]) throws Exception{
        Class<?> c = null;  
        try {
            /* Class使用名字来获得Person object */
            c = Class.forName("a.b.c.d.Person");
        } catch (ClassNotFoundException e) {
            System.out.println(e);
        }

        /* Object是所有类的父类 */
        Object p = null;
        try {
            p = c.newInstance();/* 调用类的无参构造方法 */
        } catch(InstantiationException e) {
            System.out.println(e);
        }

        /* 调用有参构造函数首先需要获得构造方法 */
        Constructor<?> con = c.getConstructor(String.class);
        Object p2 = con.newInstance("call the person");

        /* 获得本类中的属性 */
        Field name = c.getDeclaredField("name");

        /* 因为name为private,所以需设置为可被外部访问,破坏了类的封装性,不建议这么用 */
        name.setAccessible(true);
        //p、p2为实例化对象
        name.set(p, "is p");
        name.set(p2, "is p2");
        System.out.println(name.get(p));
        System.out.println(name.get(p2));
    }
}

上面代码写完后,如果有个类跟 Person 类非常相似都有获取名字和设置名字的一些属性和方法,但是程序一旦编译过后,我们如何能够动态给这个程序传入不同的类名来实现不同的功能呢,也就是Tips说到的可以加载一个运行时才得知名称的class,我们现在写好了另一个类如 Student 类,那我们就可以使用反射提供的功能,在程序运行时我们传入不同的类名从而调用到不同类的属性,先写一个 Student 类来试试:

package a.b.c.d;

public class Student {
    //如果设置为private,使用Filed的话要用setAccessible,否则不用
    private String name;

    public void setName(String name) { this.name  = name; }
    public String getName(){ return this.name; }

    public Student(){
        System.out.println("Constructor of Student");
    }

    public Student(String name){
        this.name = name;
        System.out.println("Constructor of Student, name is "+this.name);
    }
}

Student 类跟 Person 类很像,只是类名不一样,打印的东西不一样,这里需要把前面的 Reflect 类改为动态传入类名,即具体调用的类名由客户输入,做如下更改即可:

/* Class使用名字来获得Person object */
c = Class.forName(args[0]);//手动输入类名

那在输入的时候只要输入不同的类名就可以实现不同的功能:
java Reflect a.b.c.d.Person
java Reflect a.b.c.d.Student

猜你喜欢

转载自blog.csdn.net/itdo_just/article/details/79330932