------- android培训、java培训、期待与您交流! ----------
第一节:反射的概述
一、反射的简述:
1、什么是反射机制?
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
2、如何对一个类进行剖析?
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。
3、反射的好处:
反射大大的提高了程序的扩展性。
4、反射的基本操作步骤:
(1)、获得Class对象,获取某个类的字节码文件对象(如果该类不确定时,可以通过配置文件,把具体的子类存储到配置文件当中去,最后在用反射技术获取指定类)。
(2)、根据实例化对象可以获取类的属性、方法或构造函数等内容。
(3)、在根据Class对象获取的内容去调用构造函数创建对象,访问属性,调用方法。
第二节:反射的应用
一、反射涉及到的对象:
1、Class类:
(1)、Class类的实例表示正在运行的java应用程序中的类和接口。
(2)、更通俗的理解就是把java中所有的类进行抽象提取用于描述类的共性内容,这就是Class类。
2、获取Class对象的三种方式:
(1)、通过Object类中的getClass()方法。(弊端:虽然通用,但是前提必须有指定类,并对该类进行对象的创建,才可以调用getClass()方法。)
(2)、使用的任意数据类的一个静态成员class,所有的数据类型都具备的一个属性。(好处:不用new对象。但是,还需要使用具体的类。)
(3)、使用Class类中的forName(String className)方法。通过给定类名来获取对应的字节码文件对象。(好处:这种方式只要有名称即可,更为方便,扩展性更强。)
3、九个预定义的Class
(1)、包括八个基本数据类型的字节码对象和一个返回值类型void的字节码对象。
(2)、基本数据类型的包装类字节码对象与基本数据类型的字节码对象之间的关系(Integer.TYPE与int.class是相等的)。
4、Class类中常用的方法;
(1)、public static Class<?> forName(String className);给定类名的字符串形式获取对应的字节码文件对象。
(2)、public Constructor<T> getConstructor(Class<?>... parameterTypes);获取单个公共的构造方法。参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象。
(3)、public Constructor<?>[] getConstructors();所有公共构造方法。
(4)、public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes);获取单个的构造方法 参数表示的是:构造方法的构造参数的个数及数据类型的class字节码文件对象。
(5)、public Constructor<?>[] getDeclaredConstructors();所以的构造方法。
(6)、public Field getDeclaredField(String name);获取单个的成员变量。
(7)、public Field[] getDeclaredFields();获取所有的成员变量。
(8)、public Field getField(String name);获取单个的公共的成员变量。
(9)、public Field[] getFields();获取所有的公共的成员变量。
(10)、public Method getDeclaredMethod(String name, Class<?>... parameterTypes);获取单个方法,第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型。
(11)、public Method[] getDeclaredMethods();获取所有的方法。
(12)、public Method getMethod(String name,Class<?>... parameterTypes); 获取单个公共的方法,第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型
(13)、public Method[] getMethods();获取所有的公共的方法
(14)、public T newInstance();获取一个类的空参实例对象
5、Constructor类
(1)、Constructor类提供关于类的单个构造方法的信息以及对它的访问权限。
(2)、Constructor类中常用方法
(2).1 public T newInstance(Object... initargs);获取一个类的有参的实例对象。
(3)、通过指定的构造函数初始化对象
思路:
(3).1 获取字节码文件对象。
(3).2 再获取给定的构造函数。
(3).3 通过构造函数初始化对象。
(3).4 代码体现
//通过指定的构造函数初始化对象
/*
1 获取单个的构造方法(无参的和有参的)
2 获取单个的构造方法(非公共的)
3 获取全部的构造方法
4 获取全部的构造方法(非公共的)
*/
import java.lang.reflect.*;
class Demo
{
public static void main(String[] args)throws Exception
{
//1 获取单个的构造方法(无参的和有参的)
Class classForname=A.class;
Constructor constructor=classForname.getConstructor();
Constructor constructor1=classForname.getConstructor(int.class);
System.out.println(constructor.toGenericString());
System.out.println(constructor1.toGenericString());
//2 获取单个的构造方法(非公共的)
Constructor constructor2=classForname.getDeclaredConstructor(String.class);
System.out.println(constructor2.toGenericString());
//获取全部的构造方法
Constructor[] constructor3=classForname.getConstructors();
for (Constructor con:constructor3)
{
System.out.println(con.toGenericString());
}
//4 获取全部的构造方法(包含非公共的)
Constructor[] constructor4=classForname.getDeclaredConstructors();
for (Constructor con1:constructor4 )
{
System.out.println(con1.toGenericString());
}
}
}
//指定一个A类
class A
{
private int sum=2;
String s=null;
int sum1=5;
public A(){}
public A(int sum)
{
this.sum=sum;
}
A(String s)
{
this.s=s;
}
public void run()
{
System.out.println("成功了");
}
}
6、Field类
(1)、Field类提供有关类或接口的单个字段的信息,以及对它的动态访问权限。
(2)、Field类中常用方法
(2).1 public Object get(Object obj);返回指定对象上此 Field 表示的字段的值
(2).2 public void set(Object obj,Object value);进行赋值
(2).3 public void setAccessible(boolean flag);(容易出现NoSuchFieldException,IllegalAccessException异常)
(3)、通过反射获取成员变量并使用
(3).1 获取字节码文件对象。
(3).2 通过无参构造方法创建对象。
(3).3 获取单个的成员变量。
(3).4 给指定的对象变量设置新的值。
(3).5 获取该字段上的值。
(3).6 代码体现
//获取一个类中的字段
/*
1 获取一个类中的指定的字段
2 获取一个类中的指定的字段(非公共)
3 获取一个类中的所有的字段
4 获取一个类中的所有的字段(非公共)
*/
import java.lang.reflect.*;
class Demo1
{
public static void main(String[] args)throws Exception
{
//1 获取一个类中的指定的字段
Class classForName=A.class;
Object obj2=classForName.newInstance();
Field field=classForName.getField("count");
Object obj1=field.get(obj2);
System.out.println(obj1);
//22 获取一个类中的指定的字段(非公共)
Field field1=classForName.getDeclaredField("s");
Object obj3=field1.get(obj2);
System.out.println(obj3);
//3要是要访问私有的话 就要用到暴力反射
Field field2 =classForName.getDeclaredField("sum");
field2.setAccessible(true);
Object obj4=field2.get(obj2);
System.out.println(obj4);
//4获取一个类中的所有的字段
Field[] field3=classForName.getFields();
for (Field fid:field3 )
{
System.out.println(fid.get(obj2));
}
//5 获取一个类中的所有的字段(要是有私有的话不进行暴力反射会出现异常)
Field[] field4 =classForName.getDeclaredFields();
for (Field fid1:field4 )
{
fid1.setAccessible(true);
System.out.println(fid1.get(obj2));
}
}
}
//自定义一个A类
class A
{
private int sum=9;
public int count=8;
String s="你好啊";
}
7、Method类
(1)、Method类提供关于类或接口上单独某个方法的信息。
(2)、Method类中常用的方法
(2).1 public Object invoke(Object obj,Object... args);返回值是Object接收,第一个参数表示对象是谁,第二参数表示调用该方法的实际参数
(3)、通过反射获取成员方法并使用
思路:
(3).1 获取字节码文件对象。
(3).2 通过无参构造方法创建对象。
(3).3 获取单个方法并使用。
(3).4 代码体现。
//获取一个类中的方法用反射
/*
1 获取 一个类中的无参或有参的方法
2 获取 一个类中的无参或有参的方法(非公共)
3 获取一个类中的所有的方法
4 获取一个类中的所有的方法(非公共)
*/
import java.lang.reflect.*;
class Demo2
{
public static void main(String[] args)throws Exception
{
//1 获取 一个类中的无参或有参的方法
Class classForName=A.class;
Object obj=classForName.newInstance();
Method method=classForName.getMethod("run");
Method method1=classForName.getMethod("add",int.class);
method.invoke(obj);
method1.invoke(obj,5);
//2 获取 一个类中的无参或有参的方法(非公共)
Method method2=classForName.getDeclaredMethod("show");
method2.invoke(obj);
//3 获取一个类中的所有的方法
Method[] method3=classForName.getMethods();
for (Method method4:method3)
{
System.out.println(method4.toGenericString());
}
//4 获取一个类中的所有的方法(非公共)
Method[] method5 =classForName.getDeclaredMethods();
for (Method method6:method5 )
{
System.out.println(method6.toGenericString());
}
}
}
//自定义一个A类
class A
{
private int sum=9;
public void add(int sum)
{
System.out.println(sum);
}
public void run()
{
System.out.println("成功了");
}
void show()
{
System.out.println("hahahahahaha");
}
}
8、用反射执行一个类中的main方法
(1)、既然用到反射,main方法也属于Method的范围内
(2)、问题的出现
(2).1 首先要知道jdk1.4与jdk1.5的invoke方法的区别
jdk1.5:public Object invoke(Object obj,Object...args)将整个数组作为一个参数
jdk1.4: public Object invoke(Object obj,Object[] args)需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数。
(2).2 当我们通过反射方式来调用main方法时,不能使用代码mainMethod.invoke(null,new String[]{"xxx"});javac,只把当作jdk1.4的语法解释,而不把它当作jdk1.5的语法解释,
所以出现出现差数类型异常。
(3)、解决的方法:
(3).1 mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
(3).2 mainMethod.invoke(null,(Object)new String[]{"xxx"});
(4)、代码体现
//反射执行一个类中的 方法
/*
思路;
1 main方法实际也是一个方法
2 而是接收一个String类型的数组
*/
import java.lang.reflect.*;
class Demo3
{
public static void main(String[] args)throws Exception
{
Class classForName=A.class;
//第一种方式
Method method =classForName.getMethod("main",String[].class);
method.invoke(null,(Object)new String[]{"wo","lai","le"});
//第二种方式
Method method1 =classForName.getMethod("main",String[].class);
method1.invoke(null,new Object[]{new String[]{"ha","ha","le"}});
}
}
//自定义一个A类
class A
{
public static void main(String[] args)
{
for (String s:args )
{
System.out.println(s);
}
}
}
9、数组的反射
(1)、具有相同维数和元素类型的数组属于同一类型,即具有相同的Class实例对象。
(2)、数组与Object之间的关系
(2).1 基本类型的一维数组可以当做Object类型来使用,不能当做Object[]来使用。
(2).2 非基本类型的一维数组既可以当做Object类型来使用,也可以当做Object[]来使用。
(3)、如何得到某个数组中的某个元素的类型,
如:
int i = new int[2];Object[] obj=new Object[]{”ABCD”,3};
无法得到某个数组的具体类型,只能得到其中某个元素的类型。
如:
Obj[0].getClass().getName()得到的是java.lang.String。
10、反射的作用:框架
(1)、比如某房地产建筑商,建筑好房子卖给用户,由用户自己安装门窗,地板等内部结构,房子是框架,用户只要使我的的框架,把门窗装入我提供的框架中。
(2)、框架与工具类的区别
(2).1、工具类被用户的类调用,而框架则是调用用户提供的类。
(3)、框架的特点:
(3).1、就是将在开发中大量重复的代码集中起来写个通用的程序。
(3).2、就是用反射来实现,框架需要现在的类调用将来写的类。
(3).3、调用未来程序员所写的类,框架不能实现完整的功能,框架只是一些通用的代码。
(3).4、框架要依赖将来写的类才能运行。