• 黑马程序员——Java基础---反射Class类、Constructor类、Field类


    ------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! ------- 

                            反射的应用场景

    一、概述

    反射技术:

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

            简单一句话:反射技术可以对类进行解剖。因此反射技术很强大!

    二、应用场景

            以实际来说,主板上的要预留显卡、声卡等的插槽,提高了二者间的独立性。如果我们要更新声卡,则不需要将主板扔掉,从而提高了复用性,而程序中常用的作法,会提供一个配置文件,来供以后实现此程序的类来扩展功能。对外提供配置文件,让后期出现的子类直接将类名字配置到配置文件中即可。该应用程序直接读取配置文件中的内容。并查找和给定名称相同的类文件。进行如下操作:

            1)加载这个类。

            2)创建该类的对象。

            3)调用该类中的内容。

           应用程序使用的类不确定时,可以通过提供配置文件,让使用者将具体的子类存储到配置文件中。然后该程序通过反射技术,对指定的类进行内容的获取。

            好处:反射技术大大提高了程序的扩展性。

                                 反射涉及的对象

    一、概述

            反射就是把Java类中的各种成分映射成相应的java类。

            例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示。就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。

            一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。

    二、反射的基石——Class类

    1、所有的类文件都有共同属性,所以可以向上抽取,把这些共性内容封装成一个类,这个类就叫Class(描述字节码文件的对象)。

             Class类中就包含属性有field(字段)、method(方法)、construction(构造函数)。

            而field中有修饰符、类型、变量名等复杂的描述内容,因此也可以将字段封装称为一个对象。用来获取类中field的内容,这个对象的描述叫Field。同理方法和构造函数也被封装成对象Method、Constructor。要想对一个类进行内容的获取,必须要先获取该字节码文件的对象。该对象是Class类型。

            Class类描述的信息:类的名字,类的访问属性,类所属于的包名,字段名称的列表,方法名称的列表等。每一个字节码就是class的实例对象。如:classcls=Data.class;

    小知识:什么叫字节码?

            当源程序中用到类时,首先要从硬盘把这个类的那些二进制代码,一个类编译成class放在硬盘上以后,就是一些二进制代码,要把这些二进制代码加载到内存中里面来,再用这些字节码去复制出一个一个对象来。

    2、Class和class的区别

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

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

    3、获取Class对象的三种方式

            加载XX.class文件进内存时就被封装成了对象,该对象就是字节码文件对象。如何获取Class对象呢?

    方式一:

            通过对象的getClass方法进行获取。

            如:Class clazz=new Person().getClass();//Person是一个类名

            麻烦之处:每次都需要具体的类和该类的对象,以及调用getClass方法。

    方式二:

            任何数据类型都具备着一个静态的属性class,这个属性直接获取到该类型的对应Class对象。

            如:Class clazz=Person.class;//Person是一个类名

            比第一种较为简单,不用创建对象,不用调用getClass方法,但是还是要使用具体的类,和该类中的一个静态属性class完成。

    方式三:

            这种方式较为简单,只要知道类的名称即可。不需要使用该类,也不需要去调用具体的属性和行为。就可以获取到Class对象了。

            如:Class clazz=Class.forName("包名.Person");//Person是一个类名

            这种方式仅知道类名就可以获取到该类字节码对象的方式,更有利于扩展。

    注:

            1、九个预定义的Class:

                    1)包括八种基本类型(byte、short、int、long、float、double、char、boolean)的字节码对象和一种返回值为void类型的void.class。

                    2)Integer.TYPE是Integer类的一个常量,它代表此包装类型包装的基本类型的字节码,所以和int.class是相等的。基本数据类型的字节码都可以用与之对应的包装类中的TYPE常量表示

            2、只要是在源程序中出现的类型都有各自的Class实例对象,如int[].class。数组类型的Class实例对象,可以用Class.isArray()方法判断是否为数组类型的。

    4、Class类中的方法

            static Class forName(String className)

            返回与给定字符串名的类或接口的相关联的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对象所表示的类的一个新实例。

    5、通过Class对象获取类实例

            通过查看API我们知道,Class类是没有构造方法的, 因此只能通过方法获取类实例对象。之前我们用的已知类,创建对象的做法:

            1)查找并加载XX.class文件进内存,并将该文件封装成Class对象。

            2)再依据Class对象创建该类具体的实例。

            3)调用构造函数对对象进行初始化。

                 如:Person p=new Person();

     现在用Class对象来获取类实例对象的做法:

            1)查找并加载指定名字的字节码文件进内存,并被封装成Class对象。

            2)通过Class对象的newInstance方法创建该Class对应的类实例。

            3)调用newInstance()方法会去使用该类的空参数构造函数进行初始化。

                 如:

                         String className="包名.Person";

                         Class clazz=Class.forName(className);

                         Object obj=clazz.newInstance();

     1 //Person类
     2 package cn.itheima;//将网址域名倒过来,当做包名 一般不会重复
     3 
     4 public class Person {
     5     private String name;
     6     public int age;
     7     public Person(){
     8         System.out.println("Person is run");
     9     }
    10     public Person(String name,int age)//
    11     {
    12         this.age=age;
    13         this.name=name;
    14     }
    15     
    16     public String toString()//复写输出方法
    17     {
    18         return name+":"+age;
    19     }
    20 }
    21 //示例
    22 package cn.itheima;
    23 
    24 public class CreateClassDemo {
    25     public static void main(String[] args) throws Exception
    26     {
    27         createPersonClass();
    28     }
    29     //通过Class对象创建类实例方法
    30     public static void createPersonClass() throws Exception
    31     {
    32         //获取Person类的Class对象
    33         String className="cn.itheima.Person";
    34         Class clazz=Class.forName(className);
    35         //通过newInstance方法获取类的无参构造函数实例
    36         Person p=(Person)clazz.newInstance();
    37     }
    38 }

    三、Constructor类

    1、概述

            如果指定的类中没有空参数的构造函数,或者要创建的类对象需要通过指定的构造函数进行初始化。这时怎么办呢?这时就不能使用Class类中的newInstance方法了。既然要通过指定的构造函数进行对象的初始化。就必须先获取这个构造函数——Constructor。Constructor代表某个类的构造方法。

    2、获取构造方法:

            1)得到这个类的所有构造方法:如得到上面示例中Person类的所有构造方法

                  Constructor[] cons = Class.forName(“cn.itheima.Person”).getConstructors();

            2)获取某一个构造方法:

                  Constructor con=Person.class.getConstructor(String.class,int.class);

    3、创建实例对象:

            1)通常方式:Person p = new Person(“lisi”,30);

             2)反射方式:Person p= (Person)con.newInstance(“lisi”,30);

    注:

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

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

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

    示例:

     1 package cn.itheima;
     2 
     3 public class CreateClassDemo 
     4 {
     5     public static void main(String[] args) throws Exception
     6     {
     7         createPersonClass_2();
     8     }
     9 
    10 public static void createPersonClass_2() throws Exception//通过Constructor对象来创建类实例方法
    11     {
    12         //获取Person类的Class对象
    13         String className="cn.itheima.Person";
    14         Class clazz=Class.forName(className);
    15         //Class clazz=Person.class;
    16             
    17         //获取指定构造函数的类实例
    18         Constructor con=clazz.getConstructor(String.class,int.class);
    19         Person p=(Person) con.newInstance("lisi",30);
    20         System.out.println(p.toString());
    21     }
    22 }

    四、Field类

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

    2、方法

           Field getField(String s);//只能获取公有和父类中公有

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

            setAccessible(ture);

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

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

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

    示例:

     1 public static void getPersonField() throws Exception//获取Person对象的成员变量
     2 {    
     3     //如果想要给该变量赋值,必须先要有对象。
     4     Class clazz=Class.forName("cn.itheima.Person");
     5     Person p=(Person)clazz.newInstance();
     6         
     7     //获取所以的成员变量
     8     Field[] fs=clazz.getFields();
     9     for(Field f:fs)
    10     {
    11         System.out.println(f);
    12     }
    13         
    14     //获取指定的成员变量
    15     Field fage=clazz.getField("age");
    16     Field fname=clazz.getDeclaredField("name");
    17         
    18     //显示改变后的值
    19     fage.set(p, 20);
    20     System.out.println(fage.get(p));
    21         
    22     //暴力访问私有变量
    23     fname.setAccessible(true);
    24     fname.set(p, "zhangsan");
    25     System.out.println(fname.get(p));
    26 }

    四、Field类

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

    2、方法

           Field getField(String s);//只能获取公有和父类中公有

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

            setAccessible(ture);

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

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

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

    示例:

     1 //获取Person对象的成员变量
     2 public static void getPersonField() throws Exception{    
     3 //如果想要给该变量赋值,必须先要有对象。
     4     Class clazz=Class.forName("cn.itheima.Person");
     5     Person p=(Person)clazz.newInstance();
     6         
     7     //获取所以的成员变量
     8     Field[] fs=clazz.getFields();
     9     for(Field f:fs){
    10         System.out.println(f);
    11     }
    12         
    13     //获取指定的成员变量
    14     Field fage=clazz.getField("age");
    15     Field fname=clazz.getDeclaredField("name");
    16         
    17     //显示改变后的值
    18     fage.set(p, 20);
    19     System.out.println(fage.get(p));
    20         
    21     //暴力访问私有变量
    22     fname.setAccessible(true);
    23     fname.set(p, "zhangsan");
    24     System.out.println(fname.get(p));
    25 }
  • 相关阅读:
    WPF 下两种图片合成或加水印的方式(转载)
    Git reset与checkout的区别
    C# 串口导致电脑蓝屏一个可能的原因
    两个用于win7任务栏显示进度的dll
    批量远程执行shell命令工具
    基于zookeeper的主备切换方法
    代码的演变随记
    svn错误:Can't convert string from 'UTF-8' to native encoding
    not allowed to access to crontab because of pam configuration
    大数据利器
  • 原文地址:https://www.cnblogs.com/ktlshy/p/4716838.html
走看看 - 开发者的网上家园