反射

  • 反射概要
  • Java Reflection
  • Java反射机制提供的功能
  • Java反射机制研究及应用
  • 一、 Class类和实例化Class类对象
  • Class类
  • 实例化Class类对象(四种方法)
  • 二、运行时创建类对象并获取类的完整结构
  • 1.实现的全部接口和继承的父类
  • 2.获取公有的构造器和包括私有的全部构造器
  • 3.用反射创建一个对象
  • 4.获取全部的方法
  • 5.获取全部的Field和所在类的包
  • 三、通过反射调用类的指定方法、指定属性
  • 1.调用指定方法
  • 2. 调用指定属性
  • 四、动态代理



反射概要

Java Reflection

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

Java反射机制提供的功能

在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时调用任意一个对象的成员变量和方法
生成动态代理

Java反射机制研究及应用

反射相关的主要API:
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造方法

一、 Class类和实例化Class类对象

Class类

在Object类中定义了以下的方法,此方法将被所有子类继承:
      public final Class getClass()
以上的方法返回值的类型是一个Class类,此类是Java反射的源头, 实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称

反射可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。

  • Class本身也是一个类
  • Class 对象只能由系统建立对象
  • 一个类在 JVM 中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 每个类的实例都会记得自己是由哪个 Class 实例所生成
  • 通过Class可以完整地得到一个类中的完整结构

Java 反射调用 service 并执行方法 原创_开发语言


注意:Declared代表全部,可以访问全部东西包括私有
因此有很多方法都类似 比如
getDeclaredConstructors();//获取类的所有构造方法,包括公有和私有的
getDeclaredMethods();//返回此Class对象所表示的类或接口的全部方法
getDeclaredFields();// 返回此Class对象所表示的类或接口的全部Field。

实例化Class类对象(四种方法)

前提:若已知具体的类,通过类的class属性获取,该方法 最为安全可靠,程序性能最高
      实例:Class clazz = String.class;

前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象
      实例:Class clazz = “www.xyd.com”.getClass();

前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException(这个是最常用的)
      实例:Class clazz = Class.forName(“java.lang.String”);

其他方式(不做要求)
      ClassLoader cl = this.getClass().getClassLoader();
      Class clazz4 = cl.loadClass(“类的全类名”);

