zoukankan      html  css  js  c++  java
  • 黑马程序员——JAVA基础之反射

     
    ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
     
     
    Java 反射是Java语言的一个很重要的特征,它使得Java具体了“动态性”。
     
     
    反射(Reflection)其实就是动态加载一个指定的类,并获取该类中的所有的内容,而且将字节码文件封装成对象,并将字节码文件中的内容都封装成对象,这样便于操作这些成员,简单说
    反射技术可以对一个类进行解剖
     
     
    反射大大的增强了程序的扩展性
     
     
    Java 反射机制主要提供了以下功能:
     
    在运行时判断任意一个对象所属的类。
    在运行时构造任意一个类的对象。
    在运行时判断任意一个类所具有的成员变量和方法。
    在运行时调用任意一个对象的方法。
     
     

    反射的基本步骤:

    1、获得Class对象,就是获取到指定的名称的字节码文件对象

    2、实例化对象,获得类的属性、方法或构造函数

    3、访问属性、调用方法、调用构造函数创建对象

     

    class类:

    所有的类文件都有共同属性,所以可以向上抽取,把这些共性内容封装成一个类,这个类就叫Class。Class类中就包含属性有field(字段)、method(方法)、construction(构造函数)。

     

    Class和class区别:

    1、class:Java中的类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则由此类的实例对象确定,不同的实例对象有不同的属性值。

    2、Class:指的是Java程序中的各个Java类是属于同一类事物,都是Java程序的类,这些类称为Class。Class是Java程序中各个Java类的总称;它是反射的基石,通过Class类来使用反射。

     

     

    获取Class对象,有三种方式:

    1、通过每个对象都具备的方法getClass来获取,弊端:必须要创建该类对象,才可以调用getClass方法

    2、每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class,弊端:必须要先明确该类

         前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成

    3、使用的Class类中的方法,静态的forName方法

         指定什么类名,就获取什么类字节码文件对象,这种方式的扩展性最强,只要将类名的字符串传入即可

     

         //根据给定的类名来获得  用于类加载
         String classname ="cn.itcast.reflect.Person";// 来自配置文件
         Class clazz =Class.forName(classname);// 此对象代表Person.class
         //如果拿到了对象,不知道是什么类型   用于获得对象的类型
         Object obj = new Person();
         Class clazz1 =obj.getClass();// 获得对象具体的类型
         //如果是明确地获得某个类的Class对象  主要用于传参
         Class clazz2 = Person.class; 

     

     

    在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中:
    Class类:代表一个类。
    Field 类:代表类的成员变量(成员变量也称为类的属性)。
    Method类:代表类的方法。
    Constructor 类:代表类的构造方法。
    Array类:提供了动态创建数组,以及访问数组的元素的静态方法。

     

     

    Class类中常用的方法:

    static Class forName(String className):1、返回与给定字符串名的类或接口的相关联的Class对象。

    Class getClass():返回的是Object运行时的类,即返回Class对象即字节码对象

    Constructor getConstructor():返回Constructor对象,它反映此Class对象所表示的类的指定公共构造方法。

    Field getField(String name):返回一个Field对象,它表示此Class对象所代表的类或接口的指定公共成员字段。

    Field[] getFields():返回包含某些Field对象的数组,表示所代表类中的成员字段。

    Method getMethod(String name,Class… parameterTypes):返回一个Method对象,它表示的是此Class对象所代表的类的指定公共成员方法。

    Method[] getMehtods():返回一个包含某些Method对象的数组,是所代表的的类中的公共成员方法。

    String getName():以String形式返回此Class对象所表示的实体名称。

    String getSuperclass():返回此Class所表示的类的超类的名称

    boolean isArray():判定此Class对象是否表示一个数组

    boolean isPrimitive():判断指定的Class对象是否是一个基本类型。

    T newInstance():创建此Class对象所表示的类的一个新实例。

     

     

    class Person {
        private String name;
        public int age;
        public Person(){
            System.out.println("Person run");
        }
        public Person(String name,int age){
            this.age = age;
            this.name = name;
        }
        public String toString(){
            return name+":"+age;
        }
    }
    public class ClassDemo{
        public static void main(String[] args) throws Exception {
            // 获取类的全名称字符串
            String className = "cn.itheima.Person";
            
            // 获取Person类的Class对象
            Class clazz = Class.forName(className);
    
            // 通过newInstance方法获取类的无参构造函数实例
            Person p = (Person)clazz.newInstance();
        }
    }


    Constructor类

       Constructor代表某个类的一个构造方法。

     

     

    Constructor构造方法

       1、得到这个类的所有构造方法:  Constructor[] cons = Class.forName (“java.lang.String”) . getConstructors ();

       2、得到某一个构造方法:  Constructor con = String.class.getConstructor (String.class , int.class);

     

     

    创建实例对象

    通常方式:String str = new String ( new StringBuffer(“abc”));

    反射方式:String str = (String) constructor.newInstance ( new StringBuffer (“abc”));

       注:

       (1)创建实例时newInstance方法中的参数列表必须与获取Constructor的方法getConstructor方法中的参数列表一致。

       (2)newInstance():构造出一个实例对象,每调用一次就构造一个对象。

       (3)利用Constructor类来创建类实例的好处是可以指定构造函数,而Class类只能利用无参构造函数创建类实例对象。

     


     

    class Person {
        private String name;
        public int age;
        public Person(){
            System.out.println("Person run");
        }
        public Person(String name,int age){
            this.age = age;
            this.name = name;
        }
        public String toString(){
            return name+":"+age;
        }
    }
    //通过Constructor对象来创建类实例方法
    public static void ConstructorDemo() throws Exception{
        //获取Person类的Class对象
        String name = "cn.itheima.Person";
        Class clazz = Class.forName (name);
        //Class clazz = Person.class;
        //获取指定构造函数的类实例
        Constructor con = clazz.getConstructor (String.class , int.class);
        Person p = (Person) con.newInstance("lisi",30);
        System.out.println (p.toString());
    }


    Field类
                Field类代表某个类中一个成员变量

     

    Field方法

       1、Field getField(String s):只能获取公有和父类中公有

       2、Field getDeclaredField(String s):获取该类中任意成员变量,包括私有

       3、setAccessible(ture)://如果是私有字段,要先将该私有字段进行取消权限检查的能力。也称暴力访问。

       4、set(Object obj, Object value):将指定对象变量上此Field对象表示的字段设置为指定的新值。

       5、Object get(Object obj):返回指定对象上Field表示的字段的值。

     
    class Person {
        private String name;
        public int age;
        public Person(){
            System.out.println("Person run");
        }
        public Person(String name,int age){
            this.age = age;
            this.name = name;
        }
        public String toString(){
            return name+":"+age;
        }
    }
    //获取Person对象的成员变量
    public static void getPersonField() throws Exception{
    //如果想要给该变量赋值,必须先要有对象。
        Class clazz = Class.forName("cn.itheima.Person");
        Person p = (Person)clazz.newInstance();
        //获取所有的成员变量
        Field[] fs = clazz.getFields();
        for(Field f:fs){
            System.out.println(f);
        }
        //获取指定的成员变量
        Field fage = clazz.getField("age");
        Field fname = clazz.getDeclaredField("name");
        //显示改变后的值
        fage.set(p, 20);
        System.out.println(fage.get(p));
        //暴力访问私有变量
        fname.setAccessible(true);
        fname.set(p, "zhangsan");
        System.out.println(fname.get(p));
    }

    Method类
                  Method类代表某个类中成员方法。调用某个对象身上的方法,要先得到方法,再针对某个对象调用。

     

    Method方法

       Method[] getMethods():只获取公共和父类中的方法。

       Method[] getDeclaredMethods():获取本类中包含私有。

       Method getMethod("方法名",参数.class(如果是空参可以写null));

       Object invoke(Object obj ,参数):调用方法

       注:① invoke方法:如果底层是静态的,那么可以忽略指定的obj参数,填充为null。② 如果底层方法所需的“形式参数”为0,则所提供的args数组长度可以为0或null。

     

    获取类中方法

       1、通常方式:对象名.函数。如str.charAt(int index);

       2、反射方式:

       Method MethodCharAt = Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);

       3、反射调用底层方法:charAtMethod.invoke(str, int index);

       说明:如果传递给Method对象的invoke()方法的第一个参数为null,说明Method对象对应的是一个静态方法

     
     
    class Person {
        private String name;
        public int age;
        public Person(){
            System.out.println("Person run");
        }
        public Person(String name,int age){
            this.age = age;
            this.name = name;
        }
        public String toString(){
            return name+":"+age;
        }
    }
    public static void getPersonMethod() throws Exception{
        //如果想要获取方法,必须先要有对象。
        Class clazz = Class.forName("cn.itheima.Person");
        //获取所有方法
        Method[] methods = clazz.getMethods();//只获取公共的和父类中的。
        //methods = clazz.getDeclaredMethods();//获取本类中所有的方法,包含私有。
        for(Method method : methods){
            System.out.println(method);
        }
        //获取单个方法
        Method me=clazz.getMethod("toString", null);
        Object returnVaule=me.invoke(p, null);
        System.out.println(returnVaule);
    }

    数组的反射
     

    具有相同维数和元素类型的数组属于同一个类型,具有相同的Class实例对象。数组字节码的名字:有[和数组对应类型的缩写,如int[]数组的名称为:[I

    基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。

     

    如何得到某个数组中的某个元素的类型?

       例如:Object[] obj=new Object[]{“ABC”,1};无法得到某个数组的具体类型,只能得到其中某个元素的类型,如:Obj[0].getClass().getName() 得到的是java.lang.String。

     

    Arrays.asList()方法,在处理int[]是String[]时,是不同的。它会将int[]整体作为一个对象元素存入list集合。而会将String[]数组中的每一个字符串元素当做对象存入list集合。

    Array工具类(java.lang.reflect.Array)用于完成对数组的反射操作,它提供了动态创建和访问Java数组的方法:如

         Array.getLength(Object obj);//获取数组的长度

         Array.get(Object obj,int x);//获取数组中的元素

     
    import java.lang.reflect.Array;
    import java.util.Arrays;
    public class ArrayReflect {
        public static void main(String[] args) {
            int [] a1 = new int[]{1,2,3};
            int[][] a2 = new int[2][3];
            String [] a3 = new String[]{"a","b","c"};
            Object obj1 = a1;
            Object obj2 = a3;
            Object obj3 = a4;
            //Array工具类用于完成对数组的反射操作。如打印任意数值
            printObject(a1);
            printObject(a4);
            printObject("abc");
    }
        //打印任意数值
        private static void printObject(Object obj) {
            Class clazz = obj.getClass();
            //如果传入的是数组,则遍历
            if(clazz.isArray()){
                int len = Array.getLength(obj);//Array工具类获取数组长度方法
                for(int x = 0;x<len;x++){
                    System.out.println(Array.get(obj, x));//Array工具获取数组元素
                }
            }
            else
                System.out.println(obj);
        }
    }

     
     
    反射的作用——实现框架功能
     
    框架:通过反射调用Java类的一种方式。框架如同地产商建造房子,门窗和空调等等内部装修都由用户自己安装。房子就是框架,用户需使用此框架,安好门窗等放入到房地产商提供的框架中。框架和工具类的区别在于工具类被用户类调用,而框架是调用用户提供的类。

     

    框架要解决的核心问题

       我们在写框架的时候,调用的类还未出现,那么框架无法知道要被调用的类名,所以在程序中无法直接new某个类的实例对象,而要通过反射来做。

     

    简单框架的创建步骤:

       1、创建一个配置文件,在Eclipse的java项目面板中:

       “右键”——“New”——选择“File“,file name命名为config.properties,然后写入配置信息。如className=java.util.ArrayList,等号右边是配置键,右边是值。

       2、代码实现,加载此文件:

       ① 将文件读取到读取流中,要写出配置文件的路径(相对路径或绝对路径)。如:InputStream ips = new FileInputStream(“config.properties”);

       ② 用Properties类的load()方法将流中的数据装载入集合。

       ③ 关闭流资源:关闭的是读取流,因为流中的数据已经加载进内存。

       3、通过getProperty()方法获取className,即配置的值,也就是某个类名。

       4、用反射的方式,创建对象newInstance()。

       5、执行程序主体功能

     

    ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

  • 相关阅读:
    利用Flask中的werkzeug.security模块加密
    logging
    python后端架构(转)
    访问一个网页的全过程
    微服务优缺点
    HTTP协议通信原理 与常见报错信息
    SSH与ansible 部署方法与核心模块简介
    linux 普通用户批量创建账户与密码
    三次握手与4次挥手简单说明
    sed 命令简介
  • 原文地址:https://www.cnblogs.com/runwind/p/4212172.html
Copyright © 2011-2022 走看看