首页 > 编程语言 > 详细

Java高级特性—反射机制

时间:2020-09-02 11:00:28      阅读:33      评论:0      收藏:0      [点我收藏+]

Java高级特性——反射

1. 虚拟机的类加载机制

? Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制。在Java语言里面,类型的加载、连接和初始化过程都是在程序运行期间完成的。Java天生可以动态扩展的语言特性就是依赖运行期动态加载和动态连接这个特点实现的。

? 一般我们把类从加载到内存到卸载出内存的整个过程分为七个阶段:加载,验证,准备,解析,初始化,使用和卸载。其中,验证、准备和解析统称为连接。

技术分享图片

? 在这几个阶段中,加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,而解析阶段则不一定,它有时候可能会在初始化之后开始,这是为了支持Java的运行时绑定。需要特别注意的是,这里边的顺序指的是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常会互相交叉的混合进行。

类加载的时机

? 对于初始化阶段,《Java虚拟机规范》严格规定了有且只有6种情况必须对类进行”初始化“(而加载、验证、准备自然需要在此之前开始):

  • 使用new关键字实例化对象的时候
  • 读取或设置一个类型的静态字段的时候(被final修饰、已在编译期把结果放入常量池的静态字段除外)
  • 调用一个类型的静态方法的时候
  • 使用java.lang.reflect包的方法对类型进行反射调用的时候,如果类型没有进行初始化,则需要先触发其初始化
  • 当初始化类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
  • 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类

类加载器

技术分享图片

  • 根类加载器(Bootstrap class Loader)

    负责加载jdk核心类(rt.jar)

  • 扩展类加载器(Extension Class Loader)

    负责加载jdk中jre/lib/ext下的jar中的字节码文件

  • 应用程序类加载器(Application Class Loader)

    负责加载自定义类或classpath下第三方的jar中的类

  • 自定义类加载器(User Class Loader)

    通过继承ClassLoader类实现,主要重写findClass方法。

2. 反射机制概述

? JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射(Reflection)机制

  • Reflection是Java被认为动态语言的一个关键性质
  • 这个机制允许程序在运行时通过Reflection APIs取得任何一个已知名称的Class的内部信息
  • 包括其modifiers(诸如public、static等)、 superclass(例如Object)、实现了的 interfaces (例如Serializable)、也包括其fields和methods的所有信息,并可于运行时改变fields内容或调用methods。

反射机制主要提供了以下功能:

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法

反射机制的主要用途:

  • 在第三方架构中使用反射
  • 使用别人提供的类或对象时
  • 暴力破解时List<Integer>,越过泛型检测
  • 解耦合new User()new UserDto()

反射API的组成:

? 在JDK中,主要由以下类来实现Java反射机制,这些类(除了Class.java)都位于java.lang.reflect包中

  • java.lang.Class:类对象,代表类的实体,在运行的Java应用程序中表示类和接口
  • java.lang.reflect.Constructor:代表类的构造方法
  • java.lang.reflect.Method:代表类的成员方法
  • java.lang.reflect.Field:代表类的成员变量

3. Class类

? Class类非常特殊,它没有公共构造方法,而是由JVM创建。Java中,无论生成某个类的多少个对象,这些对象都会对应同一个Class对象。这个Class是由JVM创建的,通过它能够获悉整个类的结构。

  • 所以说要想解析一个类,必须先要获取到该类的字节码文件对象
  • 而解析使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象

