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