Java反射机制(笔记)

前言

JAVA反射主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
反射机制是什么?
反射机制就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
简言之,反射可以实现在运行时可以知道任意一个类的属性和方法。
为什么要用反射机制?(直接创建对象不就可以了吗,这就涉及到了动态与静态的概念)
(1)静态编译:在编译时确定类型,绑定对象,即通过。
(2)动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。

反射机制能做什么?

(1)在运行时判断任意一个对象所属的类;
(2)在运行时构造任意一个类的对象;
(3)在运行时判断任意一个类所具有的成员变量和方法;
(4)在运行时调用任意一个对象的方法;
(5)生成动态代理。
Java 反射机制的应用场景
(1)逆向代码 ,例如反编译;
(2)与注解相结合的框架 例如Retrofit;
(3)单纯的反射机制应用框架 例如EventBus;
(4)动态生成类框架 例如Gson。

反射机制的优点与缺点
(1)优点

可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。

(比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。)
(2)缺点
对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。

Class类和类类型

(1)定义

Class类是反射实现的基础,是所有类的类,而类是java.lang.Class类的实例对象。类类型就是类的类型,也就是描述一个类是什么,都有哪些东西,所以我们可以通过类类型知道一个类的属性和方法,并且可以调用一个类的属性和方法,这就是反射的基础。

(2)获取Class类的方式

三种方式,如下:

Class c1 = Code.class; 这说明任何一个类都有一个隐含的静态成员变量class,这种方式是通过获取类的静态成员变量class得到的
Class c2 = code1.getClass(); code1是Code的一个对象,这种方式是通过一个类的对象的getClass()方法获得的 
Class c3 = Class.forName("com.example.gislaozhang.Code"); 这种方法是Class类调用forName方法,通过一个类的全量限定名获得

完整实例代码:

package com.example.gislaozhang;

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //第一种:Class c1 = Code.class;
        Class class1=ReflectDemo.class;
        System.out.println(class1.getName());

        //第二种:Class c2 = code1.getClass();
        ReflectDemo demo2= new ReflectDemo();
        Class c2 = demo2.getClass();
        System.out.println(c2.getName());

        //第三种:Class c3 = Class.forName("com.trigl.reflect.Code");
        Class class3 = Class.forName("com.example.gislaozhang.ReflectDemo");
        System.out.println(class3.getName());
    }
}
输出结果:
com.example.gislaozhang.ReflectDemo
com.example.gislaozhang.ReflectDemo
com.example.gislaozhang.ReflectDemo

(3)Class的用途

1)获取成员方法Method;2)获取成员变量Field;3)获取构造函数Constructor。

详细如下:

A、获取成员方法Method

两个参数分别是方法名和方法参数类的类类型列表。

public Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 得到该类所有的方法,不包括父类的 
public Method getMethod(String name, Class<?>... parameterTypes) // 得到该类所有的public方法,包括父类的

//具体使用
Method[] methods = class1.getDeclaredMethods();//获取class对象的所有声明方法 
Method[] allMethods = class1.getMethods();//获取class对象的所有public方法 包括父类的方法 
Method method = class1.getMethod("info", String.class);//返回次Class对象对应类的、带指定形参列表的public方法 
Method declaredMethod = class1.getDeclaredMethod("info", String.class);//返回次Class对象对应类的、带指定形参列表的方法

示例代码如下(getMethod()方法):

package com.example.gislaozhang;

public class Person {
	private String name;
	private int age;
	private String msg = "hello wrold";

	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() {
	}

	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 + "岁");
	}
}
package com.example.gislaozhang;

import java.lang.reflect.Method;

	public class ReflectDemo {
	    public static void main(String[] args){
	        try {
	            Class c = Class.forName("com.example.gislaozhang.Person");
	            Object o = c.newInstance();
	            Method method = c.getMethod("fun", String.class, int.class);
	            method.invoke(o, "gislaozhang", 7);
	        } catch (Exception e) {
	            e.printStackTrace();
	        }
	    }
	}
输出结果: 我叫gislaozhang,今年7岁

其中invoke()方法参数中第一个为实例对象,后面为具体参数值。

如果想获取类中所有成员方法,如下:

1.获取所有方法的数组:
Class c = Class.forName("com.tengj.reflect.Person");
Method[] methods = c.getDeclaredMethods(); // 得到该类所有的方法,不包括父类的
或者:
Method[] methods = c.getMethods();// 得到该类所有的public方法,包括父类的

2.然后循环这个数组就得到每个方法了:
for (Method method : methods)
实例代码如下(getDeclaredMethods()方法):
package com.example.gislaozhang;

import java.lang.reflect.Method;

