JavaSE学习笔记(14.Java之反射机制)

1. 类加载过程

Java是一门编译加解析的语言,编译器先将代码编译成字节码文件(class文件),Jvm加载class文件进行解析运行!

类加载通常分三个步骤:class文件的加载、类的链接、类的初始化!

1.1 class文件的加载:

将编译后的class文件,加载到内存中,解析字节码,并生成一个Class对象!

1.1.1 类加载器介绍:

class文件的加载,并不是通过一个加载器实现,是通过多组类加载器共同实现的,共分四类:根类加载器、扩展类加载器、系统类加载器、用户类加载器!

根类加载器:在sun.boot.class.path系统变量中的路径中寻找class文件,并进行加载!

扩展类加载器:在java.ext.dirs系统变量中的路径中寻找class文件,并进行加载!类型为ExtClassLoader

系统类加载器:在java.class.path系统变量中的路径中寻找class文件,并进行加载!类型为AppClassLoader;可以通过ClassLoader.getSystemClassLoader()静态方法获取!

用户类加载器:用户自定义类加载器,根据自定义逻辑进行类加载,一般情况下父类加载器为系统类加载器!

Ps:

  • Jvm系统变量可以通过System.getProperties()方式获取并查看!
  • 上面四类类加载是存在层级关系的,从上到下:根类加载器、扩展类加载器、系统类加载器、用户类加载器
  • 上面四类类加载器只有根类加载器不是ClassLoader的子类,其余类加载器都是ClassLoader的子类,因为根类加载器是Jvm实现!

1.1.2 类加载过程介绍:

当加载一个类的时候,首先判断缓存中是否存在,如果不存在,会通过当前使用的类加载器找到根类加载器,然后一层层向下使用类加载器寻找class文件,加载成功后,退出并返回Class对象;如果到用户类加载器依然没有找到class文件,Jvm抛出ClassNotFoundException!(手动进行类加载的时候,可以指定类加载器;如果依赖于Jvm自动进行类加载的时候,默认使用系统类加载器)

1.1.3 自定义类加载器:

可以通过继承ClassLoader类,来实现自定义用户类加载器,主要通过重写loadClass()和findClass()两个方法来实现,具体细节不再展开描述!好处就是可以定制特定类加载前的部分实现逻辑!

1.2 类的链接:

将class文件加载到内存中的二进制数据合并到JRE中:

1.3 类的初始化:

  • 对类变量进行初始化赋值
  • 执行静态初始化代码块

Ps:

  • 调用ClassLoader.loadClass(类名)只会执行类的加载和链接,不会进行类的初始化,等到类使用的时候才会执行初始化!
  • 调用Class.forName(类名)会执行类的加载、链接、并强制初始化!

1.4 触发类加载条件:

  1. 创建类实例
  2. 访问类方法
  3. 访问类成员
  4. 调用Class.forName()方法
  5. 加载子类的时候,会先加载所有父类
  6. 直接使用java.exe命令运行某个主类,会先加载主类

Ps:访问static final属性的类成员变量,如果该对象在编译期间就能确定内容;编译器会在使用它的地方直接替换为它的值,因此访问的时候,不通过类访问,所以不会触发类加载!

 

2. Java的反射机制

2.1 反射机制的原理:

Java中的反射是通过类加载后生成的Class对象实现的,该对象提供了所有的类操作和类信息获取能力!

获取Class对象的方法:

  • 调用Class.forName()类方法,获取Class对象!
  • 类.class,通过类的class属性,获取Class对象!
  • 通过对象调用Object.getClass()实例方法,获取Class对象!

Ps:后面两种方式,必须保证类加载已经完成,否则无法获取;Class.forName()方式可以在类没有加载的时候调用,触发类加载!

2.2 通过反射获取信息:

2.2.1 获取构造器、获取成员、获取方法相关信息:

Ps:

  • getXXX获取某个特定类型的public属性的数据!
  • getXXXs获取所有public属性的数据!
  • getDeclaredXXX获取某个特定类型的所有属性的数据!
  • getDeclaredXXXs获取所有属性的数据!
  • 数据获取的时候,都是包括继承的数据!!!

