java反射---高级开发必须要懂的入门知识

java反射说的是一套反射机制,通过这套机制我们可以访问已经装载到JVM(java虚拟机的简称)里面的java某个对象,还可以进行检测以及修改这个对象本身的信息。可以说反射机制的功能是非常强大的,在java.lang.reflect包下的类支持了这些功能。

说到反射我们绕不开的一个类Class,它到底是什么?
其实在面向对象的世界里万事万物都是对象,类也不例外;所有运行的java虚拟机中的类都是Class类的对象。
类也是对象,类是java.lang.Class类的实例对象。

知道了这个Class类的某个对象(注意这个对象是某个类,可以是java的jdk中自带的类,也可以是我们创建的类,),通过这个对象,我们就可以知道由它创建的某个实例对象的的基本信息。这个就是反射机制的直接表现形式。这样说可能有一点拗口,下面举个例子:
比如有一个类 public class A {},通过 A a=new A();我们创建了A类的一个对象 a;

 Class clz=a.getClass();//这样就获得Class类的某个对象 clz

 //下面通过反射机制操作这个clz,我们就可以获得实例对象a的一些基本信息:比如方法,构造方法,属性,方法参数党法修饰符等

上面只是谈了一下我对反射机制的的一般理解,不知道我说明白了?下面通过代码学习一下

1.首先如何获取Class类的对象?有如下三种方式;

package com.csdn;

class Person {}//Person类

public class T {

    public static void main(String[] args) throws ClassNotFoundException {
        Person mPerson = new Person();

        // 第一种方式(任何类里面都有一个隐含的静态成员变量class)
        Class p1 = Person.class;

        // 第二种方式(已经知道了Person类的一个对象,比如上面mPerson)
        Class p2 = mPerson.getClass();

        // 第三种方式(Class.forName("类的全称"),会抛异常)
        Class p3 = Class.forName("com.csdn.Person");
        System.out.println(p1 == p2);// 结果为true
        System.out.println(p2 == p3);// 结果为true
    }

}

上面也打印了p1,p2和p3的堆内存比较结果都为true,结果证明他们都是同一个;
结果同样也说明:
Class类的某个对象(类)也是唯一的
上面三种方式,不管是通过哪一种得到的Person类都是同一个,这也很好理解,因为在JVM中我们的类只可能是一个,不会存在两个一模一样的类;

2.介绍一下反射有关的几个类,当然有很多,这里只列举几个常见的;

Field类:用于操作类中的属性
Method类:用于操作类中的方法
Constructor类:提供单个构造方法的信息
Modifier类:用于解码访问修饰符(类和成员变量的访问修饰符),这个类里面全是整型常量。每个常量表示一个访问修饰符。

3.通过反射调用方法,这里要使用到Method类(方法对象),一个成员方法就是一个Method的实例对象
为上面的Person类添加一个方法

class Person {
    private int height;

    private int addHeight(int a) {
        height = a + 1;
        return height;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }
}

下面通过Method类来进行操作,还是接着使用p1

        Class<?> p1 = Person.class;
        // getDeclaredMethods()获取的是本类自己声明的所有方法信息
        Method[] mds = p1.getDeclaredMethods();
        System.out.println("得到全部方法信息: ");

        for (int i = 0; i < mds.length; i++) 
        {
            // 得到方法的访问修饰权限
            int mModify = mds[i].getModifiers();
            // Modifier类里面是所有修饰符的整形常量信息
            // 比如此处 public static final int PRIVATE =0x2 代表 private
            // 使用Modifier.toString()方法可以将整形转化为String类型的修饰符private
            System.out.print(Modifier.toString(mModify) + " ");

            // 得到方法的返回值类型
            Class<?> mReturnType = mds[i].getReturnType();
            System.out.print(mReturnType.getName());

            // 得到方法的名称
            String mMethodName = mds[i].getName();
            System.out.print(" " + mMethodName);

            // 获取方法的参数类型
            Class<?>[] mParamType = mds[i].getParameterTypes();
            System.out.print("(");
            // 说明当前这个方法里面没有参数类型
            if (mParamType.length == 0)
                System.out.println(")");
            for (Class<?> mClass : mParamType) {
                // 获取参数类型的名称
                System.out.print(mClass.getName());
                System.out.println(")");
            }

        }

打印的结果如下:

得到全部方法信息: 
public void setHeight(int)
public int getHeight()
private int addHeight(int)

这里可以看到正好就是上面我们声明的Person类里面的三个方法,通过Method类我们可以很好的获取方法信息。其实我们还可以通过Method类来调用这个方法,这里方法是private修饰的不可以使用对象对象直接调用,利用反射可以轻松做到;比如我给 private int addHeight(int a) 方法传一个实参值是2,操作如下:

Class<?> p1 = Person.class;
Person mPerson = new Person();

//获取到某一个特定的方法对象,第一个传进去的是方法的名称,后面传入形式参数的Class类型
Method md = p1.getDeclaredMethod("addHeight", int.class);

//首先要获得访问的权限,打破封装
md.setAccessible(true);

// 调用invoke方法来调用方法第一个是Person类的对象,第二个是传递的参数
int returnValue = (Integer) md.invoke(mPerson, 2);//retrunValue是addHeight(int a)方法返回的结果

System.out.println("返回值: " + returnValue);
System.out.println("通过mPerson得出的结果:"+mPerson.getHeight());

打印结果是

返回值: 3
通过mPerson得出的结果:3

4,使用Field类来操作属性 一个Field类实例代表一个成员变量

        Class<?> p1 = Person.class;

        System.out.println("得到的属性是 : ");
        // 得到本类声明的所有属性
        Field[] mFields = p1.getDeclaredFields();

        for (int i = 0; i < mFields.length; i++) 
        {
            // 得到变量的访问修饰权限
            System.out.print(Modifier.toString(mFields[i].getModifiers()) + " ");

            // 得到属性的类型
            System.out.print(mFields[i].getType().getName() + " ");

            // 得到属性的名称
            System.out.println(mFields[i].getName());
        }

打印的结果:

得到的属性是 : 
private int height

当然同方法类似,通过Field也可以来直接为变量赋值的

        Class<?> p1 = Person.class;
        Person mPerson = new Person();
        // 获取本类声明的特定的属性,参数传入变量名称
        Field mField = p1.getDeclaredField("height");

        // 还是要设置一下访问权限,这步是必不可少的
        mField.setAccessible(true);

        // 通过setInt为对象的height属性赋值为2,因为是int型所以使用setInt
        mField.setInt(mPerson, 2);

        System.out.println("mPerson的height属性值是:" + mPerson.getHeight());

打印结果:

mPerson的height属性值是:2

5,使用Constructor类来操作构造方法 一个Constructor类实例代表一个构造方法对象
再为Person类添加如下构造方法

public Person() {
        super();
    }

    private Person(int height) {
        super();
        this.height = height;
    }

下面使用Constructor类来操作构造方法 ,接着用p1

        Class<?> p1 = Person.class;
        System.out.println("得到的构造方法:");

        // 获取本类声明的所有构造方法
        Constructor<?>[] mCons = p1.getDeclaredConstructors();
        for (int i = 0; i < mCons.length; i++) 
        {

            // 获取构造方法的访问修修饰符
            System.out.print(Modifier.toString(mCons[i].getModifiers()) + " ");
            System.out.print(mCons[i].getName());

            // 得到构造方法的参数类型
            Class<?>[] mConsClass = mCons[i].getParameterTypes();
            System.out.print("(");
            for (Class<?> css : mConsClass) {
                System.out.print(css.getName());
            }
            System.out.println(")");
        }

打印的结果:

 得到的构造方法:
public com.csdn.Person()
private com.csdn.Person(int)

当然了通过Constructor也可以创建Person类的对象

         Class<?> p1 = Person.class;
        Constructor<?>[] mConstructs = p1.getDeclaredConstructors();
        System.out.println(mConstructs.length);
        for (int i = 0; i < mConstructs.length; i++) { if (i == 0) {// 是无参的构造方法 Person person1 = (Person) mConstructs[i].newInstance(); System.out.println("打印无参的构造方法中的Height的值:"+person1.getHeight()); } else if (i == 1) 
            {// 是有参的构造方法 mConstructs[i].setAccessible(true); Person person2 = (Person) mConstructs[i].newInstance(2); System.out.println("打印无参的构造方法中的Height的值:"+person2.getHeight()); }
        }

打印结果如下:

打印无参的构造方法中的Height的值:0
打印无参的构造方法中的Height的值:2

说明一下:p1.getDeclaredConstructors()得到的数组中构造方法的顺序是按照,构造方法在Person类中的声明顺序排列的,因为Person类中 先是无参,后是有参,
所以得到的数组中 第一个元素是表示无参构造方法,第二个元素是有参的构造方法
上面写法不科学,这里只是为了演示,我知道Person只有两个构造方法,才这样写的。
第二个因为不是public修饰的构造方法,所以要使用setAccessible来打破访问权限.

到这里反射机制基础知识也就介绍完了,希望对大家有所帮助,有些地方可能写的也不是很好,有错误地方请指正!

你可能感兴趣的:(java,Class,反射机制)