【JAVA - 基础】之反射的原理与应用

一、反射简介

反射机制指的是程序在运行时能够获取自身的信息。在JAVA中,只要给定类的名字,那么就可以通过反射机制来获取类的所有信息。

1、反射的应用

  • JDBC编程中的:Class.forName("com.mysql.jdbc.Driver.class");
  • 通过文件名找到项目中的文件;
  • Hibernate、Struts、Dagger2等框架中都用到了反射机制。


2、反射的特点

  • 优点:使用反射机制可以实现动态的创建对象和编译,体现出了很大的灵活性;
  • 缺点:反射对性能有影响。


3、反射的原理

JAVA语言编译之后都会生成一个.class文件,而不是生成机器码。反射就是在程序编译之后,通过程序字节码文件找到某一个类、类中的方法以及属性等。

 

简单来说,反射是JAVA语言在运行的时候拥有的一项自观能力,其实现主要借助于以下四个类:

  • Class:类的对象;
  • Constructor:类的构造方法;
  • Field:类中的属性对象;
  • Method:类中的方法对象。

通过这四个类就可以找到并处理JAVA中的几乎所有东西了。



二、常用方法

1、获取类对象

  • 通过类名获取Class对象:Class<?>c = Class.forName(“ClassName”);(这里的ClassName是完全的路径,需要包括包路径)
  • 通过Class对象获得具体类对象:Objecto = (Object) c.newInstance();

 

2、获取类中的构造方法

以下代码中的c是2.1中获得的Class<?>类型的变量

  • 根据指定参数类型获取public构造方法:Constructorcon = c.getConstructor(Class<?> … paramTypes);
  • 获得所有public构造方法:Constructor[]cons = c.getConstructors();
  • 根据指定参数类型获取任意访问权限的构造方法:Constructor con = c.getDeclaredConstructor(Class<?> …paramTypes);
  • 获得所有任意访问权限的构造方法:Constructor[] cons = c.getDeclaredConstructors();

 

3、获取类中的方法

以下代码中的c是2.1中获得的Class<?>类型的变量

  • 根据方法名和指定参数类型获取public方法:Method m = c.getMethod(String mName, Class<?> … paramTypes);
  • 获得所有public方法:Method[] ms= c.getMethods();
  • 根据方法名和指定参数类型获得任意访问权限的方法:Method m = c.getDeclaredMethod(String mName, Class<?> …paramTypes);
  • 获得所有任意访问权限的方法:Method[] ms = c.getDeclaredMethods();

 

4、获取类中的变量

以下代码中的c是2.1中获得的Class<?>类型的变量

  • 根据变量名获取public变量:Field f =c.getField(String name);
  • 获得所有public变量:Field[] fs= c.getFields();
  • 根据变量名获取所有任意访问权限的变量:Field f = c.getDeclaredField(String name);
  • 获得所有任意访问权限的变量:Field[] fs = c.getDeclaredFields();

 

5、获取其他数据

  • 获取类实现的所有接口:Class<?>[]interfaces = c.getInterfaces();
  • 获取类继承的父类:Class<?>parent = c.getSuperclass();
  • 获取修饰符:Stringmodifier = Modifier.toString(X.getModifiers());  //X是类、方法、属性、构造方法等
  • 获取方法的返回值类型:StringreturnType = m.getReturnType().getSimpleName();
  • 获取属性的类型:Stringtype = f.getType().getSimpleName();
  • 获取名称:Stringname = X.getName();  //X是类、方法、属性、构造方法等
  • 调用方法:m.invoke(clazz,Class<?> … paramTypes);  //clazz是类对象
  • 获取属性值:Objecto = f.get(clazz);  //clazz是类对象



三、代码演示

以下代码是目标类,我们的测试类会从这个类中取出各种数据做测试:

public class DemoClass {
	// public修饰的int类型的变量
	public Integer publicInteger;
	// public修饰的String类型的变量
	public String publicString;
	// private修饰的int类型的变量
	protected Integer privateInteger;
	// private修饰的String类型的变量
	protected String privateString;

	// public修饰的无参构造方法
	public DemoClass() {
		this.publicInteger = 10;
		this.privateInteger = 20;
		this.publicString = "Public String";
		this.privateString = "Private String";
	}