2.2.2 获取类相关信息:

/*获取Class对象所属的外部类*/
public Class<?> getDeclaringClass() throws SecurityException

/*获取类中的所有内部类*/
public Class<?>[] getDeclaredClasses() throws SecurityException

/*获取类中所有public属性的内部类*/
public Class<?>[] getClasses()

/*获取类实现的接口*/
public Class<?>[] getInterfaces()

/*获取类继承的父类*/
public native Class<? super T> getSuperclass()

2.2.3 获取注解相关信息:

/*获取指定类型的注解, 包括父类注解*/
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)

/*获取指定类型的注解,不包括父类注解*/
public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass)

/*获取当前类的所有注解,包括父类注解*/
public Annotation[] getAnnotations()

/*获取当前类的所有注解,不包括父类注解*/
public Annotation[] getDeclaredAnnotations() 

/*针对Java8 重复注解功能,提供的获取指定类型注解,包括父类注解*/
public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass)

/*针对Java8 重复注解功能,提供的获取指定类型注解,不包括父类注解*/
public <A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass)

2.2.4 获取类基本信息:

/*获取包信息*/
public Package getPackage() 

/*获取类名字*/
public String getName()

/*获取类的简称*/
public String getSimpleName()

/*获取类的修饰符常量值,通过Modifier工具类解码*/
public native int getModifiers()

/*class信息判断*/
/*判断obj对象是否属于当前类的实例,与instanceof用法一致*/
public native boolean isInstance(Object obj)

/*判断当前类是否是一个接口*/
public native boolean isInterface()

/*判断当前类是否是一个数组类*/
public native boolean isArray()

/*判断当前类是否为基础类类型,Boolean、Character、Byte、Short、Integer、Long、Double、Void*/
public native boolean isPrimitive()

/*判断当前类是否是一个枚举类*/
public boolean isEnum()

/*判断当前类是否是一个匿名类*/
public boolean isAnonymousClass()

/*判断当前类是否被annotationClass注解修饰*/
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

instanceof与isInstatnce解析:

public class InstanceTest
{
	public static void main(String[] args)
	{
		ClassF f = new ClassF();
		ClassS s = new ClassS();
		/*instanceof Test*/
		System.out.println("instanceof Test");
		/*class是object本身类,编译通过,返回true*/
		System.out.println(f instanceof ClassF);
		/*class是object父类,编译通过,返回true*/
		System.out.println(s instanceof ClassF);
		/*class是object子类,编译通过,返回false*/
		System.out.println(f instanceof ClassS);
		/*class与object不存在任何关系,编译报错!!!*/
		//System.out.println(f instanceof String);
		
		/*isInstance Test*/
		System.out.println("isInstance Test");
		/*class是object本身类,编译通过,返回true*/
		System.out.println(ClassF.class.isInstance(f));
		/*class是object父类,编译通过,返回true*/
		System.out.println(ClassF.class.isInstance(s));
		/*class是object子类,编译通过,返回false*/
		System.out.println(ClassS.class.isInstance(f));
		/*class与object不存在任何关系,编译通过,返回false*/
		System.out.println(String.class.isInstance(f));
		
	}
	
}

class ClassS extends ClassF
{
    
}

class ClassF 
{
    
}

Ps:

  • instanceof是关键词,isInstance是Class对象的方法
  • 两者用法基本相同,只是当class与object没有关系的时候,instanceof编译报错;isInstance编译通过,返回false!

2.2.5 获取方法的参数信息:

Java8,Method类和Constructor类新增了一个共同的抽象类Executable,Executable类中提供了一系列关于Parameter类的方法:

/*Executable类主要方法*/
/*获取方法或构造器的参数个数*/
public int getParameterCount() 

/*获取方法或构造器的参数类型数组*/
public Type[] getGenericParameterTypes()

/*获取方法或构造器的参数数组*/
public Parameter[] getParameters()

Parameter类中也提供了一系列获取具体参数信息的方法(获取参数类型、获取参数修饰符、获取参数名、获取参数泛型类型、参数是否可变参数等等):

 

2.3 通过反射操作类:

