Java 类的反射

一、什么是反射?

Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。

二、反射的三种方式

这里需要跟大家说一下,所谓反射其实是获取类的字节码文件,也就是.class文件,那么我们就可以通过Class这个对象进行获取。

1、第一种方式

getClass

public final Class<?> getClass()

Returns the runtime class of this Object. The returned Class object is the object that is locked by static synchronized methods of the represented class.

返回类的运行实例

这个方法其实是Object的一个方法,Class继承了Object,所以我们可以直接使用。

package cn.leokim;

public class Reflact {
    public static void main(String[] args) {
        //创建一个对象
        Test t = new Test();

        //获取该对象的Class对象
        Class c = t.getClass();

        //获取类名称
        System.out.println(c.getName());
    }
}

2.第二种方式

package cn.leokim;

public class Reflact {
    public static void main(String[] args) {
        Class c = Test.class

        //获取类名称
        System.out.println(c.getName());
    }
}

3.第三种方式

package cn.leokim;

public class Reflact {
    public static void main(String[] args) {
        //这里需要注意,通过类的全路径名获取Class对象会抛出一个异常,如果根据类路径找不到这个类那么就会抛出这个异常。
        try {
            Class c = Class.forName("cn.leokim.Test");

            //获取类名称
            System.out.println(c.getName());
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }
    }
}

那么这3中方式我们一般选用哪种方式呢?第一种已经创建了对象,那么这个时候就不需要去进行反射了,显得有点多此一举。第二种需要导入类的包,依赖性太强。所以我们一般选中第三种方式。

三、通过反射获取类的构造方法、方法以及属性

1、获取构造方法

package cn.leokim;

import java.lang.reflect.Constructor;

public class Reflact {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        //加载Class对象
        Class c = Class.forName("cn.leokim.Test");

        System.out.println("======================= 获取所有公用的构造方法 =======================");
        //获取所有公用的构造方法
        Constructor[] constructors = c.getConstructors();

        for (Constructor constructor : constructors){
            System.out.println(constructor);
        }

        System.out.println("======================= 获取所有公用的构造方法 =======================");
        //获取所有构造方法
        Constructor[] declareConsturctors = c.getDeclaredConstructors();

        for (Constructor declareConsturctor : declareConsturctors){
            System.out.println(declareConsturctor);
        }

        System.out.println("======================= 获取所有 公有 & 无参 的构造方法 =======================");
        Constructor constructor = c.getConstructor(null);
        System.out.println(constructor);

        System.out.println("======================= 获取所有 公有 & 有参 的构造方法 =======================");
        Constructor constructor1 = c.getConstructor(String.class);
        System.out.println(constructor1);

        System.out.println("======================= 获取所有 私有 & 有参 的构造方法 =======================");
        Constructor declareConsturctors1 = c.getDeclaredConstructor(String.class, Integer.class);
        System.out.println(declareConsturctors1);
    }
}

结果:

image.png

2、获取类属性

Test.java

package cn.leokim;

public class Test {


    public String getName() {
        return name;
    }

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

    public String name;

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    private String sex;

    public String public_field2;
    private String private_field3;


    public Test() {
        System.out.println("no args constructor");
    }

    public Test(String name){
        System.out.println("Test: "+ name);
    }

    private Test(String name, Integer age){
        System.out.println("Name: "+ name + "Age "+ age );
    }

    public void t(){
        System.out.println("print t function.");
    }

}

Reflact.java

package cn.leokim;

import sun.jvm.hotspot.oops.ObjectHeap;

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

public class Reflact {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //加载Class对象
        Class c = Class.forName("cn.leokim.Test");

//        System.out.println("======================= 获取所有公用的构造方法 =======================");
//        //获取所有公用的构造方法
//        Constructor[] constructors = c.getConstructors();
//
//        for (Constructor constructor : constructors){
//            System.out.println(constructor);
//        }
//
//        System.out.println("======================= 获取所有公用的构造方法 =======================");
//        //获取所有构造方法
//        Constructor[] declareConsturctors = c.getDeclaredConstructors();
//
//        for (Constructor declareConsturctor : declareConsturctors){
//            System.out.println(declareConsturctor);
//        }
//
//        System.out.println("======================= 获取所有 公有 & 无参 的构造方法 =======================");
//        Constructor constructor = c.getConstructor(null);
//        System.out.println(constructor);
//
//        System.out.println("======================= 获取所有 公有 & 有参 的构造方法 =======================");
//        Constructor constructor1 = c.getConstructor(String.class);
//        System.out.println(constructor1);
//
//        System.out.println("======================= 获取所有 私有 & 有参 的构造方法 =======================");
//        Constructor declareConsturctors1 = c.getDeclaredConstructor(String.class, Integer.class);
//        System.out.println(declareConsturctors1);


        System.out.println("========= 获取所有的公共字段 ==========");
        Field[] fields = c.getFields();

        for(Field field : fields){
            System.out.println(field);
        }

