IT虾米网

Java反射(Reflection)详解

admin 2018年06月09日 编程语言 426 0

认识反射

前面说到,反射相对于RTTI的区别就是,反射是运行时发现和使用类的信息。 当我们使用一个位置类型的对象进行操作时,我们必须先加载那个类的Class对象,因此那个类的对象对于JVM来说,必须是可获取的,可以从本地,也可以从网络。然而,对于反射机制,.class在编译器是不可获得的,因此反射只能在运行时打开.class文件。

所以,反射和Class是不可分割的。置于Class是什么,怎么获取,请看我的上一篇文章 Java知识点之—Class对象
这里在啰嗦一遍,获取Class对象的方式有:

一:Class.forname(”java.lang.String”);这里的参数必须为全限定类名
二:对象.getClass()
三:类字面常量(String.class)

通过反射构造目标对象

class Bird { 
 
    public String content; 
    public Bird(String content){ 
        this.content = content; 
    } 
    public void show(){ 
        System.out.println(content); 
    } 
} 
 
public class TestReflect { 
 
    public static void main(String[] args) { 
        Class<?> birdClass = Bird.class; 
 
            Constructor<?> constructor; 
            try { 
                constructor = birdClass.getConstructor(String.class); 
                Bird bird = (Bird) constructor.newInstance("hello"); 
                bird.show();; 
            } catch (Exception e) { 
            } 
 
    } 
}

结果如下:

hello

方法解析:

//获得指定对象的构造方法,参数值传入与构造方法参数对应的类型
getConstructor(String.class)

//分为无参和有参,参数传入与构造方法参数对应的值,获得对象引用
newInstance()

接下来定义这个类供使用

class Bird { 
 
    private int age = 10; 
    public int hevity = 30; 
 
    private void say(String content){ 
        System.out.println(content); 
    } 
 
    public void run(String content){ 
        System.out.println(content); 
    } 
}

通过反射获取类中方法

获取类中函数的方法有三个,getDeclaredMethod()可以获取到类中任意的某个方法,而getMethod()只能获取public方法,如果获取private方法,会抛出异常,getMethods则是获取类中的所有方法

public static void main(String[] args) { 
        Bird bird = new Bird(); 
        Class<?> birdClass = bird.getClass(); 
        Method method; 
        Method[] methods; 
        Method pbMethod; 
        try { 
            //获取特定方法 
            method = birdClass.getDeclaredMethod("say", String.class); 
            method.setAccessible(true); 
            method.invoke(bird, "你好啊"); 
 
            System.out.println(""); 
 
            //获取类中的public方法 
            pbMethod = birdClass.getMethod("run", String.class); 
            method.setAccessible(true); 
            method.invoke(bird, "running"); 
 
            System.out.println(""); 
 
            //获取类中所有方法 
            methods = birdClass.getMethods(); 
            for(Method m : methods){ 
                System.out.println(m); 
            } 
 
        } catch (Exception e) { 
            e.printStackTrace(); 
        }  
    }

结果如下:

你好啊 
 
running 
 
public void test.Bird.run(java.lang.String) 
public final void java.lang.Object.wait() throws java.lang.InterruptedException 
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException 
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException 
public boolean java.lang.Object.equals(java.lang.Object) 
public java.lang.String java.lang.Object.toString() 
public native int java.lang.Object.hashCode() 
public final native java.lang.Class java.lang.Object.getClass() 
public final native void java.lang.Object.notify() 
public final native void java.lang.Object.notifyAll() 

方法解析:

//第一个参数指你想要获取得方法的方法名,第二个参数则是该方法的参数类型
getDeclaredMethod (String name, Class… parameterTypes)

//第一个参数指你想要获取得方法的方法名,第二个参数则是该方法的参数类型
getMethod(String name, Class… parameterTypes)

//获取所有方法,返回是一个Method[]数组
getMethods()

//设置true表示可以访问该方法
setAccessible(boolean type);

//作用:调用该方法。第一个参数表示该方法所在类的对象,第二个参数传入该方法所需的参数值
invoke(Object obj, Object… args)

需要注意的

getDeclaredMethod 和 getDeclaredMethods 包含 private、protected、default、public 的函数,并且通过这两个函数获取到的只是在自身中定义的函数,从父类中集成的函数不能够获取到。而 getMethod 和 getMethods 只包含 public 函数,父类中的公有函数也能够获取到。

通过反射获取类中属性

和上面所提到的获得方法十分相似,基本就是把method换成了field。

try { 
            //获取某个特定的属性 
            Field field = birdClass.getDeclaredField("age"); 
            field.setAccessible(true); 
            System.out.println(field.getInt(bird)); 
 
            //获取某个特定的共有属性 
            Field publicField = birdClass.getField("hevity"); 
            publicField.setAccessible(true); 
            System.out.println(publicField.getInt(bird)); 
 
            //获取所有属性 
            Field[] fields = birdClass.getFields(); 
            for(Field f : fields){ 
                System.out.println(f.getName()); 
            } 
 
            //修改某个属性值 
            Field alterField = birdClass.getDeclaredField("age"); 
            alterField.setAccessible(true); 
            alterField.setInt(bird, 100); 
            System.out.println(alterField.getInt(bird)); 
 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 

结果为:

10 
30 
hevity 
100 

方法解析:

//参数为所要获得的属性的名
getDeclaredField(String obj)

//参数为所要获得的属性的名(只能获取共有属性)
getField(String obj)

//获取所有共有属性(getMethods()则是获取所有方法,包括private)
getFields()

//参数为属性所在的类的对象,表示获取该类的该属性,同理还有getString等
getInt(Object obj)

//第一个参数为属性所在的类的对象,第二个参数为要修改的值,同理还有setString等
setInt(Object obj, int arg)

需要注意的

getDeclaredField 和 getDeclaredFields 包含 private、protected、default、public 的属性,并且通过这两个函数获取到的只是在自身中定义的属性,从父类中集成的属性不能够获取到。而 getField 和 getFields 只包含 public 属性,父类中的公有属性也能够获取到。

反射的使用场景

有时候我们需要访问某些private信息,并更改它,但是不能直接修改,这是就可以使用反射。
有时候当我们需要取得某些内部信息,但是要在运行时才能获取,例如ORM 框架,在运行时才能够获取类中的各个属性,然后通过反射的形式获取其属性名和值,存入数据库。这也是反射比较经典应用场景之一。

发布评论

分享到:

IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

深入理解 抽象类和接口详解
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。