Java基础:反射

目录

一 概述

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

所谓反射其实是获取类的字节码文件,也就是.class文件,通过Class这个对象进行获取。

Class 类的实例表示正在运行的 Java类和接口,jvm中每个类都有且只有一个class对象。(包括基本数据类型)

Class 没有公共构造方法。Class 对象 是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。

二 反射的三种方式

获取类的字节码文件有三种方式:

1、通过已实例化的java对象获取:xxx.getClass()

getClass()是Object的一个方法,Class继承了Object,所以我们可以直接使用

java 复制代码
Travel travel=new Travel();
Class c=travel.getClass();

2、通过类名获取:xxx.class

java 复制代码
Class c=Travel.class;

3、通过类的全路径获取:Class.forName("xxx")

java 复制代码
        try {
            Class c=Class.forName("com.suixing.pojo.Travel")
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

因为运行期间一个类只会有一个class对象产生,所以用不同方式多次获取同一个类的class对象,所获取到是同一个class对象

三 获取并使用类的构造方法

1、获取构造方法:

1)批量的方法
public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)

2)获取单个的方法,并调用
public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;

2、调用构造方法:

Constructor对象.newInstance(Object... initargs)

newInstance(Object... initargs):使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。

它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象。并为之调用

java 复制代码
public static void main(String[] args) throws NoSuchMethodException {
        try {
            Class c=Class.forName("com.suixing.pojo.TestPojo");

            System.out.println("***************所有公有构造方法******************");
            Constructor[] constructors=c.getConstructors();
            for(Constructor constructor : constructors){
                System.out.println(constructor);
            }

            System.out.println("*********所有的构造方法(包括:私有、受保护、默认、公有)*********");
            constructors = c.getDeclaredConstructors();
            for(Constructor constructor : constructors){
                System.out.println(constructor);
            }

            System.out.println("**********获取公有、无参的构造方法***************");
            Constructor constructor = c.getConstructor(null);//因为是无参的构造方法所以类型是一个null,不写也可以
            System.out.println(constructor);

            System.out.println("***********获取私有构造方法,并调用*************");
            constructor = c.getDeclaredConstructor(Integer.class);
            System.out.println(constructor);
            constructor.setAccessible(true);//暴力访问(忽略掉访问修饰符)
            Object o= constructor.newInstance(21);
            System.out.println(o.toString());

            System.out.println("**************获取并调用多参数的公有构造方法***********");
            constructor=c.getConstructor(new Class[]{String.class,String.class,Integer.class});
            o= constructor.newInstance("ximou","661",21);
            System.out.println(o.toString());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

四 获取并设置类的成员变量

java 复制代码
            //c是Class类的对象
            System.out.println("************获取所有公有的字段********************");
            Field[] fields=c.getFields();
            for(Field field:fields)
                System.out.println(field);

            System.out.println("************获取所有的字段********************");
            fields=c.getDeclaredFields();
            for(Field field:fields)
                System.out.println(field);

            System.out.println("**************获取私有字段并调用**************");
            Field field=c.getDeclaredField("name");
            field.setAccessible(true);
            field.set(o,"change name");
            System.out.println(o.toString());

设置字段时:需要传递两个参数,一个是实体类对象,一个是要设置的值

java 复制代码
//f是Field对象
Object obj = stuClass.getConstructor().newInstance();
f.set(obj, "刘德华");

五 获取并调用类的成员方法

java 复制代码
            System.out.println("***********获取所有的"公有"方法*************");
            c.getMethods();
            Method[] methodArray = c.getMethods();
            for(Method m : methodArray){
                System.out.println(m);
            }

            System.out.println("*************获取所有的方法,包括私有的***********");
            methodArray = c.getDeclaredMethods();
            for(Method m : methodArray){
                System.out.println(m);
            }

            System.out.println("***************获取公有的show1()方法*******************");
            Method method=c.getMethod("show1",String.class);
            Object result=method.invoke(o,("123456"));
            System.out.println("return "+result);

            System.out.println("***************获取私有的show4()方法******************");
            method=c.getDeclaredMethod("show4",int.class);
            method.setAccessible(true);
            result=method.invoke(o,123);
            System.out.println("return "+result);

            System.out.println("***************获取默认的、多个参数的show3()方法******************");
            method=c.getDeclaredMethod("show3",String.class,int.class);
            method.setAccessible(true);
            result=method.invoke(o,"sss",123);
            System.out.println("return "+result);

1、获取指定方法:

需要传入至少两个参数,第一个是调用的方法名称,第二个到第n个是方法的形参类型,切记是类型。

java 复制代码
m = stuClass.getDeclaredMethod("show4", int.class);

2、调用指定方法:

需要至少两个参数,一个是要调用的对象,其他的是实参

java 复制代码
m.setAccessible(true);//对非公有方法解除私有限定
Object result = m.invoke(obj, 20);

六 获取并调用主函数

java 复制代码
            Method method=c.getMethod("main",String[].class);
            method.invoke(null,(Object)new String[]{"a","b"});

七 反射的其他用法

1、通过反射运行配置文件内容

假设有两个业务类:

java 复制代码
public class Service1 {
    public void doService1(){
        System.out.println("业务方法1");
    }
}
java 复制代码
public class Service2 {
	public void doService2(){
        System.out.println("业务方法2");
    }
}

当需要从第一个业务方法切换到第二个业务方法的时候,若使用非反射方式,则必须修改代码,并且重新编译运行,才可以达到效果

java 复制代码
public class CommonTest {
	  public static void main(String[] args) {
		  //new Service1().doService1();
		  //必须重新修改代码
	        new Service2().doService2();
	    }
}

而使用反射方式则方便很多

------示例:

spring.txt

class=reflection.Service1
method=doService1

测试类

java 复制代码
public class ReflectTest {
	@SuppressWarnings({ "rawtypes", "unchecked" })
    public static void main(String[] args) throws Exception {
        Properties pro= new Properties();
        FileReader in = new FileReader("spring.txt");//获取输入流,从spring.txt中获取类名称和方法名称
        pro.load(in);
        in.close();
        String className = (String) pro.get("class");
        String methodName = (String) pro.get("method");
         
        //根据类名称获取类对象
        Class clazz = Class.forName(className);
        //根据方法名称,获取方法对象
        Method m = clazz.getMethod(methodName);
        //获取构造器
        Constructor c = clazz.getConstructor();
        //根据构造器,实例化出对象
        Object service = c.newInstance();
        //调用对象的指定方法
        m.invoke(service);         
    }
}

2、通过反射越过泛型检查

泛型是在编译期间起作用的。在编译后的.class文件中是没有泛型的。所有比如T或者E类型啊,本质都是通过Object处理的。所以可以通过使用反射来越过泛型。

java 复制代码
public class GenericityTest {
	public static void main(String[] args) throws Exception{
		
	ArrayList<String> list = new ArrayList<>();
	list.add("this");
	list.add("is");
	
	//list.add(5);报错
	
	/********** 越过泛型检查    **************/
	
	//获取ArrayList的Class对象,反向的调用add()方法,添加数据
	Class listClass = list.getClass(); 
	//获取add()方法
	Method m = listClass.getMethod("add", Object.class);
	//调用add()方法
	m.invoke(list, 5);
	
	//遍历集合
	for(Object obj : list){
		System.out.println(obj);
		}
	}
}
相关推荐
m0_571957581 小时前
Java | Leetcode Java题解之第543题二叉树的直径
java·leetcode·题解
一点媛艺2 小时前
Kotlin函数由易到难
开发语言·python·kotlin
姑苏风2 小时前
《Kotlin实战》-附录
android·开发语言·kotlin
奋斗的小花生3 小时前
c++ 多态性
开发语言·c++
魔道不误砍柴功3 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_2343 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨3 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
老猿讲编程4 小时前
一个例子来说明Ada语言的实时性支持
开发语言·ada
Chrikk5 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*5 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go