初识Java反射

Posted x_k

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初识Java反射相关的知识,希望对你有一定的参考价值。

Java反射原理是基于Class类而实现的,Class类代表了所有的类,通过Class类可以获取一个未知类的属性(Filed)、方法(method)、构造器(constructor)、修饰符(modifier)等。
一般情况下,需要创建一个类的实例对象,JVM会确认该类的Class对象是否加载完毕。未加载的话,JVM会根据类名查找.class文件,并载入内存,从而可以创建需要使用的类的对象。

以下代码验证均是在下面代码基础上验证。
接口:SingSong.java

package com.edu;

/**
 * 唱歌
 * 
 * @author xukai
 */
public interface SingSong 

    public static final long iid = -6564235491638363465L;

    public void singSong();

父类:PeopleSuperClass

package com.edu;

/**
 * 父类
 * 
 * @author xukai
 */
public class PeopleSuperClass 

    public static final long fid = -6564335491638363465L;

    /**
     * 子类
     */
    private People subPeople;

    /**
     * 父类
     */
    private PeopleSuperClass superPeople;

    public People getSubPeople() 
        return subPeople;
    

    public void setSubPeople(People subPeople) 
        this.subPeople = subPeople;
    

    public PeopleSuperClass getSuperPeople() 
        return superPeople;
    

    public void setSuperPeople(PeopleSuperClass superPeople) 
        this.superPeople = superPeople;
    

子类:People

package com.edu;

/**
 * 人类
 * 
 * @author xukai
 */
public class People extends PeopleSuperClass implements SingSong 

    public static final long sid = -6564335491638363466L;

    private String name;

    private Integer age;

    public String sex;

    public People() 
    

    public People(String name, Integer age) 
        super();
        this.name = name;
        this.age = age;
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public Integer getAge() 
        return age;
    

    public void setAge(Integer age) 
        this.age = age;
    

    @Override
    public String toString() 
        return "People [name=" + name + ", age=" + age + ", sex=" + sex + "]";
    

    public String helloWorld() 
        return name + " said:Hello World!";
    

    @Override
    public void singSong() 
        System.out.println("唱一首Until you");
    

    public static void show() 
        System.out.println("I'm here");
    

    public static void show(People p) 
        System.out.println(p.getName() + ",I'm here");
    

Class的创建和获取

创建:
1. 使用new关键字创建对象
2. 访问类的静态成员
3. 使用Class.forName()方法
获取:
1. Class.forName方法返回对象即为加载类的Class对象
2. 每个类的class静态属性
3. Object类的成员方法getClass()

package com.edu;

/**
 * 获取Class对象
 * 
 * @author xukai
 */
public class MyReflex 

    public static void main(String[] args) 

        // 访问People的静态成员class,加载类
        System.out.println(People.class.getName());

        // 1.Class.forName静态方法
        Class<?> c1 = null;
        try 
            c1 = Class.forName("com.edu.People");
         catch (ClassNotFoundException e) 
            System.out.println("加载类路径错误");
        

        // 2.class静态属性
        Class<?> c2 = People.class;

        // 3.Object的成员方法getClass()
        People people = new People("xukai", 22);
        Class<?> c3 = people.getClass();

        System.out.println("c1=c2 is " + (c1 == c2));

        System.out.println("c1=c3 is " + (c1 == c3));

        System.out.println("c2=c3 is " + (c2 == c3));

    


控制台信息:

从上图可以看出,通过几种创建Class的方式返回的对象都是同一个。换句话说一个类对应的Class对象是否只存在一个?有待研究

获取类的成员变量

  1. 通过Class的方法getDeclareField()
  2. 通过Class的方法getDeclareFields()
package com.edu;

import java.lang.reflect.Field;

/**
 * 获取成员变量Field
 * 
 * @author xukai
 */
public class MyField 

    public static void main(String[] args) 
        Class<?> c = People.class;
        /**
         * 获取所有成员变量<br>
         * 1.getDeclaredField(),getDeclaredFields(),作用域对此方法无影响,不会访问父类字段<br>
         * 2.getFields(),getFields(),只能访问公共成员字段,即作用域为public,访问父类字段
         */
        getDeclaredField(c);
        System.out.println("------------------华丽的分割线-----------------");
        getField(c);

        // 通过Filed操作私有成员变量
        People p = new People("xukai", 22);
        try 
            Field fName = c.getDeclaredField("name");
            Field fAge = c.getDeclaredField("age");
            try 
                // 设置可访问性
                fName.setAccessible(true);
                fAge.setAccessible(true);
                fName.set(p, "rj");
                fAge.set(p, 25);
                System.out.println(p.toString());
             catch (IllegalArgumentException e) 
                e.printStackTrace();
             catch (IllegalAccessException e) 
                e.printStackTrace();
            
         catch (NoSuchFieldException e) 
            e.printStackTrace();
         catch (SecurityException e) 
            e.printStackTrace();
        
    

    /**
     * 扫描本类的所有成员变量
     * 
     * @param c
     *            Class
     */
    private static void getDeclaredField(Class<?> c) 
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) 
            try 
                System.out.println("Field[] name=" + field.getName());
                Field singleField = c.getDeclaredField(field.getName());
                System.out.println("Field   name=" + singleField.getName());
             catch (NoSuchFieldException e) 
                e.printStackTrace();
             catch (SecurityException e) 
                /**
                 * 如果存在安全管理器s<br>
                 * 1.调用 s.checkMemberAccess(this, Member.DECLARED) 拒绝访问已声明字段<br>
                 * 2.调用者的类加载器不同于也不是当前类的类加载器的一个祖先, 并且对 s.checkPackageAccess()
                 * 的调用拒绝访问该类的包<br>
                 */
                e.printStackTrace();
            
        
    

    /**
     * 只能访问公共成员字段,即作用域为public<br>
     * 1.搜索当前类的公共成员变量 2.搜索实现的接口的公共成员变量 3.搜索父类的公共成员变量
     * 
     * @param c
     *            Class
     */
    private static void getField(Class<?> c) 
        Field[] fields = c.getFields();
        for (Field field : fields) 
            try 
                System.out.println("Field[] name=" + field.getName());
                Field singleField = c.getField(field.getName());
                System.out.println("Field   name=" + singleField.getName());
             catch (NoSuchFieldException e) 
                e.printStackTrace();
             catch (SecurityException e) 
                e.printStackTrace();
            
        
    

