------- android培训、java培训、期待与您交流! ----------
一、反射概述
1、简介
Java 反射机制是在运行状态中,对任意一个类,都能够知道这个类的属性和方法。对于任意一个对象,都能调用它的任意一个方法或属性。这种动态获取的信息以及动态调用对象的方法称为Java 的反射机制。
2、应用场景
一个已经可以使用的应用程序,因为程序已经做好可以运行,不能再进行代码的加入了。而当后期我们新的功能加入程序时,该怎么做呢?
常用的做法就是,提供一个配置文件来供以后实现此程序的类来扩展功能。对外提供配置文件让后期出现的子类直接将类名字配置到配置文件中即可。该应用程序直接读取配置文件中的内容,并查找给定名称相同的类文件。操作过程如下:
- 加载类
- 创建该类对象
- 调用该类的内容
应用程序使用的类不确定时,可以提供配置文件,让使用者将具体的子类存储到配置文件中,然后改程序通过反射技术,对指定的类内容进行获取。
3、好处
反射技术大大提高了程序的扩展性。
二、Class 类
Person 类的代表人,它的实例对象就是张三、李四这样一个个具体的人,java 中的各个 Java 类同属于一类事物,描述这类事物的 Java 类就是 Class。
Class 对应的实例对象对应各个类在内存中的字节码文件。一个类被加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码文件,不同的类的字节码文件就是不同的,所以他们再内存中的内容是不同的
1、Class 和 class 的区别
- Class:指的是 Java 程序中的各个 Java 类是属于同一事物,都是 Java 程序的类,这些类称为 Class。Class是程序中Java类的总称,是反射的基石,通过 Class 来使用反射。
- class:Java中的类用于描述一类事务的共性,该类事务有什么属性,没有什么属性,至于具体是什么属性,则有类的实例对象来确定,不同的实例对象由不同的值。
2、获取Class对象
1)、通过对象的 getClass() 方法获取
例如:Class clazz = new Person().getClass();
麻烦之处在于,需要知道具体的类和该类的对象。
2)、静态属性
例如:Class clazz = Person.class;
比较简单,不用创建对象,但是还是需要具体的类。
3)、Class的静态方法 forName(String name)
例如:Class clazz = Class.forName("包名.Person");
这种方式较为简单,不需要使用该类,也不需要调用该类的属性和方法,只要知道类的名称即可,更有利于扩展。
3、九个预定义Class类
包括八种基本类型(byte、short、int、long、char、float、double、boolean)的字节码对象,以及返回值为void类型的void.class。
Integer.TYPE 是 Integer的一个常量,它代表此包装类的基本类型的字节码,所以和 int.class 是一样的,基本类型的字节码都可以用对应包装类的TYPE常量表示。4、数组的Class实例对象
只要在源程序中出现的类型都有各自的 Class 实例对象,例如:int[].class。数组类型的 Class 实例对象,可以通过 Class.isArray()方法判断是否为数组类型。
5、Class类中的方法
6、反射
反射就是把 Java 类中的各种成分映射成响应的的 java 类。例如,一个 Java 类中用一个 Class 类的对象来表示,一个类中的组成部分:成员变量、构造函数、变量、包等也用一个个的的 Java 类来表示。表示 Java 类的 Class 类显然要提供一系列的方法,来获取其中的变量、成员方法、构造方法、包等信息,这些信息就是用响应的实例对象来表示,他们是 Field、Mothed、Constructor、Package等。
//Person类
class Person {
private String name;
public int age;
public Person(){
System.out.println("Person is run");
}
public Person(String name,int age){
this.age=age;
this.name=name;
}
public String toString(){
return name+":"+age;
}
}
//示例
class CreateClassDemo {
public static void main(String[] args) throws Exception {
//获取Person类的Class对象
Class clazz=Class.forName("Person");
//通过newInstance方法获取类的无参构造函数实例
Person p=(Person)clazz.newInstance();
}
}
三、Constructor 类
Constructor 类代表某个类中的一个构造方法。
1、得到Constructor对象
- 某各类所有构造方法:Constructor[] cons = Class.forName("java.lang.String").getConstructors()
- 得到类中某一个构造方法:Constructor cons = Class.forName("java.lang.String").getConstructor("StringBuffer.class")
2、创建实例对象
- 通常方式:String str = new String(new StringBuffer("abc"))
- 反射方式:String str = (String)cons.newinstance(new StringBuffer("abc"))
3、Class.newinstance()
该方法先得到类内部默认的构造方法,然后使用默认构造方法创建实例。
//通过Constructor对象来创建类实例方法
public static void createPersonClass_2() throws Exception{
//获取Person类的Class对象
Class clazz=Class.forName("Person");
//获取指定构造函数的类实例
Constructor con=clazz.getConstructor(String.class,int.class);
Person p=(Person) con.newInstance("lisi",30);
System.out.println(p.toString());
}
四、Field 类
Field 类代表某一个类中的成员变量。
方法:
//获取Person对象的成员变量
public static void getPersonField() throws Exception{
//如果想要给该变量赋值,必须先要有对象。
Class clazz=Class.forName("Person");
Person p=(Person)clazz.newInstance();
//获取所以的成员变量
Field[] fs=clazz.getFields();
for(Field f:fs){
System.out.println(f);
}
//获取指定的成员变量
Field fage=clazz.getField("age");
Field fname=clazz.getDeclaredField("name");
//显示改变后的值
fage.set(p, 20);
System.out.println(fage.get(p));
//暴力访问私有变量
fname.setAccessible(true);
fname.set(p, "zhangsan");
System.out.println(fname.get(p));
}
五、Method 类
1、概述
Method 代表某一个类的成员方法。
2、得到Method对象
Method method = Class.forName("java.lang.String").getMethod("charAt","int.class")
注意:如果是空参数,可以写null。
3、调用方法
method.invoke(str,1)
注意:如果传递给 Method 对象的invoke() 方法的第一个参数是null,则说明Method对应的是对象的静态方法。
4、用反射来执行main方法
启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?
- mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
- mainMethod.invoke(null,(Object)new String[]{"xxx"}); 编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
//获取Person类中的方法
public static void getPersonMethod() throws Exception{
//如果想要获取方法,必须先要有对象。
Class clazz=Class.forName("Person");
Person p=(Person)clazz.newInstance();
//获取所以方法
Method[] mes=clazz.getMethods();//只获取公共的和父类中的。
//mes=clazz.getDeclaredMethods();//获取本类中包含私有。
for(Method me:mes){
System.out.println(me);
}
//获取单个方法
Method me=clazz.getMethod("toString", null);
Object returnVaule=me.invoke(p, null);
System.out.println(returnVaule);
}
六、数组的反射
1、具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象(此处比较与值无关)。
2、代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
3、基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
4、Array工具类用于完成对数组的反射操作。
- Array.getLength(Object obj);//获取数组的长度
- Array.get(Object obj,int x);//获取数组中的元素