        System.out.println("========= 获取所有的字段(public & private) ==========");
        Field[] declaredFields = c.getDeclaredFields();

        for(Field field : declaredFields){
            System.out.println(field);
        }

        System.out.println("========= 获取公有字段并使用 ==========");
        Field field = c.getField("name");
        Object obj = c.getConstructor().newInstance();

        //为属性设置值
        field.set(obj, "LeoKim");
        Test test = (Test) obj;
        System.out.println("Name is: " + test.getName());

        System.out.println("========= 获取私有字段并使用 ==========");
        Field field1 = c.getDeclaredField("sex");
        Object obj1 = c.getConstructor().newInstance();

        //暴力反射
        field1.setAccessible(true);
        field1.set(obj1, "男");
        Test test1 = (Test) obj1;
        System.out.println(test1.getSex());
    }
}[object Object]

这里需要注意,在获取私有属性的时候如果没有进行暴力反射,那么会抛出下面这个异常。

3.获取类中的方法

先定义几个方法

public void method1(String str){
    System.out.println("public method.");
}

private void method2(){
    System.out.println("private method.");
}

String method3(String name, String sex){
    System.out.println("default method: " + name + " & " + "sex");
    return name + sex;
}

protected  void method4(){
    System.out.println("protected method.");
}

正题:

package cn.leokim;

import sun.jvm.hotspot.oops.ObjectHeap;

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

public class Reflact {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException, InterruptedException {
        //加载Class对象
        Class c = Class.forName("cn.leokim.Test");

        System.out.println("================ 获取所有的public方法 ================");
        Method[] methods = c.getMethods();

        for (Method method : methods){
            System.out.println(method);
        }

        Thread.sleep(1000);

        System.out.println("================ 获取所有的方法 ================");
        Method[] declaredMethods = c.getDeclaredMethods();

        for (Method method : declaredMethods){
            System.out.println(method);
        }

        Thread.sleep(1000);
        
        System.out.println("================ 获取特定方法带参并使用 ================");
        Method method1 = c.getMethod("method1", String.class);
        Object obj1 = c.getConstructor().newInstance();
        Test test1 = (Test) obj1;
        test1.method1("test str");
        Thread.sleep(1000);

        System.out.println("================ 获取特定方法多个参数使用 ================");
        Method method3 = c.getDeclaredMethod("method3", String.class, String.class);
        Object obj = c.getConstructor().newInstance();
        //给方法传值
        Object invoke = method3.invoke(obj, "LeoKim", "男");

        System.out.println(invoke);
    }
}

image.png

四、反射执行main方法

public static void main(String[] args) {
    for (String arg : args) {
        System.out.println(arg);
    }
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Method method = Class.forName("cn.leokim.Test").getMethod("main", String[].class);
    method.invoke(null, (Object)new String[]{"111", "222", "333"});
}

Java基于接口的动态代理

所谓代理,就是不改变原有类代码的基础上,对其方法进行增强。这里以演员举例对其进行说明。演员有出演角色的行为,他们能演戏,但他们都会与经纪公司签约,而剧组找人都是找经纪公司,向公司提供一个标准。这个过程中,演员就是原生类,经纪公司就好比是在做代理这件事。经纪公司给他们公司的演员对外宣称,低于10000的戏不演,这就是在对演员的动作进行增强。代码如下:

接口:

public interface IActor {
	public void perform(int money,String name);
	public void dangerPerform(int money);
}

实现类:

package com.dimples.service.impl;
 
import com.dimples.service.IActor;
 
public class MyActor implements IActor {
	@Override
	public void perform(int money,String name) {
		System.out.println("拿到" + money + "钱,执行一般表演");
	}
 
	@Override
	public void dangerPerform(int money) {
		System.out.println("拿到" + money + "钱,执行特殊表演");
	}
}

测试类:

package com.dimples.test;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
import com.dimples.service.IActor;
import com.dimples.service.impl.MyActor;
 
public class Test2 {
 
	public static void main(String[] args) {
		//这里注意不是final类型在下面将不能被内部类引用
		final MyActor actor = new MyActor();
                //这就是在创建代理对象,第一个参数类加载器;第二个参数被代理对象实现的接口,好告知代理对象它需要具备哪些行为;第三个参数就是我们进行增强的代码
		IActor proxyActor = (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(), actor.getClass().getInterfaces(), new InvocationHandler() {
			
			@Override
               //proxy代表代理对象,method表示执行的方法对象,args表示参数数组。被代理对象每个方法执行时都会过这个invoke方法
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				int money = (int) args[0];
				if("perform".equals(method.getName())){
					if(money>10000)
						method.invoke(actor, args);
				}else if("dangerPerform".equals(method.getName())){
					if(money>50000)
						method.invoke(actor, args);
				}
				return null;
			}
		});
		
		proxyActor.perform(10001, "hahaha");
		proxyActor.dangerPerform(50001);
	}
	
 
}

注意,基于接口的代理方式,被代理对象至少要实现一个接口!