一、引言及java.lang.Class的介绍
反射技术作为一项高新技术,其宗旨沿用了Java的设计思想:万物皆对象。不过这次的对象不是别人,而是类本身。我们都知道Java中的类是对象的共性的代表。所有的.class文件被加载后都会获得一个对应的Class实例对象,通过这个对象,我们就可以获取当前类的所有信息!
首先我们来看下类被加载如内存中的过程:
.java文件====①====.class文件====②====被调用的加载入内存
① 每个类对应一个.class文件
② Step1:加载不同的.class文件,获取到java.lang.Class实例。
Step2: 连接
Step3: 初始化
注:类的加载器分为:启动加载器和用户自定义的加载器,不同的类加载器加载。
程序实例(ClassLoader)
@Test
public void ClassLoader() throws Exception{
//系统加载器
ClassLoader systemClassLoader=ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@ad3ba4
//系统加载器的上级是扩充加载器
ClassLoader extClassLoader=systemClassLoader.getParent();
System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@126b249
//扩充加载器的上级是启动加载器
ClassLoader bootstrapClassLoader=extClassLoader.getParent();
System.out.println(bootstrapClassLoader);//null
// 由于没有足够权限访问由c/c++编写的启动加载器,故返回null
//查看自定义类的加载器
Class clazz1=Student.class;
ClassLoader extLoader=clazz1.getClassLoader();
System.out.println(extLoader);//sun.misc.Launcher$AppClassLoader@1372a1a
//从输出对比可以知晓:自定义类是由系统加载器加载的
Class clazz=String.class;
ClassLoader loader=clazz1.getClassLoader();
System.out.println(loader);//sun.misc.Launcher$AppClassLoader@1372a1a
//从输出对比可以知晓:自定义类是由系统加载器加载的
/**
* ClassLoader重点方法:读取文件夹下的文件
*/
/*ClassLoader classLoader=this.getClass().getClassLoader();
InputStream is=classLoader.getResourceAsStream("com\\heima\\jdbc.properties");
Properties properties=new Properties();
properties.load(is);
String user=properties.getProperty("user");
System.out.println(user);*/
/**
* 只能读取工程目录下的文件
*/
FileInputStream fis=new FileInputStream(new File("jdbc.properties"));
Properties properties=new Properties();
properties.load(fis);
String user=properties.getProperty("user");
System.out.println(user);
}
在获取java.lang.Class实例之后,我们便可以解析类中的所有成分。那么接下来我们一起看看类的共性有哪些吧?
我们要明确Class类是类的共性的一种描述,一般一个类的全部结构为:
所在的包
注解
修饰符 类名 extends superclass<T> implements Interfaces{
数据成员、方法、注解、构造器、内部类、实现的接口
}
类加载器:
ClassLoader getClassLoader()
包名:
Package getPackage()
修饰符
Modifier.toString( getModifiers() )
类名:
String getName()
superClass(父类):
Class<?superT>getSuperclass()
父类的类型:
/**
*
* 获取父类的泛型
* @throws Exception
* @throws InstantiationException
*/
@Test
public void testGeneric() throws Exception{
Class clazz=Student.class;
Student stu=(Student) clazz.newInstance();
ParameterizedType type=(ParameterizedType) clazz.getGenericSuperclass();
Type[] types=type.getActualTypeArguments();
System.out.println(types[0]);
}
数据成员
//获取所用数据成员时使用
Field[] getDeclaredFields()
Field[] getFields()
//调用数据成员时使用
Field getDeclaredField(String name)
Field[] getDeclaredFields()
Field getField(String name)
成员方法
//调用方法时使用
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
Method getMethod(String name, Class<?>... parameterTypes)
method.invoke
//获取所用方法时使用
Method[] getDeclaredMethods()
Method[] getMethods()
构造器:
//调用构造器时使用
Constructor<T> getConstructor(Class<?>... parameterTypes)
//获取所有构造器时使用
Constructor<?>[] getConstructors()
注解
<A extends Annotation>
A
getAnnotation(Class<A> annotationClass)
Annotation[] getAnnotations()
这样一来以Class类的一个反射技术就显现了出来,我们来看看本技术点需要掌握的内容吧!
图:反射技术重要知识点
第一个知识点java.lang.Class的介绍上面已经讲过了,这里就不赘述了。整个一个反射就是生成Class对象,解析类生成对应类的对象实例(Class的newInstance或者Constructor的newInstance),然后通过解析类的各个组成部分,并对他们进行调用
下面我们来看看生成Class对象和对应类的实例的方法有哪些吧。
二、实例化对象
通过java.lang.Class生成类的实例化对象有四种方案:
①调用从Object类中继承的getClass()方法,通过clazz.newInstance()方法生产指定类的实例
②类.class,通过clazz.newInstance()方法生产指定类的实例
③调用java.lang.Class.forName( className),通过clazz.newInstance()方法生产指定类的实例
④通过类的加载器来获取
// 通过类加载器获得
ClassLoader classLoader=this.getClass().getClassLoader();
Class clazz4=classLoader.loadClass("com.heima.Student");
System.out.println(clazz4.getName());
⑤通过以上三种方式获取到了clazz实例后通过getConstructors() 获取Constructor,然后通过newInstance()获取指定雷的实例
三、解析类的成员并调用
前面已经提到过,类的通用结构
所在的包
注解
修饰符 类名 extends superclass<T> implements Interfaces{
数据成员、方法、注解、构造器、内部类、实现的接口
}
这里注意java中一切皆对象,也就是说类中的所有成员都可以被抽象出一些共性来,并且还有一些访问这些共性的方法。
//解析的总结
因此我们要解析出类的完整的结构,在这里就是通过Class的方法获取一系列类的实例。
//调用的总结
那么调用类的成员首先要明确不同成员的功能:构造器生成对象(newInstance),方法用来调用(invoke),field用来存值和获取值(设值:
void set(Object obj, Object value)
和取值: Object get(Object obj) )
java.lang.reflect.Constructor
Constructor是抽取了构造器的共性。作为原先对象的构造器,它的作用是生成对象。
//解析构造器结构
它的一般结构:
注解
修饰符 构造器名(参数){
。。。。。
}
获取修饰符的属性:
int getModifiers()
Modifier.toString()
获取构造器的名称:
String getName()
获取注解:
T
getAnnotation(Class<T> annotationClass)
Annotation[] getDeclaredAnnotations()
获取形参类型:
Class<?>[] getParameterTypes()
//调用
T newInstance(Object... initargs)
java.lang.reflect.Field
Field是抽取了成员变量的共性。作为原先对象的数据成员,它的作用是存值和取值
//解析成员变量的结构
它的一般结构:
修饰符 类型 成员变量名
//获取修饰符
int getModifiers()
Modifier.toString(getModifiers())
//获取成员变量名
String getName()
//获取成员变量类型
Class<?> getType()
//调用对象的方法
//设置成员变量和获取成员变量值
setXxx和getXxx
//设置和获取指定对象该字段的值
Object get(Object obj)
void set(Object obj, Object value)
java.lang.reflect.Method
Method是抽取了方法的共性。作为原先对象的方法,它的作用是被类或者对象调用
//解析成员变量的结构
它的一般结构:
注解
修饰符 类型 方法名(形参) 异常{
}
//解析类的结构
//获取注解
Annotation[] getDeclaredAnnotations()
<T extends Annotation>
T
getAnnotation(Class<T> annotationClass)
//获取修饰符
Modifier.toString( int getModifiers()
)
//获取返回值类型
Class<?> getReturnType()
//获取方法名
String getName()
//获取参数类型
Class<?>[] getParameterTypes()
//获取异常类型
Class<?>[] getExceptionTypes()
//调用方法
Object invoke(Object obj, Object... args)
代码实例
package com.heima;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Properties;
import org.junit.Test;
public class ClassTest {
@Test
public void test() throws Exception {
//生成对象化实例
Class clazz=Student.class;
Student stu=(Student) clazz.newInstance();
/*Method method=clazz.getMethod("setId",Integer.class);
method.invoke(p, 12);*/
//访问public访问权限的field
Field field=clazz.getField("id");
field.setInt(stu, 20000121);
int id=field.getInt(stu);
//访问private访问权限的field
Field field1=clazz.getDeclaredField("name");
field1.setAccessible(true);
field1.set(stu, "tang");
System.out.println(field1.get(stu));
// 调用方法
Method method=clazz.getMethod("display");
method.invoke(stu);
}
/**
* 获取java.lang.Class的四种方法
* @throws Exception
*/
@Test
public void newClass() throws Exception{
//通过调用类的字节码获取
Class clazz1=Student.class;
System.out.println(clazz1.getName());
// 通过加载类名获取
Class<?> clazz2=Class.forName("com.heima.Student");
System.out.println(clazz2.getName());
//通过getClass获取
Student stu=new Student();
Class clazz3=stu.getClass();
System.out.println(clazz3.getName());
// 通过类加载器获得
ClassLoader classLoader=this.getClass().getClassLoader();
Class clazz4=classLoader.loadClass("com.heima.Student");
System.out.println(clazz4.getName());
// classLoader.loadClass("Student");
}
@Test
public void ClassLoader() throws Exception{
//系统加载器
ClassLoader systemClassLoader=ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@ad3ba4
//系统加载器的上级是扩充加载器
ClassLoader extClassLoader=systemClassLoader.getParent();
System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@126b249
//扩充加载器的上级是启动加载器
ClassLoader bootstrapClassLoader=extClassLoader.getParent();
System.out.println(bootstrapClassLoader);//null
// 由于没有足够权限访问由c/c++编写的启动加载器,故返回null
//查看自定义类的加载器
Class clazz1=Student.class;
ClassLoader extLoader=clazz1.getClassLoader();
System.out.println(extLoader);//sun.misc.Launcher$AppClassLoader@1372a1a
//从输出对比可以知晓:自定义类是由系统加载器加载的
Class clazz=String.class;
ClassLoader loader=clazz1.getClassLoader();
System.out.println(loader);//sun.misc.Launcher$AppClassLoader@1372a1a
//从输出对比可以知晓:自定义类是由系统加载器加载的
/**
* 重点方法:读取文件夹下的文件
*/
/*ClassLoader classLoader=this.getClass().getClassLoader();
InputStream is=classLoader.getResourceAsStream("com\\heima\\jdbc.properties");
Properties properties=new Properties();
properties.load(is);
String user=properties.getProperty("user");
System.out.println(user);*/
/**
* 只能读取工程下的文件
*/
FileInputStream fis=new FileInputStream(new File("jdbc.properties"));
Properties properties=new Properties();
properties.load(fis);
String user=properties.getProperty("user");
System.out.println(user);
}
/**
* 解析类中的成员
*/
@Test
public void parseFields(){
Class clazz=Student.class;
//getFields()获取本身及其父类的public类型数据成员
Field[] fields=clazz.getFields();
for(Field field:fields){
System.out.println(field.getName());
}
System.out.println("\r\n");
System.out.println("\r\n");
System.out.println("\r\n");
//getDeclaredFields()获取本身的所有被声明的成员
fields=clazz.getDeclaredFields();
for(Field field:fields){
System.out.println(Modifier.toString(field.getModifiers())+" "+field.getName());
}
//获取数据成员的修饰符
}
/**
*
* 获取父类的泛型
* @throws Exception
* @throws InstantiationException
*/
@Test
public void testGeneric() throws Exception{
Class clazz=Student.class;
Student stu=(Student) clazz.newInstance();
ParameterizedType type=(ParameterizedType) clazz.getGenericSuperclass();
Type[] types=type.getActualTypeArguments();
System.out.println(types[0]);
}
/*==============================调用指定的成员=====================================*/
/**
* 调用方法和属性值
* @throws Exception
* @throws SecurityException
*/
@Test
public void testInvoke() throws SecurityException, Exception{
//调用静态方法
Class clazz=Student.class;
Student stu=(Student) clazz.newInstance();
Method method=clazz.getDeclaredMethod("hello");
method.setAccessible(true);
method.invoke(stu);
//调用私有数据成员
Field name=clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(stu, "tang");
System.out.println(stu.toString());
//调用私有构造器
Constructor constructor=clazz.getConstructor(String.class,int.class,int.class);
constructor.setAccessible(true);
Object obj=constructor.newInstance("tang",25,25);
System.out.println(obj);
}
}
到这里为止,反射的大致情况我们已经清晰了,下面我们介绍一个由反射产生的设计模式——动态代理
在介绍动态代理之前,我们先来了解下它的前身:静态代理,从中总结下代理的规律!!!
图为静态代理的原理
实现代码:
package com.heima;
//公共接口
interface ClothFactory{
public void product();
}
//被代理类
class NikeFactory implements ClothFactory{
public void product() {
System.out.println("nike");
}
}
//代理类
class ProxyFactory implements ClothFactory{
//包装被代理对象,这样就可以控制被代理对象的调用和时机
public ClothFactory clothFactory;
public ProxyFactory(ClothFactory clothFactory) {
super();
this.clothFactory = clothFactory;
}
public void product() {
System.out.println("this is proxy");
clothFactory.product();
}
}
/**
* 静态代理设计模式测试
* @author John
*/
public class StaticProxy {
public static void main(String[] args) {
ClothFactory clothFactory=new NikeFactory();
ProxyFactory proxy=new ProxyFactory(clothFactory);
proxy.product();
}
}
好了,从上面的例子我们可以看到,代理类的建立依据于被代理类和公共接口。首先代理类通过被代理类生成代理对象,然后通过代理对象去调用与被代理对象同名的方法。
实例代码:
package com.heima;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//共同的接口,用于测试
interface Man{
public void eat();
public void walk();
}
//被代理类,用于测试
class Proxyed implements Man{
public void eat() {
System.out.println("吃饭!!!");
}
public void walk() {
System.out.println("走路!!!");
}
}
//根据公共接口和需要被代理的类动态地生成代理类
class MyProxy implements InvocationHandler{
private Object obj;
public MyProxy(Object obj) {
super();
this.obj = obj;
}
//根据公共接口和需要被代理的类动态地生成代理类
public Object blinder(){
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
}
//通过代理类加入到被代理类的方法中去
public void logIn(){
System.out.println("==================日志记录===================");
System.out.println("==================用户登入===================");
}
//通过代理类加入到被代理类的方法中去
public void logOut(){
System.out.println("==================用户登出===================");
}
/**
* 当代理类对象调用方法时,会自动调用此方法,从而控制了方法的调用时机和调用结果
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
logIn();
Object object=method.invoke(obj, args);
logOut();
return object;
}
}
/**
* 动态代理设计模式:
* 涉及到的重点的类
* @author John
*
*/
public class DynamicProxy {
public static void main(String[] args) {
//生成被代理对象
Man man=new Proxyed();
//通过被代理对象获取代理对象,获取依据:公共接口和被代理类对象
Man myProxy=(Man) new MyProxy(man).blinder();
//当调用代理类的这个方法是默认去调用了invoke
// myProxy.eat();
myProxy.walk();
}
}