java 反射机制学习
来源:互联网 发布:淘宝上的小样是真的吗 编辑:程序博客网 时间:2024/05/18 02:14
有时候听别人说反射反射的,也是一脸茫然,当时就不能愉快的交流了。所以今天赶紧看看啥是反射,提高一下自己。
说到反射,不得不提 Class
public final class Class extends Object implements Serializable, GenericDeclaration, Type, AnnotatedElementjava.lang.Object ↳ java.lang.Class<T>
这个类的定义是这样的。
Class的实例表示正在运行的Java应用程序中的类和接口。 枚举是一种类,注解是一种接口。 每个数组也属于一个类,它反映为一个Class对象,由具有相同元素类型和维数的所有数组共享。 原始Java类型(boolean,byte,char,short,int,long,float和double)和关键字void也表示为Class对象。
Class 没有公共构造函数。 相反,Class对象由Java虚拟机在(通过调用类加载器ClassLoader中的defineClass方法)加载类的时候自动创建。
/* * Constructor. Only the Java Virtual Machine creates Class * objects. */ private Class() {}
Class 构造函数是私有的,那么我们就不能通过new的方式来得到Class的实例,那我们如何得到Class 的实例呢。有三种方法。
- 使用Class 的静态方法。
//通过指定的 className ,返回和这个类或者接口相关的Class对象。/**className 类的全路径名称。initialize 该类是否应该被初始化loader 类加载器*/forName(String className);forName(String className, boolean initialize, ClassLoader loader);
2 通过一个具体类的.class 方法。
//Person 类 Class c = Person.class;
3 通过一个具体的类的对象的.getClass ()方法。
Person person = new Person(); Class c1 = person.getClass();
另外:如果要获得基本数据类型的类对象,还可以这样。
//获取int类型的类对象。 Class c2=Integer.TYPE;
在java程序运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象
现在我们获得了Class 的对象,那么我们可以通过这个Class干什么呢?
- 获取成员变量Field
- 获取成员方法Method
获取构造函数Constructor
先定义几个接口和类。
Animal
public class Animal { private int eyes; protected String name; public float weight; float height; public Animal() { } public Animal(int eyes, String name, float weight, float height) { this.eyes = eyes; this.name = name; this.weight = weight; this.height = height; } public int getEyes() { return eyes; } public void setEyes(int eyes) { this.eyes = eyes; } public String getName() { return name; } public void setName(String name) { this.name = name; } public float getWeight() { return weight; } public void setWeight(float weight) { this.weight = weight; } public float getHeight() { return height; } public void setHeight(float height) { this.height = height; }}
RunInterface
public interface RunInterface { int FieldInRunInterface = 0; void run(); void jump();}
LiveInterface
public interface LiveInterface { int FieldInLiveInterface = 1; void eat(); void sleep();}
Person 类继承Animal,实现接口RunInterface,LiveInterface
public class Person extends Animal implements RunInterface, LiveInterface { private int age; private String msg = "good morning 2017"; public Person() { } public Person(String name) { this.name = name; System.out.println(name); } private Person(int age, String name) { this.age = age; this.name = name; System.out.println(name); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void fun() { System.out.println("fun"); } private void info(String name, int age) { System.out.printf("ReflectActivity 我叫" + name + ",今年" + age + "岁"); } @Override public void run() { } @Override public void jump() { } @Override public void eat() { } @Override public void sleep() { }}
用Person 类来做测试。
1. 获取Person 类成员变量Field,有两个方法。
/**这个方法查找的是public 类型的变量,包括父类的和实现的接口中的。返回一个Field对象,该对象反映由此Class对象表示的类或接口的指定的公共成员变量。 name参数是一个String,指定所需字段的名称。查找算法是这样的1如果查找的类声明了这样一个public的成员变量,就是要查找的Field。2如果1中没有找到,就从当前类直接实现的接口中递归查找。3如果2中也没找到,就从当前类的父类递归查找。4上面三步找不到抛出NoSuchFieldException。*/getField(String name)
通过getField(String name)获取成员变量。
Class c = Person.class; try { Object o = c.newInstance();//生成一个Person实例 Field field = c.getField("FieldInLiveInterface"); int a = field.getInt(o); System.out.println(a + "");//输出1 } catch (Exception e) { e.printStackTrace(); }
/** 返回一个Field对象,该对象反映由此Class对象表示的类或接口的指定的已声明字段。 name参数是一个字符串,指定所需字段的简单名称。*/getDeclaredField(String name)
通过getDeclaredField(String name)获取私有的成员变量。
Class c = Person.class; try { Object o = c.newInstance();//生成一个Person实例 Field field1 = c.getDeclaredField("msg"); field1.setAccessible(true); String msg = (String) field1.get(o); System.out.println(msg);//输出good morning 2017 } catch (Exception e) { e.printStackTrace(); }
注意
1:Object o = c.newInstance();使用当前类的默认public型的无参构造函数。来构造当前类的实例。如果没有public型的无参构造函数,就必须使用下文所提到的方法来获取构造函数,然后再构造相应的对象。
2: field1.setAccessible(true);表示反射得到的成员变量在使用的时候,不做java的访问可见性(就是成员变量的可见性 private public 等等)检查。
3:String msg = (String) field1.get(o); get(Object obj),把得到的成员变量的值(如果是基本数据类型,会把返回值自动包装成一个Object.),提取到obj中。如果获取的成员变量是static类型的话,get(Object obj)方法的参数obj 可以为null。
既然能够获取成员变量的值,那能够修改成员变量的值吗。当然可以。
Class c = Person.class; try { Object o = c.newInstance(); Field field1 = c.getDeclaredField("msg"); field1.setAccessible(true); String msg = (String) field1.get(o); System.out.println(msg); field1.set(o,"good by 2016"); String msgModified=(String) field1.get(o); System.out.println(msgModified); } catch (Exception e) { e.printStackTrace(); }
//输出结果显示修改成功。good morning 2017good by 2016
获取当前类所有的public 成员变量,包括父类的和实现的接口中的。
//返回结果是一个数组//如果该类没有public成员变量,或者它表示数组类,原始类型或void,返回的数组长度为0.Field[] getFields ()
Class c = Person.class; Field[] fields = c.getFields(); for (Field field : fields) { System.out.println(field.getName()); }
输出结果
FieldInRunInterfaceFieldInLiveInterfaceweight
获取当前类所有的成员变量包括private,protected,public,default (package)类型,不包括继承的成员变量。
Field[] declaredFields = c.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField.getName()); }
输出结果
agemsg
2. 获取成员方法Method
获取一个public 类型的方法。包括父类的和接口中的。
/** name 表示方法名 Class...<?> parameterTypes 表示方法参数的类型。*/getMethod(String name, Class..<?> parameterTypes)
try { Method method = c.getMethod("setEyes", int.class); System.out.println("name=" + method.getName() + ",return type= " + method.getReturnType()); Method method1 = c.getMethod("run"); System.out.println("name=" + method1.getName() + ",return type= " + method1.getReturnType()); Method method2 = c.getMethod("setAge",int.class); System.out.println("name=" + method2.getName() + ",return type= " + method2.getReturnType()); } catch (NoSuchMethodException e) { e.printStackTrace(); }
//输出结果name=setEyes,return type= voidname=run,return type= voidname=setAge,return type= void
Method getDeclaredMethod 获取该类的任意类型的方法,不包括从父类继承的方法。但是包括从接口中实现的方法。
Method method1 = c.getDeclaredMethod("run"); System.out.println("name=" + method1.getName() + ",return type= " + method1.getReturnType()); Method method2 = c.getDeclaredMethod("info",String.class,int.class); System.out.println("name=" + method2.getName() + ",return type= " + method2.getReturnType()); Method method3 = c.getDeclaredMethod("jump"); System.out.println("name=" + method3.getName() + ",return type= " + method3.getReturnType());
//输出结果name=run,return type= voidname=info,return type= voidname=jump,return type= void
Method[] getMethods ():获取当前类所有的public类型的方法,包括继承父类的和接口中的。如果没有public方法,或者该类是原始数据类型或者void类型,返回的数组的长度是0.
Method[] methods = c.getMethods(); for (Method method : methods) { System.out.println(method.getName()); }
//输出结果runsleepgetAgesetAgefunjumpeatgetNamesetNamegetEyessetEyesgetWeightsetWeightgetHeightsetHeightwaitwaitwaitequalstoStringhashCodegetClassnotifynotifyAll
Method[] getDeclaredMethods ():获取当前类所有的方法,但是不包括继承父类的。如果该类是原始数据类型或者Array类型或者void类型,返回的数组的长度是0。
Method[] declaredMethods = c.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println(method.getName()); }
//输出结果runsleepgetAgesetAgefuninfojumpeat
既然愉快的获得了类的方法,那么可不可以愉快的调用一下呢?No Problem!
Class c = Person.class; Method method2 = c.getDeclaredMethod("info", String.class, int.class); System.out.println("name=" + method2.getName() + ",return type= " + method2.getReturnType()); method2.setAccessible(true);//info()方法是私有的。 method2.invoke(c.newInstance(), "dumingwei", 25);
//输出结果name=info,return type= voidReflectActivity 我叫dumingwei,今年25岁
3. 获取构造函数Constructor
//获取public 类型的构造函数Constructor<T> getConstructor (Class...<?> parameterTypes)
Constructor constructor = c.getConstructor(String.class); System.out.println(constructor.getName()); //输出结果com.example.model.Person
//获取构造函数,public ,private 都不惧。Constructor<T> getDeclaredConstructor (Class...<?> parameterTypes)
Constructor constructor1 = c.getDeclaredConstructor(int.class, String.class);System.out.println(constructor1.getName());//输出结果com.example.model.Person
getConstructors (),获取所有public 类型的构造函数
Constructor[] constructors = c.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); } //输出结果 //public com.example.model.Person(java.lang.String) //public com.example.model.Person()
getDeclaredConstructors (),获取所有的构造函数。
Constructor[] declaredConstructors = c.getDeclaredConstructors(); for (Constructor constructor : declaredConstructors) { System.out.println(constructor); }
//输出结果//这个private 好刺眼。private com.example.model.Person(int,java.lang.String)public com.example.model.Person(java.lang.String)public com.example.model.Person()
注意:如果要获取的类对象是一个静态内部类获取构造函数 和非静态内部类获取构造函数的区别。
我们在Person 类里面加一个静态内部类。
public static class Man { int beerCount;//能喝几瓶啤酒 public Man() { } public Man(int beerCount) { this.beerCount = beerCount; } public int getBeerCount() { return beerCount; } public void setBeerCount(int beerCount) { this.beerCount = beerCount; System.out.println("你能喝几瓶啤酒?" + beerCount); } }
然后获取 Man 类的构造函数。
Class clz = Person.Man.class; Constructor constructor = clz.getConstructor(int.class); System.out.println(constructor.getName()); Constructor constructor1 = clz.getDeclaredConstructor(); System.out.println(constructor1.getName());
//输出结果,好像没什么问题。com.example.model.Person$Mancom.example.model.Person$Man
把Man 类去掉 static 修饰符,变成一个非静态内部类。然后使用同样的方法再去获取Man的构造函数,就直接报错了。
java.lang.NoSuchMethodException: com.example.model.Person$Man.<init>(int) at java.lang.Class.getConstructor0(Class.java:3082) at java.lang.Class.getConstructor(Class.java:1825) at com.example.MyClass.main(MyClass.java:86) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
正确的获取非静态内部类的方式。第一个参数传入外部类的类对象。
Class clz = Person.Man.class; Constructor constructor = clz.getConstructor(Person.class,int.class); System.out.println(constructor.getName()); Constructor constructor1 = clz.getDeclaredConstructor(Person.class); System.out.println(constructor1.getName());
//输出结果com.example.model.Person$Mancom.example.model.Person$Man
至于为什么这样就可以,现在还不明白,我猜是如果不传入一个外部类对象,java虚拟机无法正确的加载这个内部类。(有待以后学习验证)。
关于 java 反射就总结到这里,关于反射的原理,什么时候应该使用反射的一系列问题,自己现在并不明白,以后再慢慢研究。
参考链接:
【1】http://www.jianshu.com/p/1a60d55a94cd
【2】http://blog.csdn.net/u014082714/article/details/50004843
【3】https://developer.android.google.cn/reference/java/lang/Class.html
- java反射机制学习
- JAVA反射机制学习
- Java反射机制学习
- JAVA反射机制学习
- java反射机制学习
- java反射机制学习
- java反射机制学习
- Java反射机制学习
- Java学习 反射机制
- Java反射机制学习
- java反射机制学习
- java反射机制学习
- Java学习:反射机制
- Java反射机制学习
- Java反射机制学习
- Java反射机制学习
- java 反射机制学习
- java 反射机制学习
- 单利模式的简单实例
- 合并两个有序列表
- 浅谈UE4引擎
- NA主要课程内容
- Python零基础入门之十一文件
- java 反射机制学习
- 集合框架_HashMap和Hashtable的区别
- import tensorflow/theano出错
- error: conflicting types for 'RTASSERTVAR'
- springMVC注解简单例子
- npm install 下载慢的解决
- android系统硬件OpenGL 3D 移植
- Web用户的身份验证及WebApi权限验证流程的设计和实现
- 活动的启动模式