《Java基础与案例开发详解》(五)

Java反射机制

反射是Java语言的特征之一,它允许动态地发现和绑定类、方法、字段,以及所有其他的语言所产生的元素,反射可以做的不仅仅是简单的列举类、字段以及方法。通过反射,还能够在需要时完成创建实例、调用方法,和访问字段。反射是Java被视为动态的关键。

Java反射提供的功能
  • 在运行时判断任意一个对象所属的类。
  • 在运行时构造一个类的对象。
  • 在运行时判断一个类所具有的成员变量和方法。
  • 在运行时调用任意一个对象的方法,通过反射甚至可以调用private方法
  • 生成动态代理。
Java反射常用类

Java反射常用类

Class类

Java在运行的时候,系统会对所有的对象进行所谓的1运行时类型标识,用来保存这些信息就是Class类。Class类封装了一个对象和接口运行时的状态。

JVM为每种类型管理着独一无二的Class对象。也就是说,每一个类型都有一个Class对象。Java程序在运行时,当需要创建某个类的实例,JVM会首先加载的类的Class对象是否存在,如果还不存在,JVM会根据类名查找对应的字节码文件并加载,接着创建对应的Class对象,最后创建这个类的实例。

所有具有相同类型和数组度共享该Class对象。

经过上面的分析,可以看出运行中的类和接口在JVM中都会有一个对应的Class对象存在,它保存了对应类和接口的类型信息。要想获取类和接口相应信息,就必须先获取Class对象。