获取Class对象的三种方式:

  1. Class类中的静态方法forName()

    Class<?> cls=Class.forName("com.sofwin.pojo.User"); //forName("全限定类名")
    User u= (User) cls.newInstance();
    
  2. 使用类名.class的方式

    Class<String> c1=String.class;
    
  3. Object类中的getClass()方法

    User user=new User();
    Class<?> class1 = user.getClass();
    User user2=(User) class1.newInstance();
    System.out.println(user==user2);//false
    
  • 获取类相关的方法

    返回值类型 方法 描述
    Class<?> forName(String className) 根据全限定类名生成该类的类对象
    Class<?>[] getClasses() 返回一个数组,包含该类中所有公共类和接口类的对象
    ClassLoader getClassLoader() 返回该类的类加载器
    String getName() 返回该类的全限定类名
    Package getPackage() 获取该类的包
    String getSimpleName() 获取类的名字
    Class<? super T> getSuperclass() 获取当前类继承的父类的类对象
    Class<?>[] getInterfaces() 获取当前类实现的所有接口的类对象
  • 获取类中相关属性的方法

    返回值类型 方法 描述
    Field[] getFields() 获取该类的所有公有属性对象,Field数组
    Field getField(String name) 根据属性名获取该类中对应的公有属性对象,Field对象
    Field[] getDeclaredFields() 获取该类所有属性的对象,Field数组
    Field getDeclaredField(String name) 根据属性名获取该类中对应的属性对象,Field对象
  • 获取类中构造器相关的方法

    返回值类型 方法 描述
    Constructor<?>[] getConstructors() 获取该类中所有公有的构造方法对象,Constructor数组
    Constructor<?> getConstructor(Class...<?> paramterTypes ) 根据参数获取该类中对应的公有构造器对象,Constructor对象
    Constructor<?>[] getDeclaredConstructor() 获取该类中的所有构造方法对象数组,Constructor数组
    Constructor<?> getDeclaredConstructor(Class...<?> paramterTypes) 根据参数获取该构造方法对象,Constructor对象
  • 获取类中方法相关的方法

    返回值类型 方法 描述
    Methods<?>[] getMethods() 获取该类中所有的公有方法对象的数组
    Methods<?> getMethod(String name,Class<?>...paeamterTypes) 根据方法名获取类中对应的public方法对象
    Methods<?>[] getDeclaredMethods() 获取该类中所有方法的对象数组
    Methods<?> getDeclaredMethod(String name.Class<?>...paramterTypes) 根据方法名和参数获取该类中对应的方法对象
  • 其它重要方法

    返回值类型 方法 描述
    T newInstance() 根据类对象创建实例对象(类中必须有无参构造方法)

4. Field类

返回值类型 方法 描述
void set(Object obj,Object value) 给obj对象的当前属性设置value值
String getName() 获取当前字段的名称
int getModifiers() 获取当前字段的修饰符
void setAccessible(boolean flag) 设置暴力访问,可以访问私有属性
Object get(Object obj) 返回obj对象当前属性的值

5. Method类

返回值类型 方法 描述
Object invoke(Object obj,Object...args) 根据类对象创建实例对象(类中必须有无参构造方法)
void setAccessible(boolean flag) 可以暴力调用私有方法
Class<?>[] getParamterTypes() 获取方法的所有参数的类对象
Class<?> getReturnType() 获取方法的返回值对象

6.Constructor类

返回值类型 方法 描述
T newInstance(Object...initargs) 调用有参构造反射生成对象
Class<?>[] getParamterTypes() 获取方法的所有参数的类对象

7. 反射越过泛型检测

public class Test1 {
	
	public static void main(String[] args) {
		List<Integer> ids=new ArrayList<Integer>();
		ids.add(1);
		ids.add(2);
		ids.add(3);
		
		ids=Test1.getList(ids);
		for (Integer id : ids) {
			System.out.println(id);
		}
	}

