- 类加载器的
loadClass()
方法,如果父类加载器为Null,则使用根类加载器 - 调用findClass(String)方法查找该类
我们可以看到前面两步是实现父类的一些逻辑,其实这里两步实现了父类委托和缓冲机制策略,而我们只需要重新findClass(String)
方法来实现我们自己的逻辑即可,这样使自定义类加载器就简单多了
四、java反射机制
前面说了类的加载器,下面开始真正学习java反射机制,java反射机制可以时我们在运行时刻获取类的信息,如:类的成员变量类型,值,方法的信息及调用方法,获取泛型类型,获取注解等等。
java反射的相关api都在java.lang.reflect
包下:学习api最好的方法还是看官网文档,因为官方文档最权威。下面我们通过一个测试类来学习反射的最常用的知识。
// 通过Class的静态方法forName加载类,该方法会初始化类
Class clazz = Class.forName("Common");
// 通过反射生成该类的实例,调用public的无参构造方法,反射生成类的实例,该类必须得有一个public的无参方法
Object newInstance = clazz.newInstance();
- 获取类的构造方法
public static void testConstructor(Class clazz){
System.out.println("-----------构造方法测试-----------");
// 获取所以的构造方法
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for(int i=0;i<declaredConstructors.length;i++){
int index = i+1; // 因为i从0开始
String str = "第"+index+"个构造方法权限修饰符为";
if(declaredConstructors[i].getModifiers() == 1){ // 这里的常量值可以查看api文档,博客下面也会帖子各种修饰符对应的数字
str+="public";
}
if(declaredConstructors[i].getModifiers() == 2){ // 这里的常量值可以查看api文档,博客下面也会帖子各种修饰符对应的数字
str+="private";
}
if(declaredConstructors[i].getModifiers() == 4){ // 这里的常量值可以查看api文档,博客下面也会帖子各种修饰符对应的数字
str+="protected";
}
str+="名称为:"+declaredConstructors[i].getName();
System.out.println(str);
}
}
- 获取类方法的相关信息
```java
/**- 这个方法通过反射获取了方法的相关信息,可以看到我们得到了方法
- 的全部信息
- 这里字符串拼接比较多,由于是测试,所以就没用StringBuffer拼接
-
@param clazz
*/
public static void testCommonMethodInfo(Class clazz){
System.out.println("-----------普通方法测试-----------");
Method[] methods = clazz.getMethods();
System.out.println(methods.length);
for(int i=0;i<methods.length;i++){
int dexI = i+1;
String str = "第"+dexI+"个方法形式为:";// 获取方法权限修饰符 int modifiers = methods[i].getModifiers(); if(modifiers == 1){ // 这里的常量值可以查看api文档,博客下面也会帖子各种修饰符对应的数字 str+="public"; } if(modifiers == 2){ // 这里的常量值可以查看api文档,博客下面也会帖子各种修饰符对应的数字 str+="private"; } if(modifiers == 4){ // 这里的常量值可以查看api文档,博客下面也会帖子各种修饰符对应的数字 str+="protected"; } String returnType = methods[i].getReturnType().getSimpleName(); // 获取方法返回类型 str+=" "+returnType+" "; String name = methods[i].getName(); // 获取方法名称 str+= name; str+="("; // 获取方法参数的类型 Class[] parameterTypes = methods[i].getParameterTypes(); for(int j=0;i<parameterTypes.length;i++){ str+=parameterTypes[j]+" "; } str+=")"; System.out.println(str);
}
}
```
- 调用类的方法
/**
* 通过反射调用方法
* @param clazz
* @throws SecurityException
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public static void testMethodInvoke(Class clazz,Object newInstance) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
System.out.println("-----------测试方法的调用-----------");
/**
* 由于我们知道类中有这种类型的方法,所以我们直接指定要获取的方法类型
* 如果不知道我们要调用的 方法的类型,我们可以获取方法的参数类型等等所有信息
* 然后匹配到我们需要调用的方法,这里做个说明,我们要调用的方法名是知道的
*/
Method sum = clazz.getMethod("sum",int.class,int.class);
System.out.println(sum);
sum.invoke(newInstance, 1,1);
Method setS = clazz.getMethod("setS",String.class);
System.out.println(setS);
setS.invoke(newInstance, "赋值啦");
}
- 测试类的属性Field
/**
* 测试类的Field
* @param clazz
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
public static void testField(Class clazz,Object obj) throws IllegalArgumentException{
System.out.println("-----------测试类的Field-----------");
// 获取所以的Field,包括private修饰的,但不能直接获取和直接改变private修饰的值
Field[] fields = clazz.getDeclaredFields();
for(int i=0;i<fields.length;i++){
int dexI = i+1;
System.out.print("第"+dexI+"个name:"+fields[i].getName()+" ");
Class type = fields[i].getType();
String typeStr = type.getName();
try{
if(typeStr.equals("int")){
System.out.println("value:"+fields[i].getInt(obj));
}else if(typeStr.equals("java.lang.String")){
// 字符串形式通过get(Object obj)方法取得,如果该Field的权限为private,则 获取值的时候会报java.lang.IllegalAccessException
System.out.println("value:"+fields[i].get(obj));
}
}catch(IllegalAccessException ex){
System.out.println("不能获取private修饰的属性值");
}
}
}
- 测试注解
/**
* 测试注解(类上的注解),属性方法上的注解分别通过
* Field对象和Method对象的getAnnotations()方法可以得到
* 和这里是一样的
* @param clazz
*/
public static void testAnnotation(Class clazz){
System.out.println("-----------测试注解-----------");
Annotation[] annotations = clazz.getAnnotations();
for(int i=0;i<annotations.length;i++){
System.out.println(annotations[i]);
}
}
- 测试泛型
/**
* 泛型测试
* @throws NoSuchFieldException
*/
private static void genericTest() throws NoSuchFieldException {
System.out.println("-----------获泛型测试-----------");
Class<ReflectTest> clazz = ReflectTest.class;
Field f = clazz.getDeclaredField("map");
// 直接使用getType()只对普通类型有效,并不能得到有泛型的类型
Class<?> type = f.getType();
// 下面的代码可以看到只输出了java.util.Map
System.out.println(" map 的类型为:"+type);
// 获取Field实例f的泛型类型
Type genericType = f.getGenericType();
// 如果genericType是ParameterizedType对象
if(genericType instanceof ParameterizedType){
ParameterizedType parameterizedType = (ParameterizedType) genericType;
// 获取原始类型
Type rawType = parameterizedType.getRawType();
System.out.println("原始类型是:"+rawType);
// 取得泛型类型的泛型参数
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for(int i=0;i<actualTypeArguments.length;i++){
System.out.println("第"+i+"个泛型类型为:"+actualTypeArguments[i]);
}
}else{
System.out.println("泛型类型获取出错");
}
}
- 获取泛型的实际类型
/**
* 获取泛型T的实际类型
*/
public static void getTType(){
System.out.println("-----------获取泛型T的实际类型-----------");
Dog dog = new DogImpl();
// 注意,下面这种写法不能获取到T的实际类型,因为范式要在编译的时候就指定类型,在运行时候指定类型是获取不到真实类型的
// Dog<Cat> dog = new Dog<Cat>();
}
修饰符常量对应的值:
由于注释说了很清楚了,所以这里就不过多介绍了,以上这些方法都是参考了api提供的一些方法自己写的一些测试,其实都只是一个简单方法的使用,但这些方法是反射中最基本最常用的方法,api方法很多,所以我们学习api的时候最好时刻查询文档,查看文档是个好习惯。
总结,java反射机制是java的一个非常重要的一个知识点,其实向spring,struts等等一些知名的框架没有一个没有使用反射,所以学好java反射是提升我们技术必不可少的,我们应该掌握java反射机制。这篇文章与其说是java反射机制的讲解,其实是我自己学习java反射的学习总结,因为学习java也好久了,回顾总结一下所写的知识点,所以文章内容有说错的部分,欢迎指出。
测试代码:http://download.csdn.net/detail/ydxlt/9310157
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
import javax.xml.ws.soap.Addressing;
/**
* 测试反射
* @author lt
*
*/
public class ReflectTest {
private Map<String,Integer> map;
public static void main(String[] args) throws Exception {
// 通过Class的静态方法forName加载类,该方法会初始化类
Class clazz = Class.forName("Common");
// 通过反射生成该类的实例,调用public的无参构造方法,反射生成类的实例,该类必须得有一个public的无参方法
Object newInstance = clazz.newInstance();
testConstructor(clazz);
testCommonMethodInfo(clazz);
testMethodInvoke(clazz,newInstance);
testField(clazz,newInstance);
testAnnotation(clazz);
genericTest();
getTType();
}
public static void testConstructor(Class clazz){
System.out.println("-----------构造方法测试-----------");
// 获取所以的构造方法
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for(int i=0;i<declaredConstructors.length;i++){
int index = i+1; // 因为i从0开始
String str = "第"+index+"个构造方法权限修饰符为";
if(declaredConstructors[i].getModifiers() == 1){ // 这里的常量值可以查看api文档,博客下面也会帖子各种修饰符对应的数字
str+="public";
}
if(declaredConstructors[i].getModifiers() == 2){ // 这里的常量值可以查看api文档,博客下面也会帖子各种修饰符对应的数字
str+="private";
}
if(declaredConstructors[i].getModifiers() == 4){ // 这里的常量值可以查看api文档,博客下面也会帖子各种修饰符对应的数字
str+="protected";
}
str+="名称为:"+declaredConstructors[i].getName();
System.out.println(str);
}
}
/**
* 通过反射调用方法
* @param clazz
* @throws SecurityException
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public static void testMethodInvoke(Class clazz,Object newInstance) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
System.out.println("-----------测试方法的调用-----------");
/**
* 由于我们知道类中有这种类型的方法,所以我们直接指定要获取的方法类型
* 如果不知道我们要调用的 方法的类型,我们可以获取方法的参数类型等等所有信息
* 然后匹配到我们需要调用的方法,这里做个说明,我们要调用的方法名是知道的
*/
Method sum = clazz.getMethod("sum",int.class,int.class);
System.out.println(sum);
sum.invoke(newInstance, 1,1);
Method setS = clazz.getMethod("setS",String.class);
System.out.println(setS);
setS.invoke(newInstance, "赋值啦");
}
/**
* 这个方法通过反射获取了方法的相关信息,可以看到我们得到了方法
* 的全部信息
* 这里字符串拼接比较多,由于是测试,所以就没用StringBuffer拼接
* @param clazz
*/
public static void testCommonMethodInfo(Class clazz){
System.out.println("-----------普通方法测试-----------");
Method[] methods = clazz.getMethods();
System.out.println(methods.length);
for(int i=0;i<methods.length;i++){
int dexI = i+1;
String str = "第"+dexI+"个方法形式为:";
// 获取方法权限修饰符
int modifiers = methods[i].getModifiers();
if(modifiers == 1){ // 这里的常量值可以查看api文档,博客下面也会帖子各种修饰符对应的数字
str+="public";
}
if(modifiers == 2){ // 这里的常量值可以查看api文档,博客下面也会帖子各种修饰符对应的数字
str+="private";
}
if(modifiers == 4){ // 这里的常量值可以查看api文档,博客下面也会帖子各种修饰符对应的数字
str+="protected";
}
String returnType = methods[i].getReturnType().getSimpleName(); // 获取方法返回类型
str+=" "+returnType+" ";
String name = methods[i].getName(); // 获取方法名称
str+= name;
str+="(";
// 获取方法参数的类型
Class[] parameterTypes = methods[i].getParameterTypes();
for(int j=0;i<parameterTypes.length;i++){
str+=parameterTypes[j]+" ";
}
str+=")";
System.out.println(str);
}
}
/**
* 测试类的Field
* @param clazz
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
public static void testField(Class clazz,Object obj) throws IllegalArgumentException{
System.out.println("-----------测试类的Field-----------");
// 获取所以的Field,包括private修饰的,但不能直接获取和直接改变private修饰的值
Field[] fields = clazz.getDeclaredFields();
for(int i=0;i<fields.length;i++){
int dexI = i+1;
System.out.print("第"+dexI+"个name:"+fields[i].getName()+" ");
Class type = fields[i].getType();
String typeStr = type.getName();
try{
if(typeStr.equals("int")){
System.out.println("value:"+fields[i].getInt(obj));
}else if(typeStr.equals("java.lang.String")){
// 字符串形式通过get(Object obj)方法取得,如果该Field的权限为private,则 获取值的时候会报java.lang.IllegalAccessException
System.out.println("value:"+fields[i].get(obj));
}
}catch(IllegalAccessException ex){
System.out.println("不能获取private修饰的属性值");
}
}
}
/**
* 测试注解(类上的注解),属性方法上的注解分别通过
* Field对象和Method对象的getAnnotations()方法可以得到
* 和这里是一样的
* @param clazz
*/
public static void testAnnotation(Class clazz){
System.out.println("-----------测试注解-----------");
Annotation[] annotations = clazz.getAnnotations();
for(int i=0;i<annotations.length;i++){
System.out.println(annotations[i]);
}
}
/**
* 获取泛型T的实际类型
*/
public static void getTType(){
System.out.println("-----------获取泛型T的实际类型-----------");
Dog dog = new DogImpl();
// 注意,下面这种写法不能获取到T的实际类型,因为范式要在编译的时候就指定类型,在运行时候指定类型是获取不到真实类型的
// Dog<Cat> dog = new Dog<Cat>();
}
/**
* 泛型测试
* @throws NoSuchFieldException
*/
private static void genericTest() throws NoSuchFieldException {
System.out.println("-----------获泛型测试-----------");
Class<ReflectTest> clazz = ReflectTest.class;
Field f = clazz.getDeclaredField("map");
// 直接使用getType()只对普通类型有效,并不能得到有泛型的类型
Class<?> type = f.getType();
// 下面的代码可以看到只输出了java.util.Map
System.out.println(" map 的类型为:"+type);
// 获取Field实例f的泛型类型
Type genericType = f.getGenericType();
// 如果genericType是ParameterizedType对象
if(genericType instanceof ParameterizedType){
ParameterizedType parameterizedType = (ParameterizedType) genericType;
// 获取原始类型
Type rawType = parameterizedType.getRawType();
System.out.println("原始类型是:"+rawType);
// 取得泛型类型的泛型参数
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for(int i=0;i<actualTypeArguments.length;i++){
System.out.println("第"+i+"个泛型类型为:"+actualTypeArguments[i]);
}
}else{
System.out.println("泛型类型获取出错");
}
}
}
@SuppressWarnings(value="unchecked")
@Deprecated
@Addressing
class Common{
public int a = 3;
private String s = "dfdff";
protected int b = 4;
int c = 5;
public Common(){
System.out.println("public Constructor invoked");
}
private Common(String s){
System.out.println("private Constructor invoked");
}
protected Common(int i){
System.out.println("protected Constructor invoked");
}
public void method1(){
System.out.println("method1 invoked");
}
public void sum(int x,int y){
int z = x+y;
System.out.println("sum="+z);
}
public void setS(String s){
System.out.println("s 的之前的值为:"+this.s);
this.s = s;
System.out.println("s 赋值之后的值为:"+this.s);
}
}
class Dog<T>{
public Class<T> clazz;
public Dog(){
getTType();
}
private void getTType() {
// getGenericSuperclass获得带有泛型的父类
ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
clazz = (Class) parameterizedType.getActualTypeArguments()[0];
System.out.println("clazz的实际类型为:"+clazz.getSimpleName());
}
}
class Cat{}
class DogImpl extends Dog<Cat>{}