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
1 获得属性
- Field f = c.getField(String name); // 获得公共属性, 此方法会先检索本类, 再检索接口, 最后检索其父类 (?).
- Field f = c.getDeclaredField(String name); // 获得类中名为 name 的属性
2 设置为可被外部访问 (注意它会破坏类的封装性)
f.setAccessible(true);
3 调用 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
$
*/
来源: http://www.bubuko.com/infodetail-2962447.html