反射

主要内容:

  1. 理解 Class 类并实例化 Class 类对象。
  2. 运行时创建类对象并获取类的完整结构。
  3. 通过反射调用类的指定方法、指定属性。
  4. 动态代理。
Java Reflection

Reflection(反射)被视为动态语言的关键,反射机制允许程序在执行期借助于 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

Java反射机制提供的功能
  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的成员变量和方法
  • 生成动态代理
Java反射机制研究及应用

反射相关的API:

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造方法

一、Class类

(1)在Object类中定义了:public final Class getClass()
此方法被所有子类继承。
该方法返回一个Class类,此类是Java反射的源头。可以通过对象反射求出类的名称。

(2)Class 类可以描述所有的类。

(3)对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个类的有关信息。

(4)Class本身也是一个类。

(5)Class对象只能由系统建立。

(6)一个类在JVM中只会有一个Class实例。

(7)一个Class对象对应的是一个加载到JVM中的一个.class文件。

(8)每个类的实例都会记得自己是由哪个Class实例所生成。

(9)通过Class可以完整地得到一个类中的完整结构。

public class Test {
    public static void main(String[] args) {
        //实例化Class类对象的常用方法

        Class c0 = Person.class; //通过类名.class创建指定类的Class实例
        Class c1 = new Person().getClass();  //通过一个类的实例对象的.getClass()
        try {
            Class c2 = Class.forName("Person"); //通过Class的静态方法forName()
                                                //使用全路径:包名.类名
                                                //此方式为常用方式
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class Person {
    String name;
    int age;
}

二、通过反射调用类的完整结构

获取接口和父类:

public class Test {
    public static void main(String[] args) {
        try {
            Class c = Class.forName("Student");
            Class su = c.getSuperclass(); //获取父类
            System.out.println(su.getName());
            Class[] interfaces = c.getInterfaces(); //获取接口
            for (Class f : interfaces) {
                System.out.println(f.getName());
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class Person {
    String name;
    int age;
}

class Student extends Person implements Move,Study {
    String school;

    public void showInfo() {
        System.out.println("学校是:" + this.school);
    }

    @Override
    public void moveType() {
        System.out.println("骑自行车");
    }

    @Override
    public void studyInfo() {
        System.out.println("学英语");
    }
}

//接口
interface Move {
    void moveType();
}
interface Study {
    void studyInfo();
}

获取全部构造器:

import java.lang.reflect.Constructor;
import java.lang.reflect.TypeVariable;

public class Test {
    public static void main(String[] args) {
        try {
            Class c = Class.forName("Student");
            Constructor[] cons = c.getConstructors(); //获取类的公有构造方法
            for (Constructor cs : cons) {
                System.out.println(cs.getName() + cs.getModifiers());
                //getModifiers()取得返回方法的修饰符,返回1代表public,返回2代表private
            }
            c.getDeclaredConstructors(); //获取类的所有构造方法,包含公有和私有
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class Student {
    int age;
    String name;
    String school;

    public Student() {
        //
    }
    public Student(String school) {
        this.school = school;
    }
    private Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

可以获取构造器、构造器的修饰符(1public还是2private)、构造器的参数类型。

那么,如何用反射的构造方法来创建对象

import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) {
        try {
            Class c = Class.forName("Student");
            try {
                Object obj = c.newInstance(); //相当于调用Student类的一个公有无参构造方法
                Student stu = (Student)obj;

                Constructor cons = c.getConstructor(String.class); //指定获取有一个String参数的公有构造方法
                Object obj1 = cons.newInstance("清华大学");
                Student stu1 = (Student) obj1;

                //另外,用过反射机制可以强制调用私有构造方法
                Constructor conss = c.getDeclaredConstructor(String.class,int.class);
                conss.setAccessible(true); //解除封装
                Student stu2 = (Student)conss.newInstance("清华大学",18);

            }
            catch (Exception e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class Student {
    int age;
    String name;
    String school;

    public Student() {
        System.out.println("调用的是public Student()");
    }
    public Student(String school) {
        this.school = school;
        System.out.println("调用的是public Student(String school)");
    }
    private Student(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("调用的是private Student(String name, int age)");
    }
}

通过反射获取类的全部方法:

import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) {
        try {
            Class student = Class.forName("Student");
//            Method[] ms = student.getMethods();  //获取公有方法
            Method[] ms = student.getDeclaredMethods();  //获取全部方法
            for (Method m : ms) {
                System.out.println("方法名:" + m.getName());
                System.out.println("返回值类型:" + m.getReturnType());
                System.out.println("修饰符:" + m.getModifiers());
                Class[] pcs = m.getParameterTypes(); //获取方法的参数类型
                if (pcs != null && pcs.length > 0) {
                    for (Class pc : pcs) {
                        System.out.println("参数类型:" + pc.getName());
                    }
                }
                System.out.println("========================");
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class Student {
    String name;
    private void test() {
        System.out.println("test()方法");
    }
    public String getName() {
        return this.name;
    }
}

通过反射获取全部Field(属性)

import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) {
        try {
            Class c = Class.forName("Student");
            Field[] fields = c.getFields(); //获取公有属性,包含父类的公有属性
            Field[] fields1 = c.getDeclaredFields(); //获取本类的所有属性 不包括父类的属性(公有也不包括)
            for (Field f : fields1) {
                System.out.println("修饰符:" + f.getModifiers());
                System.out.println("属性的类型:" + f.getType());
                System.out.println("属性的名称:" + f.getName());

                System.out.println("=======================");
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}


class Person {
    public int a;
}
class Student extends Person{
    public String name;
    public int age;
    private int grades;
}

获取类所在的包

Package p = c.getPackage();  //c是某一个类的Class实例

三、通过反射调用类中的指定方法、指定属性

1.调用指定方法

package day14;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) {
        try {
            Class c = Class.forName("day14.Student");
            //getMethod()和getMethods():加s的是返回很多方法的数组,不加是调用某一个指定的方法
            Method m = c.getMethod("setInfo",String.class,String.class);
            Constructor con = c.getConstructor();
            Object obj = con.newInstance();
            m.invoke(obj,"胡杨","清华大学");

            //如果想要调用一个私有方法
            Method m1 = c.getDeclaredMethod("test");
            m1.setAccessible(true); //接触私有封装,下面可以强制调用私有的方法
            m1.invoke(obj);

            //调用一个重载方法
            Method m2 = c.getMethod("setInfo", String.class);
            m2.invoke(obj,"胡杨林");

            //有返回值的方法
            Method m3 = c.getMethod("getSchool");
            String school = (String) m3.invoke(obj);
            System.out.println(school);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Student {
    String name;
    String school;

    public Student() {
        //
    }

    public void setInfo(String name, String school) {
        this.name = name;
        this.school = school;
        System.out.println("setInfo(String name, String school)方法");
    }

    private void test() {
        System.out.println("私有方法");
    }

    public void setInfo(String name) {
        this.name = name;
        System.out.println("重载setInfo(String name, String school)方法");
    }

    public String getSchool() {
        return this.school;
    }
}

注意:不论调用哪个方法,都是调用的obj对象的方法。obj对象实际上就是Student对象。

2.调用指定属性

package day14;

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

public class Test {
    public static void main(String[] args) {
        try {
            Class c = Class.forName("day14.Student");
            Constructor cons = c.getConstructor();
            Student stu = (Student) cons.newInstance();
            Field f1 = c.getField("name");
            f1.set(stu,"胡杨");
            String name = (String) f1.get(stu);
            System.out.println(name);

            //私有属性
            //注意是加Declared
            Field f2 = c.getDeclaredField("grades");
            f2.setAccessible(true);
            f2.set(stu,80);
            int scores = (int) f2.get(stu);
            System.out.println(scores + "分");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Student {
    public Student() {
        //
    }

    public String name;
    public int age;
    private int grades;
}

四、Java 动态代理

package day14;

public interface ITestDemo {
    void test1();
    void test2();
}
package day14;

public class TestDemoImpl implements ITestDemo {
    @Override
    public void test1() {
        System.out.println("执行test1()方法");
    }

    @Override
    public void test2() {
        System.out.println("执行test2()方法");
    }
}
package day14;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 动态代理类
 */
public class ProxyDemo implements InvocationHandler {

    Object obj; //被代理的对象
    public ProxyDemo(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        System.out.println(method.getName() + "开始执行");
        Object result = method.invoke(this.obj,objects);
        System.out.println(method.getName() + "执行结束");
        return result;
    }
}
package day14;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
        ITestDemo test = new TestDemoImpl();
        test.test1();
        test.test2();

        System.out.println("===============================");

        //若需要在执行test1()和test2()方法时需要加入一些东西
        //要求在执行方法前打印test1或test2开始执行
        //在执行方法后打印test1或test2执行完毕
        //且打印的方法名要和当时调用的方法保一致

        InvocationHandler handle = new ProxyDemo(test);
        /**
         * 第一个参数是代理对象的类加载器
         * 第二个参数是被代理的对象的接口
         * 第三个参数是代理对象
         * 返回的值就是成功被代理后的对象,返回的是Object类型,需要根据情况转换类型
         */
        ITestDemo t = (ITestDemo) Proxy.newProxyInstance(handle.getClass().getClassLoader(),test.getClass().getInterfaces(),handle);
        t.test1();
        t.test2();
        /**
         * 注意:如果一个对象想要通过Proxy.newProxyInstance()方法被代理,
         * 那么这个对象的类一定要有相应的接口
         * 就像本例中的ITestDemo接口和实现类TestDemoImpl
         */
    }
}

动态代理的步骤:

  1. 创建一个实现接口的InvocationHandler的类,它必须实现invoke方法,以实现代理的具体操作。
  2. 创建被代理的类及接口。
  3. 通过Proxy的静态方法。newProxyInstance(ClassLoader loader,Class[] interfaces, InvocationHander h) 创建一个Subject接口代理。
  4. 通过Subject代理调用RealSubject实现类的方法。

你可能感兴趣的:(反射)