java基础之反射

本文深入探讨Java反射机制的概念、原理及其实现方式,包括类的获取、成员变量、构造方法、成员方法的反射操作,以及数组的反射。通过具体代码示例,详细解析如何利用反射进行字符串替换、数组成员方法调用、集合操作等高级应用。

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

------- android培训java培训、期待与您交流! ----------

Class类:类的类型,如String类的类型就是String.class,代表类在内存中的一份字节码

获取字节码的三种方法:

类名.class,如Stirng.class

创建一个对象,如Person  p=new Person(); p.getClass()

使用Class类中的静态方法:Class.forName("完整类名"),如Class.forName("java.util.Date")

九种预定义的Class实例对象:八种基本数据类型都有字节码:boolean,byte,char,short,int ,long,float,double,还有返回值类型void

import java.lang.reflect.*;
import  static java.lang.System.*;	//静态导入包,方便打印
class Reflect
{
	public static void main(String[] args) throws Exception
	{
		
		//获取字节码的三种方法
		String str="abc";	//初始化一个字符串
		Class cla1=str.getClass();	//通过对象获取类的字节码
		Class cla2=String.class;	//类名.calss 返回字节码
		Class cla3=Class.forName("java.lang.String");	//使用静态方法forName()方法,并将类所在包传入获取字节码
	
		System.out.println(cla1==cla2);
		System.out.println(cla1==cla3);
		//打印结果显示三种方法都可以返回String类的字节码

		//判定指定的Class对象是否是基本类型
		System.out.println(cla1.isPrimitive());	//判断是否是原始类型,结果是false,String是一个实例对象,所以不是原始类型
		System.out.println(int.class.isPrimitive());	//结果正确,因为int是最原始最基本的数据类型
		System.out.println(int.class==Integer.class);
		out.println(int.class==Integer.TYPE);		//结果正确,TYPE代表基本类型包装的字节码
		out.println(int[].class isPrimitive());		//结果错误,不是原始类型
		out.println(int[].class.isArray());		//结果正确,数组类型的字节码类型就是Array
	}
}
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,如int[]

反射:反射就是把java中的各种成分映射成相应的java类,如成员变量,方法,构造方法,修饰符,包等信息也用一个个的java类来表示,获取这些成分的Class类用各自的实例对象来表示:Field,Method,Contructor,Package等

1.构造方法的反射

例子,得到StringBuffer的构造方法

import java.lang.*;
import java.lang.reflect.*;

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

		//new String(new StringBuffer("abc"));
		Constructor con=String.class.getConstructor(StringBuffer.class);	//通过此方法,获取Class对象即StringBuffer的构造方法
		
		String str=(String)con.newInstance(new StringBuffer("abc"));	//创建该构造方法的声明类的新实例,并初始化该实例
		//注意此处用到两个StringBuffer,第一个说明调用StringBuffer的构造方法,第二个是用此构造方法时,还得传入一个StringBuffer对象进去

		System.out.println(str.charAt(1));	//调用构造方法底层对应方法获取1号位的字符

	}
}


2.成员变量的反射

先创建一个反射点的类函数

public class ReflectPoint
{
	private int x;
	public int y;

	public ReflectPoint(int x,int y)
	{
		this.x=x;
		this.y=y;
	}
	
}
使用到了字段对象Field

import java.lang.reflect.*;
import java.lang.*;
import  static java.lang.System.*;	//静态导入包,方便打印
class ReflectDemo
{
	public static void main(String[] args) throws Exception
	{
		ReflectPoint rp=new ReflectPoint(3,6);	//构造反射点对象,
		
		Field Y=rp.getClass().getField("y");	//获取反射实例对象的“y”字段,即成员变量y
		out.println(Y.get(rp));		//获取y的值

		Field X=rp.getClass().getDeclaredField("x");	//加上Declared:使私有成员变量x可见

		X.setAccessible(true);	//允许获取x的值,暴力反射
		out.println(X.get(rp));
	}
}


练习:将字符串中的'b'改为‘a’

建立反射点:

public class ReflectPoint
{
	//声明三个变量
	public String str1="bascaketball";
	public String str2="back";
	public String str3="catch";

	public String toString()	//复写String中的toString方法
	{
		return str1+"--"+str2+"--"+str3;
	}	
}
接下来实现该程序

import java.lang.reflect.*;
import java.lang.*;

class ReflectTest1
{
	public static void main(String[] args) throws Exception
	{
		ReflectPoint rp=new ReflectPoint();	//构造反射点对象
		FillValue(rp);
		System.out.println(rp);	
	}

	private static void FillValue(Object obj) throws Exception
	{
		Field[] fields=obj.getClass().getFields();	//获取字段数组,元素是获取到的字段
		for(Field f:fields)
		{
			if(f.getType()==String.class)	//因为使用的是同一份String字节码,所以使用等于号
			{
				String s1=(String)f.get(obj);	//获取每一个字符串
				String s2=s1.replace('b','a');	//将字符串中的b换成a
		
				f.set(obj,s2);	//通过Field中的set方法将替换后的字符串返回
			}
		}
	}

}

3.成员方法的反射

类名.class.getMethod(”调用方法名“,调用的方法的参数字节码类型) //获取方法的字节码

invoke(对象,对象方法参数),使用Method类的此方法调用底层对应的方法

import java.lang.String.*;
import java.lang.reflect.*;
class ReflectDemo1
{
	public static void main(String[] args) throws Exception
	{

		String str="abc";	//创建一个对象

		//使用反射实现str.charAt(1)
		Method meCharAt=String.class.getMethod("charAt",int.class);	//获取charAt方法,此方法接受的参数是角标,属于Int.class类型
		
		System.out.println(meCharAt.invoke(null,1));	//调用Method 对象表示的底层charAt()方法
		
	}
}
如果invoke()调用的方法是静态的,则对象可以为空、

