------- android培训、java培训、期待与您交流! ----------
反射的概念
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
功能
1. 在运行时判断任意一个对象所属的类;2. 在运行时构造任意一个类的对象;3. 在运行时判断任意一个类所具有的成员变量和方法;4. 在运行时调用任意一个对象的方法;5. 生成动态代理。
理解反射的概念
反射就是把Java类中的各种成分映射成相应的Java类。
例如:众多的人用一个Person类来表示,那么众多的Java类就用一个Class类来表示。
Person 类代表人,它的实例对象就是张三,李四这样一个个具体的人;
Class类代表Java类,它的各个实例对象对应的就是各个类在内存中的字节码,例如:Person类的字节码,ArrayList类的字节码等等。
Class类用于表示.class文件,是所有加载进内存的字节码对象的父类。所以可以通过Class得到运行时的类。
如何得到某个class文件对应的class对象呢?
方法有3种:
1)类名.class 例如,Person.class。
2)对象.getClass() 例如,new Data().getClass()。
3)Class.forName("包名.类名"); 例如,Class.forName("java.lang.String");
代码示例:
public class ClassTest{
public static void main(String...args)throws Exception{
String str = "黑马程序员";
Class cls1 =String.class;
Class cls2 = str.getClass();
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1 == cls2);//true
System.out.println(cls2 == cls3);//true
}
}
注意:字节码文件是唯一的,所以无论怎么获取,都是同一份字节码文件。
九个预定义Class实例对象(八大原始类型+void)
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。
每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该Class 对象。
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void也表示为 Class 对象。
反射的基本步骤:
1、获得Class对象,就是获取到指定的名称的字节码文件对象。
2、实例化对象,获得类的属性、方法或构造函数。
3、访问属性、调用方法、调用构造函数创建对象。
构造方法的反射应用(Coustructor类)
Constructor类的实例对象代表类的一个构造方法。
反射公共,私有和保护的构造方法:
反射公共的需要的方法是:getConstructor();
反射私有的需要的方法是:getDeclaredConstructor();
Constructor对象代表一个构造方法,Constructor对象有的方法:得到构造方法名字,得到所属于的类,产生实例对象。
得到某个类空参数构造方法,例:
Constructor constructor = Class.forName("java.lang.String").getConstructor();
得到某个类所有的构造方法,例:
Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
得到某一个带参数的构造方法,例:
Constructor constructor =Class.forName("java.lang.String").getConstructor(StringBuffer.class);
利用构造方法创建实例对象:
通常方式:String instance = new String(new StringBuffer("黑马程序员"));
反射方式:String instance = (String)constructor.newInstance("黑马程序员");
通过Class类中的newInstance()方法也可创建类的实例,其内部工作原理是先得无参的构造方法,再用构造方法创建实例对象。
代码示例:
package com.itheima.test;
import java.lang.reflect.Constructor;
public class ReflectTest {
public static void main(String[] args) throws Exception {
Class clszz = Class.forName("java.lang.String");
Constructor constructor = clszz.getConstructor(StringBuffer.class);
String str = (String) constructor.newInstance(new StringBuffer("黑马程序员"));
char [] chs = str.toCharArray();
for(int x = 0; x<chs.length;x++){
}System.out.println(chs[x]);
}
}
成员变量的反射(Field类)
Field类代表反射某个类中的一个成员变量。
问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,那关联的是哪个对象呢?所以字段fieldAge 代表的是Age的定义,而不是具体的Age变量。
(注意访问权限的问题)也就是说,定义的是类对象,而非对象的对象。
当我们需要对其操作的时候,需要确定是那个具体的对象。
代码示例:
package com.itheima.test;
public class Person {
public int age;
private int height;
public Person(int age,int height){
this.age=age;
this.height=height;
}
}
package com.itheima.test;
import java.lang.reflect.Field;
public class ReflectTest{
public static void main(String...args) throws Exception{
Person p = new Person(20,30);
Field fieldAge = p.getClass().getField("age");
System.out.println(fieldAge.get(p));
}
}
成员方法的反射(Method类)
Method类代表某个类中的一个成员方法
得到类中的某一个方法:
调用方法:例子:Method charAt =Class.forName("java.lang.String").getMethod("charAt",int.class);
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str, 1));
如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?
说明该Method对象对应的是一个静态方法!
代码示例:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
package com.itheima.test;
import java.lang.reflect.Method;
public class ReflectTest {
public static void main(String[] args) throws Exception {
Class clszz = Class.forName(args[0]);
Method main = clszz.getMethod("main", String[].class);
main.invoke(null,"黑马程序员","黑马论坛","CSDN社区");
}
}
暴力反射
反射里的Constructor,Field,Method三个类都有一个getDeclaredXxx方法,可以不受权限控制的获取类的构造函数,
字段,方法,如果想要私有构造函数创建对象,字段赋值,方法调用的话,会自动的访问类的isAccessable,默认的是false,
所以,要想访问类中的私有成员的时候,就要调用setAccessable方法,将其改为true,
这样,就可以对类中的私有成员进行操作了.
假设有一个类,它有一个私有变量:
package com.itheima.test; public class ReflectPoint { private int priVar; public ReflectPoint(int priVar){ this.priVar =priVar; } }如果我们直接采用.get的方式,是不可能看到私有变量的。 我们可以这样:
package com.itheima.test; import java.lang.reflect.Field; public class ReflectTest { public static void main(String[] args) throws Exception { ReflectPoint pt1 = new ReflectPoint(3); Field fieldx = pt1.getClass().getDeclaredField("priVar"); fieldx.setAccessible(true); fieldx.set(pt1, 100); System.out.println(fieldx.get(pt1)); } }输出:100;
那么这个Filed是什么呢?他 是一个类,表示属性的类。通过pt1得出ReflectPoint的字节码。然后再调用getDeclaredField()方法,可以获取x属性。再通过setAccessible使得可以访问这个属性。然后可以通过set方法赋值。注意:getClass获得是ReflectPoint的字节码,与具体实例无关,因此,Field也与具体实例无关。在设置和访问的时候,均需要指定具体哪个实例。
数组与Object的关系及其反射类型
1. 具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
package com.itheima.reflect; import java.util.Arrays; public class ReflectTest { public static void main(String[] args) throws Exception { int [] a1 = new int[3]; int [] a2 = new int[4]; int [][] a3 = new int[2][3]; String[] a4 = new String[3]; System.out.println(a1.getClass() == a2.getClass()); //结果:true System.out.println(a1.getClass() == a4.getClass()); //结果:false System.out.println(a1.getClass() == a3.getClass()); //结果:false } }2. 代表数组的class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。3. 基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,即可以做Object类型使用,又可以当作Object[]类型使用。
4. Array工具类用于完成数组的反射操作。
public class ReflectTest { public static void main(String[] args) throws Exception { String[] a1 = new String[]{"a" ,"b" ,"c" }; String a2 = "xyz"; printObject(a1); printObject(a2); } public static void printObject(Object obj){ Class clazz = obj.getClass(); if(clazz.isArray()){ int len = Array.getLength(obj); for(int i = 0; i < len; i++){ System. out.println(Array.get(obj, i)); } } else{ System. out.println(obj); } } }