Java 反射机制

前言

现今在大家营造和煦或小卖部的门类中,或多或少都会凭仗几个流行非常的屌的第3方库,比方:Butter KnifeRetrofit 2Dagger 2GreenDao等,倘诺您没用过,那你供给找时间补一下呀;一时在选用后大家会惊讶他们到底是怎么产生这种简洁、高效、松耦合等许多亮点的,当然这里作者不追究它们具体怎么落到实处的
(能够看看自家前边写的几篇小说)
,而关心的是它们都用到同样的技能那正是本篇所讲的反射注解,并促成的注重性注入。

翻阅本篇作品有助于你更加好的接头那一个大形框架的原理和复习Java的知识点。为啥要把反射放在前方讲吧,实际上是因为大家学习申明的时候需求选择反射机制,所以,先读书反射有助于精晓前面包车型客车知识。

反射(Reflection)是Java 程序开荒语言的特征之一,它同意运维中的 Java
程序获得自己的音信,并且能够操作类或对象的内部属性  

何以是反光

Java 反射是Java语言的四个很主要的特点,它使得Java具体了“动态性”。

反射重假若指程序能够访问、检查评定和修改它本人景况或行为的1种力量。在计算机科学领域,反射是一类使用,它们可以自描述和自决定。那类应用通过某种机制来兑现对自个儿行为的叙述和检查实验,并能依照自个儿作为的图景和结果,调节或退换应用所讲述行为的景况和连锁的语义。

在Java中的反射机制,被称呼Reflection。(大家看看那个单词,第三个主见应该正是去支付文书档案中搜一下了。)它同意运行中的Java程序对自己实行反省,并能直接操作程序的里边属性或艺术。Reflection机制允许程序在正在施行的经过中,利用Reflection
APIs获得别的已著名称的类的在那之中国国投息,包含:package、 type parameters、
superclass、 implemented interfaces、 inner classes、 outer classes、
田野同志s、 constructors、 methods、
modifiers等,并能够在实施的进度中,动态生成Instances、改换田野(field)s内容或滋生methods。

JAVA反射

一言九鼎是指程序能够访问,检查测试和修改它本人情形或作为的壹种力量,并能依据自个儿作为的景色和结果,调解或涂改应用所描述行为的图景和有关的语义。

 

Java 反射机制重要提供了以下职能

  • 在运作时判定狂妄二个指标所属的类。

  • 在运作时组织任意一个类的靶子。

  • 在运作时判定任性3个类所享有的成员变量和方法。

  • 在运作时调用任性几个目的的艺术。

反射机制是什么

面试有相当大可能率会问到,这句话不管你能或不能够了解,不过你只要记住就足以了

反射机制正是在运行意况中,对此自由一个类,都可以理解这些类的装有属性和艺术;对于自由3个目的,都能够调用它的大肆1个方法和属性;这种动态获取的音讯以及动态调用对象的诀要的效益称为java语言的反光机制。

用一句话总计就是反光可以兑未来运行时能够清楚随机四个类性情和办法

与反射有关的类包.

简单来说利用

  1. 透过Class类获取成员变量、成员方法、接口、超类、构造方法等
  • 运维时复制对象

  • 用反射机制调用对象的办法

  • 动态创设和走访数组

  • 运作时更换田野内容

反射机制能做怎么着

反射机制入眼提供了以下职能:

  • 在运营时推断大四一个指标所属的类;

  • 在运作时组织狂妄八个类的对象;

  • 在运行时推断大4三个类所全数的分子变量和艺术;

  • 在运维时调用任意1个目的的方法;

  • 生成动态代理(ps:那个知识点也很关键,后续会为大家讲到)

java.lang.reflect.*;

Java 反射

Java 反射机制的利用场景

  • 逆向代码 ,比方反编译

  • 与申明相结合的框架 比如Retrofit

  • 只是的反射机制应用框架 比如伊夫ntBus

  • 动态生成类框架 譬如Gson

java.lang.Class;

核心类,位于java.lang.reflect包中

  • Class类:代表八个类。

  • Field 类:代表类的成员变量(成员变量也称为类的习性)。

  • Method类:代表类的法子。

  • Constructor 类:代表类的构造方法。

  • Array类:提供了动态创制数组,以及走访数组的成分的静态方法。

反射机制的优点与缺点

何以要用反射机制?间接创设对象不就能够了呢,这就事关到了动态与静态的概念

  • 静态编写翻译:在编写翻译时规定项目,绑定对象,即经过。

  • 动态编写翻译:运营时规定项目,绑定对象。动态编写翻译最大限度发挥了java的灵活性,体现了多态的行使,有以降低类之间的藕合性。

优点

  • 可以兑现动态创立对象和编写翻译,展现出相当大的狡猾,特别是在J二EE的支出中它的八面玲珑就显现的格外显眼。比方,一个重型的软件,不容许叁遍就把把它设计的很完善,当以此程序编写翻译后,发表了,当发掘要求立异有些意义时,我们不恐怕要用户把以前的卸载,再重新安装新的版本,若是这样的话,那一个软件肯定是绝非多少人用的。选用静态的话,须求把全副程序重新编译叁遍才得以兑现效益的翻新,而选拔反射机制以来,它就能够绝不卸载,只须要在运作时才动态的创导和编译,就可以落成该成效。

缺点

  • 对品质有震慑。使用反射基本上是一种解释操作,大家能够告诉JVM,大家期待做什么并且它满意我们的需求。那类操作总是慢于只一贯实行同1的操作。

 

核心 API

