Skip to main content
 首页 » 编程设计

Java反射学习笔记

2022年07月19日27zhenyulu


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 
$ 
*/

本文参考链接:https://www.cnblogs.com/hellokitty2/p/10410119.html