Java知识-反射的学习

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Java反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,它在编译期不需要知道运行的对象是谁,Java反射实际操作对象是.class文件(字节码文件)

反射机制的优点与缺点
为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念
静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
优点
可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
缺点
对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。

理解Class类和类类型
想要了解反射首先理解一下Class类,它是反射实现的基础。
类是java.lang.Class类的实例对象,而Class是所有类的类(There is a class named Class)
对于普通的对象,我们一般都会这样创建和表示:
TestBean testBean = new TestBean();

上面说了,所有的类都是Class的对象,那么如何表示呢,可不可以通过如下方式呢:
Class c = new Class();

但是我们查看Class的源码时,是这样写的:
private Class(ClassLoader loader) {
classLoader = loader;
}

可以看到构造器是私有的,只有JVM可以创建Class的对象,因此不可以像普通类一样new一个Class对象,虽然我们不能new一个Class对象,但是却可以通过已有的类得到一个Class对象,共有三种方式,如下:
Class c1 = TestBean.class;
这说明任何一个类都有一个隐含的静态成员变量class,这种方式是通过获取类的静态成员变量class得到的
Class c2 = TestBean.getClass();
code1是Code的一个对象,这种方式是通过一个类的对象的getClass()方法获得的
Class c3 = Class.forName(“com.daidai.diyviewday3.TestBean”);
这种方法是Class类调用forName方法,通过一个类的全量限定名获得

这里,c1、c2、c3都是Class的对象,他们是完全一样的,而且有个学名,叫做TestBean的类类型(class type)。通过类类型可以知道一个类的属性和方法,并且可以调用一个类的属性和方法,这就是反射的基础。

举个例子:

public class TestBean{

    public static void main(String[] args) throws Exception {

        //第一种:Class c1 = TestBean.class;
        Class class1=TestBean.class;
        System.out.println(class1.getName());

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

        //第三种:Class c3 = Class.forName("com.daidai.diyviewday3.TestBean");
        Class class3 = Class.forName("com.daidai.diyviewday3.TestBean");
        System.out.println(class3.getName());

    }
}

执行结果

com.daidai.diyviewday3.TestBean
com.daidai.diyviewday3.TestBean
com.daidai.diyviewday3.TestBean

Java反射相关操作
前面我们知道了怎么获取Class,那么我们可以通过这个Class干什么呢?
获取成员变量Field
获取成员方法Method
获取构造函数Constructor

下面来具体介绍

获取成员变量信息
想一想成员变量中都包括什么:成员变量类型+成员变量名
类的成员变量也是一个对象,它是java.lang.reflect.Field的一个对象,所以我们通过java.lang.reflect.Field里面封装的方法来获取这些信息。
单独获取某个成员变量,通过Class类的以下方法实现:

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

参数是成员变量的名字。
例如一个类TestBean有如下成员变量:

private int n;

可以通过这样方法得到变量

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

同样,如果想要获取所有成员变量的信息,可以通过以下几步
1.获取所有成员变量的数组:

Field[] fields = c.getDeclaredFields();

2.遍历变量数组,获得某个成员变量field

for (Field field : fields)

完整代码为:

public class TestBean {

    private String name;
    private int age = 10;

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

            Field[] fields = c.getDeclaredFields();
            for(Field f :fields){
                System.out.println(f.getName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果为:

10
name
age

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

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

例如类TestBean有如下方法:

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

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

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

Class c = Class.forName("com.daidai.diyviewday3.TestBean");  //先生成class
Object o = c.newInstance();                           //newInstance可以初始化一个实例
Method method = c.getMethod("fun", String.class, int.class);//获取方法
method.invoke(o, "aJun", 27);                              //通过invoke调用该方法,参数

Method method2 = c.getDeclaredMethod("data", String.class, int.class);
method2.setAccessible(true);
method2.invoke(o, "aJun", 27);

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

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

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

for (Method method : methods)

完整代码如下:

public class TestBean {

    private String name;
    private int age;

    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 void fun(String name, int age) {
        System.out.println("我叫" + name + ",今年" + age + "岁");
    }

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

    public static void main(String[] args) throws Exception {
        try {
            Class c = Class.forName("com.daidai.diyviewday3.TestBean");
            Object o = c.newInstance();
            Method method = c.getMethod("fun", String.class, int.class);
            method.invoke(o, "aJun", 27);

            Method method2 = c.getDeclaredMethod("data", String.class, int.class);
            method2.setAccessible(true);
            method2.invoke(o, "aJun", 27);
            
            Method[] methods = c.getDeclaredMethods();
            for(Method m:methods){
                String  methodName= m.getName();
                System.out.println(methodName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:

我叫aJun,今年27岁
我叫aJun,今年27岁
main
getName
setName
data
getAge
setAge
fun

这里如果把c.getDeclaredMethods();改成c.getMethods();执行结果如下,多了很多方法,以为把Object里面的方法也打印出来了,因为Object是所有类的父类:

main
getName
setName
setAge
getAge
fun
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll

获取构造函数
类的成构造函数也是一个对象,它是java.lang.reflect.Constructor的一个对象,所以我们通过java.lang.reflect.Constructor里面封装的方法来获取这些信息。
单独获取某个构造函数,通过Class类的以下方法实现:

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

这个参数为构造函数参数类的类类型列表。
例如类TestBean有如下一个构造函数:

public TestBean(String name, int age) {
    // code body
}

那么就可以通过:

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

来获取这个构造函数。
完整代码如:

public class TestBean {

    private String name;
    private int age = 10;

    public TestBean(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    @Override
    public String toString() {
        return "TestBean{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

执行结果为:

TestBean{name='aJun', age=27}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值