一. 反射机制概念
主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。在java中,只要给定类的名字 或者该类的对象实例, 那么就可以通过反射机制来获得类的所有信息。
反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接。但是反射使用不当会成本很高!
类中有什么信息,利用反射机制就能可以获得什么信息,不过前提是得知道类的名字或者该类的对象实例。
二. 反射机制的作用
1.在运行时判断任意一个对象所属的类;
2.在运行时获取类的对象;
3.在运行时访问java对象的属性,方法,构造方法等。
三. 反射机制的优点与缺点
首先要搞清楚为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念。
静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
反射机制的优点:可以实现动态创建对象和编译,体现出很大的灵活性(特别是在J2EE的开发中它的灵活性就表现的十分明显)。通过反射机制我们可以获得类的各种内容,进行了反编译。对于JAVA这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。
比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
反射机制的缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它 满足我们的要求。这类操作总是慢于只直接执行相同的操作。
4 Class 类的使用
下面通过一段代码以及注释看下获取一个类的类类型的三种方式。具体的解释看注释
public class ClassDemo01 {
public static void main(String[] args) {
//java 中万事万物都是对象
//Food 这个类也是一个实例对象 是Class 类的实例对象
Food food=new Food();
//那Class 类的实例对象怎么表示呢源码
//任何一个类 都是class 的实例对象 这个对象有三种表示的方式
//第一种 类.class
Class c1=Food.class; //实例也告诉我们们任何一个类都有一个隐含的静态成员变量
//第二种表达方式 已知该类的对象通过getClass()方式
Class c2=food.getClass();
/*
*c1 c2 表示的是 Food类的类类型 原文翻译是(class type)
*指的是 Food 本生就是一个对象 是 Class类创建 的对象
*但是Food本身也有实例对象 将 c1 和c2 成为 Food 的 类类型
*感觉描述的优点绕自己都看不懂了 。。。
*总结一下 万事万物皆对象 类也是对象 是Class类的对象 这个对象称为该类的类类型
*/
//那么c1 和c2 相等么c1 是等于 c2 的一个类只可能是Class类的一个实例对象
System.out.println(c1==c2);
//第三种 表达方式forName
Class c3=null;
try {
c3=Class.forName("反射.Food");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//也是相等的和上面描述的一样
System.out.println(c2==c3);
//可以通过该类的类类型创建该对象的实例
try {
Food food1=(Food) c1.newInstance();//前提要求有无参构造
food1.print();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class Food{
public void print() {
System.out.println("我叫Food");
}
}
除了类有类类型基本数据类型和关键字也有类类型
public class ClassDemo2 {
public static void main(String[] args) {
//数据类型也有类类型
Class c1=int.class;
Class c2=String.class;
//关键字也有类类型 在类里面声明的都有
Class c3=void.class;
System.out.println(c1.getName());
System.out.println(c2.getName());
//不带包名
System.out.println(c2.getSimpleName());
}
5 动态加载类
比如我们要实现如下的一个方法:
public class Office {
public static void start(String str) {
if(str.equals("Word")) {
Word word=new Word();
word.start();
}
if(str.equals("Excel")) {
Excel e=new Excel() ;
e.start();
}
}
}
用到的office 类型 上面是通常的写法假设我们要实现100个这样的office 那岂不是要些以100 这样的if 条件如果其中一个出错了整个就没有办法用了。哪有什么更简便的方法么。下面介绍一种利用反射机制的方法。
public class OfficeButter {
public static void main(String[] args) {
start("反射.Excel");
}
public static void start(String str) {
try {
//动态加载
Class c=Class.forName(str);
//那么接下来做强制类型转换的时候 该转转那个类呢 此时我们设置一个借口 让word 和excel 实现它
try {
OfficeAble ab=(OfficeAble) c.newInstance();
ab.start();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}
让word excel 都实现一个接口
public class Word implements OfficeAble{
public void start() {
System.out.println("world");
}
}
public class Excel implements OfficeAble{
public void start() {
System.out.println("Excel");
}
}
public interface OfficeAble {
public void start() ;
}
通过如上的方式可以实践java 的动态加载类
6 反射获取类的方法
通过java.lang.reflect.Method; 获取类中的方法其中有
getDeclaredMethods获得自定义的方法,getMethods获得所有的public 的方法包括父类继承来的等下面给出一个例子
public static void printClassMessage(Object obj) {
//首先获取类类型
Class c1=obj.getClass(); //c 就是该类的类类型
// public final native Class<?> getClass()
//获取类的名称
System.out.println(c1.getName());
/*获得全部对象 里面包括所有的public 的方法 包括父类继承的
*c1.getDeclaredMethods(); 取得所有自己声明的方法不论访问权限
*/
Method[] me=c1.getMethods();
for(int i=0;i<me.length;i++) {
System.out.println(me[i].getReturnType());//获取方法的返回值类型 类类型 如 String 返回的是String.class
//要获取返回值的名称
System.out.println(me[i].getReturnType().getName());
System.out.println(me[i].getName()+"(");//获取方法的名称
//获取参数类型 得到的是类类型 例如 int 的到的是 int.class
Class[] prarm=me[i].getParameterTypes();
for (Class class1 : prarm) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
7反射获取类的成员变量
通过java.lang.reflect.Field; 获取类中的方法其中有
getDeclareFields获得自定义的成员变量,getFields获得所有的public 的成员变量包括父类继承来的等下面给出一个例子
public static void printClassField(Object obj) {
//首先获取类类型
Class c1=obj.getClass(); //c 就是该类的类类型
/*
* 成员变量也是对象
* java.lang.reflect.Field
* Field 类封装关于成员变量的操作
* getFields() 获取所有public 成员变量的信息
* getDeclareFields 取得所有自定义的成员变量
*
*/
Field[] fs=c1.getDeclaredFields(); //取得所有的类类型
for (Field field : fs) {
Class fieldType=field.getType();
String typeName=fieldType.getName();
String fieldName=field.getName();
System.out.println(typeName);
System.out.println(fieldName);
}
}
8 反射获取类的构造函数
通过java.lang.reflect.Constructor; 获取类中的方法其中有
getDeclareConstructors获得自定义的构造函数,getConstructors获得所有的public 的构造函数包括父类继承来的等下面给出一个例子
public static void printClassConsur(Object obj) {
/*
* 成员变量也是对象
* java.lang.reflect.Constructor
* Field 类封装关于成员变量的操作
* getConstructors() 获取所有public 成员变量的信息
* getDeclareConstructors 取得所有自定义的成员变量
*
*/
Class c1=obj.getClass();
Constructor[] cu=c1.getConstructors();
for (Constructor constructor : cu) {
//constructor
System.out.print(constructor.getName());
//得到的是参数列表的类类型
Class[] types=constructor.getParameterTypes();
System.out.print("(");
for (Class class1 : types) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
9 反射的基本操作
上面都是一个概念一类的东西下面用一个例子描述一下反射的基本操作
public class MethodDemo01 {
public static void main(String[] args) {
A a=new A();
Class c1=a.getClass();
//获得参数
try {
Method print=c1.getDeclaredMethod("print", new Class[] {int.class,int.class});
//Method print=c1.getDeclaredMethod("print", int.class,int.class);
Object obj=print.invoke(a, new Object[] {10,20});
// Object obj=print.invoke(a, 10,20);
print=c1.getDeclaredMethod("print", new Class[] {String.class,String.class});
obj=print.invoke(a, new Object[] {"hello","world"});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class A{
public void print(int a,int b) {
System.out.println(a+b);
}
public void print(String a , String b) {
System.out.println(a.toUpperCase()+b.toUpperCase());
}
}
通过放射获取A类的print方法实现方法的动态调用
10 通过反射了解泛型的本质
相信泛型大家都接触过这里便不再介绍了泛型的一个主要的功能就是格式化数据类型,防止错误的数据,那能不能用其他的方法赋值呢下面给出一个例子
public class MethodDemo04 {
public static void main(String[] args) {
List list1=new ArrayList<>();
List<String> list2=new ArrayList<String>();
list2.add("hello");
//list2.add(22);报错
/*
* 泛型
*/
Class c1=list1.getClass();
Class c2=list2.getClass();
System.out.println(c1==c2);
/*发现打印的结果为true 说明java 编译之后的集合时去泛型话的
*泛型的一个主要的功能是为了格式化数据类型防止输入错误 但是只在编译阶段有效
*绕过编译就无效了
*下面做一个测试
*/
try {
Method m=c2.getMethod("add", Object.class);
m.invoke(list2,20);
System.out.println(list2);///[hello, 20]
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
通过运行结果可以发现 将int 类型的值成功赋值到string类型的list 中这个说明了在编译过程中是存在泛型的绕过编译就无效了
反射如果灵活的运用它,能够使我们的代码更加灵活,但是它也有它的缺点,就是运用它会使我们的软件的性能降低,复杂度增加,所以还要我们慎重的使用它。
文章地址 http://www.haha174.top/article/details/256239
源码地址 https://github.com/haha174/daylx