Java反射机制

Java反射基本使用

Posted by Jack on May 15, 2018

JAVA反射


Java反射机制定义

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

用一句话总结就是反射可以实现在运行时可以知道任意一个类的属性和方法。

反射机制的优点与缺点

为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念

  • 静态编译:在编译时确定类型,绑定对象,即通过。

  • 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。

优点

可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。

缺点

对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。

获取Class对象

每个类的字节码,在内存中只有一份,每一份字节码就是一个Class的实例对象,比如要得到Person的字节码,可以有下面三种写法

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.jack.reflect.ReflectDemo");
    System.out.println(class3.getName());
  }
}

执行结果:

com.jack.reflect.ReflectDemo
com.jack.reflect.ReflectDemo
com.jack.reflect.ReflectDemo

说明每个类的字节码,在内存中只有一份,无论你用三种方式的哪一种方式去取,得到的都是同一份字节码。

Java反射相关操作

前面我们知道了怎么获取Class,那么我们可以通过这个Class干什么呢? 总结如下:

  • 获取成员方法Method

  • 获取成员变量Field

  • 获取构造函数Constructor

获取成员方法Method

单独获取某一个方法是通过Class类的以下方法获得的:

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

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

例如类A有如下一个方法:

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

现在知道A有一个对象a,那么就可以通过:

Class c = Class.forName("com.jack.reflect.Person");  //先生成class
Object o = c.newInstance();   //newInstance可以初始化一个实例
Method method = c.getMethod("fun", String.class, int.class);//获取方法
method.invoke(o, "Jack", 18);  //通过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.jack.reflect.Person");
             Object o = c.newInstance();
             Method method = c.getMethod("fun", String.class, int.class);
             method.invoke(o, "Jack", 18);
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}

执行结果:

我叫Jack,今年18岁

有时候我们想获取类中所有成员方法的信息,要怎么办。可以通过以下几步来实现:

1.获取所有方法的数组:

    Class c = Class.forName("com.jack.reflect.Person");

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

2.然后循环这个数组就得到每个方法了:

    for (Method method : methods)

完整代码如下:

public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.jack.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

获取成员变量Field

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

完整代码如下:

public class ReflectDemo {
    public static void main(String[] args){
        try {
            Class c = Class.forName("com.jack.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

同样,如果想要获取所有成员变量的信息,如下所示:

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

执行结果:

name
age
msg

获取构造函数Constructor

单独获取某个构造函数,通过Class类的以下方法实现:

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

这个参数为构造函数参数类的类类型列表。

例如类A有如下一个构造函数:

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.jack.reflect.Person");
            //获取构造函数
            Constructor constructor = c.getDeclaredConstructor(String.class);
            constructor.setAccessible(true);//设置是否允许访问,因为该构造器是private的,所以要手动设置允许访问,如果构造器是public的就不需要这行了。
            constructor.newInstance("Jack");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:

Jack

注意:Class的newInstance方法,只能创建只包含无参数的构造函数的类,如果某类只有带参数的构造函数,那么就要使用另外一种方式:fromClass.getDeclaredConstructor(String.class).newInstance(“Jack”);

获取所有的构造函数

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.jack.reflect.Person()
public com.jack.reflect.Person(java.lang.String)

参考链接

Java 反射

Java基础与提高干货系列——Java反射机制