Java从入门到精通 十六章 反射

本文深入解析Java反射机制,涵盖Class类、构造方法、成员变量、成员方法的访问,以及Annotation注解的应用。通过实例演示如何获取类信息,修改私有成员变量,执行私有方法,了解反射在权限控制和动态调用上的强大功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

反射机制作为框架的基础,在后续框架学习如spring中有着非常重要的作用.现在还想起来17年的时候问同事 反射是啥.现在想想这也是基础够不扎实的问题。本章主要将2个东西,一个是反射,就是针对正在运行的程序我们可以知道对象实例类中的方法 成员变量 构造方法等信息,这些都是可以通过类的方式进行管理的。如构造方法Constructors类,方法Method类,字段类Field.同时还有个可以修改访问权限的东西,如private修饰符的属性通过setAccessible(true) 原来不可以访问的属性,通过上面三个类中包含的方法设置一下就没问题了. 另外一个就是Annotation注解,实际书写的方式是@interface的方式.通过对书中的内容打了一遍,大体的感受就是Annotation注解就是对 类构造方法 成员变量等注释 通过Annotation方式,同时设置权限为@Rentetion为Runtime的方式即可解决问题.

Class类与反射
书上关于如何获取Class类只介绍了 xxx.getClass();xxx表示对象引用类型.实际上有三种获取 反射类的方式
1.Class.forName(“package.class”);
2.ClassName.class
3.xxx.getClass();

package core.reflect;
public class ReflectDemo {
	public static void main(String[] args) {
		// 第一种方式  需要捕获异常 最常用的一种
		try {
			Class<?> class1 = Class.forName("core.reflect.Example_03");
		} catch (ClassNotFoundException ex) {
			ex.printStackTrace();
		}
		//第二种方式   对象都创建获取类干嘛(本章中的例子 基本都是通过该种方式  因为通过反射获取类信息 前提必须是 java源码被编译为class文件)
		Example_03 example_03 = new Example_03();
		Class<? extends Example_03> class2 = example_03.getClass();
		
	    //第三种方式  需要导包 依赖性太强
		Class<Example_03>  example_032=Example_03.class;

	}
}

反射返回的类中包含的信息
通过java反射机制,可以在程序中访问已经装载到JVM中的Java对象的描述.实现访问、监测、描述Java对象本身信息的功能.java.lang.reflect。下面讲的Class类中一些常用的方法,主要可以获取Class类中的包名,类名,声明的构造方法,成员方法,成员变量,内部类及内部类的声明类(内部类 在类的高级特性中已经讲过了 分为成员内部类 局部内部类 静态内部类 匿名内部类等)。需要注意的一点就是通过getDeclareConstructors()与getConstructors获取的构造方法是不同的,加了Declare的是只获取本类中的构造方法,而不加的是获取本类及父类中的构造方法.同样的获取成员方法及成员变量的道理是一样的.

在这里插入图片描述

访问构造方法
Example_01类中包含3中构造方法,实现了方法的重载,同时修饰符有一个也设置为private。Main_01.class主要是 获取运行的类,同时通过class类的成员方法返回构造方法类数组。然后访问构造方法的数据

Example_01.class

package core.reflect;
public class Example_01 {
	String  s;
	int i,i2,i3;
	private Example_01() {
		super();
	}
	
	protected Example_01(String s, int i) {
		this.s = s;
		this.i = i;
	}

	/**
	 * 含有可变长度参数的构造方法
	 * @param strings
	 */
	public Example_01(String ...strings) throws NumberFormatException {
	   if(0<strings.length){
		  i=Integer.valueOf(strings[0]);
	   }
	   if(1<strings.length){
		   i2=Integer.valueOf(strings[1]);
	   }
	   if(2<strings.length){
		   i3=Integer.valueOf(strings[2]);
	   }
	}
	
	
	public void print() {
		System.out.println("s="+s);
		System.out.println("i="+i);
		System.out.println("i2="+i2);
		System.out.println("i3="+i3);
	}

}

Main_01.class

package core.reflect;