public class text {
    public static void main(String[] args) {
        Person p = new Person();
        Class clazz = p.getClass();//clazz对象中就包含对象p所属的Person类的所有的信息

        //实例化Class类对象
        Class c0 = Person.class;//通过类名.class创建指定类的Class实例

        Class c1 = p.getClass();//通过一个类的实例对象的getClass()方法,获取对应实例对象的类的Class实例

        try {
            //通过Class的静态方法forName(String className)来获取一个类的Class实例
            //forName(String className)方法中的参数是你要获取的Class实例的类的全路径(包名.类名)
            Class c2 = Class.forName("D:\\JavaProjects\\b站草稿\\反射\\src\\Person.java");
            //这个是获取Class实例的常用方式
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

二、运行时创建类对象并获取类的完整结构

实现的全部接口: Interface

所继承的父类: Superclass

全部的构造器: Constructor

全部的方法: Method

全部的Field: Field

1.实现的全部接口和继承的父类

使用反射可以取得:
1.实现的全部接口
      public Class<?>[] getInterfaces()
确定此对象所表示的类或接口实现的接口。

2.所继承的父类
      public Class<? Super T> getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。

public class text1 {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("Student");//通过包名.类名的字符串,调用Class.forName方法获取指定类的Class实例

            Class superclazz = clazz.getSuperclass();//获取父类
            System.out.println("父类:" + superclazz.getName());

            Class[] interfaces = clazz.getInterfaces();//获取接口
            for (Class anInterface : interfaces) {
                System.out.println("接口:" + anInterface);
            }

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

2.获取公有的构造器和包括私有的全部构造器

public Constructor[] getConstructors( )
     返回此 Class 对象所表示的类的所有public构造方法。
public Constructor[] getDeclaredConstructors( )
     返回此 Class 对象表示的类声明的所有构造方法。

Constructor类中:
     取得修饰符: public int getModifiers( );
     取得方法名称: public String getName( );
     取得参数的类型:public Class<?>[] getParameterTypes( );

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

public class text2 {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("Student");//通过包名.类名的字符串,调用Class.forName方法获取指定类的Class实例

            Constructor[] constructors = clazz.getConstructors();//获取public的方法
            for (Constructor c : constructors) {
                //getModifiers取得方法的修饰符,返回数组1代表public
                System.out.println("方法名为:" + c.getName() + "修饰符为:" + c.getModifiers());
            }

            //输出方法加参数
            for (Constructor c1 : constructors) {
                System.out.println(c1);
            }

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


            Constructor[] constructors1 = clazz.getDeclaredConstructors();//获取类的所有构造方法,包括公有和私有的
            for (Constructor c1 : constructors1) {
                //getModifiers取得方法的修饰符,返回数组1代表public,返回数组2代表是private
                System.out.println("方法名为:" + c1.getName() + "修饰符为:" + c1.getModifiers());
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
//
方法名为:Student修饰符为:1
方法名为:Student修饰符为:1
public Student(java.lang.String)
public Student()
===========================
方法名为:Student修饰符为:2
方法名为:Student修饰符为:1
方法名为:Student修饰符为:1

3.用反射创建一个对象

注意:
1.9之前创建对象的方法是:

  • Object obj = clazz.newInstance();//相当于调用Student类的无参公有的构造方法
    Student stu = (Student)obj;
  • Constructor c = clazz.getConstructor(String.class);//指定获取有一个参数并且为String类型的公有的构造方法
    Student stu1 = (Student)c.newInstance(“第一中学”);//newInstance实例化对象,相当于调用public Student

1.9之后方法变成:

  • Object obj = clazz.getConstructor().newInstance();//调用public无参构造创建对象
  • Object obj1 = clazz.getConstructor(String.class).newInstance(“第一中学”);//调用public有参构造创建对象

但是调用私有构造方法创建对象没变还是;

  • //调用private构造方法创建对象
    Constructor constructor = clazz.getDeclaredConstructor(String.class,int.class);
    //解除私有分装
    constructor.setAccessible(true);
    Student student = (Student) constructor.newInstance(“zhangsan”,14);

再注意:解除私有分装的方法为 constructor.setAccessible(true);

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

public class text3 {
    public static void main(String[] args) {
        try {


            Class clazz = Class.forName("Student");//通过包名.类名的字符串,调用Class.forName方法获取指定类的Class实例
            try {
                //调用public无参构造创建对象
                Object obj = clazz.getConstructor().newInstance();
                // Student stu = (Student)obj;


                //调用public有参构造创建对象 1.9之后的新方法
                //因为getConstructor接收的参数类型是class,所以传参数时要用String.class转为class
                Object obj1 = clazz.getConstructor(String.class).newInstance("第一中学");


                //调用private构造方法创建对象
                Constructor constructor = clazz.getDeclaredConstructor(String.class,int.class);
                //解除私有分装
                constructor.setAccessible(true);
                Student student = (Student) constructor.newInstance("zhangsan",14);

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


        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
//
调用的是public Student()
调用的是public Student(String school)
调用的是private Student(String name,int age)

4.获取全部的方法

获取方法:
     public Method[] getDeclaredMethods()
返回此Class对象所表示的类或接口的全部方法
     public Method[] getMethods()
返回此Class对象所表示的类或接口的public的方法

Method类中:
     public Class<?> getReturnType()取得全部的返回值
     public Class<?>[] getParameterTypes()取得全部的参数
     public int getModifiers()取得修饰符

import java.lang.reflect.Method;

public class text4 {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("Student");//通过包名.类名的字符串,调用Class.forName方法获取指定类的Class实例

            Method[] m = clazz.getMethods();
            for (Method method : m) {//获取到类的所有公有的方法
                System.out.println("方法名" + method.getName());
                System.out.println("返回值类型" + method.getReturnType());
                System.out.println("修饰符" + method.getModifiers());
                System.out.println("----------------------------");
            }

            System.out.println("===================================================");
            Method[] m1 = clazz.getDeclaredMethods();
            for (Method method1 : m1) {//获取本类所有方法,包含公有和私有
                System.out.println("方法名" + method1.getName());
                System.out.println("返回值类型" + method1.getReturnType());
                System.out.println("修饰符" + method1.getModifiers());
                System.out.println("----------------------------");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
//
方法名studyinfo
返回值类型void
修饰符1
----------------------------
方法名getSchool
返回值类型class java.lang.String
修饰符1
----------------------------
方法名movetype
返回值类型void
修饰符1
----------------------------
方法名showinfo
返回值类型void
修饰符1
----------------------------
方法名wait
返回值类型void
修饰符273
----------------------------
方法名wait
返回值类型void
修饰符17
----------------------------
方法名wait
返回值类型void
修饰符17
----------------------------
方法名equals
返回值类型boolean
修饰符1
----------------------------
方法名toString
返回值类型class java.lang.String
修饰符1
----------------------------
方法名hashCode
返回值类型int
修饰符257
----------------------------
方法名getClass
返回值类型class java.lang.Class
修饰符273
----------------------------
方法名notify
返回值类型void
修饰符273
----------------------------
方法名notifyAll
返回值类型void
修饰符273
----------------------------
===================================================
方法名test
返回值类型void
修饰符2
----------------------------
方法名studyinfo
返回值类型void
修饰符1
----------------------------
方法名getSchool
返回值类型class java.lang.String
修饰符1
----------------------------
方法名movetype
返回值类型void
修饰符1
----------------------------
方法名showinfo
返回值类型void
修饰符1
----------------------------

注意:getDeclaredMethods()返回的是本类!写的所有方法,并不包括其他没写出来的方法

5.获取全部的Field和所在类的包

     public Field[] getFields()
返回此Class对象所表示的类或接口的public的Field。获取类的公有的属性,包含父类的公有属性
     public Field[] getDeclaredFields()
返回此Class对象所表示的类或接口的全部Field。获取本类的(不包括父类的属性)所有的属性,包括私有

Field方法中:
     public int getModifiers() 以整数形式返回此Field的修饰符
     public Class<?> getType() 得到Field的属性类型
     public String getName() 返回Field的名称。

import java.lang.reflect.Field;

public class text5 {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("Student");//通过包名.类名的字符串,调用Class.forName方法获取指定类的Class实例

            Field[] f = clazz.getFields();//获取类的公有的属性,包含父类的公有属性
            for (Field fi : f) {
                System.out.println("属性名称"+fi.getName());
                System.out.println("修饰符为" + fi.getModifiers());
                System.out.println("属性的类型" + fi.getType());
                System.out.println("------------------------");
            }
            System.out.println("========================");

            Field[] f1 = clazz.getDeclaredFields();//获取本类的(不包括父类的属性)所有的属性,包括私有
            for (Field fi1 : f1) {
                System.out.println("属性名称"+fi1.getName());
                System.out.println("修饰符为" + fi1.getModifiers());
                System.out.println("属性的类型" + fi1.getType());
                System.out.println("------------------------");
            }

            Package p = clazz.getPackage();//获取类所在的包
			System.out.println(p.getName());

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
//
属性名称school
修饰符为1
属性的类型class java.lang.String
------------------------
属性名称name
修饰符为1
属性的类型class java.lang.String
------------------------
========================
属性名称school
修饰符为1
属性的类型class java.lang.String
------------------------
属性名称privateField
修饰符为2
属性的类型class java.lang.String
------------------------

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

1.调用指定方法

通过反射,调用类中的方法,通过Method类完成。步骤:

  • 通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
  • 2.之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。  

Object invoke(Object obj, Object … args)

1.Object 对应原方法的返回值,若原方法无返回值,此时返回null
2.若原方法若为静态方法,此时形参Object obj可为null
3.若原方法形参列表为空,则Object[] args为null
4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

import java.lang.reflect.Method;

public class text6 {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("Student");//通过包名.类名的字符串,调用Class.forName方法获取指定类的Class实例
            /**
             * 注意:下面不论是反射调用setInfo还是test方法
             * 都调用的obj对象的方法,obj对象实际上就是student对象
             */
            //用无参构造创建新对象
            Object object = clazz.getConstructor().newInstance();
            Method m = clazz.getMethod("setInfo",String.class,String.class);//三个参数,得到名称叫setInfo,参数是String,String的方法
            m.invoke(object,"zhangsan","fristSchool");//要用一个方法需要一个对象才能使用,所以这里传入对象

            //如果想要调用一个私有方法
			Method m1 = clazz.getDeclaredMethod("test", String.class);//获取方法名为test,参数为1个String类型的方法
			m1.setAccessible(true);//解除私有的封装,下面可以强制调用私有的方法
			m1.invoke(object, "李四");

			//调用一个重载方法
			Method m2 = clazz.getMethod("setInfo", int.class);//setInfo的重载方法
			m2.invoke(object, 1);

            //有返回值的方法
			Method m3 = clazz.getMethod("getSchool");//这是获取方法名为getSchool并且没有参数的方法
			String school = (String)m3.invoke(object);//调用有返回值的但是没有参数的方法
			System.out.println(school);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
//
调用的是public Student()
这个是setInfo(String name,String school)方法
这是私有方法private void test(String name)
这个是public void setInfo(int age)方法
fristSchool

2. 调用指定属性

在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。
      public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。
      public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。

在Field中:
      public Object get(Object obj) 取得指定对象obj上此Field的属性内容
      public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容

注:在类中属性都设置为private的前提下,在使用set()和get()方法时,首先要使用Field类中的setAccessible(true)方法将需要操作的属性设置为可以被外部访问。public void setAccessible(true)访问私有属性时,让这个属性可见。

import java.lang.reflect.Field;

public class text7 {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("Student");//通过包名.类名的字符串,调用Class.forName方法获取指定类的Class实例
/**
             * 注意:下面不论是反射调用setInfo还是test方法
             * 都调用的obj对象的方法,obj对象实际上就是student对象
             */
            Object obj = clazz.getConstructor().newInstance();
            Field f = clazz.getField("school");//获取名称为school的属性
            f.set(obj,"thefrist");//对stu对象的school属性设置值"thefrist"
            String s = (String) f.get(obj);//获取stu对象的school属性的值
            System.out.println(s);


            //如果是私有的属性
            Field f1 = clazz.getDeclaredField("privateField");

            f1.setAccessible(true);//解除私有的封装,下面就可以强制的调用这个属性

            f1.set(obj, "测试私有属性");
            System.out.println(f1.get(obj));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

四、动态代理

Proxy :专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。

   创建一个动态代理类所对应的Class对象

      static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 直接创建一个动态代理对象

Java 反射调用 service 并执行方法 原创_后端_02

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

    @Override
    public void test2() {
        System.out.println("执行test2()方法");
    }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

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

    Object obj;//被代理的对象

    public ProxyDemo(Object obj){
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {

        System.out.println(method.getName() + " 方法开始执行");

        Object result = method.invoke(this.obj, args);//执行的是指定代理对象的指定的方法

        System.out.println(method.getName() + " 方法执行完毕");
        return result;
    }

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

public class text8 {
    public static void main(String[] args) {
        /**
         * 注意:如果一个对象想要通过Proxy.newProxyInstance方法被代理,
         * 那么这个对象的类一定要有相应的接口
         * 就像本类中的ITestDemo接口和实现类TestDemoImpl
         */

        ITestDemo test = new TestDemoImpl();//用接口去接收实例对象(多态)
        test.test1();
        test.test2();
        System.out.println("======================");
        /**
         * 需求:
         * 在执行test1和test2方法时需要加入一些东西
         * 在执行方法前打印test1或test2开始执行
         * 在执行方法后打印test1或test2执行完毕
         * 打印的方法名要和当时调用方法保存一致
         */

        InvocationHandler hander = new ProxyDemo(test);

        /**
		 * Proxy.newProxyInstance(ClassLoader, interfaces, h)
                * 参数1是代理对象的类加载器
                * 参数2是被代理的对象的接口
                * 参数3是代理对象
                *
		 * 返回的值就成功被代理后对象,返回的是Object类型,需要根据当时的情况去转换类型
                */

        ITestDemo t = (ITestDemo) Proxy.newProxyInstance(hander.getClass().getClassLoader(),test.getClass().getInterfaces(),hander);
        t.test1();
        System.out.println("-----------------------");
        t.test2();

    }
}