java.lang.reflect包中除了提供了Class类,同时也提供了Package、Method、Field、Constructor、Array、Type、ParameterizedType等类,为获取到的反射数据提供操作能力!

2.3.1 创建对象:

通过反射创建对象有两种方式:Class对象的newInstance方法和Constructor对象的newInstance方法!

public class ReflectTest
{
	public static void main(String[] args)
	{
		/*通过反射创建对象的两种方式*/
		/*通过Class对象的newInstance()方法,只能访问public属性的构造器*/		
		try 
		{
			ReflectPublic r1 = ReflectPublic.class.newInstance();
			r1.hello();						
		} 
		catch (Exception e)
		{		
			e.printStackTrace();
		}			
		
		/*通过Constructor对象的newInstance()方法,可以通过setAccessible设置,不进行访问权限检测,访问private属性的构造器*/		
		Constructor<ReflectPrivate> constructor;
		try 
		{
			constructor = ReflectPrivate.class.getDeclaredConstructor();
			constructor.setAccessible(true);
			ReflectPrivate r2 = constructor.newInstance();
			r2.hello();
		} 
		catch (Exception e1) 
		{
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}	
		
	}
	
}

class ReflectPrivate
{
	private ReflectPrivate()
	{
		
	}
	
	public void hello()
	{
		System.out.println("private hello");
	}
}

class ReflectPublic
{
	public ReflectPublic()
	{
		
	}
	
	public void hello()
	{
		System.out.println("public hello");
	}
}

Ps:setAccessible()不进行类型访问权限设置,同样可以使用在Method和Field两个类,用来访问private属性的方法和成员!

2.3.2 调用方法:

通过Method类中的invoke方法进行方法调用(Object入参为null,调用静态方法)!

public class MothodTest
{
	public static void main(String[] args)
	{
		try
		{		
			/*通过反射调用类的静态方法*/
			Method method = ReflectMothod.class.getMethod("staticfun");			
			method.invoke(null);
			
			/*通过反射对象的实例方法*/
			ReflectMothod r = new ReflectMothod();
			Method method1 = ReflectMothod.class.getMethod("fun");	
			method1.invoke(r);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}		
		
	}
	
}

class ReflectMothod
{
	public static void staticfun()
	{
		System.out.println("staticfun()");
	}
	
	public static void fun()
	{
		System.out.println("fun()");
	}
}

2.3.3 访问成员:

代码示例:

public class FieldTest
{
	public static void main(String[] args)
	{
		ReflectField r = new ReflectField();
		r.i = 1;
		r.s = "hello";
		try
		{
			/*获取普通类型的实例成员,并修改*/
			Field field = ReflectField.class.getField("i");
			System.out.println(field.getInt(r));			
			field.setInt(r, 100);
			
			/*获取引用类型的实例成员,并修改*/
			Field field1 = ReflectField.class.getField("s");
			System.out.println(field1.get(r));			
			field1.set(r, "hello100");
			
			/*获取类静态成员,并修改*/
			
			Field field2 = ReflectField.class.getField("b");
			System.out.println(field2.getBoolean(null));			
			field2.setBoolean(null, true);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		
		System.out.println(r.i);
		System.out.println(r.s);
		System.out.println(ReflectField.b);
	}
	
}

class ReflectField
{
	public int i;
	public String s;
	public static boolean b;
}

结果输出:

2.3.4 操作数组:

java.lang.reflect包中还提供了一个Array类,用来进行数组类型的反射行为!

public class ArrayTest
{
	public static void main(String[] args)
	{
		int[] a = new int[5];
		
		/*通过Array类反射创建数组*/
		int[] a1 = (int [])Array.newInstance(int.class, 5);
		
		/*通过Array类反射操作数组*/
		Array.set(a1, 0, 1);
		System.out.println(Array.get(a1, 0));
	}	
}

 

3. 动态代理:

Java基于反射的动态代理就是,将一个接口中的所有方法实现都抽象到InvocationHandler接口中的invoke方法中,再通过Proxy或者其子类创建这个接口的实现类,实现类中通过InvocationHandler接口中的invoke方法来代理原接口中的方法!

public class ReflectProxyTest
{
	public static void main(String[] args)
	{
        /*通过Proxy.newProxyInstance方法,创建ReflectProxy接口的实现类对象*/
		ReflectProxy reflectProxy = 
		(ReflectProxy)Proxy.newProxyInstance(ReflectProxy.class.getClassLoader(), new Class[]{ReflectProxy.class}, new InvocationHandlerTest());
		
		System.out.println(reflectProxy.fun(100));
		reflectProxy.run();
		
		try
		{
            /*通过Proxy.getProxyClass方法,创建ReflectProxy接口的实现类的Class对象*/
			Class<?> cls = Proxy.getProxyClass(ReflectProxy.class.getClassLoader(), new Class[]{ReflectProxy.class});
            /*通过Class对象获取构造器,参数必须是InvocationHandler.class*/
			Constructor<?> constructor = cls.getConstructor(new Class[]{InvocationHandler.class});
            /*通过构造器创建ReflectProxy接口的实现类对象*/
			ReflectProxy reflectProxy1 = (ReflectProxy)constructor.newInstance(new Object[]{new InvocationHandlerTest()});
			
			System.out.println(reflectProxy1.fun(500));
			reflectProxy1.run();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		
	}	
}

interface ReflectProxy
{
	int fun(int i);
	void run();
}


/*实现InvocationHandler接口中的invoke方法,用于代理ReflectProxy接口中的fun方法和run方法
*invoke的参数说明:
*proxy为动态代理对象
*method当前调用的方法
*args当前调用方法的入参
*/
class InvocationHandlerTest implements InvocationHandler
{
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
	{
		if ("fun".equals(method.getName()))
		{
			System.out.println("do fun() arg is "+args[0]);
			return args[0];
		}
		
		if ("run".equals(method.getName()))
		{
			System.out.println("do run()");			
		}
		return null;
	}
	
}

结果输出:

动态代理原理:Proxy.getProxyClass通过反射获取到接口中的所有抽象方法,再通过ProxyGenerator.generateProxyClass()方法生成一个接口的实现类的字节码文件(这个实现类中的有一个入参为InvocationHandler的构造器,并将所有方法的实现都转为InvocationHandler的invoke方法进行代理!),最终返回这个接口实现类的Class对象!

 

4. 基于动态代理实现AOP:

可以通过Java对接口的动态代理实现对特定方法增加切面,实现面向切面编程!示例如下:

public class ReflectProxyTest
{
	public static void main(String[] args)
	{
        /*通过Proxy.newProxyInstance方法,创建ReflectProxy接口的实现类对象*/
		ReflectProxy reflectProxy = 
		(ReflectProxy)Proxy.newProxyInstance(ReflectProxy.class.getClassLoader(), new Class[]{ReflectProxy.class}, new InvocationHandlerTest(new ReflectProxyTemp1()));
		
		System.out.println(reflectProxy.fun(100));
		reflectProxy.run();
	}	
}

interface ReflectProxy
{
	int fun(int i);
	void run();
}

/*通过实现InvocationHandler接口,来实现对ReflectProxyTemp1类中方法增加切面*/
class InvocationHandlerTest implements InvocationHandler
{
	private Object tager;
	
	public InvocationHandlerTest(Object tager)
	{
		this.tager = tager;
	}
	
	private void aopFun1()
	{
		System.out.println("aopFun1()");
	}
	
	private void aopFun2()
	{
		System.out.println("aopFun2()");
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
	{
		/*增加Aop切面1*/
		this.aopFun1();
		
		/*通过tager将原始实现传入InvocationHandler实现类*/
		method.invoke(this.tager, args);
		
		/*增加Aop切面2*/
		this.aopFun2();
		
		if ((null != args) && (0 != args.length))
		{
			return args[0];
		}
		
		return null;
	}	
}

/*原始类型,期望在ReflectProxyTemp1类的fun()方法和run()方法上增加切面*/
class ReflectProxyTemp1 implements ReflectProxy
{
	@Override
	public int fun(int i) 
	{
		System.out.println("ReflectProxyTemp1 fun() i is " + i);
		return 0;
	}

	@Override
	public void run() 
	{		
		System.out.println("ReflectProxyTemp1 run()");
	}
	
}

结果输出:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章