什么是java反射机制
动态语言
当程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。
静态语言
静态语言是在编译时变量的数据类型即可确定的语言。
反射机制
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
IT行业里这么说,没有反射也就没有框架,现有的框架都是以反射为基础。在实际项目开发中,用的最多的是框架,填的最多的是类,反射这一概念就是将框架和类揉在一起的调和剂。所以,反射才是接触项目开发的敲门砖!
java反射机制常用的类
Class
在面向对象的世界里,万事万物皆是对象。而在java语言中,static修饰的东西不是对象,但是它属于类。普通的数据类型不是对象,例如:int a = 5;它不是面向对象,但是它有其包装类 Integer 或者分装类来弥补了它。
实例化对象有三种表示方法:
public class Demo(){
F f=new F();
//第一种表达方式
Class c1=F.class;//这种表达方式同时也告诉了我们任何一个类都有一个隐含的静态成员变量class
//第二种表达方式
Class c2=f.getClass();//这种表达方式在已知了该类的对象的情况下通过getClass方法获取
//第三种表达方式
Class c3 = null;
try {
c3 = Class.forName("com.text.F");//类的全称
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
class F{}
其次他们都是同一个Class吗
System.out.println(c1 == c2)? or System.out.println(c1 == c3)?
答案肯定是ture。这表明不论c1,orc2,orc3都代表了F类的类类型,也就是说一个类只可能是Class类的一个实例对象。
理解了Class的概念,我们也可以通过类的类类型创建该类的对象实例,用c1 or c2 or c3的newInstance()方法:
Public class Demo1{
try {
Foo foo = (Foo)c1.newInstance();//foo就表示F类的实例化对象
foo.print();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}}
class F{
void print(){
}
}
方法的反射
public class ClassUtil {
public static void printClassMethodMessage(Object obj){
//要获取类的信息》》首先我们要获取类的类类型
Class c = obj.getClass();
//我们知道Object类是一切类的父类,所以我们传递的是哪个子类的对象,c就是该子类的类类型。
//接下来我们要获取类的名称
System.out.println("类的名称是:"+c.getName());
/*
*我们知道,万事万物都是对象,方法也是对象,是谁的对象呢?
* 在java里面,方法是Method类的对象
*一个成员方法就是一个Method的对象,那么Method就封装了对这个成员
*方法的操作
*/
//如果我们要获得所有的方法,可以用getMethods()方法,这个方法获取的是所有的Public的函数,包括父类继承而来的。如果我们要获取所有该类自己声明的方法,就可以用getDeclaredMethods()方法,这个方法是不问访问权限的。
Method[] ms = c.getMethods();//c.getDeclaredMethods()
//接下来我们拿到这些方法之后干什么?我们就可以获取这些方法的信息,比如方法的名字。
//首先我们要循环遍历这些方法
for(int i = 0; i < ms.length;i++){
//然后可以得到方法的返回值类型的类类型
Class returnType = ms[i].getReturnType();
//得到方法的返回值类型的名字
System.out.print(returnType.getName()+" ");
//得到方法的名称
System.out.print(ms[i].getName()+"(");
//获取参数类型--->得到的是参数列表的类型的类类型
Class[] paramTypes = ms[i].getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
}
总结思路:
通过方法的反射得到该类的名称步骤:
- 获取该类的类类型
- 通过类类型获取类的方法(getMethods())
- 循环遍历所获取到的方法
- 通过这些方法的getReturnType()得到返回值类型的类类型,又通过该类类型得到返回值类型的名字
- getName()得到方法的名称,getParameterTypes()获取这个方法里面的参数类型的类类型。
成员变量的反射
首先我们需要认识到成员变量也是对象,是java.lang.reflect.Field类的对象,那么也就是说Field类封装了关于成员变量的操作。既然它封装了成员变量,我们又该如何获取这些成员变量呢?它有这么一个方法:
public class ClassUtil {
public static void printFieldMessage(Object obj){
Class c = obj.getClass();
//Field[] fs = c.getFields();
}
这里的getFields()方法获取的所有的public的成员变量的信息。和方法的反射那里public的成员变量,也有一个获取所有自己声明的成员变量的信息:
Field[] fs = c.getDeclaredFields();
我们得到它之后,可以进行遍历(既然封装了Field的信息,那么我们就可以得到Field类型)
for (Field field : fs) {
//得到成员变量的类型的类类型
Class fieldType = field.getType();
String typeName = fieldType.getName();
//得到成员变量的名称
String fieldName = field.getName();
System.out.println(typeName+" "+fieldName);
}
构造函数的反射
不论是方法的反射、成员变量的反射、构造函数的反射,我们只需要知道:要想获取类的信息,首先得获取类的类类型。
public static void printConMessage(Object obj){
Class c = obj.getClass();
/*
* 首先构造函数也是对象,是java.lang.Constructor类的对象
* 也就是java.lang. Constructor中封装了构造函数的信息
* 和前面说到的一样,它也有两个方法:
* getConstructors()方法获取所有的public的构造函数
* getDeclaredConstructors()方法得到所有的自己声明的构造函数
*/
//Constructor[] cs = c.getConstructors();
Constructor[] cs = c.getDeclaredConstructors();
for (Constructor constructor : cs) {
//我们知道构造方法是没有返回值类型的,但是我们可以:
System.out.print(constructor.getName()+"(");
//获取构造函数的参数列表》》得到的是参数列表的类类型
Class[] paramTypes = constructor.getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
Class类的动态加载类
静态加载类
一般就是指new ClassName(),这个在编译时就会检查。无论你这个类用不用,代码逻辑能不能执行到,编译期都会对其编译。不存在这个类无法通过编译。
动态加载类
我们想要的就是我用那个类就加载那个类,也就是常说的运行时刻加载,动态加载类。Class.forName("ClassName"),就是所谓的动态加载,动态加载的类在源程序编译时可以缺席。
我们可以建这么一个类:
Class All{
Public static void start(){
try{
Class cl= Class.forName(args[0]);
//通过类类型,创建该类的对象
cl.newInstance();
}catch(Exception e){
e.printStackTrace();
}
}
}
前面我们在分析Class实例化对象的方式的时候,Class.forName(“类的全称”),它不仅仅表示了类的类类型,还表示了动态加载类。当我们javac All.java的时候,它不会报任何错误,也就是说在编译的时候是没有错误的。只有当我们具体用某个类的时候,那个类不存在,它才会报错。
如果加载的类是B类,就需要:
B bt = (B) cl.newInstance();
万一加载的是C类呢,可以改成
C ct = (C) cl.newInstance();
但是如果我想用很多的类或者加载很多的类,该怎么办?我们可以统一一个标准,不论C类还是B类或者其他的类,比如定义一个标准
Stand s = (Stand) cl.newInstance();
只要B类和C类都是这个标准的就行了。
Class All{
Public static void start(){
try{
Class cl= Class.forName(args[0]);
//通过类类型,创建该类的对象
Stand s = (Stand) cl.newInstance();
s.start();
}catch(Exception e){
e.printStackTrace();
}
}
}
interface Stand {
Public void start();
}
现在如果我想要用B类,我们只需要:
Class B implements Stand{
Public void start(){
System.out.print("B...satrt");
}
}
结果
B...satrt