Java反射学习笔记


1.什么是反射
通常使用一个类需要先import"包.类" ----> 通过new实例化 ----> 取得实例化对象
而反射:实例化对象 ----> getClass方法 ---->得到完整的“包.类”名称
反射操作中一切的操作都是使用Object来完成的,类、数组的引用都可以使用Object进行接收。


2.反射实现原理
每一个类在编译时都会生成一个.class文件,JVM把每一个.class文件加载进内存后就会在内存中
为每一个类创建一个class object,class object中包含包.类名称、构造方法、方法、属性。内存
中有这个class object之后我们就可以使用它来实例化对象。注意在内存中一个class只有一个class object,
这个class object是用来描述类本身的。

3.反射对象实例化
有三种方法来使用class object来实例化对象:
(1) Class<?> c = Class.forName("包.类"); //c是一个泛型。
(2) Class<?> c = new X().getClass(); //X是类名
(3) Class<?> c = X.class

然后使用c.newInstance();来实例化对象.
错:
Person p = c.newInstance(); //没有import Persion就不能直接使用Person
对:
Object p = c.newInstance(); //Object类是所有类的父类,newInstance获取一个Person类,再向上转化为Object类。

4.对于一个类在内存中有一个class object, 对于基本数据类型,它也是一个类,它里面也有class,eg: int.class

5.使用反射后就不需要import类了。但是没有import Person类,就不能直接使用Person类及其成员。

6.若不想处理一个函数中的异常,又想编译时不报error,就可以加throws Exception把异常给扔出去。
eg:
public static void main(String args[]) throws Exception {
}

7.newInstance()实际上是去调用那个类的无参构造方法。若是没有写,类里面会有一个默认的什么都不做的无参构造方法(和C++一样)。
若想使用有参构造方法来实例化对象,首先要获得它的构造方法,方法如下:
Constructor<?> con = c.getConstructor(class...<?> parameterTypes); //注意参数类型也是一个class
eg:
Constructor<?> con = c.getConstructor(String.class); //获取构造方法,String为参数类型,要传入String.class
Object P2 = con.newInstance("XiaoMing");

使用Constructor需要import java.lang.reflect.Constructor;参考Android开发官网。

8.获得并调用类的方法
Method meth = c.getMethod(String name, Class...<?>parameterTypes); //参数:成员方法名,成员方法的参数类型
Object obj = meth.invoke(Object receiver, Object...args);
对于静态方法,invoke的第一个参数可以写为null.

9.通过反射获取设置类/对象的属性
有2种方法:
(1)使用getMethod获得getter,setter方法,然后invoke这些方法来实现读取/设置属性。

(2)使用Field
①获得属性
Field f = c.getField(String name); //获得公共属性,此方法会先检索本类,再检索接口,最后检索其父类(?)。
Field f = c.getDeclaredField(String name); //获得类中名为name的属性

②设置为可被外部访问(注意它会破坏类的封装性)
f.setAccessible(true);

③调用get/set方法
Object obj = f.get(Object objet); //object为实例化对象
f.set(Object object, Object value);

注意:getField只能获得公共属性,这些公共属性要么是本类的,要么是它实现的接口的
要么是他的父类的。若想获得本类的所有属性可以使用getDeclaredField,它可以获取私有属性
也可以是其它的public属性。

10.使用反射的好处
(1)增加程序的灵活性,比如可以通过参数传入类的名称,也可以通过配置文件传入类的名称,然后实例化出不同的对象。

11.Me: 一个.java文件中只能定义一个public class, 但是可以定义多个非public的class
error: class Reflect is public, should be declared in a file named Reflect.java

12.Me: javac编译一个.java文件后,其内部的每一个类N都会生成一个N.class文件。

二、例子

demo1:Reflect.java实现三种方法实例化对象

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<?> 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());

        int arr1[] = {1,2,3};
        int arr2[] = {1,2,3,4};
        int arr3[][] = {{1,2,3,4},{1}};

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

        Class<?> c7 = int.class;            //基本类型也有一个class

        System.out.println(c4.getName());
        System.out.println(c5.getName());
        System.out.println(c6.getName());
        System.out.println(c7.getName());

        System.out.println((c4 == c5));    //都是一维数组,类别是一样的,这里是ture
        System.out.println((c4 == c6)); //false 一维数组和二维数组
    }
}

/*
$ javac -d . Reflect.java 
$ java a.b.c.d.Reflect
a.b.c.d.Person
a.b.c.d.Person
a.b.c.d.Person
[I
[I
[[I
int
true
false
*/

demo2: 使用成员方法和属性和反射增加灵活性

Person.java

package a.b.c.d;

public class Person {
    public String name;

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

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

Student.java

package a.b.c.d;

public class Student {
    public 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("Constructor2 of Student, name is "+this.name);
    }
};

Reflect.java

//import a.b.c.d.Person;    //反射不需要import了

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Field;

public class Reflect {
    public static void main(String args[]) throws Exception {    //不想处理类中的异常,扔掉异常以免编译不过
        Class<?> c = null;

        try {
            c = Class.forName("a.b.c.d.Person");    //实例化对象,传入参数"a.b.c.d.Person"可以在运行时更改,改为args[1]获得灵活性
        } catch (ClassNotFoundException e) {
            System.out.println(e);
        }

        Object p = null;    //由于没有import Person不能直接使用Persion类,Object类是所有类的父类

        try {
            p = c.newInstance(); //这里默认使用的是无参的构造函数
        } catch (InstantiationException e) {
            System.out.println(e);
        }

        /*
         * 获取构造函数,想使用有参构造函数,参数为String,因此这里
         * 传参是String.class
         */
        Constructor<?> con = c.getConstructor(String.class);
        Object p2 = con.newInstance("xiaoming"); /*调用有参构造函数*/

        /*获取“setName()”成员方法*/
        Method set = c.getMethod("setName", String.class);
        /*分别是实例化对象p2和p调用setName()方法*/
        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));

        /*获取属性成员name*/
        Field name = c.getDeclaredField("name");

        /*
         * 可以看出在使用setAccessible的时候破坏了类的封装性,name属性本来是私有的,
         * 现在强制被设置为可以被外界访问.
         * 所以一般不介意使用这个方法来操作成员变量,而是通过set/get方法访问。
         */
        //name.setAccessible(true); /*不设置它会产生异常,若是把name属性指定为public的,就不用设置了。*/

        /*设置属性成员name的值*/
        name.set(p, "www");    //设置p这个实例化对象的name属性,注意设置哪个对象的属性
        name.set(p2, "100ask");
        System.out.println(name.get(p));
        System.out.println(name.get(p2));
        
    }
}

使用:

/*
$ javac -d . Person.java 
$ javac -d . Student.java 
$ javac Reflect.java 
$ java Reflect a.b.c.d.Person
Constructor of Person
Constructor2 of Person, name is xiaoming
abc
123
www
100ask
$ 
$ java Reflect a.b.c.d.Student
Constructor of Student
Constructor2 of Student, name is xiaoming
abc
123
www
100ask
$
*/

猜你喜欢

转载自www.cnblogs.com/hellokitty2/p/10410119.html