Java反射机制详解

1、反射机制

1.1什么是反射机制

反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

1.2反射机制能做什么

在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成动态代理。

1.3反射机制的相关API

通过一个对象获得完整的包名和类名
实例化一个类对象(使用构造函数,默认的和带参数的)
返回一个类实现的接口
取得一个类的父类
获得一个类的全部构造函数
可以通过反射调用一个类的方法
调用一个类的set和get方法
通过反射操作属性
通过反射取得并修改数组的信息
通过反射修改数组大小

2、获取一个Class对象(反射)的三种方式

java.lang.Class 类是Java中的反射中心,所有反射都是获取他的一个对象,这个对象表示运行时程序中的一个类,然后再用这个对象去做其他的操作,我们有三种方法可以获取到这个对象的引用。

2.1类文字

类文字是类名称后跟一个点和class。

Class<?> c1 = MyClass.class;

还可以获取原始数据类型的类对象,每个包装器原始数据类型类具有名为 TYPE 的静态字段,它具有对它表示的基本数据类型的类对象的引用。

 Class c = boolean.class;
    c = Boolean.TYPE;
    c = void.class;
    c = Void.TYPE;
2.2使用Object类的getClass()方法

Object类有一个 getClass()方法,它返回引用类对象的Class对象。

Class<?> c2 = new MyClass().getClass();
2.3使用Class类的forName()方法

Class类 forName() static方法返回对Class对象的引用,他有两个重载方法:

//接受一个类的限定名作为参数加载该类,并返回其对象引用。
//如果类已经加载,它将返回对Class对象的引用。
Class<?>   forName(String  className)
//可以在加载时控制是否初始化该类,我们也可以传入类加载器。
Class<?>   forName(String name,  boolean initialize, ClassLoader loader)
		Class<?> c3 = null;
		try {
			// 第一种方式
			c3 = Class.forName("reflect.MyClass");
			
			// 第二种方式
			ClassLoader cLoader = Object.class.getClassLoader();
			c3 = Class.forName("reflect.MyClass", false, cLoader);
		} catch (ClassNotFoundExceptione) {
			e.getMessage();
		}

完整样例如下:

package reflect;

class MyClass {
	static {
		System.out.println("加载这个类。。。");
	}
}

public class Main {
	public static void main(String[] args) {

		Class<?> c1 = MyClass.class;
		Class<?> c2 = new MyClass().getClass();
		Class<?> c3 = null;
		try {
			// 第一种方式
			c3 = Class.forName("reflect.MyClass");

			// 第二种方式
			ClassLoader cLoader = Object.class.getClassLoader();
			c3 = Class.forName("reflect.MyClass", false, cLoader);
		} catch (ClassNotFoundException e) {
			e.getMessage();
		}

	}

}

3、通过Class对象获取一个类的信息(类名、包名、修饰符等)

  • Class 中的 getSimpleName()方法,获取简单类名;getName()获取完整的类名,包括包名和类名;

  • Class 中的 getModifiers()方法返回类的所有修饰符。getModifiers()方法返回一个整数。我们必须调用 java.lang.reflect.Modifier.toString(int modifiers)以获得修饰符的文本形式。

  • 要获取超类的名称,使用 Class 中的 getSuperclass()方法。如果对Object类调用getSuperclass()方法,它将返回null,因为它没有超类。

  • 要获取类实现的所有接口的名称,使用 getInterfaces()

package reflect;

import java.io.Serializable;

public abstract class MyClass implements Cloneable, Serializable {
	private static final long serialVersionUID = 1L;
}
public class Main {
	public static void main(String[] args) {
		Class<?> c = null;
		try {
			// 第一种方式
			c = Class.forName("reflect.MyClass");
		} catch (ClassNotFoundException e) {
			e.getMessage();
		}
		System.out.println("简单类名:" + c.getSimpleName());
		System.out.println("完整类名:" + c.getName());
		System.out.println("类型名:" + c.getTypeName());

		Package p1 = c.getPackage();
		System.out.println("\n包名:"+p1.getName());
		
		int modifiers = c.getModifiers();
		String mod = Modifier.toString(modifiers);
		System.out.println("\n修饰符:" + mod);

		// 获取所有接口
		Class[] interfaces = c.getInterfaces();
		System.out.println("\n实现的接口:");
		for (Class in : interfaces) {
			System.out.println(in.toString());
		}

		Class superClass = c.getSuperclass();
		System.out.println("\n父类:"+superClass.getName());
	}
}

输出结果如下:
在这里插入图片描述

4、通过Class对象获取一个类的字段

可以使用java.lang.reflect.Field类来获取关于类中的字段的信息,以下四种方法在Class类可以返回关于字段的 Field 对象:

Field[] getFields()//返回该类中声明的或继承父类的所有公共字段