练习:对数组参数的成员方法进行反射,

import  java.lang.reflect.*;
class ReflectDemo2
{
	public static void main(String[] args) throws Exception
	{
		//TestArguements.main(new String[]{"11","22","33"});通过反射调用main方法
	
		String myClassName=args[0];	//获取即将要调用的第一个类
		Method MainMethod=Class.forName(myClassName).getMethod("main",String[].class);	//获取成员方法

		MainMethod.invoke(null,new Object[]{new String[]{"11","22","33"}});	//调用成员方法的在字节码底层的方法,并将字符串数组作为Object数组的一个元素传入,相当于封包
	}
}

class TestArguements
{
	public static void main(String[] args)
	{
		for(String arg:args)
		{
			System.out.println(arg);
		}
	}
}
注意在运行时,还得传递参数路径:在dos命令行里默认的就是当前路径,所以只要在参数上加上类名就好,如此便可以将类作为参数传入主函数中

d:\java0217\reflect\javac ReflectDemo2  TestArguements


4.数组的反射:


import java.lang.reflect.*;
class ReflectDemo3
{
	public static void main(String[] args)
	{
		//新建几种不同的基本类型数组
		int[] a1=new int[]{1,2,3};
		int[] a2=new int[3];
		int[][] a3=new int[2][3];
		String[] a4=new String[]{"a","b","c"};

		System.out.println(a1.getClass()==a2.getClass());	//两个一维数组的字节码相同
		System.out.println(a1.getClass().getName());		//结果是[I,表示int型的字节码
		//获取父类的字节码名,结果都是Object
		System.out.println(a1.getClass().getSuperclass().getName());	
		System.out.println(a4.getClass().getSuperclass().getName());

		printObject(a1);
	}

	private static void printObject(Object obj)	//创建打印方法
	{
		Class cl=obj.getClass();	//创建类类型对象
		if(cl.isArray())	//判断是否数组类型
		{
			int len=Array.getLength(obj);//Array方法是属于java.lang.reflect中的,凭借Array中的各种方法可以操作数组
			for(int x=0;x<len;x++)
			{
				System.out.println(Array.get(obj,x));//get(Object,index)返回指定数组对象中索引组件的值
			}
		}		
	}
}
对于以上成员变量,用对象接收

		Object obj1=a1;	//将一维数组封装为一个对象
		Object obj2=a4;	//将字符串数组封装为一个对象
		//Object[] obj3=a1;	//注意,int[]是基本类型的数组,不属于Objct[],所以此处不能接收int数组中的元素,这句话通不过
		printObject(a1);

练习:建立集合,向集合中存入反射点对象

import java.lang.*;
import java.lang.reflect.*;
import java.util.*;
class ReflectTest2
{
	public static void main(String[] args)
	{
		Collection c=new HashSet();	//新建一个集合
		
		//创建反射点对象,并传入参数
		ReflectPoint1 rp1=new ReflectPoint1(1,2);
		ReflectPoint1 rp2=new ReflectPoint1(3,4);
		ReflectPoint1 rp3=new ReflectPoint1(1,2);

		//将反射点中得到的元素添加到集合中
		c.add(rp1);
		c.add(rp2);
		c.add(rp3);
		
		rp1.y=7;	//修改y的值之后,删除时就找不到原来的rp1,所以无法删除,此时就会出现内存泄露
		c.remove(rp1);	//删除元素
		System.out.println(c.size());
		
	}


}
打印的结果是2

注意的是,修改了y的值之后,删除时就找不到原来的rp1所以无法删除。此处就引出了内存泄露,注意,这是hashCode里的特点,

就是说使用hashCode可能会发生内存泄露

反射的作用:实现框架功能

注意:框架和锁都是已经在内存中写好了的,区别是:使用框架,是使用框架调用所写的类,而锁则是工具类,类可以直接调用锁,简而言之,就是框架调用你,而锁是你调用它

练习:构造一个框架,可以调用框架中的类

先创建一个配置文件:conflag.properties;内容编辑为:className=java.util.ArrayList

import java.lang.*;
import java.lang.reflect.*;
import java.util.*;
import java.io.*;
class ReflectTest3
{
	public static void main(String[] args) throws Exception
	{
	
		FileInputStream fis=new FileInputStream("config.properties");	//创建文件读取流,能够读取配置文件中的内容

		Properties pp=new Properties();		//新建配置文件对象

		pp.load(fis);	//将读取流中的文件加载进配置文件对象中

		fis.close();	//此处因为对象关联的系统资源没有被释放,所以会有内存泄露

		String className=pp.getProperty("className");	//根据键获取配置文件中对应的值,即框架中的类

		Class cl=Class.forName(className);	//得到框架中的类之后,再获取其字节码
		Collection c=(Collection)cl.newInstance();	//创建此class对象所表示的类的一个新实例,即为ArrayList.class的新实例ArrayList
		//Collection c=(Collection)Class.forName(className).newInstance();	//缩写以上两句话
		
		//创建反射点对象,并传入参数
		ReflectPoint1 rp1=new ReflectPoint1(1,2);
		ReflectPoint1 rp2=new ReflectPoint1(3,4);
		ReflectPoint1 rp3=new ReflectPoint1(1,2);

		//将反射点中得到的元素添加到集合中
		c.add(rp1);
		c.add(rp2);
		c.add(rp3);

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


} 
以上就使用了一个小框架,可以通过反射调用ArrayList类









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值