Java反射机制Class的学习
引言:
首先要明白Class的作用,在我们创建一个类的对象时,其实是有许多弊端的:
Object object=new Object();
- 必须使用new关键字
- 必须存在这个类
- 必须引入这个包
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
最后,这是笔者第一次写博客,而且才大二,有纰漏在所难免,有错误或者繁琐的地方欢迎大佬指点一二,感激不尽。