public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.example.gislaozhang.Person");
            Method[] methods = c.getDeclaredMethods();
            for(Method m:methods){
                String  methodName= m.getName();
                System.out.println(methodName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
输出结果:

getName
setName
getAge
setAge
fun
fun

如果把c.getDeclaredMethods();改成c.getMethods();执行结果如下,多了很多方法,以为把Object里面的方法也打印出来了,因为Object是所有类的父类:
getName
setName
getAge
setAge
fun
fun
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll

B、获取成员变量Field

想一想成员变量中都包括什么:成员变量类型+成员变量名
类的成员变量也是一个对象,它是java.lang.reflect.Field的一个对象,所以我们通过java.lang.reflect.Field里面封装的方法来获取这些信息。
单独获取某个成员变量,通过Class类的以下方法实现:

public Field getDeclaredField(String name) // 获得该类自身声明的所有变量,不包括其父类的变量
public Field getField(String name) // 获得该类自所有的public成员变量,包括其父类变量

//具体实现
Field[] allFields = class1.getDeclaredFields();//获取class对象的所有属性 
Field[] publicFields = class1.getFields();//获取class对象的public属性 
Field ageField = class1.getDeclaredField("age");//获取class指定属性 
Field desField = class1.getField("des");//获取class指定的public属性
其中参数是成员变量的名称。
实例代码如下:
package com.example.gislaozhang;

import java.lang.reflect.Field;

public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.example.gislaozhang.Person");
            //获取成员变量
            Field field = c.getDeclaredField("msg"); //因为msg变量是private的,所以不能用getField方法
            Object o = c.newInstance();
            field.setAccessible(true);//设置是否允许访问,因为该变量是private的,所以要手动设置允许访问,如果msg是public的就不需要这行了。
            Object msg = field.get(o);
            System.out.println(msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
输出结果:hello wrold
如果想要获取所有成员变量的信息,可以通过以下几步:
1.获取所有成员变量的数组:

Field[] fields = c.getDeclaredFields();

2.遍历变量数组,获得某个成员变量field

for (Field field : fields)
完整实例代码如下:
package com.example.gislaozhang;

import java.lang.reflect.Field;

public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.example.gislaozhang.Person");
            Field[] fields = c.getDeclaredFields();
            for(Field field :fields){
                System.out.println(field.getName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
输出结果:
name
age
msg

C、获取构造函数Constructor

最后再想一想构造函数中都包括什么:构造函数参数
同上,类的成构造函数也是一个对象,它是java.lang.reflect.Constructor的一个对象,所以我们通过java.lang.reflect.Constructor里面封装的方法来获取这些信息。
单独获取某个构造函数,通过Class类的以下方法实现:

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //  获得该类所有的构造器,不包括其父类的构造器
public Constructor<T> getConstructor(Class<?>... parameterTypes) // 获得该类所以public构造器,包括父类

//具体
Constructor<?>[] allConstructors = class1.getDeclaredConstructors();//获取class对象的所有声明构造函数 
Constructor<?>[] publicConstructors = class1.getConstructors();//获取class对象public构造函数 
Constructor<?> constructor = class1.getDeclaredConstructor(String.class);//获取指定声明构造函数 
Constructor publicConstructor = class1.getConstructor(String.class);//获取指定声明的public构造函数
其中参数为构造函数参数类的类类型列表。
实例代码如下:
package com.example.gislaozhang;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.example.gislaozhang.Person");
            //获取构造函数
            Constructor constructor = c.getDeclaredConstructor(String.class);
            constructor.setAccessible(true);//设置是否允许访问,因为该构造器是private的,所以要手动设置允许访问,如果构造器是public的就不需要这行了。
            constructor.newInstance("ahnu");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
输出结果: ahnu
注意:Class的newInstance方法,只能创建只包含无参数的构造函数的类,如果某类只有带参数的构造函数,那么就要使用另外一种方式:

fromClass.getDeclaredConstructor(String.class).newInstance("ahnu");
获取所有的构造函数,可以通过以下步骤实现:
1.获取该类的所有构造函数,放在一个数组中:

Constructor[] constructors = c.getDeclaredConstructors();

2.遍历构造函数数组,获得某个构造函数constructor:

for (Constructor constructor : constructors)
实例代码如下:
package com.example.gislaozhang;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class ReflectDemo {
    public static void main(String[] args){
    	try{
    		 Class c = Class.forName("com.example.gislaozhang.Person");
            Constructor[] constructors = c.getDeclaredConstructors();
            for(Constructor constructor:constructors){
                System.out.println(constructor);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
输出结果:

public com.example.gislaozhang.Person()
private com.example.gislaozhang.Person(java.lang.String)

D、其它方法

Annotation[] annotations = (Annotation[]) class1.getAnnotations();//获取class对象的所有注解 
Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);//获取class对象指定注解 
Type genericSuperclass = class1.getGenericSuperclass();//获取class对象的直接超类的 
Type Type[] interfaceTypes = class1.getGenericInterfaces();//获取class对象的所有接口的type集合
获取class对象的信息:
boolean isPrimitive = class1.isPrimitive();//判断是否是基础类型 
boolean isArray = class1.isArray();//判断是否是集合类
boolean isAnnotation = class1.isAnnotation();//判断是否是注解类 
boolean isInterface = class1.isInterface();//判断是否是接口类 
boolean isEnum = class1.isEnum();//判断是否是枚举类 
boolean isAnonymousClass = class1.isAnonymousClass();//判断是否是匿名内部类 
boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//判断是否被某个注解类修饰 
String className = class1.getName();//获取class名字 包含包名路径 
Package aPackage = class1.getPackage();//获取class的包信息 
String simpleName = class1.getSimpleName();//获取class类名 
int modifiers = class1.getModifiers();//获取class访问权限 
Class<?>[] declaredClasses = class1.getDeclaredClasses();//内部类 
Class<?> declaringClass = class1.getDeclaringClass();//外部类

getSuperclass():获取某类的父类  
getInterfaces():获取某类实现的接口
Java反射机制思维导图:


参考资料

http://www.daidingkang.cc/2017/07/18/java-reflection-annotations/    

猜你喜欢

转载自blog.csdn.net/gislaozhang/article/details/78758245