Field[] getDeclaredFields()//返回只在该类中声明的所有字段

//下面两个是通过字段名来返回Filed对象
Field getField(String name)
Field getDeclaredField(String name)

样例如下:

package reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

class MySuperClass {
	public int super_id = -1;
	public String super_name = "Unknown";
	protected int super_age = 18;
}

class MyClass extends MySuperClass {
	private String sex = "男";
	protected int age = 18;
	public int id = -1;
	public String name = "Unknown";

}

public class Main {
	public static void main(String[] args) {
		Class<?> c = null;
		try {
			c = Class.forName("reflect.MyClass");
		} catch (ClassNotFoundException e) {
			e.getMessage();
		}

		// 获取只在该类中声明的字段
		System.out.println("只在该类中声明的所有字段:");
		Field[] fields1 = c.getDeclaredFields();
		for (Field f : fields1) {
			// 获取字段修饰符
			int mod = f.getModifiers();
			String modifiers = Modifier.toString(mod);

			// 获取字段类型
			Class<?> type = f.getType();
			String typeName = type.getSimpleName();

			// 获取字段名
			String fieldName = f.getName();

			System.out.println(modifiers + "  " + typeName + "  " + fieldName);
		}

		// 获取所有字段
		System.out.println("\n在该类中声明的和继承父类的公共字段:");
		Field[] fields2 = c.getFields();
		for (Field f : fields2) {
			// 获取字段修饰符
			int mod = f.getModifiers();
			String modifiers = Modifier.toString(mod);

			// 获取字段类型
			Class<?> type = f.getType();
			String typeName = type.getSimpleName();

			// 获取字段名
			String fieldName = f.getName();

			System.out.println(modifiers + "  " + typeName + "  " + fieldName);
		}

		// 获取所有字段
		System.out.println("\n通过字段名获取一个字段,并获取字段的属性:");
		try {
			MyClass p = (MyClass) c.newInstance();
			Field name = c.getField("name");
			String nameValue = (String) name.get(p);
			System.out.println("name = " + nameValue);
			name.set(p, "abc");
			nameValue = (String) name.get(p);
			System.out.println("name = " + nameValue);
		} catch (InstantiationException | IllegalAccessException
				| NoSuchFieldException | SecurityException
				| IllegalArgumentException e) {
			System.out.println(e.getMessage());
		}
	}
}

输出结果:
在这里插入图片描述

5、通过Class对象获取一个类的构造函数

java.lang.reflect.Constructor 类的实例表示一个构造方法。方法和构造方法继承自一个通用的抽象超类java.lang.reflect.Executable。以下四种方法来自 Class 类中获取Constructor对象:

Constructor[] getConstructors()//返回当前类和超类的所有公共构造函数。
Constructor[]  getDeclaredConstructors()//返回当前类的所有构造函数

//通过参数类型获取构造函数函数
Constructor<T> getConstructor(Class...  parameterTypes)
Constructor<T> getDeclaredConstructor(Class...  parameterTypes)

样例如下:

package reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.ArrayList;

class MyClass<T> {

	public MyClass(int i, int j, String s) {

	}

	public MyClass(T t) {

	}

	public int getInt(String a) {
		return 0;
	}
}

public class Main {
	public static void main(String[] args) {
		Class<MyClass> c = MyClass.class;

		// 返回当前类和父类的所有公共构造函数
		Constructor[] constructors = c.getConstructors();
		
		ArrayList<String> constructDescList = getConstructorsDesciption(
				constructors);
		for (String desc : constructDescList) {
			System.out.println(desc);
		}
	}

	public static ArrayList<String> getConstructorsDesciption(
			Constructor[] constructors) {
		ArrayList<String> constructorList = new ArrayList<>();
		for (Constructor constructor : constructors) {
			String modifiers = getModifiers(constructor);

			String constructorName = constructor.getName();

			constructorList.add(modifiers + "  " + constructorName + "("
					+ getParameters(constructor) + ") "
					+ getExceptionList(constructor));
		}
		return constructorList;
	}

	public static ArrayList<String> getParameters(Executable exec) {
		Parameter[] parms = exec.getParameters();
		ArrayList<String> parmList = new ArrayList<>();
		for (int i = 0; i < parms.length; i++) {

			int mod = parms[i].getModifiers() & Modifier.parameterModifiers();
			String modifiers = Modifier.toString(mod);
			String parmType = parms[i].getType().getSimpleName();
			String parmName = parms[i].getName();
			String temp = modifiers + "  " + parmType + "  " + parmName;
			if (temp.trim().length() == 0) {
				continue;
			}
			parmList.add(temp.trim());
		}
		return parmList;
	}

