------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
反射
一.反射的概述
反射就是把Java类中的各种成分映射成相应的java类。
例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示。就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。
二.反射的基础——Class类
(1)所有的类文件都有共同属性,所以可以向上抽取,把这些共性内容封装成一个类,这个类就叫Class(描述字节码文件的对象)。 Class类中就包含属性有field(字段)、method(方法)、construction(构造函数)。
field中有修饰符、类型、变量名等复杂的描述内容,因此也可以将字段封装称为一个对象。用来获取类中field的内容,这个对象的描述叫Field。同理方法和构造函数也被封装成对象Method、Constructor。要想对一个类进行内容的获取,必须要先获取该字节码文件的对象。该对象是Class类型。
Class类描述的信息:类的名字,类的访问属性,类所属于的包名,字段名称的列表,方法名称的列表等。每一个字节码就是class的实例对象。
(2)获取Class对象的三种方法
第一种方法:
通过实例对象的getClass方法进行获取。
例如:Class cla=new Person().getClass();
第二种方法:
任何数据类型都具备着一个静态的属性class,这个属性直接获取到该类型的对应字节码对象。
例如 :Class cla=Person.class;
第三种方法:
通过Class类的forName方法,就可以获得字节码对象,这种方法比较常见。
例如:Class cla=Class.forName("day25.Person")其中"day25.Person"为 包名.类名
(3)Class类中的方法
static Class forName(String className):返回与给定字符串名的类或接口的相关联的Class对象。
Class getClass():返回的是Object运行时的类,即返回Class对象即字节码对象
Constructor getConstructor():返回Constructor对象,它反映此Class对象所表示的类的指定公共构造方法。
Field getField(String name): 返回一个Field对象,它表示此Class对象所代表的类或接口的指定公共成员字段。
Field[] getFields(): 返回包含某些Field对象的数组,表示所代表类中的成员字段。
Method getMethod(String name,Class… parameterTypes)
返回一个Method对象,它表示的是此Class对象所代表的类的指定公共成员方法。
Method[] getMehtods():返回一个包含某些Method对象的数组,是所代表的的类中的公共成员方法。
String getName():以String形式返回此Class对象所表示的实体名称。
String getSuperclass():返回此Class所表示的类的超类的名称
boolean isArray():判定此Class对象是否表示一个数组
boolean isPrimitive(): 判断指定的Class对象是否是一个基本类型。
T newInstance():创建此Class对象所表示的类的一个新实例。
三.构造方法(Constructor)的反射应用
(1)构造函数概述:
构造函数是给对象进行初始化的,通过构造函数可以创建对象。
(2)获取构造函数的方法有两种;
获取某一个构造方法:
Constructor con=Class.forName(“day25.Person”).getConstructor(String.class,int.class);
获取这个类的所有构造方法:如得到上面示例中Person类的所有构造方法
Constructor[] cons = Class.forName(“day25.Person”).getConstructors();
(3)创建实例对象:
通常方式:Person p = new Person(“lisi”,30);
反射方式:Person p= (Person)con.newInstance(“lisi”,30);
注:
1、创建实例时newInstance方法中的参数列表必须与获取Constructor的方法getConstructor方法中的参数列表一致。
2、newInstance():构造出一个实例对象,每调用一次就构造一个对象。
3、利用Constructor类来创建类实例的好处是可以指定构造函数,而Class类只能利用无参构造函数创建类实例对象。
例子:
package day25;
import java.lang.reflect.*;
class Person
{
private String name;
public int age;
Person()
{
System.out.println("haha");
}
public Person(String name,int age )
{
this.name=name;
this.age=age;
}
public String toString()
{
return name+" "+age;
}
}
public class ReflecDemo {
public static void main(String[] args) throws Exception
{
Constructor();
}
public static void Constructor()throws Exception
{
Class cla=Class.forName("day25.Person");//获取字节码对象
Constructor con=cla.getConstructor(String.class,int.class);//通过字节码对象获取带参数的构造函数
Person p=(Person)con.newInstance("lisi",23);//通过构造函数创建对象并初始化
System.out.println(p.toString());
}
四.成员变量(Field)的反射
(1)类中的成员变量包括公共的、私有的,都属于Field类。
(2)获取成员变量的方法:
Field getField(String s);只能获取公有和父类中公有
Field getDeclaredField(String s);获取该类中任意成员变量,包括私有
setAccessible(ture); 如果是私有字段,要先将该私有字段进行取消权限检查的能力。也称暴力访问。
set(Object obj, Object value);将指定对象变量上此Field对象表示的字段设置为指定的新值。
get(Object obj);返回指定对象上Field表示的字段的值。
例子:
package day25;
import java.lang.reflect.*;
class Person
{
private String name;
public int age;
Person()
{
System.out.println("haha");
}
public Person(String name,int age )
{
this.name=name;
this.age=age;
}
public String toString()
{
return name+" "+age;
}
}
public class ReflecDemo {
public static void main(String[] args) throws Exception
{
Field();
}
public static void Field() throws Exception
{
Class cla=Class.forName("day25.Person");
Person p=(Person)cla.newInstance();//调用空参构造函数并创建对象
Field fname=cla.getDeclaredField("name");//获取私有成员变量
Field fage=cla.getField("age");//获取公有成员变量
fage.set(p, 30);//给指定对象赋值
fname.setAccessible(true);//暴力访问私有成员变量
fname.set(p, "zhangsan");//给指定对象赋值
System.out.println(fage.get(p));//返回指定成员变量的值
System.out.println(fname.get(p));
//System.out.println(p.toString());
}
五.成员方法(Method)的反射
(1)Method类代表某个类中的一个成员方法。调用某个对象身上的方法,要先得到方法,再针对某个对象调用。
(2)获取方法:
Method[] getMethods();//只获取公共和父类中的方法。
Method[] getDeclaredMethods();//获取本类中包含私有。
Method getMethod("方法名",参数.class(如果是空参可以写null));
Object invoke(Object obj ,参数);//调用方法
如果方法是静态,invoke方法中的对象参数可以为null。例如:invoke(null,参数)
例子:
package day25;
import java.lang.reflect.*;
class Person
{
private String name;
public int age;
Person()
{
System.out.println("haha");
}
public Person(String name,int age )
{
this.name=name;
this.age=age;
}
public String toString()
{
return name+" "+age;
}
public static int setAge(int age)
{
return age;
}
}
public class ReflecDemo {
public static void main(String[] args) throws Exception
{
Method();
}
public static void Method() throws Exception
{
Class cla=Class.forName("day25.Person");
Constructor con=cla.getConstructor(String.class,int.class);
//获取非私有方法
Person p =(Person)con.newInstance("wangwu",27);
Method me=cla.getMethod("toString",null);//获取非私有、无参toString的方法
Object returnVaule=me.invoke(p, null);//调用对象(p)的toString方法
System.out.println(returnVaule);//打印函数返回值
//获取私有方法
Method me1=cla.getMethod("setAge",int.class);//获取私有、有参数setAge的方法
Object returnVaule1=me1.invoke(null, 20);//调用私有、有参数setAge的方法
System.out.println(returnVaule1);//打印函数返回值
}
}
(3)练习:
package day25;
/**
* 写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
*/
import java.lang.reflect.*;
public class ReflecText
{
public static void main(String[] args) throws Exception
{
String claName=args[0];
Class cla=Class.forName(claName);//获取字节码对象
Method methodMain=cla.getMethod("main",String[].class);//获取main方法
//强制转换为超类Object,不用拆包
methodMain.invoke(null, (Object)new String[]{"haha","567","heiehi"});
}
}
class Text
{
public static void main(String[] args)
{
for(String arg : args)
{
System.out.println(arg);
}
}
}