在 java.lang.Object
类中定义了getClass()方法,因而对此自由一个Java对象,都得以通过此方法得到对象的等级次序。

知情Class类和类品种

想要驾驭反射首先知道一下Class类,它是反光完毕的基础。
类是java.lang.Class类的实例对象,而Class是全部类的类(There is a class
named Class)
对此常见的靶子,大家一般都会那样创立和表示:

Code code1 = new Code();

地方说了,全数的类都以Class的指标,那么哪些表示呢,可以还是不可以由此如下情势啊:

Class c = new Class();

可是我们查阅Class的源码时,是如此写的:

private  Class(ClassLoader loader) { 
    classLoader = loader; 
}

能够看看构造器是个体的,唯有JVM能够创设Class的指标,由此不可能像普通类一样new二个Class对象,固然大家不可能new一个Class对象,然而却得以透过已某个类获得一个Class对象,共有三种情势,如下:

Class c1 = Code.class; 这说明任何一个类都有一个隐含的静态成员变量class,这种方式是通过获取类的静态成员变量class得到的
Class c2 = code1.getClass(); code1是Code的一个对象,这种方式是通过一个类的对象的getClass()方法获得的 
Class c3 = Class.forName("com.trigl.reflect.Code"); 这种方法是Class类调用forName方法,通过一个类的全量限定名获得

那边,c一、c二、c三都以Class的指标,他们是截然1致的,而且有个学名,叫做Code的类类型(class
type)。
那边就让人竟然了,后边不是说Code是Class的靶子呢,而c一、c二、c三也是Class的对象,那么Code和c一、c二、c3不就1律了吗?为啥还叫Code什么类品种?这里并非纠结于它们是或不是1律,只要领会类类型是干什么的就好了,从名称想到所包含的意义,类品种正是类的门类,也便是讲述一个类是什么,都有哪些东西,所以我们得以经过类类型知道一个类的性质和艺术,并且能够调用二个类的习性和方式,那正是反光的底蕴。

举个轻便例子代码:

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //第一种:Class c1 = Code.class;
        Class class1=ReflectDemo.class;
        System.out.println(class1.getName());

        //第二种:Class c2 = code1.getClass();
        ReflectDemo demo2= new ReflectDemo();
        Class c2 = demo2.getClass();
        System.out.println(c2.getName());

        //第三种:Class c3 = Class.forName("com.trigl.reflect.Code");
        Class class3 = Class.forName("com.tengj.reflect.ReflectDemo");
        System.out.println(class3.getName());
    }
}

实践结果:

com.tengj.reflect.ReflectDemo
com.tengj.reflect.ReflectDemo
com.tengj.reflect.ReflectDemo

上面来看反射到底能做怎么样:

获取类的全体名字

  • public String getName() :得到类的欧洲经济共同体名字。

Java反射相关操作

在此处先看一下sun为大家提供了那些反射机制中的类:
java.lang.Class;
java.lang.reflect.Constructor; java.lang.reflect.Field;
java.lang.reflect.Method;
java.lang.reflect.Modifier;

后面我们知道了怎么获取Class,那么大家能够透过这几个Class干什么吧?
总计如下:

  • 获得成员方法Method

  • 获取成员变量Field

  • 收获构造函数Constructor

上边来具体介绍

 

赢得构造方法

  • Constructor getConstructor(Class[] params)
    据书上说构造函数的参数,重回1个切实可行的具备public属性的构造函数

  • Constructor getConstructors() 再次回到全体具备public属性的构造函数数组

  • Constructor getDeclaredConstructor(Class[] params)
    依据构造函数的参数,再次来到2个切实可行的构造函数(不分public和非public属性)

  • Constructor getDeclaredConstructors()
    归来该类中全部的构造函数数组(不分public和非public属性)

一. 得到成员方法音信

七个参数分别是方法名和办法参数类的类类型列表。

public Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 得到该类所有的方法,不包括父类的 
public Method getMethod(String name, Class<?>... parameterTypes) // 得到该类所有的public方法,包括父类的

//具体使用
Method[] methods = class1.getDeclaredMethods();//获取class对象的所有声明方法 
Method[] allMethods = class1.getMethods();//获取class对象的所有public方法 包括父类的方法 
Method method = class1.getMethod("info", String.class);//返回次Class对象对应类的、带指定形参列表的public方法 
Method declaredMethod = class1.getDeclaredMethod("info", String.class);//返回次Class对象对应类的、带指定形参列表的方法

举个例证:

比如说类A有如下贰个格局:

public void fun(String name,int age) {
        System.out.println("我叫"+name+",今年"+age+"岁");
    }

目前驾驭A有三个对象a,那么就足以经过:

Class c = Class.forName("com.tengj.reflect.Person");  //先生成class
Object o = c.newInstance();                           //newInstance可以初始化一个实例
Method method = c.getMethod("fun", String.class, int.class);//获取方法
method.invoke(o, "tengj", 10);                         //通过invoke调用该方法,参数第一个为实例对象,后面为具体参数值

完整代码如下:

public class Person {
    private String name;
    private int age;
    private String msg="hello wrold";
 public String getName() {
        return name;
  }

    public void setName(String name) {
        this.name = name;
  }

    public int getAge() {
        return age;
  }

    public void setAge(int age) {
        this.age = age;
  }

    public Person() {
    }

    private Person(String name) {
        this.name = name;
  System.out.println(name);
  }

    public void fun() {
        System.out.println("fun");
  }

    public void fun(String name,int age) {
        System.out.println("我叫"+name+",今年"+age+"岁");
  }
}