	public static ArrayList<String> getExceptionList(Executable exec) {
		ArrayList<String> exceptionList = new ArrayList<>();
		for (Class<?> c : exec.getExceptionTypes()) {
			exceptionList.add(c.getSimpleName());
		}
		return exceptionList;
	}

	public static String getModifiers(Executable exec) {
		int mod = exec.getModifiers();
		if (exec instanceof Method) {
			mod = mod & Modifier.methodModifiers();
		} else if (exec instanceof Constructor) {
			mod = mod & Modifier.constructorModifiers();
		}
		return Modifier.toString(mod);
	}
}

输出结果:
在这里插入图片描述

6、通过Class对象获取一个类的实例方法

java.lang.reflect.Method 类的实例表示一个方法。方法和构造方法继承自一个通用的抽象超类java.lang.reflect.Executable。有以下四种方法获取一个Class类中Method实例:

Method[] getMethods()//返回当前类和父类的所有公共方法
Method[] getDeclaredMethods()//返回当前类的所有方法

//通过方法名和方法参数获取Method对象
Method getMethod(String name, Class<?>... parameterTypes)
Method getDeclaredMethod(String name, Class<?>... parameterTypes)

方法中的参数由 Parameter 类的对象表示,Executable 类中的getParameters()方法获取所有参数作为 Parameter 的数组。默认情况下,参数名称不存储在类文件中。参数类的名称将类似于arg0,arg1等。我们可以通过编译source来保存类文件中的实际参数名代码使用 -parameters 选项与 javac 编译器。

Executable类的getExceptionTypes()方法类返回一个由Executable抛出的异常数组。

Executable类的getModifiers()方法将修饰符作为int返回。

Executable类的getTypeParameters()方法返回一个TypeVariable数组,该数组表示通用方法或构造函数的类型参数。

package reflect;

import java.beans.Expression;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.ArrayList;

class MyClass<T> {

	public MyClass(int i, int j, String s) {

	}

	public MyClass(T t) {

	}

	public String getString(String a) throws Exception {
		if (a == null) {
			throw new Exception();
		}
		return a;
	}

	public int getInt(int b, int c) throws ArithmeticException {
		if (c == 0) {
			throw new ArithmeticException();
		}
		return b / c;
	}
}

public class Main {
	public static void main(String[] argv) {
		Class<MyClass> cls = MyClass.class;
		Method[] methods = cls.getDeclaredMethods();
		for (Method m : methods) {
			System.out.println("方法名:" + m.getName());
			System.out.println("修饰符:" + getModifiers(m));
			System.out.println("参数列表:" + getParameters(m));
			System.out.println("返回类型:" + m.getReturnType().getSimpleName());
			System.out.println("异常列表:" + getExceptionList(m));
			System.out.println();
		}
	}

	public static ArrayList<String> getParameters(Executable exec) {
		Parameter[] parms = exec.getParameters();
		ArrayList<String> parmList = new ArrayList<>();
		for (int i = 0; i < parms.length; i++) {

			int mod = parms[i].getModifiers() & Modifier.parameterModifiers();
			String modifiers = Modifier.toString(mod);
			String parmType = parms[i].getType().getName();
			String parmName = parms[i].getName();
			String temp = modifiers + "  " + parmType + "  " + parmName;
			if (temp.trim().length() == 0) {
				continue;
			}
			parmList.add(temp.trim());
		}
		return parmList;
	}

	public static ArrayList<String> getExceptionList(Executable exec) {
		ArrayList<String> exceptionList = new ArrayList<>();
		for (Class<?> c : exec.getExceptionTypes()) {
			exceptionList.add(c.getSimpleName());
		}
		return exceptionList;
	}

	public static String getModifiers(Executable exec) {
		int mod = exec.getModifiers();
		if (exec instanceof Method) {
			mod = mod & Modifier.methodModifiers();
		} else if (exec instanceof Constructor) {
			mod = mod & Modifier.constructorModifiers();
		}
		return Modifier.toString(mod);
	}
}

输出结果:
在这里插入图片描述

7、通过Class对象创建一个类的对象

我们可以使用反射动态创建类的对象,通过调用其中一个构造函数。然后我们可以访问对象的字段的值,设置它们的值,并调用它们的方法。

有两种方法来创建对象:

  • 使用no-args构造函数
  • 使用带参数的构造函数
6.1无参构造函数

如果有一个Class对象的引用,则可以利用Class类的newInstance()方法创建一个类的实例,此方法不使用参数,并且等效的使用new运算符利用无参构造函数创建出来的实例对象。

package reflect;

class MyClass {
	public int age = 18;
	public String name = "huangqiqi";

	public MyClass() {
		System.out.println("构造函数。。。");
	}
}