	private static List<Integer> getList(List<Integer> ids) {
		//获取ids的类对象
		Class clazz=ids.getClass();
		//获取add()方法对象
		try {
			Method method = clazz.getDeclaredMethod("add", Object.class);
			//暴力破解
			method.setAccessible(true);
			//反射调用该方法,放入字符串
			method.invoke(ids, "test");
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return ids;
	}
}

8. 使用反射实现MyBatis的查询结果封装(底层)

public static <T> List<T> executeQuery(Class resultType,String sql,Object...objects){
    Connection conn = null; 
    PreparedStatement pstmt = null; 
    ResultSet rs = null; 
    List result = new ArrayList(); 
    try {
        conn = JdbcUtil.getConnection(); 
        pstmt = conn.prepareStatement(sql); 
        //处理占位符 
        for(int i=0;i<objects.length;i++) { 
            pstmt.setObject(i+1, objects[i]); 
        }
        rs = pstmt.executeQuery(); 
        ResultSetMetaData metaData = rs.getMetaData(); 
        int columnCount = metaData.getColumnCount(); 
        while(rs.next()) { 
            //每次循环生成对象 
            Object obj = resultType.newInstance(); 
            //给未知的成员变量赋值(查询结果的列名就是我们的属性名称) 
            for(int i=0;i<columnCount;i++) { 
                //获取到查询结果的列名 
                String columnName = metaData.getColumnLabel(i+1); 
                Field field = resultType.getDeclaredField(columnName); 
                if(field!=null) { 
                    field.setAccessible(true); 
                    if(field.getType()==Integer.class) { 
                        field.set(obj, Integer.parseInt(rs.getObject(columnName)==null?"0":rs.getObject(columnName).toS tring())); 
                    }
                    if(field.getType()==Float.class) { 
                        field.set(obj, Float.parseFloat(rs.getObject(columnName)==null?"0":rs.getObject(columnName).toS tring())); 
                    }
                    if(field.getType()==Double.class) {
                        field.set(obj, Double.parseDouble(rs.getObject(columnName)==null?"0":rs.getObject(columnName).t oString())); 
                    }
                    if(field.getType()==String.class) { 
                        field.set(obj, rs.getObject(columnName)==null?"":rs.getObject(columnName).toString());
                    } 
                } 
            }
            //将obj对象加入到result集合中 
            result.add(obj); 
        } 
    }catch(Exception ex) {
    
    }finally { 
        JdbcUtil.closeAll(conn, pstmt, rs); 
    }
    return result; 
}

9. JDK动态代理

? 在Java中代理有两种方式:

  • 基于JDK的动态代理
  • 基于字节码增强的动态代理(cglib)

JDK的动态代理只能代理接口或实现过接口的类的对象

代理对象可以对目标对象的相关方法进行功能扩展(增强)

返回值 方法 描述
static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces, InvocationHandler h) 生成目标对象的代理对象(目标对象必须实现过接口)

Proxy代理实例对象

public class Test7 { 
    public static void main(String[] args) throws NoSuchFieldException, SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, InvocationTargetException { 
        UserDao dao = new UserDaoImpl();//目标对象 
        /**
        * newProxyInstance中的参数 
        * 参数1:类加载器 
        * 参数2:目标类中实现的接口的类对象数组 
        * 参数3:是代理实例的调用处理程序 实现的接口 
        * InvacationHandler接口的实例的 
        * 在代理对象中如何去执行目标对象的方法
        */ 
        UserDao daoProxy= (UserDao)Proxy.newProxyInstance(Test7.class.getClassLoader(), dao.getClass().getInterfaces(), new InvocationHandler() { 
            /**
            * 参数1:代理实例 
            * 参数2:被调用的方法 
            * 参数3:方法的参数 
            * 返回值:目标方法的返回值 
            */   
          @Override 
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //反射调用目标方法 
                Object obj = method.invoke(dao, args); 
                return obj; 
            } 
        }); 
        //调用目标对象的方法 
        dao.test(); 
        daoProxy.test(); 
        System.out.println(daoProxy instanceof UserDao); 
    } 
}

Proxy代理接口对象

public class UserDAOProxyFactory { 
    public static UserDAO createUserDAO() { 
        /**
        * 类加载器:system classLoader 
        * 参数2:实例的代理对象----该实例实现过的接口的类对象的数组 
        * 接口的代理对象----该接口的类对象的数组 
        */ 
        UserDAO dao = (UserDAO)Proxy.newProxyInstance(UserDAO.class.getClassLoader(), new Class[] {UserDAO.class}, new InvocationHandler() { 
            @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if(method.getName().contains("save")) { 
                    System.out.println("saveUser......"); 
                }
                if(method.getName().contains("delete")) { 
                    System.out.println("delete......."); 
                }
                return null; 
            } 
        });
        return dao; 
      }
}

Java高级特性—反射机制

原文:https://www.cnblogs.com/xxyang/p/13600269.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!