public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.tengj.reflect.Person");
            Object o = c.newInstance();
            Method method = c.getMethod("fun", String.class, int.class);
            method.invoke(o, "tengj", 10);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

进行结果:

我叫tengj,今年10岁

什么,是或不是深感很屌,我们假如知道这么些类的门道全称就会讥讽它于鼓掌之间。

一时大家想猎取类中兼有成员方法的消息,要如何是好。能够透过以下几步来贯彻:

1.获取具有办法的数组:

Class c = Class.forName("com.tengj.reflect.Person");
Method[] methods = c.getDeclaredMethods(); // 得到该类所有的方法,不包括父类的
或者:
Method[] methods = c.getMethods();// 得到该类所有的public方法,包括父类的

二.然后循环那么些数组就收获各种方法了:

for (Method method : methods)

一体化代码如下:
person类跟上边同样,这里以及背后就不贴出来了,只贴关键代码

public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.tengj.reflect.Person");
            Method[] methods = c.getDeclaredMethods();
            for(Method m:methods){
                String  methodName= m.getName();
                System.out.println(methodName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

施行结果:

getName
setName
setAge
fun
fun
getAge

此间借使把c.getDeclaredMethods();改成c.getMethods();实行结果如下,多了成都百货上千艺术,感到把Object里面包车型大巴秘籍也打字与印刷出来了,因为Object是全数类的父类:

getName
setName
getAge
setAge
fun
fun
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
  1. 获取Class对象

    1).Object.getCalss();
    2).Class.forName(String)
      (String的写法:包名.类名.就会创建包名.类名对应的那个对象)
      ( 注:1.2只适用于引用类型)
    3).封装类.TYPE
      返回对应的基本类型的Class对象.
      Integer.TYPE对应的是int的Class对象
      注:3只适用于基本类型
    4).类型.Class。
      注:4是通用的
    
    上面的4种方法,只有方法2是动态的,只要换一个包就可以了.它具有动态潜质.所以真正意义的想体现动态编程只能使用方法2.
    eg:
    
     1 package com.company;
     2 
     3 public class Main {
     4     public static void main(String[] args) throws Exception {
     5         Person person = new Person("batty");
     6         /* clazz 是储存Person类内容(constructor、field、method等)的类
     7                  也就是说,Class是储存类信息的类
     8         */
     9         Class clazz = Class.forName("com.company.Person");
    10         System.out.println("person.getClass(): " + person.getClass());  // class com.company.Person
    11         System.out.println("clazz.getClass(): " + clazz.getClass());  // class java.lang.Class
    12         System.out.println("clazz.getName(): " + clazz.getName());  // com.company.Person
    13         System.out.println("clazz.getClass().getName(): " + clazz.getClass().getName());  // java.lang.Class
    14         System.out.println("person.getClass(): "+ person.getClass());  // class com.company.Person
    15         System.out.println("Class.forName(\"Person\"): " + Class.forName("com.company.Person"));  // class com.company.Person
    16         System.out.println("String.TYPE " + Integer.TYPE);  // int
    17         System.out.println("person.class: " + Person.class);  // class com.company.Person
    18     }
    19 }
    20 class Person{
    21     String name;
    22     Person(String name) {
    23         this.name = name;
    24     }
    25 }
    

     

  2. 获得类中的构造函数

    [获取构造<根据参数类型>](使用时一般用不带declared的,即蓝色字体)
     Constructor<T> getConstructor(Class<?>... parameterTypes) 
          返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。 
     Constructor<?>[] getConstructors() 
          返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。 
     Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 
          返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。 
     Constructor<?>[] getDeclaredConstructors() 
          返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。
    

     具体使用例子:利用反射调用构造函数

     1 package Reflect;
     2 import java.lang.reflect.Constructor;
     3 class Demo() {
     4     String name;
     5     Demo() {}
     6     Demo(String name) {
     7         this.name = name;
     8     }
     9     @Override
    10     String toString(){
    11         return "["+this.name+"]";
    12     }
    13 }
    14 public class test() {
    15     public static void main(String[] args) {
    16         Class<?> test = null;
    17         try {
    18             test = Class.forName(Reflect.Demo);
    19         } catch (Exception e) {
    20             e.printStackTrace();
    21         }
    22         Demo d1 = null;
    23         Demo d2 = null;
    24         Constructor<?> cons[]=test.getConstructors();
    25         try {
    26             d1=(Demo)cons[0].newInstance();
    27             d2=(Demo)cons[1].newInstance("TestDemo");
    28         } catch(Exception e){
    29             e.printStackTrace();
    30         }
    31         System.out.println(d1);
    32         System.out.println(d2);
    33     }
    34 }
    

     

  3. 取得类中的成员函数(method)

    [获取方法<方法名加上参数类型>](使用时一般用不带declared的,即蓝色字体)
     Method getMethod(String name, Class<?>... parameterTypes) 
          返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 
     Method[] getMethods() 
          返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。 
     Method getDeclaredMethod(String name, Class<?>... parameterTypes) 
          返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。 
     Method[] getDeclaredMethods() 
          返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 
    
    具体使用例子:利用反射调用方法
    
     1 package Reflect;
     2 import java.lang.reflect.Constructor;
     3 class Demo() {
     4     String name;
     5     Demo() {}
     6     Demo(String name) {
     7         this.name = name;
     8     }
     9     void demoTest(String name) {
    10         System.out.println(this.name);
    11     }
    12     @Override
    13     String toString(){
    14         return "["+this.name+"]";
    15     }
    16 }
    17 public class test() {
    18     public static void main(String[] args) {
    19         Class<?> test = null;
    20         try {
    21             test = Class.forName(Reflect.Demo);
    22         } catch (Exception e) {
    23             e.printStackTrace();
    24         }
    25          try{
    26             //调用Person类中的sayChina方法
    27             Method method = test.getMethod("demoTest");
    28             method.invoke(test.newInstance());
    29         }catch (Exception e) {
    30             e.printStackTrace();
    31         }
    32     }
    33 }
    

     

  4. 获得类中的成员变量(田野)

    [获取属性<根据属性名>](使用时一般用是带declared的,即蓝色字体,因为属性一般都是私有的)
    Field getField(String name) 
          返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。 
     Field[] getFields() 
          返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。 
     Field getDeclaredField(String name) 
          返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 
     Field[] getDeclaredFields() 
          返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
    

    •有declared的措施是帮忙个体,不过不援助承继,无declared的不二等秘书籍支持承接,不援救个体,且只好抽取public的事物。

    •因而取属性的时候一般的话是带declared的,因为属性一般都以私有的,取方法时一般是不带declared的,取构造时相似也是不带declared的.

     

    实际使用例子:利用反射获取字段田野同志

     1 package Reflect;
     2 import java.lang.reflect.Constructor;
     3 class Demo() {
     4     String name;
     5     Demo() {}
     6     Demo(String name) {
     7         this.name = name;
     8     }
     9     @Override
    10     String toString(){
    11         return "["+this.name+"]";
    12     }
    13 }
    14 public class test() {
    15     public static void main(String[] args) {
    16         Class<?> test = null;
    17         try {
    18             test = Class.forName(Reflect.Demo);
    19         } catch (Exception e) {
    20             e.printStackTrace();
    21         }
    22         // 取得本类的全部属性
    23         Field[] field = test.getDeclaredFields();
    24         for (int i = 0; i < field.length; i++) {
    25             // 权限修饰符
    26             int mo = field[i].getModifiers();
    27             String priv = Modifier.toString(mo);
    28             // 属性类型
    29             Class<?> type = field[i].getType();
    30             System.out.println(priv + " " + type.getName() + " "
    31                     + field[i].getName() + ";");
    32         }
    33     }
    34 }
    

     

