JAVA反射机制

JAVA反射机制

Java 反射机制是 Java 语言的一个重要特性。在学习 Java 反射机制前,大家应该先了解两个概念,编译期和运行期。

编译期是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。

运行期是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。

Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。

Java 反射机制在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法。此外,在 ORM 中间件的实现中,运用 Java 反射机制可以读取任意一个 JavaBean 的所有属性,或者给这些属性赋值。

参考地址: http://c.biancheng.net/view/6907.html

优点

  • 能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。
  • 与 Java 动态编译相结合,可以实现无比强大的功能。
  • 对于 Java 这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

缺点

  • 反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
  • 反射调用方法时可以忽略权限检查,获取这个类的私有方法和属性,因此可能会破坏类的封装性而导致安全问题。

API

参考: https://blog.csdn.net/Hellowenpan/article/details/107560498

1、取得一个类的Class对象的三种方式

//第一种
Class<?> clazz1 = Class.forName("com.ramelon.reflect.entity.Child");
//第二种
Class<? extends Child> clazz2 = new Child().getClass();
//第三种
Class<?> clazz3 = Child.class;

2、获取一个类的父类以及所有实现的接口

Class<?> superclass = clazz1.getSuperclass();
Class<?>[] interramelonces = clazz1.getInterramelonces();
//取得一个类的全类名(包名 + 类名)
clazz1.getName()
//取得类的简单名称
clazz1.getSimpleName();

3、构造函数

1、取得一个类的所有构造函数

Class<?> clazz1 = Class.forName("xxxx");
Constructor<?>[] constructors = clazz1.getConstructors();
Constructor<?>[] constructors1 = clazz1.getDeclaredConstructors();

2、根据参数类型取得构造函数

//取得只有一个参数且参数类型为String的构造方法
Constructor<?> constructor1 = clazz1.getDeclaredConstructor(String.class);
System.out.println(constructor1);

4、通过反射实例化对象

1、通过Class对象的newInstance()方法

Object instance = clazz1.newInstance();

2、通过构造方法实例化对象

//取得无参构造
Constructor<?> constructor2 = clazz1.getConstructor();
//通过无参构造创建一个对象
Object child1 = constructor2.newInstance();
//取得指定参数的构造方法对象
Constructor<?> constructor3 = clazz1.getConstructor(String.class, int.class);
//通过构造方法对象创建一个对象
constructor3.newInstance("wenpan",21);

5、反射操作属性

1、取得本类的所有属性(不包含父类和接口)

Field[] declaredFields = clazz1.getDeclaredFields();
for (int i = 0; i < declaredFields.length; i++) {
	//属性名称
	String fieldName = declaredFields[i].getName();
	//属性的类型
	String fieldType = declaredFields[i].getType().getName();
	//属性修饰符
	String fieldModifier = Modifier.toString(declaredFields[i].getModifiers());
	System.out.println(fieldModifier + " " + fieldType + " " + fieldName + ";");
}

2、取得实现的接口里的所有属性

Field[] fields = clazz1.getFields();
for (int i = 0; i < fields.length; i++) {
	//属性名称
	String fieldName = fields[i].getName();
	//属性的类型
	String fieldType = fields[i].getType().getName();
	//属性修饰符
	String fieldModifier = Modifier.toString(fields[i].getModifiers());
	System.out.println(fieldModifier + " " + fieldType + " " + fieldName + ";");
}

3、通过指定属性名取得属性

//通过属性名取得属性
Field field = clazz1.getDeclaredField("name");
//访问私有属性需要设置Accessible为true才可以更改或读取值(get 或 set)
field.setAccessible(true);
//取得instance对象里面的属性值
System.out.println("更改前的name值:" + field.get(instance));
//更改instance对象里的name属性值
field.set(instance,"文攀啊");
System.out.println("更改后的name属性值:" + field.get(instance));

通过属性名获取属性: Field field = clazz1.getDeclaredField("name"); 返回的是属性对象: Field

6、反射操作方法

1、取得一个类的所有方法(包括父类和实现的接口)

Method[] methods = clazz1.getMethods();

Method[] methods = clazz1.getMethods();
//输出取得的方法
for (int i = 0; i < methods.length; i++) {
    StringBuffer buffer = new StringBuffer();
    //方法修饰符
    String modifier = Modifier.toString(methods[i].getModifiers());
    //返回值类型
    String returnType = methods[i].getReturnType().getSimpleName();
    //方法名
    String name = methods[i].getName();
    //方法参数类型
    Class<?>[] parameterTypes = methods[i].getParameterTypes();
    buffer.append(modifier).append(" ").append(returnType).append(" ").
        append(name).append("(");
    for (int j = 0; j < parameterTypes.length; j++) {
        buffer.append(parameterTypes[j].getSimpleName()).append(" arg").append(j);
        if(j < parameterTypes.length - 1){
            buffer.append(",");
        }
    }
    buffer.append(")");
    //方法抛出的异常信息
    Class<?>[] exceptionTypes = methods[i].getExceptionTypes();
    if(exceptionTypes.length > 0){
        buffer.append(" throws");
    }
    for (int k = 0; k < exceptionTypes.length; k++) {
        buffer.append(exceptionTypes[k].getSimpleName());
        if(k < exceptionTypes.length -1){
            buffer.append(",");
        }
    }
    buffer.append("{ }");
    System.out.println(buffer);
}

