参考:
http://www.jianshu.com/p/1a60d55a94cd
http://blog.youkuaiyun.com/ljphhj/article/details/12858767
1、java反射应用场景
首先说一下为什么要用反射。考虑这么一个场景,我们要通过网络请求获取一个字段,这个字段名字就是我们要实例化类的类名,那么我们在编码阶段是没办法获取我们要实例化类的名字的,那怎么办呢?这个时候反射就派上用场了,我们可以在运行时通过网络请求获取到的类相关信息获得这个类的实例、内部方法以及变量信息,然后继续我们的调用。
//这里的calssName也许我们编码的时候并不知道具体是什么,需要运行时才能赋值
Object service = Class.forName(className).newInstance();
2、反射到底是什么
嗯,有了上面的介绍,对反射是不是有了一个模糊的概念?那就深入(貌似也没什么深入哈哈)看看反射的定义吧:
Reflection enables Java code to discover information about the fields, methods and
constructors of loaded classes, and to use reflected fields, methods, and constructors
to operate on their underlying counterparts, within security restrictions.
大致意思是反射机制可以在编码时候不知道运行对象是谁的情况下,在运行时动态获取对应对象的field、methods或者constructors,注意的核心是编码时需要的东西可能不确定以及运行时动态加载,很多文章只说后者,但是我真的搞半天都不明白为什么要动态加载呀T^T
发射可以实现的功能:
(1)运行时通过不同构造器创建任意一种对象
(2)运行时获取对象的成员变量和方法,以及调用对象方法(甚至可以调用private方法)
2、反射机制优缺点
优点
可以实现动态创建对象和编译,体现出很大的灵活性。
缺点
对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。
3、理解Class类和类类型,三种获得Class类的方法
(1)Class类
所有类是java.lang.Class类的实例对象,也就是说Class是所有类的类
查看Class的源码看Class的构造器:
private Class(ClassLoader loader) {
classLoader = loader;
}
发现Class类的构造器是私有的,所以不能通过Class c=new Class()来获得Class的对象了,有三种方法获取,见下。
(2)获取Class类的三种方法
假设有一个类Code,它的一个实例对象是code1:
/**
* 1、Class c=类.class;
* 这说明任何一个类都有一个隐含的静态成员变量class,这种方式
* 是通过获取类的静态成员变量class得到的
*/
Class c1 = Code.class;
//2、Class c=对象.getClass();
Class c2 = code1.getClass();
//3、Class c=Class.forName(String);其中String是Code类的完整路径(包名.类名)
Class c3 = Class.forName("com.trigl.reflect.Code");
(3)类类型
上面的c1,c2,c3就是Code的类类型。类类型顾名思义,就是类的类型,也就是描述一个类是什么, 都有哪些东西,所以我们可以通过类类型知道一个类的属性和方法,并且可以调用一个类的属性和方法,这也是反射的基础。
4、反射机制相关操作
(1)创建对象实例
无参构造:
Person person = (Person) class1.newInstance();
带参构造:
person2 = (Person) class1.getConstructor(int.class, String.class)
.newInstance(21, "Vivien");
(2)通过Java反射机制获得类的基本信息
有了java.lang.Class类 的对象,就可以通过其中的方法来获取到该类中的构造方法、域和方法。对应的方法分别是getConstructor、getField和getMethod。这三个方法还有相应的getDeclaredXXX版本,区别在于getDeclaredXXX版本的方法只会获取该类自身所声明的元素,而不会考虑继承下来的。
Class class1 = null;
class1 = Class.forName("demo.SuperMan");
Class superClass = class1.getSuperclass();
System.out.println("Demo6:SuperMan类的父类名:" + superClass.getName());
System.out.println("===============");
Field[] fields = class1.getDeclaredFields();
for (Field field : fields) {
System.out.println("类中的成员:" + field);
}
String nameString = class1.getClassLoader().getClass().getName();
System.out.println("类加载器类名:" + nameString);
System.out.println("===============");
//获取的方法只有自身定义的,没有继承方法
Method[] methods = class1.getDeclaredMethods();
for (Method method : methods) {
System.out.println("Demo6取得SuperMan类的方法:");
System.out.println("方法名:" + method.getName());
System.out.println("方法返回类型:" + method.getReturnType());
System.out.println("方法访问修饰符:"
+ Modifier.toString(method.getModifiers()));
System.out.println("方法代码写法:" + method);
}
System.out.println("======================");
Class interfaces[] = class1.getInterfaces();
for (Class inter : interfaces) {
System.out.println("实现的接口类名:" + inter.getName());
}
输出:
Demo6:SuperMan类的父类名:demo.Person
===============
类中的成员:private boolean demo.SuperMan.BlueBriefs
类加载器类名:sun.misc.Launcher$AppClassLoader
===============
Demo6取得SuperMan类的方法:
方法名:fly
方法返回类型:void
方法访问修饰符:public
方法代码写法:public void demo.SuperMan.fly()
Demo6取得SuperMan类的方法:
方法名:walk
方法返回类型:void
方法访问修饰符:public
方法代码写法:public void demo.SuperMan.walk(int)
Demo6取得SuperMan类的方法:
方法名:setBlueBriefs
方法返回类型:void
方法访问修饰符:public
方法代码写法:public void demo.SuperMan.setBlueBriefs(boolean)
Demo6取得SuperMan类的方法:
方法名:isBlueBriefs
方法返回类型:boolean
方法访问修饰符:public
方法代码写法:public boolean demo.SuperMan.isBlueBriefs()
======================
实现的接口类名:demo.ActionInterface
(3)通过Java反射机制操作类变量和类方法
//操作类变量,set、get
Class class1 = null;
class1 = Class.forName("demo.Person");
Object obj = class1.newInstance();
Field personNameField = class1.getDeclaredField("name");
personNameField.setAccessible(true);//因为"name"变量是私有的
personNameField.set(obj, "程秋平");
System.out.println("Demo5:修改属性之后得到属性变量值是:" + personNameField.get(obj));
//调用类方法
Class class2 = null;
class2 = Class.forName("demo.SuperMan");
System.out.println("Demo7:\n调用无参方法fly()");
Method method = class2.getMethod("fly");
method.invoke(class2.newInstance());
System.out.println("调用有参方法walk(int m):");
method = class2.getMethod("walk", int.class);
method.invoke(class2.newInstance(), 100);
输出:
Demo5:修改属性之后得到属性变量值是:程秋平
Demo7:
调用无参方法fly()
超人会飞耶~~
调用有参方法walk(int m):
超人会走耶~~走了100米
Person类:
package copypeople;
public class Person {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(){
}
private Person(String name){
this.name=name;
System.out.println(name);
}
public void fun()
{
System.out.println("fun");
}
public void fun(String name,int age)
{
System.out.println("我叫"+name+",今年"+age);
}
}
SuperMan类:
package demo;
public class SuperMan extends Person implements ActionInterface{
private boolean BlueBriefs;
public void fly()
{
System.out.println("超人会飞耶~~");
}
public boolean isBlueBriefs()
{
return BlueBriefs;
}
public void setBlueBriefs(boolean blueBriefs){
BlueBriefs=blueBriefs;
}
@Override
public void walk(int m) {
// TODO Auto-generated method stub
System.out.println("超人会走耶~~"+"走了"+m+"米");
}
}
ActionInterface:
package demo;
public interface ActionInterface {
public void walk(int m);
}
(4)通过反射机制访问类的private方法
首先要知道的就是getDeclaredMethod()和getMethod()的区别,getDeclaredMethod()可以访问类的所有方法,包括父类、private等,getMethod()可以访问类的所有public方法,所以如果要访问private方法要用前者。比如在这个例子中,我在SuperMan()类中加了方法:
private void test(int m){
System.out.println("我是private方法,我可以走"+m+"米");
}
然后在Demo6中加代码:
method=class1.getDeclaredMethod("test", int.class);
method.setAccessible(true);
method.invoke(class1.newInstance(),12);
运行结果:
我是private方法,我可以走12米
(5)通过反射理解集合泛型的本质
结论: Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译到了运行期就无效了。
举例验证:
public static void main(String[] args) {
List list1 = new ArrayList(); //没有泛型
List<String> list2 = new ArrayList<String>(); //有泛型
//1.首先观察正常添加元素方式,在编译器检查泛型,
//这个时候如果list2添加int类型会报错
list2.add("hello");
// list2.add(20); // 报错!list2有泛型限制,只能添加String,添加int报错
System.out.println("list2的长度是:" + list2.size()); //此时list2长度为1
//2.然后通过反射添加元素方式,在运行期动态加载类,首先得到list1和
//list2的类类型相同,然后再通过方法反射绕过编译器来调用add方法,看能
//否插入int型的元素
Class c1 = list1.getClass();
Class c2 = list2.getClass();
System.out.println(c1 == c2); //结果:true,说明类类型完全相同
//验证:我们可以通过方法的反射来给list2添加元素,这样可以绕过编译检查
try {
//通过方法反射得到add方法
Method m = c2.getMethod("add", Object.class);
// 给list2添加一个int型的,上面显示在编译器是会报错的
m.invoke(list2, 20);
//结果:2,说明list2长度增加了,并没有泛型检查
System.out.println("list2的长度是:" + list2.size());
} catch (Exception e) {
e.printStackTrace();
}
//综上可以看出,在编译器的时候,泛型会限制集合内元素类型保持一致,但是
//编译器结束进入运行期以后,泛型就不再起作用了,即使是不同类型的元素也
//可以插入集合。
}
运行结果:
list2的长度是:1
true
list2的长度是:2