获得类的分子方法

  • Method getMethod(String name, Class[] parameterTypes)
    依附方法名和参数,重临1个有血有肉的装有public属性的点子

  • Method[] getMethods() 回来全部具备public属性的诀窍数组

  • Method getDeclaredMethod(String name, Class[] params)
    据书上说方法名和参数,重回1个有血有肉的艺术(不分public和非public属性)

  • Method[] getDeclaredMethods()
    回到该类中的全部的方法数组(不分public和非public属性),不包括承继来的法子

一. 到手成员变量音讯

想一想成员变量中都包罗怎么样:分子变量类型+成员变量名

类的积极分子变量也是四个对象,它是java.lang.reflect.Field的1个对象,所以大家通过java.lang.reflect.Field其间封装的主意来赢得这一个音信。

单独赢得某些成员变量,通过Class类的以下措施实现:

参数是成员变量的名字

public Field getDeclaredField(String name) // 获得该类自身声明的所有变量,不包括其父类的变量
public Field getField(String name) // 获得该类自所有的public成员变量,包括其父类变量

//具体实现
Field[] allFields = class1.getDeclaredFields();//获取class对象的所有属性 
Field[] publicFields = class1.getFields();//获取class对象的public属性 
Field ageField = class1.getDeclaredField("age");//获取class指定属性 
Field desField = class1.getField("des");//获取class指定的public属性

比方:

比方二个类A有如下成员变量:

private int n;

借使A有多少个指标a,那么就能够这样得到其成员变量:

Class c = a.getClass();
Field field = c.getDeclaredField("n");

完整代码如下:

