Java学习笔记八(反射)

 

 1.介绍

反射为Java程序在运行时提供了动态的能力,利用反射可以在运行时对程序进行动态的控制。本篇博客着重讲解一下Java中的反射。

 

 2.Class类的使用

在Java运行过程中,每个类被加载后都会在内存中产生一个对应的Class类对象,因此通过Class类的对象就可以拿到有关类的相关信息。下面演示一个实例。

<span style="font-family:SimSun;font-size:18px;">package com.Reflect;

//用来被加载类的父类
class MyFather 
{
	//父类的公共成员变量
	public int memberFather;
	//父类的公共方法
	public void methodFather()
	{
		System.out.println("我是从父类继承而来的方法methodFather!!!");
	}	
}
//用来被加载的类
class MySon extends MyFather 
{
	
	//子类的公共成员变量
	public int memberSonPublic;
	//子类的私有成员变量
	private int memberSonPrivate;
	//子类的公共方法
	public void methodSonPublic()
	{
		
		System.out.println("我是子类自己的方法methodSonPublic!!!");
	}	
	//子类的保护方法
	protected void methodSonProtected()
	{
		System.out.println("我是子类自己的方法methodSonProtected!!!");
	}
}
//主类
public class Sample34_1
{
	public static void main(String args[])
	{
     try
     {
	       //加载指定的类
	       Class c=Class.forName("com.Reflect.MySon");
	       //创建加载类的对象
	       MySon ms=(MySon)c.newInstance();
	       System.out.println(ms.getClass());
	       //调用创建对象的方法
	       System.out.println("===============调用创建对象的方法===================");
	       ms.methodSonProtected();
	       ms.methodSonPublic();
	       ms.methodFather(); 
	       //打印加载类的详细信息
	       System.out.println("==================加载类的信息======================");   
	       System.out.println(c.getName()+"类自己声明了"
	                        +c.getDeclaredFields().length+"个成员变量。");   
	       System.out.println(c.getName()+"类对外公布的方法有"
	                        +c.getMethods().length+"个。");  	
     }
	   catch(Exception e)
	   {
	   		e.printStackTrace();
	   }
	}
}
</span>


上面的实例通过Class对象的ForName方法加载相应的class对象,并通过Class对象的newInstance方法创建了其对象,紧接着调用了对象中的方法,接着打印了加载类的一些信息。


 3.Field类的使用

Field类的对象代表成员变量,携带成员变量的信息,注意的是与Class类类似,不可以通过构造器创建Field类的对象,对象都是通过Class类对象提供的get系列方法创建出来的。

<span style="font-family:SimSun;font-size:18px;">package com.Reflect;

import java.util.*;
import java.lang.reflect.*;
//自定义用来测试的类
class Student
{
	public int sage;//年龄
	private int sno;//学号
	public boolean gender;//性别 true-男  false-女
	public String sname;//姓名
	//构造器
	public Student(int sage,int sno,boolean gender,String sname)
	{
		
		this.sage=sage;
		this.sno=sno;
		this.gender=gender;
		this.sname=sname;
	}
}
//主类
public class Sample34_4
{
	public static void main(String args[])
	{
        try
        {
	        //创建Student类对象
	        Student tom=new Student(21,10001,true,"Tom");
	        //获取Student类对应的Class对象
	        Class dc=tom.getClass();
	        //获取Student类所有可以访问的成员变量对应的Field数组
	        Field[] fieldArray=dc.getFields();
	        //打印Student类对象各成员变量的详细信息
	        System.out.println("成员变量名\t成员变量类型\t\t成员变量值");
	        int size=fieldArray.length;
	        //循环处理Field数组
	        for(int i=0;i<size;i++)
	        {
	        	Field tempf=fieldArray[i];
	        	//打印成员变量名称
	        	System.out.print(tempf.getName()+"\t\t");
	        	//打印成员变量类型
	        	System.out.print(tempf.getType().toString()
	        	+((tempf.getType().toString().length()>7)?"\t":"\t\t\t"));
	        	//打印成员变量值
	        	System.out.println(tempf.get(tom));
	        }        	
        }
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
}
</span>

上述实例中,首先定义了Strudent类的对象,然后通过Student类对应的Class对象获取可以访问的成员变量的Field数组,紧接着就是调用Field类的方法,对成员变量的信息进行打印操作