import java.lang.reflect.Constructor;
public class Main_01 {
	public static void main(String[] args) {
		// 调用类的可变长度构造方法
		Example_01 example_01 = new Example_01("10", "20", "30");
		Class<? extends Example_01> class1 = example_01.getClass();
		// 利用class类的getDeclaredConstructors()方法
		Constructor<?>[] constructors = class1.getDeclaredConstructors();
		// 遍历每个构造方法
		for (int i = 0; i < constructors.length; i++) {
			// 单个构造方法类
			Constructor<?> constructor = constructors[i];
			System.out.println(constructor + "判断是否为可变长度参数:" + constructor.isVarArgs());
			// 构造方法参数类型
			System.out.println("构造方法的参数类型数组:");
			Class<?>[] paramters = constructor.getParameterTypes();
			for (int j = 0; j < paramters.length; j++) {
				System.out.println(" " + paramters[j]);
			}
			// 构造方法可能返回的异常
			System.out.println("构造方法可能返回的异常:");
			Class<?>[] exceptions = constructor.getExceptionTypes();
			for (int j = 0; j < exceptions.length; j++) {
				System.out.println(" " + exceptions[j]);
			}
			// 通过构造方法类 使用newInstance()方法创建对象
			Example_01 example_012 = null;
			while (example_012 == null) {
				try {
					if (i == 2) {
						example_012 = (Example_01) constructor.newInstance();
					} else if (i == 1) {
						example_012 = (Example_01) constructor.newInstance("7", 5);// 父类对象实例
					} else {
						Object[] parameters = new Object[] { new String[] {
								"100", "200", "300" } };
						example_012 = (Example_01) constructor
								.newInstance(parameters);
					}
				} catch (Exception ex) {
					// TODO: handle exception
					System.out.println("使用newInstance创建对象实例 失败 设置setAccessible方法");
					constructor.setAccessible(true);
				}
			}
			if (example_012 != null) {
				example_012.print();
				System.out.println();
			}
		}
	}
}

运行结果

