Java 反射详解

Java基础

浏览数:211

2019-2-23

AD:资源代下载服务

Java 反射机制是 Java 一个很重要的特性,它使得 Java 能够具有 “ 动态性 ”。
对于一种语言来说,如果能在程序运行的时候,动态的改变程序的结构或者变量类型,那么这种语言就属于动态语言。
按照这个角度来看,ptyhon,ruby等属于动态语言,而java,c++则不是动态语言。而 java 却能通过 反射实现动态语言的一些特性。
说多了也晕,还是看看实例,来了解下反射的强大吧。

Java 反射能做到的:

  1. 在运行时检测类信息(父类,接口等),以及成员变量,方法,构造器以及它们的详细信息(限定名,类型,参数列表,注解等)
  2. 还可以在运行时实例化指定类的对象,调用方法,访问成员变量(可以访问私有变量和方法)
  3. 可以在运行时构造指定类的数组
  4. 通过反射实现动态代理

运用反射可以做到很多很强大的事情。比如 android 开发中的 ButterKnife 库。

一般来说android经常要从xml文件里获取大量空间,会显得非常臃肿

public class ButterActivity extends AppCompatActivity {

    TextView ansTextView;
    EditText par1,par2;
    Button cal;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_butter);
        ansTextView=(TextView)findViewById(R.id.tv_ans);
        par1=(EditText)findViewById(R.id.edt_par1);
        par2=(EditText)findViewById(R.id.edt_par2);
        cal=(Button)findViewById(R.id.btn_add);
        cal.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String x=par1.getText().toString();
                String y=par2.getText().toString();
                ansTextView.setText(x+y);
            }
        });

    }
}

而使用 ButterKnife 之后

public class ButterActivity extends AppCompatActivity {

    @Bind(R.id.tv_ans)TextView ansTextView;
    @Bind(R.id.edt_par1)EditText par1;
    @Bind(R.id.edt_par2)EditText par2;
    @Bind(R.id.btn_add)Button cal;
    @Nullable  @OnClick(R.id.btn_add)void add(View view){
       String x=par1.getText().toString();
       String y=par2.getText().toString();
       ansTextView.setText(x+y);
   }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_butter);
        ButterKnife.bind(this);
      }


这样写,代码是不是清晰了很多,变得非常优雅.
以前的版本中,ButterKnife 中就是使用 反射机制,获取注解的内容,来实现很多功能。
关于注解的话,可以看看我关于 java 注解的总结 http://www.jianshu.com/p/fc8aae3c319e

还有许多有名的开源库都是使用反射来实现强大的功能。

然后呢,反射还可以做些平时无法做到的事情,比如说在类的外部调用一个类的私有方法,访问类的私有变量等。
这些都可以通过反射做到。

所以如果java 要进阶学习的话,反射是很有必要了解的。
首先,介绍一下反射api中一些重要的类

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

Class :代表一个类。
Field :代表类的成员变量(成员变量也称为类的属性)。
Method 代表类的方法。
Constructor :代表类的构造方法。
Array :提供了动态创建数组,以及访问数组的元素的静态方法。
Parameter :代表参数类型

我们通过一个 Class 对象可以获得类的相关信息,它们保存在 Method,Constructor..等中,具体用法,下面一一介绍。

Class 类

如果要在运行时检查类的相关信息,或者是实现其它的操作,都得需要获得类的 Class 对象,

我们有三种获得Class的方法:
比如有有一个Apple类:

  Class<?>c1=Apple.class;
  Class<?>c2=new Apple().getClass();
  //这里要写出完整名称,包括包名,如果找不到该类会抛出ClassNotFoundException 异常
  Class<?>c2=Class.forName("Apple"); 

