Day15 JavaSE 反射机制(下)

JavaSE 反射机制(下)

四、获取类的方法

  • 获取方法函数:

    • public Method[] getDeclaredMethods()

      返回此Class对象所表示的类或接口的全部方法。

    • public Method[] getMethods()

      返回此Class对象所表示的类或接口的public的方法。

  • 常用方法:(在获取方法过程中)

    方法名 功能
    Method[] getMethods() 获取类的所有公有方法
    Method[] getDeclaredMethods() 获取类的所有方法(公有+私有)
    getName() 获取方法名称
    getReturnType() 获取返回值类型
    getModifiers() 获取修饰符(public–>1; private–>2)
    getParameterTypes() 获取方法的参数类型,返回Class[], 数组元素个数为参数个数
  • 上述方法案例:

    package com.reflection;
    //父类
    public class Person {
        public String name;
        int age;
    }
    
    package com.reflection;
    //子类
    public class Student extends Person{
        String school;
      	private String privateField;
        //无参构造
        public Student(){
            System.out.println("调用的是public Student()");
        }
        //有参构造
        public Student(String school){
            System.out.println("调用的是public Student(String school)");
            this.school = school;
        }
        //私有有参构造
        private Student(String name, int age){
            System.out.println("调用的是private Student(String name, int age)");
            this.name = name;
            this.age = age;
        }
        	
     		public void moveType() {
            System.out.println("骑自行车上学");
        }
      
      	public void studyInfo() { System.out.println("学习中学知识"); }
    
        public String getSchool(){
            return this.school;
        }
      
      	private void test(String name){
        }
    }
    
    package com.reflection;
    
    import java.lang.reflect.Method;
    
    public class Test3 {
        public static void main(String[] args) {
            try {
                Class clazz3 =  Class.forName("com.reflection.Student");
              
    	          //获取到类的所有公有方法
                Method[] ms = clazz3.getMethods(); 
    
                for (Method m : ms) {
                    System.out.println("方法名: "+ m.getName());
                    System.out.println("返回值类型: "+ m.getReturnType());
                    System.out.println("修饰符: "+ m.getModifiers());
    
                    Class[] pcs = m.getParameterTypes();//获取方法的参数类型,返回Class数组,方法参数个数等于数组元素个数
                    if (pcs.length>0){
                        for (Class pc : pcs) {
                            System.out.println("参数类型: " + pc.getName());
                        }
                    }
                    System.out.println("-----------------------------------------------");
                }
    
                System.out.println("\n========分==================割===================线===========\n");
    
    	          //获取类的所有方法,包含公有私有
                Method[] ams = clazz3.getDeclaredMethods();
    
                for (Method m : ams) {
                    System.out.println("方法名: "+ m.getName());
                    System.out.println("返回值类型: "+ m.getReturnType());
                    System.out.println("修饰符: "+ m.getModifiers());
    
                    Class[] pcs = m.getParameterTypes();//获取方法的参数类型,返回Class数组,方法参数个数等于数组元素个数
                    if (pcs.length>0){
                        for (Class pc : pcs) {
                            System.out.println("参数类型: " + pc.getName());
                        }
                    }
                    System.out.println("-----------------------------------------------");
                }
    
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    /*运行结果:
    方法名: moveType
    返回值类型: void
    修饰符: 1
    -----------------------------------------------
    方法名: studyInfo
    返回值类型: void
    修饰符: 1
    -----------------------------------------------
    方法名: getSchool
    返回值类型: class java.lang.String
    修饰符: 1
    -----------------------------------------------
    
    ========分==================割===================线===========
    
    方法名: test
    返回值类型: void
    修饰符: 2
    参数类型: java.lang.String
    -----------------------------------------------
    方法名: moveType
    返回值类型: void
    修饰符: 1
    -----------------------------------------------
    方法名: studyInfo
    返回值类型: void
    修饰符: 1
    -----------------------------------------------
    方法名: getSchool
    返回值类型: class java.lang.String
    修饰符: 1
    -----------------------------------------------
    */
    

五、获取类的属性和包

  • 获取属性函数:

    • public Field[] getDeclaredFields()

      返回此Class对象所表示的类或接口的全部属性。

    • public Field[] getFields()

      返回此Class对象所表示的类或接口的public的属性。

    注意:1. getDeclaredFields()仅获取本类中所有属性,不包含父类。

    2. getFields()获取本类及其父类中的public修饰属性。

  • 获取包函数:

    • Package getPackage()

      返回此Class对象所在的包名称。

  • 常用方法:(在获取属性和包中)

    方法名 功能
    getFields() 获取类(本类及父类)的公有属性
    getDeclaredFields() 获取本类中所有的属性(公有+私有)
    getModifiers() 获取属性修饰符
    getType() 获取属性的类型
    getName() 获取属性名称
    Package getPackage() 获取该类所属包名
  • 以上方法案例展示:

    注:父类Person()、子类Student()同(四)中案例。

    package com.reflection;
    
    import java.lang.reflect.Field;
    
    public class Test5 {
        public static void main(String[] args) {
            try {
                Class clazz4 = Class.forName("com.reflection.Student");
                Field[] fs = clazz4.getFields(); //获取类中所有公有属性,同时包含父类的属性
    
                for (Field f : fs) {
                    System.out.println("修饰符: " + f.getModifiers());
                    System.out.println("属性类型: " + f.getType());
                    System.out.println("属性名称: " + f.getName());
                    System.out.println("-----------------------------------");
                }
    
                System.out.println("\n==========分===========割===========线===========\n");
    
                Field[] afs = clazz4.getDeclaredFields(); //获取类中所有属性(私有+公有),不包含父类的属性。
    
                for (Field af : afs) {
                    System.out.println("修饰符: " + af.getModifiers());
                    System.out.println("属性类型: "+af.getType());
                    System.out.println("属性名称: " + af.getName());
                    System.out.println("-----------------------------------");
                }
    
                System.out.println("\n==========分===========割===========线===========\n");
    
    
                Package p = clazz4.getPackage();
                System.out.println(p.getName());
    
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    /*运行结果:
    修饰符: 1
    属性类型: class java.lang.String
    属性名称: school
    -----------------------------------
    修饰符: 1
    属性类型: class java.lang.String
    属性名称: name
    -----------------------------------
    
    ==========分===========割===========线===========
    
    修饰符: 1
    属性类型: class java.lang.String
    属性名称: school
    -----------------------------------
    修饰符: 2
    属性类型: class java.lang.String
    属性名称: privateField
    -----------------------------------
    
    ==========分===========割===========线===========
    
    com.reflection
    
     */
    

六、调用类中指定方法、指定属性

– 调用指定方法

注:方法调用前需要通过构造器创建obj对象,为之后的方法调用做准备!

实际调用分以下四种情况讨论:

  • 调用公有方法

    Method m = clazz.getMethod("调用方法名","参数.class"...);

    m.invoke(obj,"args"...); //此处的invoke英文意思有“唤起”的意思,此处表示向对象obj中注入参数。

  • 调用私有方法

    需要额外注明m.setAccessible(true),该语句表示解除私有封装,强行调用。

    其他语句同上。

  • 调用重载方法

    只需注意填入参数的不同即可。

  • 调用有返回值的方法

    需要注意对返回值进行接收,接受过程中可能需要对对象进行强转。

以上情况案例展示:

注:该案例引入Teacher类,其父类Person同上。

package com.reflection;
//Teacher类
public class Teacher extends Person{

    public String school;
	  private String privateField;

  	//私有方法
    private void test(String name){
        this.name = name;
        System.out.println("这里调用private void test(String name)方法");
    }

  	//公有方法
    public void setInfo(String name, String school){
        this.name = name;
        this.school = school;
        System.out.println("这里调用public void setInfo(String name, String school)方法");
    }

    //重载方法
    public void SetInfo(String school){
        this.school = school;
        System.out.println("这里调用public void setInfo(String school)方法");
    }
	
  	//有返回值方法
    public String getSchool(){
        System.out.println("这里调用public String getSchool()方法");
        return school;
    }

}
package com.reflection;
//调用指定方法
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Test6 {
    public static void main(String[] args) {
        try {
            /*
                以下不论是反射调用setInfo还是test方法
                都调用的obj对象的方法,obj对象实际上就是Teacher对象
             */
            Class clazz5 = Class.forName("com.reflection.Teacher");

            Constructor con = clazz5.getConstructor(); //获取无参构造
            Object obj = con.newInstance(); //使用无参构造创建对象

            //调用公有方法
            Method m = clazz5.getMethod("setInfo", String.class, String.class); //得到名称为setInfo,参数是(String,String)的方法
            m.invoke(obj,"卡莉","第一中学");//参数1为需要实例化的对象,参数2为调用当前方法的实际参数

            //调用私有方法(需要解封)
            Method m1 = clazz5.getDeclaredMethod("test", String.class);
            m1.setAccessible(true); //解除私有封装,则可调用私有方法
            m1.invoke(obj,"苏瞻");

            //调用重载方法,只需注意填入参数不同即可
            Method m2 = clazz5.getMethod("SetInfo", String.class);
            m2.invoke(obj,"第二中学");

            //调用有返回值的方法,需要对返回值进行接收
            Method m3 = clazz5.getMethod("getSchool");
            String school = (String)m3.invoke(obj);//调用有返回值但是没有参数的方法
            System.out.println(school);

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

– 调用指定属性

注:方法调用前需要通过构造器创建obj对象,此处案例强转为Teacher对象,为之后调用属性准备!

实际调用分两种情况讨论:

  • 调用公有属性

    Field f = clazz.getField("属性名称");

    f.set(对象名,“传入参数”); //设置对应属性值

    f.get(对象名);//获取对象的对应属性值

  • 调用私有属性

    Field f = clazz.getDeclaredField("私有属性名称");

    f.setAccessible(true) //非常重要!!!进行解除封装

    f.set(对象名,“传入参数”)//设置对应属性值

    f.get(对象名);//获取对象的对应属性值

以上情况案例展示:

注:Teacher类同上

package com.reflection;

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

public class Test7 {
    public static void main(String[] args) {
        try {
            Class clazz6 = Class.forName("com.reflection.Teacher");
            //反射创建一个对象
            Constructor con = clazz6.getConstructor();
            Teacher tea = (Teacher)con.newInstance();

            //调用公有属性
            Field f = clazz6.getField("school");
            f.set(tea,"第三中学");//对tea对象的school属性设置值 "第三中学"
            String school = (String)f.get(tea); //获取tea对象的school属性值
            System.out.println(school);

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

            //调用私有属性:使用getDeclareField()方法,并解除封装
            Field f1 = clazz6.getDeclaredField("privateField");
            f1.setAccessible(true);
            f1.set(tea,"测试私有属性");
            String field = (String)f1.get(tea);
            System.out.println(field);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/*运行结果:
第三中学
----------------
测试私有属性
 */

七、动态代理

什么是动态代理?

例:一个Java项目,其中有100个java类,每个java有10个方法,总共1000个方法。现在有一个需求,需要在每个java方法上加上2句话,在方法执行前输出“这个方法开始执行”,在方法执行后输出“这个方法已经完成”。此时就需要使用到动态代理!

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

动态代理步骤:

  1. 创建需要被代理的类以及接口。

  2. 创建一个实现接口InvocationHandler的类,它必须实现invoke方法,以完成代理的具体操作。(ProxyDemo为实现接口的类,test为被代理对象),则使用如下语句生成代理对象handler。

    InvocationHandler handler = new ProxyDemo(test);

  3. 通过Proxy的静态方法,返回一个Subject接口代理。

    newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

  4. 通过Subject代理调用RealSubject实现类的方法。

注意如果一个对象想要通过Proxy.newProxyInstance方法被代理,那么这个对象的类一定要有相应的接口。

案例展示:

package com.proxy;
//被代理的接口
public interface ITestDemo {
    void test1();
    void test2();
}
package com.proxy;
//实现被代理接口的类
public class TestDemoImpl implements ITestDemo{
    @Override
    public void test1() {
        System.out.println("执行test1()方法");
    }

    @Override
    public void test2() {
        System.out.println("执行test2()方法");
    }
}
package com.proxy;
//实现了InvocationHandler接口的动态代理类
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 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; //返回Object对象
    }
}

注意:其中invoke()方法有三个参数:

  1. 动态代理类的引用,通常情况下不需要它。但可以使用getClass()方法,得到proxy的Class类从而取得实例的类信息,如方法列表,annotation等。

  2. 方法对象的引用,代表被动态代理类调用的方法。从中可得到方法名,参数类型,返回类型等等

  3. args对象数组,代表被调用方法的参数。注意基本类型(int,long)会被装箱成对象类型(Interger, Long)

package com.proxy;
//测试类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Test1 {
    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 handler = new ProxyDemo(test); //handler为代理对象,代理test

        //参数1为代理对象的类加载器
        //参数2为被代理的对象的接口
        //参数3为代理对象
        //返回的值是成功被代理后的对象,返回Object类型,需要根据当时的情况去转换类型
        ITestDemo t = (ITestDemo) Proxy.newProxyInstance(handler.getClass().getClassLoader(),test.getClass().getInterfaces(),handler);
        t.test1();
        System.out.println("====================================");
        t.test2();
    }
}
/*测试结果:
执行test1()方法
执行test2()方法
====================================
test1方法开始执行
执行test1()方法
test1方法执行完成
====================================
test2方法开始执行
执行test2()方法
test2方法执行完成
*/

注意:Proxy.newProxyInstance()方法有三个参数:

  1. 代理对象类加载器(Class Loader)

  2. 被代理对象的接口

  3. InvocationHandler接口实例对象(也称代理对象)。所有动态代理类的方法调用,都会交由InvocationHandler接口实现类里的invoke()方法去处理。这是动态代理的关键所在。

写在最后

我以后会经常抱你。

To Dottie!

发布了25 篇原创文章 · 获赞 19 · 访问量 1144

猜你喜欢

转载自blog.csdn.net/qq_44958172/article/details/104832030