控制台信息:

需要注意getDeclaredFiled和getFiled的区别

方法作用域限制访问实现接口成员变量访问继承父类成员变量
getDeclaredFiled不能不能
getDeclaredFileds不能不能
getFiled限制为公共public
getFileds限制为公共public

获取类的方法(method)

  1. 通过Class的方法getMethod()
  2. 通过Class的方法getMethods()
package com.edu;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 获取类的方法
 * 
 * @author xukai
 */
public class MyMethod 

    public static void main(String[] args) 
        Class<?> c = People.class;
        Method[] methods = c.getMethods();
        for (Method method : methods) 
            System.out.println(method + "\\t\\t" + method.getName());
        
        System.out.println("------割-----");
        try 
            // getMethod(String name, Class<?>... parameterTypes),第二个参数为方法参数
            Method method = c.getMethod("show", new People("zj", 23).getClass());
            System.out.println(method);
            try 
                // 使用invoke调用方法
                System.out.println("------割-----");
                // 调用show方法
                method.invoke(new People("xukai", 22), new People("zj", 23));
             catch (IllegalAccessException e) 
                e.printStackTrace();
             catch (IllegalArgumentException e) 
                e.printStackTrace();
             catch (InvocationTargetException e) 
                e.printStackTrace();
            
         catch (NoSuchMethodException e) 
            e.printStackTrace();
         catch (SecurityException e) 
            e.printStackTrace();
        
    

控制台信息:

需要注意getMethod(String name, Class<\\?>… parameterTypes)的第二个参数为Class类型。
Method.invoke(Object obj, Object… args)第一个参数为调用对象,第二个参数为方法参数

反射的简单应用

创建一个类的实例对象

package com.edu;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
 * 使用反射实例化一个对象<br>
 * 建议使用newInstance方法创建对象,在使用setters方法赋值
 * 
 * @author xukai
 */
public class ReflexApplication 

    public static void main(String[] args) 
        Class<? extends People> c = People.class;
        People p = null;
        // 1.使用Class.newInstance创建对象
        try 
            /**
             * 使用此方法可以有效地绕过编译时的异常检查,而在其他情况下编译器都会执行该检查<br>
             * 调用空参构造方法
             */
            p = c.newInstance();
            System.out.println(p);
         catch (InstantiationException e) 
            e.printStackTrace();
         catch (IllegalAccessException e) 
            e.printStackTrace();
        

        // 2.使用带参数构造方法
        try 
            Constructor<? extends People> con = c.getConstructor(new String("zhouyang").getClass(),
                    new Integer(23).getClass());
            try 
                p = con.newInstance("ft", 24);
                System.out.println("constructor create: " + p);
             catch (InstantiationException e) 
                e.printStackTrace();
             catch (IllegalAccessException e) 
                e.printStackTrace();
             catch (IllegalArgumentException e) 
                e.printStackTrace();
             catch (InvocationTargetException e) 
                e.printStackTrace();
            
         catch (NoSuchMethodException e) 
            e.printStackTrace();
         catch (SecurityException e) 
            e.printStackTrace();
        

        // 使用反射重写toString()
        People people = new People("zt", 23);
        System.out.println(people);
        overrideToString(people);
    

    /**
     * 使用反射重写toString
     * 
     * @param obj
     *            重写对象
     */
    public static void overrideToString(Object obj) 
        StringBuffer sb = new StringBuffer();
        Field[] fields = obj.getClass().getDeclaredFields();
        sb.append(obj.getClass().getSimpleName() + "[");
        for (Field f : fields) 
            try 
                if (!f.isAccessible()) 
                    f.setAccessible(true);
                    sb.append(f.getName());
                    sb.append("=");
                    sb.append(f.get(obj));
                    sb.append(", ");
                
             catch (IllegalArgumentException e) 
                e.printStackTrace();
             catch (IllegalAccessException e) 
                e.printStackTrace();
            
        
        sb.append("]");
        System.out.println(sb);
    


控制台信息:

一般使用Constructor创建实例太过于麻烦,常用newInstance创建实例,然后通过setter方法给成员变量赋值。

以上是关于初识Java反射的主要内容,如果未能解决你的问题,请参考以下文章

初识Java反射

1 初识反射

初识TomCat之1——Java体系理解

java注解-初识

java注解-初识

初识反射之二