创建class对象
CeShi c  = new CeShi();
        Class c1 = c.getClass();//调用Class类的getClass方法
        System.out.println(c1.toString());
        try {
            Class c2 = Class.forName("java.lang.String");//通过forName去获取与之字符串对应的Class对象

            System.out.println(c2.toString());
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Class c3 = CeShi.class;//直接通过类型.class获取该类型的Class对象。
        System.out.println(c3.toString());
Class对象的使用(获取方法、字段、构造函数、参数类型等信息)

这里写图片描述

这里写图片描述

    try {
            Class c = Class.forName("java.util.ArrayList");
            String packagename = c.getPackage().getName();//获取该Class对象的包的实体全限定名
            System.out.println(packagename);
            int mod = c.getModifiers();//获取类的修饰符,返回是一个整数表示修饰符的值。
            String modifier = Modifier.toString(mod);//将对应的修饰符值换成修饰符字符串
            System.out.println(modifier);
            String classname = c.getName();//获取类的全限定名
            System.out.println(classname);
            Class superclass = c.getSuperclass();//获取Class对象直接父类对象
            System.out.println(superclass);
            Class[] interfaces = c.getInterfaces();//获取Class对象的实现的Class对象数组
            for(Class class1:interfaces) {
                System.out.println(class1.getName());//获取每一个接口Class对象的全限定名
            }
            Field[] fields = c.getDeclaredFields();//获取该类public字段的对象数组集合
            for(Field field:fields) {
                if(field.getType().isArray()) {
                    System.out.println(field.getType().getComponentType().getName()+"[]");//数组类型需要特别处理
                }
                System.out.println(field.getType());//获取字段的类型
                System.out.println(field.getName());//获取字段名字

            }
            Constructor[] constructors = c.getDeclaredConstructors();//获取该类Class对象所有构造器的对象数组
            for(Constructor constructor:constructors) {
                String constructorname = constructor.getName();//获取每一个构造器的名字
                String moder = Modifier.toString(constructor.getModifiers());//获取构造器的修饰符
                Class[] c1 = constructor.getParameterTypes();//获取构造器所有构造方法中所有参数类型的Class对象数组
                for(Class classname1:c1) {
                    if(classname1.isArray()) {
                        String type = classname1.getComponentType().getName()+"[]";//获取参数类型是数组的
                        System.out.println(type);
                    }
                    System.out.println(classname1.getName());//获取非数组类型的参数类型
                }
            }
                Method[] methods = c.getDeclaredMethods();//获取所有方法的对象数组。
                for(Method method :methods) {
                    String moder1 = Modifier.toString(method.getModifiers());//获取方法的修饰符
                    System.out.println(moder1);
                    Class methodreturn =method.getReturnType();//获取方法的返回值类型的Class对象
                    String type = null;
                    if(methodreturn.isArray()) {
                         type = methodreturn.getComponentType().getName()+"[]";//获取方法的返回值的类型
                    }else {
                         type =  methodreturn.getName();//返回非数组类型
                    }
                    System.out.println(type);
                    Class[] methodclass  = method.getParameterTypes();
                    String method1type  =null;
                    for(Class methodtype:methodclass) {
                        if(methodtype.isArray()) {
                             method1type = methodreturn.getComponentType().getName()+"[]";//获取方法的参数的类型
                        }else {
                             method1type = methodtype.getName();//返回非数组的参数类型
                        }
                        System.out.println(method1type);
                    }
                }

        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
动态创建对象

在之前绝大部分创建都是通过new,因为在编译期间,就已经知道他们的对应类的名称,可是,如果当我们需要在运行期间来创建对象,就需要用到反射。

创建对象的两种方式
  1. 无参构造
    try {
            Class c = Class.forName("java.util.Date");
            Date d =  (Date) c.newInstance();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
  1. 使用构造参数

第一步:获取指定类的Class对象。
第二步:通过Class对象获取满足指定参数类型要求的后遭函数方法类对象。
第三步:调用指定的Constructor对象的newInstance方法,传入对应参数作对象。

  try {
        Class c = Class.forName("java.util.Date");
        Constructor constructor = c.getConstructor(long.class);
        Date date = (Date) constructor.newInstance(12356);
        System.out.println(date);
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (SecurityException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InstantiationException 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();
    }
调用方法

使用反射就可以取得指定方法的对象代表,根据对象代表可以去获取方法,在通过invoke,执行该方法。需要invoke返回值就是该方法的返回值。

代码:

Class c;
            try {
                c = Class.forName("com.example.test.Test22");
                Test22 t = (Test22) c.newInstance();//获取对象代表
                Method m = c.getDeclaredMethod("show", String.class);//根据方法名来获取方法,第二个参数是方法的参数类型。
                m.invoke(t, "我是反射调用的方法!");//执行方法,第一个参数是作用的对象,后面是方法的参数。
                //上面的方法执行只能用于方法是public,用private修饰就不可以的,所以有了下面的方法。

                Method m1 = c.getDeclaredMethod("show1",String.class);
                m1.setAccessible(true);//取消访问检查,如果不加这一句直接访问private方法,会报错
                //Class com.example.test.Test23 can not access a member of class com
                //.example.test.Test22 with modifiers "private"
                m1.invoke(t,"我是反射调用的private的方法");

            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (SecurityException 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();
访问成员变量的值

通过它的getXXX方法来获取字段的值,通过setXXX方法来动态修改指定对象的值。

代码:

                Method m1 = c.getDeclaredMethod("show1",String.class);
                m1.setAccessible(true);//取消访问检查,如果不加这一句直接访问private方法,会报错
                //Class com.example.test.Test23 can not access a member of class com
                //.example.test.Test22 with modifiers "private"
                m1.invoke(t,"我是反射调用的private的方法");
                Field f = c.getDeclaredField("name");//根据字段名来获取字段
                f.set(t, "laoqiang");//通过set去改变set、get
                System.out.println(t.getName());
                Field f1 = c.getDeclaredField("age");
                f1.setAccessible(true);//取消字段访问检查
                f1.setInt(t, 23);
                System.out.println(f1.getInt(t));//t就是创建的对象。
数组

数组也是一个对象,可以通过反射来查看各个数组的属性信息。

            int[] a = new int[5];
            System.out.println(a.getClass().getComponentType().getName());
            Object c = Array.newInstance(int.class, 5);//创建数组对象,
            //直接使用Array的newInstance,第一个是数组的类型,第二个是数组的大小。
            for(int i = 0;i<5;i++) {
                Array.set(c, i, i*3);//给数组设置值,也是通过set,第一个参数是作用的对象,
                //第一个是数组的位置,第三个是值
            }
            for(int i = 0;i<5;i++) {
            System.out.println(Array.getInt(c, i));
            }
反射与动态代理

代理模式是Java中很常见的一种的设计模式,在企业应用高级框架大量用到,它的原理主要是反射。

代理原理图

静态代理

一个客户不想或者不能直接引用另一个对象,需要通过代理来间接操作目标对象,代理就是在客户端和目标对象之间起中介作用。

代码如下:

//客户的要求

package com.example.test;

public interface ClothFactory {
   void makeCloth();//客户的要求
}

//中介、代理


public class ClothProxy implements ClothFactory {
    //为什么要实现这个接口,你必须要具备用户的要求。
    private Company c;//定义你寻找的真正能实现客户要求的公司。


    public ClothProxy(Company c) {
        super();
        this.c = c;
    }


    @Override
    public void makeCloth() {
        // TODO Auto-generated method stub
        c.makeCloth();//委托给真正的公司去做衣服
    }

}

//真正执行力的公司


public class Company implements ClothFactory {
//为什么要实现这个接口,你必须要具备用户的要求。
    @Override
    public void makeCloth() {
        // TODO Auto-generated method stub
        System.out.println("我生产好了");
    }

}
动态代理

原理:就是利用反射机制在运行时动态创建目标对象的代理对象。

动态代理的实现:


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynaProxyHandler implements InvocationHandler {
  private Object target;
  //这个构造方法是创建代理类的实例
    public Object newProxyInstance(Object target) {
        this.target = target;
        /**
         * 第一个参数是:定义代理类的类加载器,比如在这,我传入一个object对象,这是会由这个对象的ClassLoader对象来对生成的代理对象进行加载。
         * 第二个参数是:代理类要实现的接口,表示的是我将要给我需要代理的对象提供了什么接口,如果我们实现了一组接口,那么这个代理对象就对外宣称实现该接口,这样就能调用接口中的方法
         * 第三个参数是:指派方法调用的调用处理程序,需要传入一个InvocationHandler对象,表示的是当我这个代理对象在调用方法的时候,会关联到哪一个invocationHandler对象,一般这个对象就是实现InvocationHandler接口的类   
         */
        return Proxy.newProxyInstance(this.target.getClass().getClassLoader(),this.target.getClass().getInterfaces(),this);

    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        Object result  = null;
        //这里可以添加在执行代理类方法之前所要执行的代码
        result =  method.invoke(this.target,args);
        //这里可以添加在执行代理类方法之后所要执行的代码
        return result;
    }

}

测试代码如下:

        DynaProxyHandler dph = new DynaProxyHandler();
        ClothFactory c = (ClothFactory) dph.newProxyInstance(new Company());//
        c.makeCloth();//这个调用方法涉及到多态,父类是接口,调用时实现其接口的方法。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.1 Java语言发展简史2 1.2 认识Java语言3 1.2.1 Java语言特性3 1.2.2 JavaApplet4 1.2.3 丰富的类库4 1.2.4 Java的竞争对手5 1.2.5 Java在应用领域的优势7 1.3 Java平台的体系结构7 1.3.1 JavaSE标准版8 1.3.2 JavaEE企业版10 1.3.3 JavaME微型版11 1.4 JavaSE环境安装和配置12 1.4.1 什么是JDK12 1.4.2 JDK安装目录和实用命令工具介绍12 1.4.3 设置环境变量13 1.4.4 验证配置的正确性14 1.5 MyEcilpse工具介绍JavaSE环境安装和配置15 1.6 本章练习16 第2章 2.1 什么是程序18 2.2 计算机中的程序18 2.3 Java程序19 2.3.1 Java程序中的类型19 2.3.2 Java程序开发三步曲21 2.3.3 开发Java第一个程序21 2.3.4 Java代码中的注释23 2.3.5 常见错误解析24 2.4 Java类库组织结构和文档27 2.5 Java虚拟机简介28 2.6 Java技术两种核心运行机制29 2.7 上机练习30 第3章 3.1 变量32 3.1.1 什么是变量32 3.1.2 为什么需要变量32 3.1.3 变量的声明和赋值33 3.1.4 变量应用实例33 3.2 数据的分类34 3.2.1 Java中的八种基本数据类型34 3.2.2 普及二进制36 3.2.3 进制间转换37 3.2.4 基本数据类型间转换38 3.2.5 数据类型应用实例38 3.2.6 引用数据类型39 3.3 关键字.标识符.常量39 3.3.1 变量命名规范39 3.3.2 经验之谈-常见错误的分析与处理40 3.3.3 Java标识符命名规则41 3.3.4 关键字42 3.3.5 常量42 3.4 运算符43 3.4.1 算术运算符43 3.4.2 赋值操作符45 3.4.3 关系操作符47 3.4.4 逻辑操作符48 3.4.5 位操作符49 3.4.6 移位运算符49 3.4.7 其他操作符50 3.5 表达式52 3.5.1 表达式简介52 3.5.2 表达式的类型和值52 3.5.3 表达式的运算顺序52 3.5.4 优先级和结合性问题52 3.6 选择结构54 3.6.1 顺序语句54 3.6.2 选择条件语句54 3.6.3 switch结构59 3.6.4 经验之谈-常见错误的分析与处理65 3.6.5 Switch和多重if结构比较66 3.7 循环语句66 3.7.1 While循环67 3.7.2 经验之谈-常见while错误70 3.7.3 do-while循环72 3.7.4 for循环74 3.7.5 经验之谈-for常见错误76 3.7.6 循环语句小结78 3.7.7 break语句79 3.7.8 continue语句82 3.8 JavaDebug技术84 3.9 本章练习85 第4章 4.1 一维数组90 4.1.1 为什么要使用数组90 4.1.2 什么是数组91 4.1.3 如何使用数组92 4.1.4 经验之谈-数组常见错误97 4.2 常用算法98 4.2.1 平均值,最大值,最小值98 4.2.3 数组排序102 4.2.3 数组复制103 4.3 多维数组105 4.3.1 二重循环105 4.3.2 控制流程进阶107 4.3.3 二维数组111 4.4 经典算法113 4.4.1 算法-冒泡排序113 4.4.2 插入排序115 4.5 增强for循环116 4.6 本章练习117 第5章 5.1 面向过程的设计思想120 5.2 面向对象的设计思想120 5.3 抽象121 5.3.1 对象的理解121 5.3.2 Java抽象思想的实现122 5.4 封装124 5.4.1 对象封装的概念理解124 5.4.2 类的理解125 5.4.3 Java类模板创建125 5.4.4 Java中对象的创建和使用127 5.5 属性130 5.5.1 属性的定义130 5.5.2 变量131 5.6 方法132 5.6.1 方法的定义132 5.6.2 构造方法135 5.6.4 方法重载138 5.6.5 自定义方法138 5.6.6 系统提供方法139 5.6.7 方法调用140 5.6.8 方法参数及其传递问题144 5.6.9 理解main方法语法及命令行参数147 5.6.1 0递归算法147 5.7 this关键字148 5.8 JavaBean149 5.9 包150 5.9.1 为什么需要包?150 5.9.2 如何创建包151 5.9.3 编译并生成包:151
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值