Class 类有这些常用的方法:

  //获得类的完整名字。
  String getName():
  //修饰符 ,可以通过 Modifier.toString(int) 得到对应的修饰符名称
  int getModifiers(); 
  //包信息
  Package getPackage(); 
  //获得父类Class
  Class getSuperclass(); 
  //类的所有注解
  Annotation[] getAnnotations(); 
  //实现的接口,不包括父类实现的借口
  Class[] getInterfaces(); 
  //获得类的public类型的属性。
  Fields[] getFields():
  //获得类声明的所有属性,不包括父类属性。
  Fields[] getDeclaredFields():
  //获得类的public类型的方法。
  Methods[] getMethods():
  //获得类声明的所有方法。
  Methods[] getDeclaredMethods():
  //获得类的特定方法,name指定方法的名字,parameterTypes 指定方法的参数类型。
  Method getMethod(String name, Class[] parameterTypes):
  //获得类的public类型的构造方法。
  Constructor[] getConstructors():
  //获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
  Constructor[] getConstructor(Class[] parameterTypes):
  //通过类的不带参数的构造方法创建这个类的一个对象。
  //类必须要有一个public的默认构造器
  newInstance():

然后我们用一个例子,看看具体怎么使用这些方法,这个例子可以表明,我们可以通过获得一个类的Class获得关于这个类的详细信息,例如,类名,包名,注解,变量类型,限定符等。

打印类的相关信息

class Fruit
{
    private String name;
    public String color;
    Fruit(){
        name="Furit";
        color="White";
    }
    private void print(){
        System.out.println("print");
    }
    public void tell(String name){
        System.out.println("tell "+name);
    }
}
class Apple implements  Cloneable
{
    private String location;
    public String price;
    public Apple(){
        this("0");
    }
    private Apple(int x){
        this(""+x);
    }
    public Apple(String price){
        this.price=price;
        location="cn";
    }
    private void pt(){
        System.out.println("pt");
    }
    public void pp(){
        System.out.println("pp");
    }
}


public class Demo {

    public static void printConstructors(Constructor[]constructors){
        for(Constructor i:constructors){
            System.out.print("\t"+Modifier.toString(i.getModifiers())+" "+i.getName()+"(");
             printParameters(i.getParameters());
            System.out.println(")");
        }
    }

    public static void printFields(Field []fields){
        for(Field i:fields){
            System.out.println("\t"+Modifier.toString(i.getModifiers())+" "+i.getType().getName()+" "+i.getName());
        }
    }

    public static void printParameters(Parameter []parameters){
        for(Parameter i:parameters){
            System.out.print(i.getType().getName()+" "+i.getName());
        }
    }

    public static void printMethods(Method []methods){
        for(Method i:methods){
            System.out.print("\t"+Modifier.toString(i.getModifiers())+" "+i.getReturnType().getName()+" "+i.getName()+"(");
            printParameters(i.getParameters());
            System.out.println(")");
        }
    }
    public static void printClassInformation(Class cl){
        System.out.println("package:\t"+cl.getPackage().getName());
        System.out.println("modifer:\t"+Modifier.toString(cl.getModifiers()));
        System.out.println("name:\t"+cl.getName());
        Class[]interfaces=cl.getInterfaces();
        printInterfaces(interfaces);
    }
    
    public static void printInterfaces(Class[]interfaces){
        for(Class i:interfaces){
            System.out.print(i.getName());
        }
    }
    
    public static void main(String []args){
        Class cl=Apple.class;
        printClassInformation(cl);
        System.out.println("constructors");
        printConstructors(cl.getConstructors());
        System.out.println("declared constructors");
        printConstructors(cl.getDeclaredConstructors());
        System.out.println("fields");
        printFields(cl.getFields());
        System.out.println("declared fields");
        printFields(cl.getDeclaredFields());
        System.out.println("methods");
        printMethods(cl.getMethods());
        System.out.println("declared methods");
        printMethods(cl.getDeclaredMethods());
    }
}

输出结果:

package: yzr.t1
modifer:
name: yzr.t1.Apple
java.lang.Cloneableconstructors
public yzr.t1.Apple(java.lang.String arg0)
public yzr.t1.Apple()
declared constructors
public yzr.t1.Apple(java.lang.String arg0)
private yzr.t1.Apple(int arg0)
public yzr.t1.Apple()
fields
public java.lang.String price
declared fields
private java.lang.String location
public java.lang.String price
methods
public void pp()
public final void wait()
public final void wait(long arg0int arg1)
public final native void wait(long arg0)
public boolean equals(java.lang.Object arg0)
public java.lang.String toString()
public native int hashCode()
public final native java.lang.Class getClass()
public final native void notify()
public final native void notifyAll()
declared methods
private void pt()
public void pp()

Constructor 类

Constructor 类呢,顾名思义,是跟类的构造器有关的,我们通过获得它,可以查看构造器的参数列表,参数类型,甚至可以通过它实例化一个对象。

获得方法:
通过Class 类的方法:

  //获得public的构造器
  Constructor[]  getConstructors(); 
  //获得具有指定参数列表的构造器
  Constructor[] getConstructor(Class[] parameterTypes);
  //获得声明的所有构造器
  Constructor[] getDeclaredConstructor();

Constructor 具有的一些方法:

  //声明该构造器的类
  Class getDeclaringClass() ;
  //构造器的参数列表类型
  Class[] getParameterTypes(); 
  //构造器的参数列表
  Parameter[] getParameters(); 
  //修饰符
  int getModifiers() ; 

实例化一个对象,

我们可以通过 Constructor 直接实例化一个对象,而不是通过 new 的方法。
这里通过Constructor实例化对象 ,参数列表必须一一对应

  public static void main(String []args)
        throws NoSuchMethodException,
        IllegalAccessException,
        InvocationTargetException,
        InstantiationException
    {
        Class cl=Apple.class;
        //可能异常 NoSuchMethodException
        Constructor constructor=cl.getConstructor(String.class);
        Apple apple=(Apple)constructor.newInstance("110");
        System.out.print(apple.price);

    }

Filed 类:

Filed 也就跟 类的字段相关的东西,我们可以通过它了解一个类的属性的限定符,类型,注解,等等,甚至可以通过它对一个对象赋值,取值,这个值可以是私有的。
获得方法:
通过Class类的方法:

  //获得类的public类型的属性,不包括父类属性
  Fields[] getFields():
  //获得类声明的所有属性,不包括父类属性。
  Fields[] getDeclaredFields():
  //指定字段名称,限定为public
  Field getField(String filedName); 
  //指定字段名称,当前类声明的属性
  Field getDeclaredField(String fieldName)

Field 常用方法:

  //修饰符
  int getModifiers() ; 
  //获得字段名称
  String getName(); 
  //获得字段类型
  Class getType(); 
  //获得所有注解
  Annotation[] getAnnotations(); 

  //对var1 设置字段值为var2
  void set(Object var1, Object var2); 
  //返回var1的字段的值
  Object get(Object var1); 

这里举个例子,通过 Filed 给一个对象赋值,然后通过 Filed获取它的值。

通过Field访问变量

public static void main(String []args) 
    throws NoSuchFieldException, 
    IllegalAccessException {
        Class cl=Apple.class;
        Apple apple=new Apple();
        //可能异常 NoSuchFieldException
        Field field=cl.getField("price");
        //可能异常 IllegalAccessException
        field.set(apple,"100");
        System.out.print(field.get(apple));
}

Method 类:

Method 也就是保存一个类的方法相关的信息,同样可以了解到它之上的返回类型,参数列表,名称,注解等信息,也可以通过它调用类的方法,这个方法可以是私有的。

Method 获得方法

通过Class

  //获得该类的所有public方法,包括父类
  Method [] getMethods();
  //获得类声明的所有方法(包括private),不包括父类
  Method [] getDeclaredFields()
  //获得指定方法名和参数列表的方法,public 
  Method getMethod(String name,Class<?>..classs);
  //获得当前类声明的所有方法中指定名字和参数列表的方法
  Method getDeclaredMethod(String name,Class<?>..classs);