 4.Method类的使用

Method类的对象代表一个方法,携带方法有关的信息,该对象只能通过Class类对象的get方法进行得到

<span style="font-family:SimSun;font-size:18px;">package com.Reflect;

import java.util.*;
import java.lang.reflect.*;
//自定义用来测试的类
class ForMethod 
{
	//声明静态方法sayHello,功能为在屏幕上打印字符串
	public static void sayHello(String name)
	{
		System.out.println("你好,"+name+"!!!");
	}
	//声明非静态方法generateNum,功能为产生min与max之间的随机数
	public String generateNum(int max,int min)
	{
		return (Math.random()*(max-min)+min)+"";
	}
}
//主类
public class Sample34_5
{
	public static void main(String args[])
	{
        try
        {
	        //创建ForMethod类对象
	        ForMethod fm=new ForMethod();
	        //获取ForMethod类对应的Class对象
	        Class fmc=fm.getClass();
	        //获取可以访问的方法对应的Method数组
	        Method[] ma=fmc.getMethods();
	        //对数组进行扫描打印方法的信息
	        System.out.println("方法名称\t返回值类型\t\t参数列表");
	        int size=ma.length;
	        for(int i=0;i<size;i++)
	        {
	        	Method tempm=ma[i];
	        	//打印方法名称
	        	String mname=tempm.getName();
	        	System.out.print(mname+((mname.length()>7)?"\t":"\t\t"));
	        	//打印方法的返回值类型
	        	String mReturnType=tempm.getReturnType().getName();
	        	System.out.print(mReturnType+((mReturnType.length()>15)?"\t":
	        	                    (mReturnType.length()>10)?"\t\t":"\t\t\t"));
	        	//循环打印方法的参数序列
	        	Class[] ca=tempm.getParameterTypes();
	        	int csize=ca.length;
	        	if(csize==0)
	        	{
	        		System.out.print("没有参数");
	        	}
	        	for(int j=0;j<csize;j++)
	        	{
	        		System.out.print(ca[j].getName()+((j==csize-1)?"":", "));
	        	}	        	
	        	//换行
	        	System.out.println();
	        }
	        //通过反射调用静态方法sayHello
	        System.out.println("==========通过反射调用静态方法sayHello===========");
	        ma[0].invoke(null,new Object[]{"王强"});
	        //通过反射调用非静态方法generateNum
	        System.out.println("========通过反射调用非静态方法generateNum========");
	        System.out.println(ma[1].invoke(fm,
	                           new Object[]{new Integer(100),new Integer(1000)}));
        }
		catch(Exception e)
		{
			e.printStackTrace();
		}          	
	}
}
</span>

该实例中首先创建了ForMethod类的对象,然后通过反射打印了Formethod类的所有可以访问到的方法,最后通过反射调用了ForMethod类中声明的两个方法


 5.Constructor类的使用

Constructor类代表一个构造器,携带有关构造器的相关信息,也是只能通过Class类对象的get系列方法获得。

<span style="font-family:SimSun;font-size:18px;">package com.Reflect;

import java.util.*;
import java.lang.reflect.*;
//自定义用来测试的类
class Student1
{
	String sname;//姓名
	int sage;//年龄
	//声明无参构造器
	public Student1()
	{
		sname="Tom";
		sage=23;
	}
	//声明有参构造器
	public Student1(String sname,int sage)
	{
		this.sname=sname;
		this.sage=sage;
	}
	//声明一个普通方法
	public void sayHello()
	{
		System.out.println("您好,我是"+sname+",今年"+sage+"岁!!!");
	}
}
//主类
public class Sample34_6
{
	public static void main(String args[])
	{
        try
        {
	        //获取Student类对应的Class对象
	        Class sc=Student1.class;
	        //获取可以访问的构造器对应的Constructor数组
	        Constructor[] ca=sc.getConstructors();
	        //对数组进行扫描打印构造器的信息
	        System.out.println("构造器名称\t\t参数列表");
	        int size=ca.length;
	        for(int i=0;i<size;i++)
	        {
	        	Constructor tempc=ca[i];
	        	//打印构造器名称
	        	String cname=tempc.getName();
	        	System.out.print(cname+"\t\t");
	        	//循环打印构造器的参数序列
	        	Class[] pa=tempc.getParameterTypes();
	        	int psize=pa.length;
	        	if(psize==0)
	        	{
	        		System.out.print("没有参数");
	        	}
	        	for(int j=0;j<psize;j++)
	        	{
	        		System.out.print(pa[j].getName()+((j==psize-1)?"":", "));
	        	}	        	
	        	//换行
	        	System.out.println();
	        }
	        
	        //使用反射调用有参构造器创建对象
	        Student1 stu=(Student1)ca[0].newInstance(new Object[0]);
	        //调用创建对象的sayHello方法
	        stu.sayHello();	        
	        //使用反射调用有参构造器创建对象
	        stu=(Student1)ca[1].newInstance(new Object[]{"王强",new Integer(25)});
	        //调用创建对象的sayHello方法
	        stu.sayHello();     
        }
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
}
</span>

上述实例中,首先获得了Student类对应的Class对象,紧接着打印了所有Student类可以访问的构造器的信息,最后通过反射调用了Student类的构造器创建了两个对象并调用了SayHello方法。


 6.取消访问限制

当用Class对象的getDeclaredXXXs方法获得Field、Method或Constructor时,由于访问修饰符的限制,可能有些字段、方法或者构造器访问不到。如果要访问的话,需要先解除限制,然后再访问

若希望解除限制,需要使用java.lang.reflect.AccessibleObject类。

<span style="font-family:SimSun;font-size:18px;">package com.Reflect;

import java.lang.reflect.*;
//自定义用来测试的类
class Employee1
{
	private String sname;//员工姓名
	//私有方法
	private void sayHello()
	{
		System.out.println("您好,我是"+sname
		             +",恭喜您成功访问了private的方法sayHello!!!");
	}
}
//主类
public class Sample34_8
{
	public static void main(String args[])
	{
		try
		{
			//创建Employee对象
			Employee1 tom=new Employee1();
			//获取Employee类对应的Class对象
			Class ec=tom.getClass();
			//获取Employee类声明的成员变量对应的Field数组
			Field[] fa=ec.getDeclaredFields();
			//设置sname成员变量的访问限制为允许
			fa[0].setAccessible(true);
			//设置sname成员变量的值
			fa[0].set(tom,"Tom");
			//获取Employee类声明的方法对应的Method数组
			Method[] ma=ec.getDeclaredMethods();
			//设置所有方法的访问限制为允许
			//ma[0].setAccessible(true);
			AccessibleObject.setAccessible(ma,true);
			//调用sayHello方法
			ma[0].invoke(tom,new Object[0]);			
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}	    	
	}
}
</span>

上述实例就是通过解除访问修饰符的限制来访问到私有变量。



评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值