	// public修饰的四个参数的构造方法,参数类型:int,String,int,String
	public DemoClass(Integer publicInteger, String publicString, Integer privateInteger, String privateString) {
		this.publicInteger = publicInteger;
		this.publicString = publicString;
		this.privateInteger = privateInteger;
		this.privateString = privateString;
	}

	// private修饰的两个参数的构造方法,参数类型:int,int
	protected DemoClass(Integer publicInteger, Integer privateInteger) {
		this.publicInteger = publicInteger;
		this.privateInteger = privateInteger;
	}

	// public修饰的无参的方法,返回int变量
	public int getPublicInteger() {
		return publicInteger;
	}

	// public修饰的四个参数的方法,参数类型:int,int,String,String,无返回值
	public void setParameters(Integer publicInteger, Integer privateInteger, String publicString,
			String privateString) {
		this.publicInteger = publicInteger;
		this.privateInteger = privateInteger;
		this.publicString = publicString;
		this.privateString = privateString;
	}

	// private修饰的两个参数的方法,参数类型:int,int,返回boolean类型的变量
	protected boolean setSomeParams(Integer publicInteger, Integer privateInteger) {
		this.publicInteger = publicInteger;
		this.privateInteger = privateInteger;
		return true;
	}

	@Override
	public String toString() {
		return "DemoClass [publicInteger=" + publicInteger + ", publicString=" + publicString + ", privateInteger="
				+ privateInteger + ", privateString=" + privateString + "]";
	}
}

编写这个类需要注意的有以下几点:

  • 变量和参数的类型、方法的返回值类型不能是int、float、double,必须是封装类Integer、Float、Double;
  • 用private修饰的变量、方法和构造方法是不能通过反射获取的,使用其他修饰符都能获取到。


以下是测试类中的代码:

public class Test {
	public static void main(String[] args) {
		try {
			/**
			 * 获取和类有关的数据
			 */
			// 通过类名得到Class<?>对象
			Class<?> clazz = Class.forName("com.itgungnir.testreflect.democlass.DemoClass");
			System.out.println("Class Created");

			// 获取这个类实现的接口
			System.out.println("----------------------------------------------------------------------------");
			Class<?>[] interfaces = clazz.getInterfaces();
			if (interfaces.length == 0) {
				System.out.println("No Interfaces Implemented");
			}

			// 获取这个类的父类
			System.out.println("----------------------------------------------------------------------------");
			Class<?> parent = clazz.getSuperclass();
			System.out.println("Parent -----> " + parent.getName());

			/**
			 * 获取和构造方法有关的数据
			 */
			// 通过Class<?>对象获取DemoClass类的对象
			// newInstance()方法调用的是无参的构造方法,如果没有无参的构造方法,则会报错
			System.out.println("----------------------------------------------------------------------------");
			DemoClass c = (DemoClass) clazz.newInstance();
			System.out.println(c.toString());

			// 获取DemoClass对象中public修饰的无参构造方法
			System.out.println("----------------------------------------------------------------------------");
			Constructor<?> con1 = clazz.getConstructor();
			c = (DemoClass) con1.newInstance();
			System.out.println(c.toString());

			// 获取DemoClass对象中public修饰的四个参数的构造方法,参数类型:Integer,String,Integer,String
			System.out.println("----------------------------------------------------------------------------");
			Constructor<?> con2 = clazz.getConstructor(Integer.class, String.class, Integer.class, String.class);
			c = (DemoClass) con2.newInstance(100, "", 100, "aaa");
			System.out.println(c.toString());

			// 获取DemoClass对象中protected修饰的两个参数的构造方法,参数类型:Integer,Integer
			System.out.println("----------------------------------------------------------------------------");
			Constructor<?> con3 = clazz.getDeclaredConstructor(Integer.class, Integer.class);
			c = (DemoClass) con3.newInstance(55, 66);
			System.out.println(c.toString());

			// 获取DemoClass对象中所有public修饰的构造方法
			System.out.println("----------------------------------------------------------------------------");
			Constructor<?>[] cons1 = clazz.getConstructors();
			c = (DemoClass) cons1[0].newInstance(0, "Hello", 111111, "World");
			System.out.println(c.toString());
			c = (DemoClass) cons1[1].newInstance();
			System.out.println(c.toString());

			// 获取DemoClass对象中所有任意访问权限的构造方法
			System.out.println("----------------------------------------------------------------------------");
			Constructor<?>[] cons2 = clazz.getDeclaredConstructors();
			c = (DemoClass) cons2[0].newInstance(-1, -2);
			System.out.println(c.toString());
			c = (DemoClass) cons2[1].newInstance(0, "Hello", 111111, "World");
			System.out.println(c.toString());
			c = (DemoClass) cons2[2].newInstance();
			System.out.println(c.toString());

			/**
			 * 获取和变量有关的数据
			 */
			// 获取DemoClass对象中public修饰的Integer类型的变量publicInteger
			// 获取属性值时传入的Object类型的参数是DemoClass类的对象
			System.out.println("----------------------------------------------------------------------------");
			Field f1 = clazz.getField("publicInteger");
			System.out.println(f1.get(c));

			// 获取DemoClass对象中public修饰的String类型的变量privateInteger
			System.out.println("----------------------------------------------------------------------------");
			Field f2 = clazz.getDeclaredField("privateInteger");
			System.out.println(f2.get(c));

			// 获取DemoClass对象中public修饰的Integer类型的变量publicString
			System.out.println("----------------------------------------------------------------------------");
			Field f3 = clazz.getField("publicString");
			System.out.println(f3.get(c));

			// 获取DemoClass对象中protected修饰的String类型的变量privateString
			System.out.println("----------------------------------------------------------------------------");
			Field f4 = clazz.getDeclaredField("privateString");
			System.out.println(f4.get(c));

			// 获取DemoClass对象中所有public修饰的变量
			System.out.println("----------------------------------------------------------------------------");
			Field[] fs1 = clazz.getFields();
			for (Field f : fs1) {
				System.out.println(Modifier.toString(f.getModifiers()) + " " + f.getType().getSimpleName() + " "
						+ f.getName() + " = " + f.get(c) + ";");
			}

			// 获取DemoClass对象中所有任意访问权限的变量
			System.out.println("----------------------------------------------------------------------------");
			Field[] fs2 = clazz.getDeclaredFields();
			for (Field f : fs2) {
				System.out.println(Modifier.toString(f.getModifiers()) + " " + f.getType().getSimpleName() + " "
						+ f.getName() + " = " + f.get(c) + ";");
			}

			/**
			 * 获取和方法有关的数据
			 */
			// 获取DemoClass对象中public修饰的无参方法getPublicInteger,返回Integer类型变量
			System.out.println("----------------------------------------------------------------------------");
			Method m1 = clazz.getMethod("getPublicInteger");
			System.out.println(m1.invoke(c));

			// 获取DemoClass对象中public修饰的四个参数的方法setParameters,参数类型:Integer,Integer,String,String,无返回值
			System.out.println("----------------------------------------------------------------------------");
			Method m2 = clazz.getMethod("setParameters", Integer.class, Integer.class, String.class, String.class);
			m2.invoke(c, 111, 1111, "abc", "abcdef");
			System.out.println(c.toString());

			// 获取DemoClass对象中protected修饰的两个参数的方法setSomeParams,参数类型:Integer,Integer,返回boolean类型变量
			System.out.println("----------------------------------------------------------------------------");
			Method m3 = clazz.getDeclaredMethod("setSomeParams", Integer.class, Integer.class);
			System.out.println(m3.invoke(c, 0, 0));

			// 获取DemoClass对象中所有public修饰的方法(包括Object类中的public方法)
			System.out.println("----------------------------------------------------------------------------");
			Method[] ms1 = clazz.getMethods();
			for (Method m : ms1) {
				System.out.println(Modifier.toString(m.getModifiers()) + " " + m.getReturnType().getSimpleName() + " "
						+ m.getName() + "(){...}");
			}

			// 获取DemoClass对象中所有任意访问权限的方法(所有自定义的方法)
			System.out.println("----------------------------------------------------------------------------");
			Method[] ms2 = clazz.getDeclaredMethods();
			for (Method m : ms2) {
				System.out.println(Modifier.toString(m.getModifiers()) + " " + m.getReturnType().getSimpleName() + " "
						+ m.getName() + "(){...}");
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		}
	}
}

要想得到不同的数据,只需要参照上面的代码进行编写即可。

唯一值得注意的是,当调用getFields()、getDeclaredFields()、getMethods()、getDeclaredMethods()、getConstructors()、getDeclaredConstructors()等能获取数组类型的结果的方法时,返回的数组中的方法、变量或构造方法的顺序和类中定义的顺序是反过来的!

以上就是对Java中反射的一些总结,希望对大家有帮助!


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值