美文网首页
Java笔记之——反射

Java笔记之——反射

作者: Zhuang_ET | 来源:发表于2018-07-13 10:12 被阅读0次

    这篇文章来谈谈Java基础——反射的内容。主要通过以下几点进行介绍:反射机制、反射的使用及有关反射的API。

    一、反射机制

    反射机制是Java语言中一个非常重要的特性,它允许程序在运行时进行自我检查,同时也允许对其内部的成员进行操作。反射机制能够实现在运行时对类进行装载,因此能够增加程序的灵活性,但是不恰当地使用反射机制,也会影响系统的性能。

    具体来说,就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和获取其属性。主要是通过反射把类中的各种成分,如成员变量、方法、构造方法等信息,映射成一个个的类对象,如Field、Method、Constructor等。

    二、反射的基本使用

    1. 获取Class类的三种方式:
    • 类名.class;
    • 对象名.getClass();
    • Class.forName("要加载的类名")。(这里需要完整的类名,及需要把包也写进来)
    1. 通过上述三种方式获取到特定类的Class类,即该类的字节码后,就可以通过该Class对象获取构造方法对象、方法对象、域对象等。
    • 通过getConstructor(Class<?>... parameterTypes)获取构造方法对象;
    • 通过getMethod(String name,Class<?>... parameterTypes)获取方法对象;
    • 通过getField(String name)获取域对象。
    1. Constructor、Method、Field的使用
    • 调用Constructor类的newInstance(Object... initargs)方法可新建对象;
    • 调用Method类的invoke(Object obj, Object... args)方法调用对象上相应的方法;
    • 调用Field类的get(Object obj)方法获得相应的域值,调用set(Object obj, Object value)修改相应的域值。

    三、举个栗子

    首先定义类:

    package com.zhuanget;
    
    public class Animal {
    
        private int age;
        protected String sex;
        public String name;
    
        public Animal() {
            this.age = 0;
            this.sex = "male";
            this.name = "animal";
        }
    
        public Animal(String name) {
            this.age = 0;
            this.sex = "male";
            this.name = name;
        }
    
        public Animal(String name,String sex) {
            this.age = 0;
            this.sex = sex;
            this.name = name;
        }
    
        public Animal(String name,String sex,int age) {
            this.age = age;
            this.sex = sex;
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void eat() {
            System.out.println(name+"正在吃吃吃。");
        }
    
        public void sleep() {
            System.out.println(name+"正在睡觉!!!");
        }
    
        @Override
        public String toString() {
            return "Animal[name:"+name+",age:"+age+",sex:"+sex+"]";
        }
        
        private void owner(String name) {
            System.out.println("owner: "+name);
        }
    }
    

    测试类及测试结果:

    public class Test {
        @SuppressWarnings("unchecked")
        public static void main(String[] args){
            try {
                //通过Class.forName()获取类的Class类
                Class animalClass = Class.forName("com.zhuanget.Animal");
                
                //获取该类的构造器方法类数组
                Constructor[] animalClassConstructors = animalClass.getConstructors();
                System.out.println("============ constructors =============");
                for (int i = 0; i < animalClassConstructors.length; i++) {
                    System.out.println("constructors["+i+"]="+animalClassConstructors[i]);
                }
                //通过参数类型,这里是String,查找相应的构造方法,即Animal(String name)构造方法
                System.out.println("============ constructor ============");
                Constructor constructor = animalClass.getConstructor(String.class);
                System.out.println("constructor="+constructor);
                
                //获取该类的所有公有的方法,包括从父类中继承来的,如java.lang.Object.equals()
                System.out.println("============ methods =============");
                Method[] animalClassMethods = animalClass.getMethods();
                for (int i = 0; i < animalClassMethods.length; i++) {
                    System.out.println("methods["+i+"]="+animalClassMethods[i]);
                }
    
    
                /**
                 * 获取在该类中定义的所有方法,
                 * 包括private、protected、public,
                 * 包括重写父类的,但不包括从父类中继承来的
                 */
                System.out.println("============ declaredMethods =============");
                Method[] declaredMethods = animalClass.getDeclaredMethods();
                for (int i = 0; i < declaredMethods.length; i++) {
                    System.out.println("declaredMethods["+i+"]="+declaredMethods[i]);
                }
                
                //获取类中与相应的方法名、方法参数相对应的方法
                System.out.println("=========== method ============");
                Method method = animalClass.getMethod("setAge", int.class);
                System.out.println("method="+method);
                
                //获取该类的所有公有的域
                System.out.println("============ fields =============");
                Field[] fields = animalClass.getFields();
                for (int i = 0; i < fields.length; i++) {
                    System.out.println("fields["+i+"]="+fields[i]);
                }
                
                //获取该类中所有的域,包括private、protected、public
                System.out.println("============ declaredFields ===========");
                Field[] declaredFiels = animalClass.getDeclaredFields();
                for (int i = 0; i < declaredFiels.length; i++) {
                    System.out.println("declaredFields["+i+"]="+declaredFiels[i]);
                }
                
                //获取该类中域名为name的域类
                System.out.println("========= field =========");
                Field field = animalClass.getField("name");
                System.out.println("field="+field);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    测试结果:

    ============ constructors =============
    constructors[0]=public com.zhuanget.Animal(java.lang.String,java.lang.String,int)
    constructors[1]=public com.zhuanget.Animal(java.lang.String,java.lang.String)
    constructors[2]=public com.zhuanget.Animal(java.lang.String)
    constructors[3]=public com.zhuanget.Animal()
    ============ constructor ============
    constructor=public com.zhuanget.Animal(java.lang.String)
    ============ methods =============
    methods[0]=public java.lang.String com.zhuanget.Animal.toString()
    methods[1]=public java.lang.String com.zhuanget.Animal.getName()
    methods[2]=public void com.zhuanget.Animal.setName(java.lang.String)
    methods[3]=public void com.zhuanget.Animal.sleep()
    methods[4]=public void com.zhuanget.Animal.setAge(int)
    methods[5]=public void com.zhuanget.Animal.setSex(java.lang.String)
    methods[6]=public int com.zhuanget.Animal.getAge()
    methods[7]=public java.lang.String com.zhuanget.Animal.getSex()
    methods[8]=public void com.zhuanget.Animal.eat()
    methods[9]=public final void java.lang.Object.wait() throws java.lang.InterruptedException
    methods[10]=public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
    methods[11]=public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
    methods[12]=public boolean java.lang.Object.equals(java.lang.Object)
    methods[13]=public native int java.lang.Object.hashCode()
    methods[14]=public final native java.lang.Class java.lang.Object.getClass()
    methods[15]=public final native void java.lang.Object.notify()
    methods[16]=public final native void java.lang.Object.notifyAll()
    ============ declaredMethods =============
    declaredMethods[0]=public java.lang.String com.zhuanget.Animal.toString()
    declaredMethods[1]=public java.lang.String com.zhuanget.Animal.getName()
    declaredMethods[2]=public void com.zhuanget.Animal.setName(java.lang.String)
    declaredMethods[3]=public void com.zhuanget.Animal.sleep()
    declaredMethods[4]=public void com.zhuanget.Animal.setAge(int)
    declaredMethods[5]=public void com.zhuanget.Animal.setSex(java.lang.String)
    declaredMethods[6]=public int com.zhuanget.Animal.getAge()
    declaredMethods[7]=public java.lang.String com.zhuanget.Animal.getSex()
    declaredMethods[8]=public void com.zhuanget.Animal.eat()
    declaredMethods[9]=private void com.zhuanget.Animal.owner(java.lang.String)
    =========== method ============
    method=public void com.zhuanget.Animal.setAge(int)
    ============ fields =============
    fields[0]=public java.lang.String com.zhuanget.Animal.name
    ============ declaredFields ===========
    declaredFields[0]=private int com.zhuanget.Animal.age
    declaredFields[1]=protected java.lang.String com.zhuanget.Animal.sex
    declaredFields[2]=public java.lang.String com.zhuanget.Animal.name
    ========= field =========
    field=public java.lang.String com.zhuanget.Animal.name
    

    可以看到,由getMethods()方法得到的,是类中public的方法,private void owner(String name)方法未被输出,同时,Object类中的方法也都被获取到了;由getDeclaredMethods()方法获取到的,是该类中声明的方法,包括private的,父类中的方法并未被输出。


    下面再通过例子说明如何利用Class类实例对象:

    public class Test {
        @SuppressWarnings("unchecked")
        public static void main(String[] args){
            try {
                //通过Class.forName()获取类的Class类
                Class animalClass = Class.forName("com.zhuanget.Animal");
                Object obj = animalClass.newInstance();
                Animal animal = (Animal) obj;
                Field field = animalClass.getField("name");
                field.set(animal,"狗");
                System.out.println(animal);
    
                Field field2 = animalClass.getDeclaredField("age");
                //解除私有限定,由于age是私有属性,不加该句会报错
                field2.setAccessible(true);
                field2.set(animal,3);
                System.out.println(animal);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    运行结果:

    Animal[name:狗,age:0,sex:male]
    Animal[name:狗,age:3,sex:male]
    

    四、其他应用

    通过反射加载配置文件:

    • 类加载器加载配置文件:
    InputStream inputStream = Animal.class.getClassLoader()
    .getResourceAsStream("conf.properties");
    

    即通过类.class.getClassLoader().getResourceAsStream("配置文件")的形式获取配置文件输入流。

    • 类.class.getResourceAsStream()加载,实质还是调用类加载器。截取Class.java的源码如下:
    public InputStream getResourceAsStream(String name) {
            name = resolveName(name);
            ClassLoader cl = getClassLoader0();
            if (cl==null) {
                // A system class.
                return ClassLoader.getSystemResourceAsStream(name);
            }
            return cl.getResourceAsStream(name);//这里依旧是通过类加载器调用
        }
    

    将conf.properties文件(文件中只定义了className=animal)读出的过程如下:

    InputStream inputStream = Animal.class.getClassLoader().
        getResourceAsStream("conf.properties");//通过类加载器加载,这是相对路径读取
    InputStream is = Animal.class.
        getResourceAsStream("/conf.properties");//加斜杆,从根路径找
    if (is==null) {
       throw new RuntimeException("file is not found");
    }
    Properties prop = new Properties();
    prop.load(is);
    String className = null;
    if (prop.containsKey("className")) {
       className = prop.getProperty("className");
    }
    System.out.println(className);
    //运行结果
    "animal"
    

    相关文章

      网友评论

          本文标题:Java笔记之——反射

          本文链接:https://www.haomeiwen.com/subject/qkurpftx.html