public class Main {
	public static void main(String[] args) {
		Class<MyClass> personClass = MyClass.class;
		MyClass p = null;
		try {
			p = personClass.newInstance();
		} catch (InstantiationException | IllegalAccessException e) {
			System.out.println(e.getMessage());
		}
		System.out.println(p);
		System.out.println("age = " + p.age);
		System.out.println("name = " + p.name);
	}
}
6.2有参构造函数

还可以通过调用特定的构造函数使用反射创建对象。它涉及两个步骤。

  • 获取构造函数的实例

    Constructor<MyClass> cons  = myClass.getConstructor(int.class, String.class);
    
  • 调用带有参数的newInstance来创建一个对象

    MyClass chris = cons.newInstance(1, "abc");
    

样例如下:

package reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

class MyClass {
	public MyClass(int i, String s) {
		System.out.println("called");
		System.out.println(i);
		System.out.println(s);
	}
}

public class Main {
	public static void main(String[] args) {
		Class<MyClass> myClass = MyClass.class;
		try {
			// 获取构造函数的实例
			Constructor<MyClass> cons = myClass.getConstructor(int.class,
					String.class);
			// 通过newInstance来调用它
			MyClass chris = cons.newInstance(1, "abc");
			System.out.println(chris);
		} catch (NoSuchMethodException | SecurityException
				| InstantiationException | IllegalAccessException
				| IllegalArgumentException | InvocationTargetException e) {
			System.out.println(e.getMessage());
		}
	}
}

输出结果:
在这里插入图片描述

6.3通过构造的实例调用方法

调用一个方法,基本上分为三个步骤:

  • 通过Class对象获取一个类的实例对象obj
  • 通过Class对象的getMethod(String name, Class<?>... parameterTypes)方法获取一个Method的对象,name为方法名,后面参数为参数类型的Class对象
  • 通过Method的对象调用invoke(Object obj, Object... args)方法,其中obj就是第一步中获得的对象,后面的参数就是要传入的实参,如果方法没有参数就不写

样例如下:

package reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class MyClass {
	private String name;

	public MyClass() {
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}
}

public class Main {
	public static void main(String[] args) {
		Class<MyClass> myClass = MyClass.class;
		try {
			MyClass p = myClass.newInstance();

			// 调用setName方法
			Method setName = myClass.getMethod("setName", String.class);
			setName.invoke(p, "huangqiqi");

			// 调用getName方法
			Method getName = myClass.getMethod("getName");
			String name = (String) getName.invoke(p);
			System.out.println(name);
		} catch (InstantiationException | IllegalAccessException
				| NoSuchMethodException | SecurityException
				| IllegalArgumentException | InvocationTargetException e) {
			System.out.println(e.getMessage());
		}
	}
}

输出结果:
在这里插入图片描述

8、通过反射创建、获取、修改数组信息

这个其实挺简单的,主要用的就是java.lang.reflect.Array这个类,话不多说,一切都在代码里面。

样例如下:

package reflect;

import java.lang.reflect.Array;

public class Main {
	public static void main(String[] args) {

		// 修改数组值-------------------------------------------------
		int[] array = { 1, 2, 3, 4, 5 };
		// 得到数组类型
		Class<?> demo = array.getClass().getComponentType();
		System.out.println("数组类型: " + demo.getName());
		System.out.println("数组长度  " + Array.getLength(array));
		System.out.println("数组的第一个元素: " + Array.get(array, 0));
		Array.set(array, 0, 100);
		System.out.println("修改之后数组第一个元素为: " + Array.get(array, 0));
		System.out.println();

		// 修改数组大小----------------------------------------------
		int[] temp = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
		print(temp);
		int[] newTemp = (int[]) arrayInc(temp, 15);
		print(newTemp);
		String[] atr = { "a", "b", "c" };
		String[] str1 = (String[]) arrayInc(atr, 8);
		print(str1);
	}

	/**
	 * 修改数组大小
	 */
	public static Object arrayInc(Object obj, int len) {
		/*
		 * 创建一个长度为len的数组,getComponentType()是Class中的方法, 返回数组的元素类型的Class对象
		 * 如果这个类型不是数组类型,则返回null
		 */
		Class<?> arr = obj.getClass().getComponentType();
		Object newArr = Array.newInstance(arr, len);

		// 获取数组长度
		int co = Array.getLength(obj);

		// 复制一个数组,从指定的下表开始
		System.arraycopy(obj, 0, newArr, 0, co);

		return newArr;
	}

	/**
	 * 打印
	 */
	public static void print(Object obj) {
		Class<?> c = obj.getClass();
		if (!c.isArray()) {// 判断是否为数组类型
			return;
		}
		System.out.println("数组长度为: " + Array.getLength(obj));
		for (int i = 0; i < Array.getLength(obj); i++) {
			// 得到数组指定下标的元素
			System.out.print(Array.get(obj, i) + " ");
		}
		System.out.println();
	}

}

输出结果:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yelvens

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值