public core.reflect.Example_01(java.lang.String[]) throws java.lang.NumberFormatException判断是否为可变长度参数:true
构造方法的参数类型数组:
 class [Ljava.lang.String;
构造方法可能返回的异常:
 class java.lang.NumberFormatException
s=null
i=100
i2=200
i3=300

protected core.reflect.Example_01(java.lang.String,int)判断是否为可变长度参数:false
构造方法的参数类型数组:
 class java.lang.String
 int
构造方法可能返回的异常:
s=7
i=5
i2=0
i3=0

private core.reflect.Example_01()判断是否为可变长度参数:false
构造方法的参数类型数组:
构造方法可能返回的异常:
使用newInstance创建对象实例 失败 设置setAccessible方法
s=null
i=0
i2=0
i3=0


Constructor构造方法类中包含的api

  1. isVarArgs 是否包含可变长度参数 也就是String…strings这种形式的
  2. getParameterTypes获取请求参数类型数组
  3. getExceptionTypes 获取构造方法可能抛出异常的数组
  4. newInstacen(Object …objectt)判断能否通过当前构造方法类 创建一个实例,为null或者是有一个参数的时候很简单,但是多个参数的时候需要以二维数组的形式创建参数new Object[]{new String[]{“100”,“200”,“300”}}这样的系数内购会。
  5. setAccessible(boolean flag)设置构造方法访问权限,如果之前构造方法使用的private,通过这个方法设置为true即可访问,后面的成员方法及字段类同样有这个方法很重要.
  6. getModifiers()获取权限修饰符 返回的整数。具体可以用在Modifiers方法中的isPublic. isPrivate.如果是当前方法名的修饰符。返回true。否则返回false。

访问成员变量
Example_02 包含了不同修饰符及不同类型的数据,通过访问Main_02中访问当前jvm中对象的类信息,设置相关的具体信息,如果无法访问通过设置setAccessible的方法解决。最后打印类参数值.

Example_02.class

package core.reflect;
public class Example_02 {
   int  i;
   public  float f;
   protected boolean  b;
   private String string;
}

Main_02.class

package core.reflect;
import java.lang.reflect.Field;
import com.sun.xml.internal.fastinfoset.algorithm.BooleanEncodingAlgorithm;
public class Main_02 {
   public static void main(String[] args) {
	Example_02  example_02=new Example_02();
	Class<? extends Example_02>  class1=example_02.getClass();
	Field[]  fields=class1.getDeclaredFields();
	for (int i = 0; i < fields.length; i++) {
		Field field=	fields[i];
		System.out.println("Name:"+field.getName());
		Class<?>  filedType=field.getType();
		System.out.println("Type:"+field.getType());
		boolean isTurn=true;
		while(isTurn){
			try {
			    isTurn=false;
			    System.out.println("修改前的值为:"+field.get(example_02));
			    //判断成员变量的类型是否为int型
			    if(filedType.equals(int.class)){
			    	//利用filed类型修改对象类型
			    	System.out.println("利用setInt方法修改变量的值:");
			    	field.setInt(example_02, 168);
			    }else  if(filedType.equals(float.class)){
			    	System.out.println("利用setfloat方法修改变量的值:");
			    	field.setFloat(example_02, 99.99F);
			    }else if(filedType.equals(boolean.class)){
			    	System.out.println("利用setInt方法修改变量的值:");
			    	field.setBoolean(example_02, true);
			    }else{
			    	System.out.println("利用setInt方法修改变量的值:");
			    	field.set(example_02, "Mqtt");
			    }
			    System.out.println("修改后数据值:"+field.get(example_02));
			} catch (Exception ex) {
				// TODO: handle exception
				System.out.println("针对private类型私有类成员变量无法修改");
				field.setAccessible(true);
				isTurn=true;//出现异常重新设置访问权限 类似构造方法中的setAccessible
			}
		}
	}
}
}

运行结果

Name:i
Type:int
修改前的值为:0
利用setInt方法修改变量的值:
修改后数据值:168
Name:f
Type:float
修改前的值为:0.0
利用setfloat方法修改变量的值:
修改后数据值:99.99
Name:b
Type:boolean
修改前的值为:false
利用setInt方法修改变量的值:
修改后数据值:true
Name:string
Type:class java.lang.String
针对private类型私有类成员变量无法修改
修改前的值为:null
利用setInt方法修改变量的值:
修改后数据值:Mqtt

Filed类中的常用方法

  1. getName获取字段名
  2. getType获取字段类型
  3. get(Object obj)获取字段名对应的值
  4. set(Object obj,Object value)设置成员变量中的值
  5. getInt(Object obj)获取对象中类型为int成员变量的值
  6. setInt(Object obj,int i) 设置对象中类型为int的obj的值
  7. 同理的还有boolean,float等方法

访问方法
通过反射访问加载到jvm中对象的成员方法。主要是Method方法中的getMethos获取方法名,获取参数类型,返回类型等方法需要注意。
Example_03.class类

package core.reflect;


public class Example_03 {
	static void staticMethod() {
		System.out.println("执行staticMethod()方法");
	}

	public int publicMethod(int i) {
		System.out.println("执行publicMethod()方法");
		return i * 100;
	}

	protected int protectedMethod(String s, int i) throws NumberFormatException {
		System.out.println("执行了proctedMethod()方法");
		return Integer.valueOf(s) + i;
	}

	
	@SuppressWarnings("unused")
	private String privateMethod(String... params) {
		System.out.println("执行了privateMethod()方法");
		StringBuilder stringBuilder = new StringBuilder();
		for (int i = 0; i < params.length; i++) {
			stringBuilder.append(params[i]);
		}
		return stringBuilder.toString();
	}

}

Main_03.class类

package core.reflect;

import java.lang.reflect.Method;
public class Main_03 {

	public static void main(String[] args) {
		Example_03 example_03 = new Example_03();
		Class<? extends Example_03> class1 = example_03.getClass();
		Method[] methods = class1.getDeclaredMethods();

		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			String name = method.getName();
			System.err.println("method方法名:" + name);
			System.out.println("是否带有可变长度参数:" + method.isVarArgs());
			System.out.println("方法的参数类型");
			Class<?>[] classes = method.getParameterTypes();
			for (int j = 0; j < classes.length; j++) {
				Class<?> class2 = classes[j];
				System.out.println(class2.getName());
			}
			// 方法可能抛出的异常
			Class<?>[] exceptions = method.getExceptionTypes();
			for (int j = 0; j < exceptions.length; j++) {
				Class<?> class2 = exceptions[j];
				System.out.println("抛出的异常:" + class2);
			}
			// 类似访问成员变量 如果权限为private那么 修改权限 然后执行该方法
			boolean flag = true;
			while (flag) {
				flag = false;
				try {
					if ("staticMethod".equals(name)) {
						System.out.println(method.invoke(example_03));
					} else if ("publicMethod".equals(name)) {
						System.out.println(method.invoke(example_03,2));
					}else if("protectedMethod".equals(name)){
						System.out.println(method.invoke(example_03, "7",5));
					}else{
						System.out.println(method.invoke(example_03,new Object[]{new String[]{"A","B","C"}} ));
						//重点掌握针对不定长参数传入方法
					}
				} catch (Exception ex) {
					// TODO: handle exception
					System.out.println(name+"通过反射机制执行成员方法失败");
					method.setAccessible(true);
					flag=true;
				}

			}
			System.out.println();
		}
	}
}

运行结果

method方法名:protectedMethod
是否带有可变长度参数:false
方法的参数类型
java.lang.String
int
抛出的异常:class java.lang.NumberFormatException
method方法名:staticMethod执行了proctedMethod()方法
12


是否带有可变长度参数:false
方法的参数类型
执行staticMethod()方法
null

method方法名:privateMethod
是否带有可变长度参数:true
方法的参数类型
[Ljava.lang.String;
privateMethod通过反射机制执行成员方法失败
执行了privateMethod()方法
ABC

method方法名:publicMethod
是否带有可变长度参数:false
方法的参数类型
int
执行publicMethod()方法
200


Method方法类中的常用方法
1.getName()方法名
2.getParmeters()获取方法参数数组
3.getReturnType()获取返回的数据类型
4.getExceptionType()获取错误类型数组

Annotation功能
未完待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值