Java反射机制Class的学习

Java反射机制Class的学习

引言

首先要明白Class的作用,在我们创建一个类的对象时,其实是有许多弊端的:

Object object=new Object();
  1. 必须使用new关键字
  2. 必须存在这个类
  3. 必须引入这个包

Java中有这样一个机制,可以直接不受这些限制,直接创建对象,即Class机制。Class通过反射某个类创建该类的Class实例,虽然这个实例不是该类,但是二者一模一样,Class类就好像该类的影子一样,Class实例拥有该类的所有属性和方法,包括私有对象。

创建Class实例的三种方法

        //第一种方法,先创建该类对象,再调用getClass()方法
		Object object=new Object();
		Class class1=object.getClass();
		
		//第二种方法,不用创建该类对象,但类必须存在,使用类的.class
		Class class2=Object.class;
		
		try {
			//第三种方法,不用创建该类对象,类也可以不存在,forName中参数为类的全名,前面属于哪个包也要写上
			Class class3=Class.forName("java.lang.Object");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

要注意的是,即使我调用了这三种方法,也只会创建一个影子,即class1,class2,class3的hashCode是一样的,不信可以调用class1.hashCode(),会发现一样的,class1= =class2,class2= =class3,class1= =class3,class1.equals(class2)均为true。

获取并调用该类的构造方法

获取该类的构造方法,要调用一个java.lang.reflect.Constructor类来进行操作。
首先我们制作一个该类,即要投影的目标,我们创建三个构造函数和三个成员变量,一个toString()方法。

package classTest;

public class Example {

	public String name;
	public int id;
	private double num;
	
	public Example() {
	}
	public Example(int id) {
		this.id = id;
	}
	private Example(String name, int id, double num) {
		this.name = name;
		this.id = id;
		this.num = num;
	}
	@Override
	public String toString() {
		return "Example [name=" + name + ", id=" + id + ", num=" + num + "]";
	}
	
}

接下来我们获取并调用他

package classTest;

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

public class ClassConstructor {

	public static void main(String[] args) {
		try {
			//首先我们实例化一个Class类对象,作为Example的影子
			Class c=Class.forName("classTest.Example");
			
			//getDeclaredConstructors()得到Example的所有构造方法,包括私有,返回为构造器数组
			Constructor cons[]=c.getDeclaredConstructors();
			for (Constructor con:cons) {
				
				//利用getModifiers()得到当前构造器的修饰符,并利用Modifier.toString()将之简化,
				//不简化就是1,2这种数,java用0,1,2...代表private,public...
				System.out.print(Modifier.toString(con.getModifiers())+" ");
				
				//利用con.getName()得到当前构造器的名字
				String str1=con.getName();
				//名字是包名.包名....类名,而我们只想要类名
				if ((str1 != null) && (str1.length() > 0)) {   
		            int dot = str1.lastIndexOf('.');   //文件名向前检索.并返回索引值
		            if ((dot >-1) && (dot < (str1.length() - 1))) {   
		               str1=str1.substring(dot + 1);   //从最后一个.号后面开始截取
		            }   
		        }   
				System.out.print(str1+"(");
				
				//利用con.getParameterTypes()得到当前构造器的所有参数并变成参数数组
				Class paras[]=con.getParameterTypes();
				for(int i=0;i<paras.length;i++) {
					
					//利用paras[i].getSimpleName()得到每个参数的精简名称,非精简就是java.lang.String这种
					System.out.print(paras[i].getSimpleName()+"...");
					if(i<paras.length-1) {
						System.out.print(",");//这里是除了最后一个参数,其余参数后面都输出一个逗号以示区分
					}
				}//for循环依次获得参数
				
				System.out.println("){ 具体的方法体  }");
			}//for——each循环获得每个构造器
			
			Constructor cs1=c.getDeclaredConstructor();//现在我们用cs1获得该类的无参构造方法
			Object object=cs1.newInstance();//利用cs1.newInstance()将该构造方法实例化一个对象,这就是调用
			System.out.println(object.toString());//调用实例化对象的函数
			
			
			Constructor cs2=c.getDeclaredConstructor(int.class);//用cs2获得该类的有一个参数的构造方法
			object=cs2.newInstance(123);
			System.out.println(object.toString());
			
			Constructor cs3=c.getDeclaredConstructor(String.class,int.class,double.class);
			cs3.setAccessible(true);//cs3所代表的构造方法是private的,需要设置为可获得才能继续调用
			object=cs3.newInstance("傅越彬彬",123,12.3);
			System.out.println(object.toString());
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
}

下面是输出结果:

private Example(String…,int…,double…){ 具体的方法体 }
public Example(int…){ 具体的方法体 }
public Example(){ 具体的方法体 }
Example [name=null, id=0, num=0.0]
Example [name=null, id=123, num=0.0]
Example [name=傅越彬彬, id=123, num=12.3]

前三行我们得到了三个构造方法(包括私有的)
后三行我们调用了这三个构造方法并实例化了一个对象,调用其toString()方法来显示

获取并操作该类的成员变量

这个操作与获取并调用构造方法很像,也有一个import java.lang.reflect.Field;来帮助我们进行操作。
Example的代码不变,下面是获取并调用该类的成员变量的java文件

package classTest;

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

public class ClassField {

	public static void main(String[] args) {
		try {
			//首先我们实例化一个Class类对象,作为Example的影子
			Class c=Class.forName("classTest.Example");
			
			//getDeclaredFields()得到Example的所有成员变量,包括私有,返回为变量数组
			Field fs[]=c.getDeclaredFields();
			for(Field f:fs) {
				System.out.print(Modifier.toString(f.getModifiers())+" ");//利用getModifiers()得到当前变量的修饰符
				System.out.print(f.getType().getSimpleName()+" ");//利用getType()得到当前变量的数据类型
				System.out.println(f.getName());//利用getName()得到当前变量的名称
			}
			
			
			Constructor cs=c.getConstructor();//现在我们先弄到一个无参构造器
			Example example=(Example)cs.newInstance();//并用之实例化一个Example对象
			//利用getDeclaredField("num")得到该对象的名为num的成员变量,起名为field
			Field field=c.getDeclaredField("num");
			field.setAccessible(true);//num是私有的,field是num的影子,设置num可获得,方可访问field
			System.out.println(field.get(example));//利用变量的get()方法,得到对象example的num的值
			field.set(example, 2.33333);//利用变量的set()方法,修改对象example的num的值
			System.out.println(field.get(example));
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		}
	}
}

下面是操作结果:

public String name
public int id
private double num
0.0
2.33333

前三行是三个成员变量
第四个是double类型变量num的值(默认为0.0)
然后我们修改了他为2.33333
第五个为修改后的值

获取并调用成员方法

这个操作与获取并调用构造方法也很像,也有一个java.lang.reflect.Method;来帮助我们进行操作。
Example的代码不变,下面是获取并调用该类的成员变量的java文件

package classTest;

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

public class ClassMethod {

	public static void main(String[] args) {
		try {
			//首先我们实例化一个Class类对象,作为Example的影子
			Class c = Class.forName("classTest.Example");
			
			Method ms[]=c.getDeclaredMethods();//getDeclaredMethods()得到Example的所有方法,包括私有,返回为方法数组
		for(Method m:ms) {
			System.out.print(Modifier.toString(m.getModifiers())+" ");//利用getModifiers()得到当前方法的修饰符
			System.out.print(m.getReturnType().getSimpleName()+" ");//利用getReturnType()得到当前方法的返回类型
			System.out.print(m.getName()+"(");//利用getName()得到当前方法的名字
			Class paras[]=m.getParameterTypes();//利用getParameterTypes()得到当前方法的所有参数并变成参数数组
			for(int i=0;i<paras.length;i++) {
				System.out.print(paras[i].getSimpleName()+".. ");
				if(i<paras.length-1) {
					System.out.print(",");
				}
			}
			System.out.print(")");
			
			Class excs[]=m.getExceptionTypes();//利用getExceptionTypes()得到当前方法的所有可能抛出的异常并变成数组
			if(excs.length>0) {
				System.out.print("throws");
				for(int i=0;i<excs.length;i++) {
					System.out.print(excs[i].getSimpleName());
					if(i<excs.length-1) {
						System.out.print(",");
					}
				}
			}
			System.out.println("{ 具体的方法体 }");
		}
		
		
		Constructor constructor=c.getConstructor();//用constructor获得该类的无参构造方法
		Object object=constructor.newInstance();//实例化一个对象
		Method method=c.getDeclaredMethod("toString");//利用getDeclaredMethod("toString")得到无参方法toString()
		//注意,要是该方法有参数,就在方法名字后面添加参数,例如:getDeclaredMethod("toString",int.class,....)
		System.out.println(method.invoke(object));//利用方法对象.invoke(拥有该方法的该类对象)开始调用该方法
		
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {		
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
}

执行结果:

public String toString(){ 具体的方法体 }
Example [name=null, id=0, num=0.0]

当然了,我们的Example类只有一个方法,显得过于单薄,我们实例化一个Class类时,把classTest.Example换成java.lang.String,这个String类的方法可就多了:

public boolean equals(Object… ){ 具体的方法体 }
public String toString(){ 具体的方法体 }
public int hashCode(){ 具体的方法体 }
public int compareTo(String… ){ 具体的方法体 }
public volatile int compareTo(Object… ){ 具体的方法体 }
public int indexOf(String… ,int… ){ 具体的方法体 }
public int indexOf(String… ){ 具体的方法体 }
public int indexOf(int… ,int… ){ 具体的方法体 }
public int indexOf(int… ){ 具体的方法体 }
static int indexOf(char[]… ,int… ,int… ,char[]… ,int… ,int… ,int… ){ 具体的方法体 }
static int indexOf(char[]… ,int… ,int… ,String… ,int… ){ 具体的方法体 }
public static String valueOf(int… ){ 具体的方法体 }
public static String valueOf(long… ){ 具体的方法体 }
public static String valueOf(float… ){ 具体的方法体 }
public static String valueOf(boolean… ){ 具体的方法体 }
public static String valueOf(char[]… ){ 具体的方法体 }
public static String valueOf(char[]… ,int… ,int… ){ 具体的方法体 }
public static String valueOf(Object… ){ 具体的方法体 }
public static String valueOf(char… ){ 具体的方法体 }
public static String valueOf(double… ){ 具体的方法体 }
public char charAt(int… ){ 具体的方法体 }
private static void checkBounds(byte[]… ,int… ,int… ){ 具体的方法体 }
public int codePointAt(int… ){ 具体的方法体 }
public int codePointBefore(int… ){ 具体的方法体 }
public int codePointCount(int… ,int… ){ 具体的方法体 }
public int compareToIgnoreCase(String… ){ 具体的方法体 }
public String concat(String… ){ 具体的方法体 }
public boolean contains(CharSequence… ){ 具体的方法体 }
public boolean contentEquals(CharSequence… ){ 具体的方法体 }
public boolean contentEquals(StringBuffer… ){ 具体的方法体 }
public static String copyValueOf(char[]… ){ 具体的方法体 }
public static String copyValueOf(char[]… ,int… ,int… ){ 具体的方法体 }
public boolean endsWith(String… ){ 具体的方法体 }
public boolean equalsIgnoreCase(String… ){ 具体的方法体 }
public static transient String format(Locale… ,String… ,Object[]… ){ 具体的方法体 }
public static transient String format(String… ,Object[]… ){ 具体的方法体 }
public void getBytes(int… ,int… ,byte[]… ,int… ){ 具体的方法体 }
public byte[] getBytes(Charset… ){ 具体的方法体 }
public byte[] getBytes(String… )throwsUnsupportedEncodingException{ 具体的方法体 }
public byte[] getBytes(){ 具体的方法体 }
public void getChars(int… ,int… ,char[]… ,int… ){ 具体的方法体 }
void getChars(char[]… ,int… ){ 具体的方法体 }
private int indexOfSupplementary(int… ,int… ){ 具体的方法体 }
public native String intern(){ 具体的方法体 }
public boolean isEmpty(){ 具体的方法体 }
public static transient String join(CharSequence… ,CharSequence[]… ){ 具体的方法体 }
public static String join(CharSequence… ,Iterable… ){ 具体的方法体 }
public int lastIndexOf(int… ){ 具体的方法体 }
public int lastIndexOf(String… ){ 具体的方法体 }
static int lastIndexOf(char[]… ,int… ,int… ,String… ,int… ){ 具体的方法体 }
public int lastIndexOf(String… ,int… ){ 具体的方法体 }
public int lastIndexOf(int… ,int… ){ 具体的方法体 }
static int lastIndexOf(char[]… ,int… ,int… ,char[]… ,int… ,int… ,int… ){ 具体的方法体 }
private int lastIndexOfSupplementary(int… ,int… ){ 具体的方法体 }
public int length(){ 具体的方法体 }
public boolean matches(String… ){ 具体的方法体 }
private boolean nonSyncContentEquals(AbstractStringBuilder… ){ 具体的方法体 }
public int offsetByCodePoints(int… ,int… ){ 具体的方法体 }
public boolean regionMatches(int… ,String… ,int… ,int… ){ 具体的方法体 }
public boolean regionMatches(boolean… ,int… ,String… ,int… ,int… ){ 具体的方法体 }
public String replace(char… ,char… ){ 具体的方法体 }
public String replace(CharSequence… ,CharSequence… ){ 具体的方法体 }
public String replaceAll(String… ,String… ){ 具体的方法体 }
public String replaceFirst(String… ,String… ){ 具体的方法体 }
public String[] split(String… ){ 具体的方法体 }
public String[] split(String… ,int… ){ 具体的方法体 }
public boolean startsWith(String… ,int… ){ 具体的方法体 }
public boolean startsWith(String… ){ 具体的方法体 }
public CharSequence subSequence(int… ,int… ){ 具体的方法体 }
public String substring(int… ){ 具体的方法体 }
public String substring(int… ,int… ){ 具体的方法体 }
public char[] toCharArray(){ 具体的方法体 }
public String toLowerCase(Locale… ){ 具体的方法体 }
public String toLowerCase(){ 具体的方法体 }
public String toUpperCase(){ 具体的方法体 }
public String toUpperCase(Locale… ){ 具体的方法体 }
public String trim(){ 具体的方法体 }

总结

其实三个操作大同小异,反射确实很有用,最后我举一个实际遇到过的例子来作为结束。
这个例子并没有多高超的反射应用,只是作为基础练习,毕竟笔者才大二啊。。。
我有一个BaseController类,并由派生出了许多子类(共20个),我将这些子类的名字作为常量数组储存:

static final String[] CONTROLLER_CLASSNAMES = {
			LOGIN_CONTROLLER,
			CHANGE_PASSWORD_CONTROLLER,
			GET_ALL_USERS_CONTROLLER,
			CLEAR_ALL_USERS_CONTROLLER,
			DELETE_USER_CONTROLLER,
			GET_USER_CONTROLLER,
			CREATE_USER_CONTROLLER,
			UPDATE_USER_CONTROLLER,
			UPLOAD_DOCUMENT_CONTROLLER,
			DOWNLOAD_DOCUMENT_CONTROLLER,
			GET_ALL_DOCUMENTS_CONTROLLER,
			CLEAR_ALL_DOCUMENTS_CONTROLLER,
			GET_DOCUMENT_CONTROLLER,
			UPLOAD_ARCHIVE_CONTROLLER,
			DOWNLOAD_ARCHIVE_CONTROLLER,
			GET_ALL_ARCHIVES_CONTROLLER,
			CLEAR_ALL_ARCHIVES_CONTROLLER,
			GET_ARCHIVE_CONTROLLER,
			DELETE_ARCHIVE_CONTROLLER,
			UPDATE_ARCHIVE_CONTROLLER,
	};

我要把这些数组成员放在一个HashMap<String, BaseController> controllers里,这个controllers要put进去20个值,而且每个值由一个String和一个对象组成,难道要我一个一个弄吗?这时,利用反射就可以两步搞定:

for (String controllerClassname : Constants.CONTROLLER_CLASSNAMES) {
			BaseController baseController;
			try {
				baseController = (BaseController) Class.forName(controllerClassname).newInstance();
				controllers.put(baseController.getClass().getName(), baseController);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

遍历数组,然后实例化对象并放入Map中,搞定!

end

最后,这是笔者第一次写博客,而且才大二,有纰漏在所难免,有错误或者繁琐的地方欢迎大佬指点一二,感激不尽。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值