2、取得一个类的所有方法(不包括父类和实现的接口

Method[] declaredMethods = clazz1.getDeclaredMethods();

3、通过反射调用某个对象的方法

要调用某个对象的方法,首先这个对象得存在,所以可以通过反射创建一个对象

public static void main(String[] args) throws Exception {
    //使用反射将Child类的Class对象加载进来
    Class<?> clazz1 = Class.forName("com.ramelon.reflect.entity.Child");
    //创建一个实例对象(使用反射调用方法必须要有实例对象)
    Object instance = clazz1.newInstance();
    //通过方法名称和指定的参数类型获取指定方法
    Method method = clazz1.getDeclaredMethod("setName", String.class);
    //调用Method对象的invoke方法执行instance对象的方法
    method.invoke(instance,"文攀啊");
    //通过方法名称和指定的参数类型获取指定方法
    Method getNameMethod = clazz1.getMethod("getName");
    //调用Method对象的invoke方法执行方法
    String name = (String)getNameMethod.invoke(instance);
    //输出执行方法返回的结果
    System.out.println(name);
}

通过方法名方法参数类型取得对象的方法:Method method = clazz1.getDeclaredMethod(“setName”, String.class);

7、反射操作注解

1、通过反射获取类上的注解,类属性上的注解

 public static void main(String[] args) throws Exception{

        User user = new User();
        // 取得类上的所有注解
        Annotation[] annotations = user.getClass().getAnnotations();
        // 获取类上指定类型的注解
        MyAnnotation annotation = user.getClass().getAnnotation(MyAnnotation.class);
        // 获取类的某个属性上的所有注解
        Annotation[] allAnnotations = user.getClass().getDeclaredField("name").getAnnotations();
        // 获取类的某个属性上指定类型的注解
        MyAnnotation annotation1 = user.getClass().getDeclaredField("name").getAnnotation(MyAnnotation.class);
        // 获取注解的属性
        String value = annotation1.value();
        String pattern = annotation1.pattern();
    }

8、反射实例应用

1、使用反射向一个int集合添加一个String元素

**原理:**首先通过反射取得该List集合的add()方法,然后使用反射调用该方法向list集合里面添加一个String类型元素

//创建一个int类型的集合
ArrayList<Integer> list = new ArrayList<Integer>();
//取得集合的添加方法
Method addMethod = list.getClass().getMethod("add", Object.class);
//执行集合的add方法,向集合中添加一个String类型元素
addMethod.invoke(list, "wenpan");
System.out.println("取得元素=========================>" + list.get(0));

2、通过反射修改数组元素的值

**原理:**其实就是通过反射中的 Array.get()Array.set()来读取和修改数组中的元素值。

int temp[] = {1,2,3,4,5};
System.out.println("数组第一个元素:" + Array.get(temp,0));
Array.set(temp,0,100);
System.out.println("修改之后数组第一个元素为:" + Array.get(temp,0));

扩展方法:

int temp[] = {1,2,3,4,5};
//取得数组的类型,该方法为动态扩展数组大小做铺垫
Class<?> componentType = temp.getClass().getComponentType();
System.out.println("数组类型:" + componentType.getName());
System.out.println("数组长度:" + Array.getLength(temp));

3、通过反射修改数组的大小

**原理:**其本质就是通过反射得到原数组的类型,然后通过Array.newInstance()方法根据数组类型创造出一个指定长度的新数组,最后使用System.arraycopy()方法将原数组的值拷贝到新数组中

public static Object arrayInc(Object obj, int length) {
    //取得传入数组的类型,以便于创造出同类型的数组
    Class<?> componentType = obj.getClass().getComponentType();
    //根据传入的数组类型创建出新的指定长度的数组实例
    Object newArr = Array.newInstance(componentType, length);
    //原有的数组长度
    int originArrLen = Array.getLength(obj);
    //将原有的数组数据拷贝到新的数组中去
    System.arraycopy(obj,0,newArr,0,originArrLen);
    //返回修改大小后的数组
    return newArr;
}

4、通过反射实现通用工厂模式

class ramelonctory {

	/**
	 * 通过传入的全类名返回该类的对象
	 *  但是有一点仍然很麻烦,就是需要知道完整的包名和类名,这里可以使用properties配置文件来完成。
	 *  java读取配置文件可实现完全解耦
	 * @param className
	 * @return
	 */
	public static Object getInstance(String className){
		Object instance = null;
		try {
			//通过反射创建对象实例
			instance = Class.forName(className).newInstance();
		}catch (Exception e) {
			e.printStackTrace();
		}
		return instance;
	}
}
public static void main(String[] args) {
    //反射工厂
    Apple apple = (Apple)ramelonctory.getInstance("com.ramelon.reflect.Apple");
    apple.method();

    Banana banana = (Banana)ramelonctory.getInstance("com.ramelon.reflect.Banana");
    banana.method();
}

你可能感兴趣的:(java,反射机制)