public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.tengj.reflect.Person");
            //获取成员变量
            Field field = c.getDeclaredField("msg"); //因为msg变量是private的,所以不能用getField方法
            Object o = c.newInstance();
            field.setAccessible(true);//设置是否允许访问,因为该变量是private的,所以要手动设置允许访问,如果msg是public的就不需要这行了。
            Object msg = field.get(o);
            System.out.println(msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

实施结果:

hello wrold

同一,如若想要获取具备成员变量的音信,能够通过以下几步

一.获得具有成员变量的数组:

Field[] fields = c.getDeclaredFields();

二.遍历变量数组,获得某些成员变量田野同志

for (Field field : fields)

总体代码:

public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.tengj.reflect.Person");
            Field[] fields = c.getDeclaredFields();
            for(Field field :fields){
                System.out.println(field.getName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

推行结果:

name
age
msg

 写个博客写了差不五个月了,今后才纪念把那篇写完发布出去,笔者的首先篇博客就那样诞生了。。。。。。。。。。。。。。。。。。

猎取类的成员变量(成员属性)

  • Field getField(String name)
    传说变量名,再次来到一个具体的具备public属性的分子变量

  • Field[] getFields() 回去具备public属性的成员变量的数组

  • Field getDeclaredField(String name)
    依据变量名,重返三个分子变量(不分public和非public属性)

  • Field[] getDelcaredField()
    回去全体成员变量组成的数组(不分public和非public属性)

一. 到手构造函数

末尾再想壹想构造函数中都包罗哪些:构造函数参数
同上,类的成构造函数也是1个目的,它是java.lang.reflect.Constructor的3个目的,所以我们透过java.lang.reflect.Constructor中间封装的方法来获得那个音信。

独立赢得某些构造函数,通过Class类的以下办法完毕:

本条参数为构造函数参数类的类类型列表

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //  获得该类所有的构造器,不包括其父类的构造器
public Constructor<T> getConstructor(Class<?>... parameterTypes) // 获得该类所以public构造器,包括父类

//具体
Constructor<?>[] allConstructors = class1.getDeclaredConstructors();//获取class对象的所有声明构造函数 
Constructor<?>[] publicConstructors = class1.getConstructors();//获取class对象public构造函数 
Constructor<?> constructor = class1.getDeclaredConstructor(String.class);//获取指定声明构造函数 
Constructor publicConstructor = class1.getConstructor(String.class);//获取指定声明的public构造函数

举个例证:

譬如说类A有如下1个构造函数:

public A(String a, int b) {
    // code body
}

那便是说就足以经过:

Constructor constructor = a.getDeclaredConstructor(String.class, int.class);

来得到那么些构造函数。

全部代码:

public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.tengj.reflect.Person");
            //获取构造函数
            Constructor constructor = c.getDeclaredConstructor(String.class);
            constructor.setAccessible(true);//设置是否允许访问,因为该构造器是private的,所以要手动设置允许访问,如果构造器是public的就不需要这行了。
            constructor.newInstance("tengj");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

实行结果:

tengj

瞩目:Class的newInstance方法,只可以创设只包涵无参数的构造函数的类,假如某类只有带参数的构造函数,那么将在选取其余一种方法:

fromClass.getDeclaredConstructor(String.class).newInstance("tengj");

获得具备的构造函数,可以由此以下步骤实现:

1.收获该类的具有构造函数,放在多个数组中:

Constructor[] constructors = c.getDeclaredConstructors();

贰.遍历构造函数数组,得到有些构造函数constructor:

for (Constructor constructor : constructors)

完整代码:

public class ReflectDemo {
    public static void main(String[] args){
            Constructor[] constructors = c.getDeclaredConstructors();
            for(Constructor constructor:constructors){
                System.out.println(constructor);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

实施结果:

public com.tengj.reflect.Person()
public com.tengj.reflect.Person(java.lang.String)

 

获得类、属性、方法的修饰域

类Class、Method、Constructor、Field都有三个public方法int
getModifiers()。该措施重返1个int类型的数,表示被修饰对象( Class、
Method、 Constructor、 Field )的梳洗类型的组合值。

//打印输出方法的修饰域

int mod = methods[i].getModifiers();

System.out.print(Modifier.toString(mod) + "");

一. 别的艺术

讲解要求使用的

Annotation[] annotations = (Annotation[]) class1.getAnnotations();//获取class对象的所有注解 
Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);//获取class对象指定注解 
Type genericSuperclass = class1.getGenericSuperclass();//获取class对象的直接超类的 
Type Type[] interfaceTypes = class1.getGenericInterfaces();//获取class对象的所有接口的type集合

获得class对象的新闻

boolean isPrimitive = class1.isPrimitive();//判断是否是基础类型 
boolean isArray = class1.isArray();//判断是否是集合类
 boolean isAnnotation = class1.isAnnotation();//判断是否是注解类 
boolean isInterface = class1.isInterface();//判断是否是接口类 
boolean isEnum = class1.isEnum();//判断是否是枚举类 
boolean isAnonymousClass = class1.isAnonymousClass();//判断是否是匿名内部类 
boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//判断是否被某个注解类修饰 
String className = class1.getName();//获取class名字 包含包名路径 
Package aPackage = class1.getPackage();//获取class的包信息 
String simpleName = class1.getSimpleName();//获取class类名 
int modifiers = class1.getModifiers();//获取class访问权限 
Class<?>[] declaredClasses = class1.getDeclaredClasses();//内部类 
Class<?> declaringClass = class1.getDeclaringClass();//外部类

getSuperclass():获取某类的父类  
getInterfaces():获取某类实现的接口

创立类的三个实例

// 利用newInstance()方法,获取构造方法的实例

Object obj = cls.newInstance();

// Class的newInstance方法,仅提供默认无参的实例化方法,类似于无参的构造方法

// Constructor的newInstance方法,提供了带参数的实例化方法,类似于含参的构造方法

Constructor ct = cls.getConstructor(null);

Object obj = ct.newInstance(null);

透过反射精晓会集泛型的实质

庞大的知识点,驾驭就足以了。后续会为我们写一篇关于泛型的篇章。

首先下定论:

Java中集中的泛型,是防御错误输入的,只在编写翻译阶段有效,绕过编写翻译到了运维期就不算了。

上面通过贰个实例来表达:

/**
 * 集合泛型的本质
 */
public class GenericEssence {
    public static void main(String[] args) {
        List list1 = new ArrayList(); // 没有泛型 
        List<String> list2 = new ArrayList<String>(); // 有泛型


        /*
         * 1.首先观察正常添加元素方式,在编译器检查泛型,
         * 这个时候如果list2添加int类型会报错
         */
        list2.add("hello");
//      list2.add(20); // 报错!list2有泛型限制,只能添加String,添加int报错
        System.out.println("list2的长度是:" + list2.size()); // 此时list2长度为1


        /*
         * 2.然后通过反射添加元素方式,在运行期动态加载类,首先得到list1和list2
         * 的类类型相同,然后再通过方法反射绕过编译器来调用add方法,看能否插入int
         * 型的元素
         */
        Class c1 = list1.getClass();
        Class c2 = list2.getClass();
        System.out.println(c1 == c2); // 结果:true,说明类类型完全相同

        // 验证:我们可以通过方法的反射来给list2添加元素,这样可以绕过编译检查
        try {
            Method m = c2.getMethod("add", Object.class); // 通过方法反射得到add方法
            m.invoke(list2, 20); // 给list2添加一个int型的,上面显示在编译器是会报错的
            System.out.println("list2的长度是:" + list2.size()); // 结果:2,说明list2长度增加了,并没有泛型检查
        } catch (Exception e) {
            e.printStackTrace();
        }

        /*
         * 综上可以看出,在编译器的时候,泛型会限制集合内元素类型保持一致,但是编译器结束进入
         * 运行期以后,泛型就不再起作用了,即使是不同类型的元素也可以插入集合。
         */
    }
}

实行理并了结果:

list2的长度是:1
true
list2的长度是:2

调用方法

  • public Object invoke(Object obj, Object… args)
    调用静态方法时,第二个参数为 null

  • public void setAccessible(boolean flag) 能够更动私有方法的权限

思考导图

带动驾驭上述所讲的知识点

图片 1

开展阅读
Java反射机制深远详解 – 金星十一郎 –
博客园
Java反射入门 – Trigl的博客 –
CSDN博客
Java反射机制 – 一块胸部肌肉 –
天涯论坛
Java 反射机制浅析 – 孤旅者 –
腾讯网
反射机制的接头及其用途 – 天天提高一丝丝! –
ITeye博客
Java动态代理与反射详解 – 浩大王 –
新浪

原理

java虚拟机有一个运营时数据区,这几个数据区又被分成方法区,堆区和栈区,大家这里须求明白的第三是方法区。方法区的要害功能是积存棉被服装载的类的类型音讯,当java虚拟机装载有些项目标时候,需求类装载器定位相应的class文件,然后将其读入到java虚拟机中,紧接着虚拟机提取class中的类型音信,将这个新闻存款和储蓄到方法区中。这个音讯首要包蕴:

  • 本条类别的全限定名

  • 其1项指标一直超类的全限定名

  • 本条项目是类类型依旧接口类型

  • 其一类型的走访修饰符

  • 此外直接超接口的全限定名的不改变列表

  • 该品种的常量池

  • 字段消息

  • 方式音讯

  • 除去常量以外的持有类变量

  • 3个到class类的引用

JAVA注解

应用

概念及效率

  1. 概念
  • 解说即元数据,就是源代码的元数据
  • 讲解在代码中增加音讯提供了1种情势化的秘诀,能够在持续中更便利的
    使用这么些数量
  • Annotation是壹种选用于类、方法、参数、变量、构造器及包表明中的特殊修饰符。它是一种由JS本田CR-V-175规范选取用来叙述元数据的1种工具。
  1. 作用
  • 浮动文书档案
  • 盯住代码正视性,实现替代配置文件功用,减弱配置。如Spring中的一些讲解
  • 在编写翻译时实行格式检查,如@Override等
  • 每当你创建描述符性质的类照旧接口时,一旦中间包括重复性的行事,就可以设想动用注明来简化与自动化该进度。

分析 Json 格式数据,并利用反射创造对应对象

什么是java注解?

在java语法中,使用@标识作为起头,并在@前边紧跟表明名。被运用于类,接口,方法和字段之上,举个例子:

@Override
void myMethod() { 
......
}

那其间@Override便是注脚。这么些评释的作用也正是报告编写翻译器,myMethod()方法覆写了父类中的myMethod()方法。

动用反射调用私有方法

import java.lang.reflect.Constructor;

import java.lang.reflect.Method;

public class LoadMethodEx {

/**

* 在运行时加载指定的类,并调用指定的方法

* @param cName            Java的类名

* @param MethodName    方法名

* @param params        方法的参数值

* @return

*/

public Object Load(String cName, String MethodName, Object[] params) {

Object retObject = null;

try {

// 加载指定的类

Class cls = Class.forName(cName);    // 获取Class类的对象的方法之二

// 利用newInstance()方法,获取构造方法的实例

// Class的newInstance方法只提供默认无参构造实例

// Constructor的newInstance方法提供带参的构造实例

Constructor ct = cls.getConstructor(null);

Object obj = ct.newInstance(null);

//Object obj = cls.newInstance();

// 根据方法名获取指定方法的参数类型列表

Class paramTypes[] = this.getParamTypes(cls, MethodName);

// 获取指定方法

Method meth = cls.getMethod(MethodName, paramTypes);

meth.setAccessible(true);

// 调用指定的方法并获取返回值为Object类型

retObject = meth.invoke(obj, params);

} catch (Exception e) {

System.err.println(e);

}

return retObject;

}

/**

* 获取参数类型,返回值保存在Class[]中

*/

public Class[] getParamTypes(Class cls, String mName) {

Class[] cs = null;

/*

* Note: 由于我们一般通过反射机制调用的方法,是非public方法

* 所以在此处使用了getDeclaredMethods()方法

*/

Method[] mtd = cls.getDeclaredMethods();

for (int i = 0; i < mtd.length; i++) {

if (!mtd[i].getName().equals(mName)) {    // 不是我们需要的参数,则进入下一次循环

continue;

}

cs = mtd[i].getParameterTypes();

}

return cs;

}

}

参考:
http://www.cnblogs.com/crazypebble/archive/2011/04/13/2014582.html
http://lavasoft.blog.51cto.com/62575/43218

java中置放的注释

java中有两个放置的申明:

  • @Override:代表最近的诀要定义将掩盖超类中的方法,要是出现错误,编写翻译器就能够报错。

  • @Deprecated:假设采用此证明,编写翻译器会见世警示消息。

  • @SuppressWarnings:不经意编写翻译器的警告音讯。

本文不在演说二种内置注明的采纳剧情和方法,感兴趣的请看这里

元注解

自定义注脚的时候利用的,也正是自定义注明的讲解;(这句话小编自个儿说的,不知道对不对)

元注解的功效正是担当注脚别的注脚。Java5.0概念了四个正经的meta-annotation类型,它们被用来提供对其他annotation类型作表达。

Java5.0概念的多个元注脚:

  1. @Target

  2. @Retention

  3. @Documented

  4. @Inherited

java八加了五个新注明,后续笔者会讲到。

那个项目和它们所支持的类在java.lang.annotation包中得以找到。

@Target

@Target表明了Annotation所修饰的目的限定:Annotation可被用来
packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和地面变量(如循环变量、catch参数)。在Annotation类型的注脚中使用了target可进一步清楚其修饰的对象。

效用:用于描述注脚的运用范围(即:被描述的表明能够用在怎么着地点)

取值(ElementType)有:

类型 用途
CONSTRUCTOR 用来描述构造器
FIELD 用于描述域
LOCAL_VARIABLE 用来描述局地变量
METHOD 用于描述方法
PACKAGE 用以描述包
PARAMETER 用于描述参数
TYPE 用以描述类、接口(包涵表明类型) 或enum评释

比如说那一个注清热示只幸而措施中动用:

@Target({ElementType.METHOD})
public @interface MyCustomAnnotation {

}

//使用
public class MyClass {
   @MyCustomAnnotation
   public void myMethod()
   {
    ......
   }
}

@Retention

@Retention定义了该Annotation被保存的小时长度:有些Annotation仅出现在源代码中,而被编写翻译器甩掉;而另一些却被编写翻译在class文件中;编写翻译在class文件中的Annotation大概会被虚拟机忽略,而另1部分在class被装载时将被读取(请小心并不影响class的施行,因为Annotation与class在应用上是被分其余)。使用那一个meta-Annotation能够对
Annotation的“生命周期”限制。

意义:表示须求在什么样等级保存该注释音信,用于描述表明的生命周期(即:被描述的讲解在怎么范围内有效)

取值(RetentionPoicy)有:

类型 用途 说明
SOURCE 在源文件中有效(即源文件保留) 仅出现在源代码中,而被编译器丢弃
CLASS 在class文件中有效(即class保留) 被编译在class文件中
RUNTIME 在运行时有效(即运行时保留) 编译在class文件中

运用示例:

/***
 * 字段注解接口
 */
@Target(value = {ElementType.FIELD})//注解可以被添加在属性上
@Retention(value = RetentionPolicy.RUNTIME)//注解保存在JVM运行时刻,能够在运行时刻通过反射API来获取到注解的信息
public @interface Column {
    String name();//注解的name属性
}

@Documented

@Documented用于描述此外类型的annotation应该被当做被标记的程序成员的公共API,由此得以被举个例子javadoc此类的工具文书档案化。Documented是二个标志评释,未有成员。

职能:将评释包蕴在javadoc中

示例:

java.lang.annotation.Documented
@Documented
public @interface MyCustomAnnotation { //Annotation body}

@Inherited

  • 是二个符号注脚

  • 阐释了某些被标注的等级次序是被接续的

  • 运用了@Inherited修饰的annotation类型被用来多少个class,则那么些annotation将被用来该class的子类
    @Inherited
    annotation类型是被注解过的class的子类所承袭。类并不从落到实处的接口继承annotation,方法不从它所重载的诀要承继annotation

  • 当@Inherited
    annotation类型标记的annotation的Retention是RetentionPolicy.RUNTIME,则反射API巩固了这种传承性。假如大家利用java.lang.reflect去询问3个@Inherited
    annotation类型的annotation时,反射代码检查将展开职业:检查class和其父类,直到发掘钦命的annotation类型被开掘,恐怕到达类承袭结构的顶层。

意义:允许子类继承父类中的注脚

示范,这里的MyParentClass
使用的笺注标明了@Inherited,所以子类可以持续这几个申明消息:

java.lang.annotation.Inherited
@Inherited
public @interface MyCustomAnnotation {
}

@MyCustomAnnotation
public class MyParentClass { 
  ... 
}

public class MyChildClass extends MyParentClass { 
   ... 
}

自定义评释

格式

public @interface 注解名{
  定义体
}

批注参数的可扶助数据类型:

  • 享有骨干数据类型(int,float,double,boolean,byte,char,long,short)
  • String 类型
  • Class类型
  • enum类型
  • Annotation类型
  • 上述全体品类的数组

规则

  • 修饰符只可以是public 或暗中同意(default)
  • 参数成员只可以用基本类型byte,short,int,long,float,double,boolean三种为主项目和String,Enum,Class,annotations及这几个项目标数组
  • 举个例子只有三个参数成员,最佳将名称设为”value”
  • 注解成分必须有鲜明的值,能够在讲授中定义私下认可值,也能够使用注明时钦点,非宗旨项指标值不得为null,常采用空字符串或0作暗许值
  • 在展现2个成分存在或缺点和失误的意况时,定义一下相当值来表示,如空字符串或负值

示例:

/**
 * test注解
 * @author ddk
 *
 */ 
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestAnnotation {
    /**
     * id
     * @return
     */
    public int id() default -1;
    /**
     * name
     * @return
     */
    public String name() default "";
}

注明管理器类库

java.lang.reflect.AnnotatedElement

Java使用Annotation接口来代表先后成分前边的讲解,该接口是具有Annotation类型的父接口。除了这一个之外,Java在java.lang.reflect
包下新扩大了AnnotatedElement接口,该接口代表先后中尚可申明的先后成分,该接口首要有如下多少个落实类:

  • Class:类定义
  • Constructor:构造器定义
  • Field:累的分子变量定义
  • Method:类的艺术定义
  • Package:类的包定义

java.lang.reflect
包下重要包罗部分落到实处反射作用的工具类,实际上,java.lang.reflect
包全体提供的反射API扩充了读取运营时Annotation新闻的技能。当一个Annotation类型被定义为运维时的Annotation后,该申明工夫是运转时可知,当class文件棉被服装载时被保留在class文件中的Annotation才会被虚拟机读取。

AnnotatedElement
接口是全部程序成分(Class、Method和Constructor)的父接口,所以程序通过反射获取了有些类的AnnotatedElement对象之后,程序就足以调用该对象的如下八个个方式来拜访Annotation新闻:

  • 办法一:<T extends Annotation> T getAnnotation(Class<T>
    annotationClass):
    再次回到改程序成分上存在的、钦点项指标笺注,假如该类型阐明不存在,则赶回null。
  • 方法2:Annotation[]
    getAnnotations():再次来到该程序元素上设有的持有表明。
  • 艺术三:boolean is AnnotationPresent(Class<?extends Annotation>
    annotationClass):剖断该程序成分上是或不是包涵钦命项指标注释,存在则赶回true,不然重返false.
  • 方法4:Annotation[]
    getDeclaredAnnotations():再次来到直接存在于此成分上的持有注释。与此接口中的别的方法不一样,该方法将忽略承袭的批注。(如果没有注释直接存在于此成分上,则赶回长度为零的3个数组。)该格局的调用者能够Infiniti制改换重临的数组;那不会对任何调用者再次回到的数组发生其余影响。

表明管理器示例:

/***********注解声明***************/

/**
 * 水果名称注解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
    String value() default "";
}

/**
 * 水果颜色注解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
    /**
     * 颜色枚举
     * @author peida
     *
     */
    public enum Color{ BULE,RED,GREEN};

    /**
     * 颜色属性
     * @return
     */
    Color fruitColor() default Color.GREEN;

}

/**
 * 水果供应者注解
 * @author peida
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
    /**
     * 供应商编号
     * @return
     */
    public int id() default -1;

    /**
     * 供应商名称
     * @return
     */
    public String name() default "";

    /**
     * 供应商地址
     * @return
     */
    public String address() default "";
}

/***********注解使用***************/

public class Apple {

    @FruitName("Apple")
    private String appleName;

    @FruitColor(fruitColor=Color.RED)
    private String appleColor;

    @FruitProvider(id=1,name="陕西红富士集团",address="陕西省西安市延安路89号红富士大厦")
    private String appleProvider;

    public void setAppleColor(String appleColor) {
        this.appleColor = appleColor;
    }
    public String getAppleColor() {
        return appleColor;
    }

    public void setAppleName(String appleName) {
        this.appleName = appleName;
    }
    public String getAppleName() {
        return appleName;
    }

    public void setAppleProvider(String appleProvider) {
        this.appleProvider = appleProvider;
    }
    public String getAppleProvider() {
        return appleProvider;
    }

    public void displayName(){
        System.out.println("水果的名字是:苹果");
    }
}

/***********注解处理器***************/
//其实是用的反射


public class FruitInfoUtil {
    public static void getFruitInfo(Class<?> clazz){

        String strFruitName=" 水果名称:";
        String strFruitColor=" 水果颜色:";
        String strFruitProvicer="供应商信息:";

        Field[] fields = clazz.getDeclaredFields();

        for(Field field :fields){
            if(field.isAnnotationPresent(FruitName.class)){
                FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
                strFruitName=strFruitName+fruitName.value();
                System.out.println(strFruitName);
            }
            else if(field.isAnnotationPresent(FruitColor.class)){
                FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
                strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
                System.out.println(strFruitColor);
            }
            else if(field.isAnnotationPresent(FruitProvider.class)){
                FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
                strFruitProvicer=" 供应商编号:"+fruitProvider.id()+" 供应商名称:"+fruitProvider.name()+" 供应商地址:"+fruitProvider.address();
                System.out.println(strFruitProvicer);
            }
        }
    }
}

/***********输出结果***************/
public class FruitRun {

    /**
     * @param args
     */
    public static void main(String[] args) {

        FruitInfoUtil.getFruitInfo(Apple.class);

    }

}

====================================
 水果名称:Apple
 水果颜色:RED
 供应商编号:1 供应商名称:陕西红富士集团 供应商地址:陕西省西安市延安路89号红富士大厦

Java 八 中注明新天性

  • @Repeatable
    元注解,表示被修饰的讲明能够用在同1个注明式恐怕项目充分五个同样的笺注(包蕴不一样的属性值)

  • @Native 元证明,当地点法

  • java八 中Annotation 能够被用在别的利用 Type 的位置

  //初始化对象时
 String myString = new @NotNull String();
 //对象类型转化时
 myString = (@NonNull String) str;
 //使用 implements 表达式时
 class MyList<T> implements @ReadOnly List<@ReadOnly T>{
 ...
 }
 //使用 throws 表达式时
 public void validateValues() throws @Critical ValidationFailedException{
 ...
 }

思想导图

图片 2

张开阅读

长远领会Java:评释 – 牛奶、不加糖 –
天涯论坛
Java 表明基础知识 – 简书
【译】从java表明分析ButterKnife职业流程 –
简书

Author

发表评论

电子邮件地址不会被公开。 必填项已用*标注