文章目录
概述 |
为什么需要反射
在解释反射前先认识一下java的静态语言是什么。
动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。
静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。
如:Java、C、C++。
java是静态语言??那为什么java还能够如此灵活受欢迎呢?java虽然不是动态语言,但是它可以成为“准动态语言”,也就是说java有一定的动态性,即可以利用反射机制、字节码操作获得类似动态语言的特性。java的动态性让编程的时候更加灵活了!
反射是什么
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并且能够直接操作任意对象的内部属性及方法。
那反射是如何实现的呢?在JVM层面,在加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就可以包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,投过这个镜子看到类的结构,所以,我们形容的称之为:反射。
示例
在静态语言中,使用一个变量时,必须知道他的类型。在java中,变量的类型信息在编译时都保存到了class文件中,这样在运行时才能保证准确无误;换句话说,程序在运行时的行为都是固定的,如果想在运行时改变,使用反射就可以满足。
在Spring中,有这样的java bean配置:
<bean id="someID" class="net.liujiacai.Foobar">
<property name="someField" value="someValue" />
</bean>
spring在处理这个bean标签时,发现class属性指定的是net.liujiacai.Foobar这个类,就会调用Class.forName(String)来实例化这个类,再通过反射,可以取到someField属性的值了。
如果我们想改变这个程序运行时的信息,我们这里直接修改bean,property的属性即可,无需重新编译。
在动态语言中,使用变量不需要声明类型,因而不需要这反射这种机制。
比如在javascript中,我们知道有个变量foobar,不管foobar有没有sayHello()属性,我们都可以这么写:
foobar.sayHello()
因为没有类型检查,这里这么写是允许的。至于在运行时报不报错,就要看运行时foobar的真正值了。
反射的实现--API |
java反射提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时条用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
反射是可以得到任何他想要的类的信息啊,下面具体如何通过Reflection API来实现上述的功能。
获取Class的实例
首先定义一个Person类:
public class Person {
private String name;
public int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private Person(String name) {
this.name = name;
}
public Person() {
System.out.println("Person()");
}
public void show(){
System.out.println("你好,我是一个人");
}
private String showNation(String nation){
System.out.println("我的国籍是:" + nation);
return nation;
}
}
获取Class类的实例的四种方式:
@Test
public void test3() throws ClassNotFoundException {
//方式一:调用运行时类的属性:.class
Class clazz1 = Person.class;
System.out.println(clazz1);
//方式二:通过运行时类的对象,调用getClass()
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(clazz2);
//方式三:调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName("com.rxs.java.Person");
// clazz3 = Class.forName("java.lang.String");
System.out.println(clazz3);
System.out.println(clazz1 == clazz2);
System.out.println(clazz1 == clazz3);
//方式四:使用类的加载器:ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("com.rxs.java.Person");
System.out.println(clazz4);
System.out.println(clazz1 == clazz4);
}
class com.rxs.java.Person
Person()
class com.rxs.java.Person
class com.rxs.java.Person
true
true
class com.rxs.java.Person
true
获取ClassLoader
实践类加载机制中的过程。
@Test
public void test1(){
//对于自定义类,使用系统类加载器进行加载
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader);
//调用系统类加载器的getParent():获取扩展类加载器
ClassLoader classLoader1 = classLoader.getParent();
System.out.println(classLoader1);
//调用扩展类加载器的getParent():无法获取引导类加载器
//引导类加载器主要负责加载java的核心类库,无法加载自定义类的。
ClassLoader classLoader2 = classLoader1.getParent();
System.out.println(classLoader2);
ClassLoader classLoader3 = String.class.getClassLoader();
System.out.println(classLoader3);
}
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@a09ee92
null
null
创建运行时的类的对象
newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。
要想此方法正常的创建运行时类的对象,要求:
1.运行时类必须提供空参的构造器
2.空参的构造器的访问权限得够。通常,设置为public。
在javabean中要求提供一个public的空参构造器。原因:
1.便于通过反射,创建运行时类的对象
2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
@Test
public void test1() throws IllegalAccessException, InstantiationException {
Class<Person> clazz = Person.class;
Person obj = clazz.newInstance();
System.out.println(obj);
}
Person()
Person{name='null', age=0}
获取类的属性信息
获取类中属性的权限修饰符、数据类型、变量名:
@Test
public void test2(){
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f : declaredFields){
//1.权限修饰符
int modifier = f.getModifiers();
System.out.print(Modifier.toString(modifier) + "\t");
//2.数据类型
Class type = f.getType();
System.out.print(type.getName() + "\t");
//3.变量名
String fName = f.getName();
System.out.print(fName);
System.out.println();
}
}
private java.lang.String name
public int age
获取类中的方法信息
获取类中方法的权限修饰符 返回值类型 方法名(参数类型1 形参名1,…) :
@Test
public void test2(){
Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for(Method m : declaredMethods){
//1.获取方法声明的注解
Annotation[] annos = m.getAnnotations();
for(Annotation a : annos){
System.out.println(a);
}
//2.权限修饰符
System.out.print(Modifier.toString(m.getModifiers()) + "\t");
//3.返回值类型
System.out.print(m.getReturnType().getName() + "\t");
//4.方法名
System.out.print(m.getName());
System.out.print("(");
//5.形参列表
Class[] parameterTypes = m.getParameterTypes();
if(!(parameterTypes == null && parameterTypes.length == 0)){
for(int i = 0;i < parameterTypes.length;i++){
if(i == parameterTypes.length - 1){
System.out.print(parameterTypes[i].getName() + " args_" + i);
break;
}
System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
}
}
System.out.print(")");
//6.抛出的异常
Class[] exceptionTypes = m.getExceptionTypes();
if(exceptionTypes.length > 0){
System.out.print("throws ");
for(int i = 0;i < exceptionTypes.length;i++){
if(i == exceptionTypes.length - 1){
System.out.print(exceptionTypes[i].getName());
break;
}
System.out.print(exceptionTypes[i].getName() + ",");
}
}
System.out.println();
}
}
public java.lang.String toString()
public java.lang.String getName()
public void setName(java.lang.String args_0)
public void setAge(int args_0)
public int getAge()
public void show()
private java.lang.String showNation(java.lang.String args_0)
调用运行时类中的指定的构造器
@Test
public void testConstructor() throws Exception {
Class clazz = Person.class;
//private Person(String name)
/*
1.获取指定的构造器
getDeclaredConstructor():参数:指明构造器的参数列表
*/
Constructor constructor = clazz.getDeclaredConstructor(String.class);
//2.保证此构造器是可访问的
constructor.setAccessible(true);
//3.调用此构造器创建运行时类的对象
Person per = (Person) constructor.newInstance("Tom");
System.out.println(per);
}
Person{name='Tom', age=0}
扩展
这些都是Class类常用的方法,在此就不一一展开了。正是因为反射机制提供的这些API,使得能够载运行时实现动态的效果,提高java程序的灵活性,在学习源码时反射总是能巧妙并且简单的使程序更加优美,学无止境,一起进步!
感谢大神们的分享:
https://segmentfault.com/q/1010000002583151
https://www.cnblogs.com/onlywujun/p/3519037.html
https://www.zhihu.com/question/28570203