Method 常用方法

  //Method的常用方法:
  //获得Method的参数列表类型
  Class[] getParameterTypes();
  //获得参数列表
  Parameter[] getParameters();
  //返回类型
  Class getReturnType();

通过Method调用方法:

  //如果是static 方法,target可以为null
  Object invoke(Object target,Object...objects);
```java


```java
public static void main(String []args) {
        Class cl=Fruit.class;
        Fruit fruit=new Fruit();
        Method method=null;
        try {
            method=cl.getMethod("tell",String .class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        try {
            Object ret=method.invoke(fruit,"hello");
            System.out.println(ret);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

getter 和 setter

public static boolean isGetter(Method method){

  if(!method.getName().startsWith("get"))      return false;

  if(method.getParameterTypes().length != 0)   return false;

  if(void.class.equals(method.getReturnType()) return false;

  return true;

}

 
public static boolean isSetter(Method method){

  if(!method.getName().startsWith("set")) return false;

  if(method.getParameterTypes().length != 1) return false;

  return true;

}

访问私有成员变量:

重头戏来了,这里演示了下,怎么使用 反射,来做到一个平时无法做到的事情: 访问私有成员变量,是不是非常震惊。

public static void main(String []args) {
        Class cl=Fruit.class;
        Fruit fruit=new Fruit();
        Field field=null;
        try {
            field=cl.getDeclaredField("name");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        field.setAccessible(true);
        try {
            field.set(fruit,"acc");
            System.out.println(field.get(fruit));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
}

setAccessible(true)这行代码,通过调用setAccessible()方法会关闭指定类Field实例的反射访问检查,
这行代码执行之后不论是私有的、受保护的以及包访问的作用域,你都可以在任何地方访问,即使你不在
他的访问权限作用域之内。但是你如果你用一般代码来访问这些不在你权限作用域之内的代码依然是不可
以的,在编译的时候就会报错。

注解:

注解是java另一个特性,通过注解来给java中得类,成员变量,方法等提供一些信息,如果注解的类型是在运行时生效的,那么我们可以通过反射获得这些注解的值,然后做出一些强大的功能,开始所说的butterknife就是一种。

public class Test {


    //类注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface MyTypeAnnonation {
        public String name();
        public String value();
    }

    //方法注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface  MyMethodAnnonation{
        public String value();
    }

    //变量注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface  MyFieldAnnonation{
        public String value();
    }

    //参数注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.PARAMETER)
    public @interface  MyParameterAnnonation{
        public  String value();
    }

    @MyTypeAnnonation(name = "hello",value = "val")
    static class Student
    {
        @MyFieldAnnonation(value = "field")
        public String name;
        @MyMethodAnnonation(value = "method")
        public void  print(){

        }

        public void set( @MyParameterAnnonation( value = "parameter") String name){

        }

    }

    public static  void main(String []args){

        Annotation annotations[]=Student.class.getAnnotations();
        for(Annotation annotation:annotations){
            if(annotation instanceof MyTypeAnnonation){
                MyTypeAnnonation myAnnonation=(MyTypeAnnonation)annotation;
                System.out.println(myAnnonation.name()+" "+myAnnonation.value());
            }
        }

        try {
            Method method=Student.class.getMethod("print",null);
            Annotation annotation=method.getAnnotation(MyMethodAnnonation.class);
            MyMethodAnnonation  myMethodAnnonation=(MyMethodAnnonation)annotation;
            System.out.println(((MyMethodAnnonation) annotation).value());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        try {
            Field field=Student.class.getField("name");
            Annotation annotation=field.getAnnotation(MyFieldAnnonation.class);
            MyFieldAnnonation myFieldAnnonation=(MyFieldAnnonation)annotation;
            System.out.println(myFieldAnnonation.value());
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        try {
            Method method=Student.class.getMethod("set",new Class[]{String.class});
            Parameter parameters[]=method.getParameters();
            for(Parameter parameter:parameters){
                Annotation  annotation=parameter.getAnnotation(MyParameterAnnonation.class);
                MyParameterAnnonation myParameterAnnonation=(MyParameterAnnonation)annotation;
                System.out.println(myParameterAnnonation.value());
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

数组

public class T2 {


    public static Object newArrayByName(String name,int size,Object value){
        Class cl=null;
        try {
           cl=Class.forName(name);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return  null;
        }
        Object object=Array.newInstance(cl,size);
        for(int i=0;i<size;i++){
            Array.set(object,i,value);
        }
        return  object;
    }

    public static  void main(String []args){
        int []nums=(int [])java.lang.reflect.Array.newInstance(int.class,10);
        Array.set(nums,0,1);
        System.out.println(Array.get(nums,0));


        Class cl=String[].class;
        System.out.println(cl.getName());
        //数组的成员类型
        Class compment=cl.getComponentType();
        System.out.println(compment.getName());

        try {
            //基础数据类型不能通过forName获得Class
            Class c2=Class.forName("[Ljava.lang.String;");
            System.out.println(c2.getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        Class c3=int.class;
        Class c3a=Array.newInstance(c3,0).getClass();
        System.out.println(c3a.getName());


        String strs[]=(String[])newArrayByName("java.lang.String",10,"he");
        for(int i=0;i<strs.length;i++)
            System.out.println(strs[i]);

    }
}

泛型

public class T1 {

    static class Fruit<T>
    {
        T mT;
        Fruit(T xt){
            mT=xt;
        }
        //无法通过反射获得返回的T的类型
        public List<T> get(){
            return null;
        }
    }

    static class Car
    {
        //可以通过反射获得返回的List<String>的String类型
        public List<String>get(){
            return null;
        }
    }


    public static  void main(String []args){

        /*
       方法的泛型返回值类型   method.getGenericReturnType();
       方法的参数泛型类型     method.getGenericParameterTypes();
       变量的泛型返回值类型   field.getGenericType();
        */
        Car car=new Car();
        Method method=null;
        try {
          method=Car.class.getMethod("get",null);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        Type type=method.getGenericReturnType();
        if(type instanceof ParameterizedType){
            ParameterizedType ptype=(ParameterrizedType)type;
            Type types[]=ptype.getActualTypeArguments();
            for(Type tp:types){
                System.out.println(tp.getTypeName());
            }
        }
        
        try {
            Method xmethod= Fruit.class.getMethod("get",null);
            Type typ=xmethod.getGenericReturnType();
            if(typ instanceof  ParameterizedType){
              //  System.out.print("yes");
                ParameterizedType tp=(ParameterizedType)typ;
                Type tps[]=tp.getActualTypeArguments();
                for(Type t:tps){
                    System.out.println(t.getTypeName());
                }
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}


动态代理

interface Subject{
    public void talk();
}
class RealSubject implements  Subject{
    @Override
    public void talk() {
        System.out.print("real subject");
    }
}
class  MyProxyHandler implements InvocationHandler{

    private  Object proxide;
    MyProxyHandler(Object proxide){
        this.proxide=proxide;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("\n\tbefore invoke");
        Object ret=method.invoke(proxide,args);
        System.out.println("\n\tafter invoke");
        return  ret;
    }
}

public class T3 {


    public  static void main(String []args){
            RealSubject realSubject=new RealSubject();
            Subject subject=(Subject) Proxy.newProxyInstance(
                    Subject.class.getClassLoader(),
                    new Class[]{Subject.class},
                    new MyProxyHandler(realSubject));
            subject.talk();
    }


}

我的 github 地址:https://github.com/Thereisnospon 有兴趣的同学可以来看看。