Java中的反射到底是个啥?

本文详细介绍了Java反射机制,包括其概念、获取类对象的各种方法,如通过对象、类名、静态方法,以及设计模式如工厂模式和单例模式的应用。此外,还讲解了枚举、注解及其在代码中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

最近看面试题的时候,看到有关反射的面试,由于上课学的时候老师压根没讲反射的内容,所以今天又来补漏洞啦

一、反射是什么

反射到底是个啥?《Java核心技术》书中给出的解释是:能够分析类能力的程序称为反射。反射机制可以用来:

  • 在运行时分析类的能力
  • 在运行时查看对象,例如,编写一个toString
  • 实现通用的数组操作代码
  • 利用Method对象,这个对象很像C++中的函数指针

通俗来说,反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

因此,反射是一种功能强大且复杂的机制。使用它的主要人员是工具构造者,而不是应用程序员。

二、类对象

  • 类的对象:基于某个类new出来的对象,也称为实例对象。
  • 类对象:类加载的产物,封装了一个类的所有信息(类名、父类、接口、属性、方法、构造方法)

也就是说,每个类加载到内存都对应一个Class对象,每个类有且只有一个Class对象
在这里插入图片描述
显示当前程序所加载的类:JVM参数-verbose:class
在这里插入图片描述
在这里插入图片描述

三、获取类对象

3.1 通过类的对象,获取类对象

/**
 * @author: Radish
 * @date: 2020-10-07 19:10
 */
public class TestPerson {
    public static void main(String[] args) {
        Person person = new Person("张三");
        Class<?> aClass = person.getClass();
        System.out.println(aClass);
    }
}

在这里插入图片描述

3.2 通过类名获取类对象

/**
 * @author: Radish
 * @date: 2020-10-07 19:10
 */
public class TestPerson {
    public static void main(String[] args) {
        System.out.println(Person.class);
    }
}

在这里插入图片描述

3.3 通过静态方法获取类对象(推荐)

/**
 * @author: Radish
 * @date: 2020-10-07 19:10
 */
public class TestPerson {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> aClass = Class.forName("com.reflect.Person");
    }
}

在这里插入图片描述
若类名不存在,则会抛出异常
在这里插入图片描述

3.4 常用方法

使用反射获取类的构造方法,创建对象

//使用反射获取类的构造方法,创建对象
    public static void reflectOpe2() throws Exception {
        //(1)获取类对象
        Class<?> aClass = Class.forName("com.reflect.Person");
        //(2)获取类的构造方法Constructor
        System.out.println("===========获取类的构造方法============");
        Constructor<?>[] cons = aClass.getConstructors();
        for (Constructor<?> con : cons) {
            System.out.println(con);
        }
        //(3)获取类中的无参构造
        System.out.println("===========获取类的无参构造============");
        Constructor<?> con = aClass.getConstructor();
        System.out.println(aClass);
        Person lisi = (Person) con.newInstance();
        System.out.println(lisi);
        //(4)获取类中带参构造方法
        System.out.println("===========获取类的带参构造============");
        Constructor<?> con1 = aClass.getConstructor(String.class, int.class);
        Person p2 = (Person) con1.newInstance("李四", 23);
        System.out.println(p2);
    }

在这里插入图片描述
使用反射获取类中的方法,并调用方法

//3使用反射获取类中的方法,并调用方法
    public static void reflectOpe3() throws Exception {
        //(1)获取类对象
        Class<?> aClass = Class.forName("com.reflect.Person");
        //(2)获取方法Method对象
        System.out.println("========获取方法Method对象========");
        //Method[] methods = aClass.getMethods();  获取公开的方法,包括从父类继承的方法
        Method[] methods = aClass.getDeclaredMethods(); //获取类中的所有方法,包括私有、默认、保护的、不包含继承的方法
        for (Method method : methods) {
            System.out.println(method);
        }
        //(3)获取单个方法
        System.out.println("===========获取单个方法===========");
        System.out.println("----------不带参----------");
        Method eatMethod = aClass.getMethod("eat");
        //调用方法
        //正常调用方法 Person person = new Person(); person.eat;
        Person zhangsan = (Person) aClass.newInstance();
        eatMethod.invoke(zhangsan);
        //toString
        Method toStringMethod = aClass.getMethod("toString");
        Object result = toStringMethod.invoke(zhangsan);
        System.out.println(result);
        System.out.println("----------带参----------");
        Method eatMethod2 = aClass.getMethod("eat", String.class);
        eatMethod2.invoke(zhangsan, "鸡腿");
        //(4)获取私有方法
        System.out.println("===========获取私有方法===========");
        Method privateMethod = aClass.getDeclaredMethod("privateMethod");
        //设置访问权限无效
        privateMethod.setAccessible(true);
        privateMethod.invoke(zhangsan);
        //(5)获取静态方法
        System.out.println("===========获取静态方法===========");
        Method staticMethod = aClass.getMethod("staticMethod");
        staticMethod.invoke(null);
    }

这里需要注意几点:①getMethods方法是获取公开的方法,包括从父类继承的方法;getDeclaredMethods方法是获取类中的所有方法,包括私有、默认、保护的、不包含继承的方法。②在获取私有方法时,需设置访问权限无效Method setAccessible(true)

使用反射实现一个可以调用任何对象方法的通用方法

    //4使用反射实现一个可以调用任何对象方法的通用方法
    public static Object invokeAny(Object obj, String methodName, Class<?>[] types,Object...args) throws Exception {
        //1获取类对象
        Class<?> aClass = obj.getClass();
        //2获取方法
        Method method = aClass.getMethod(methodName, types);
        //3调用
        return method.invoke(obj,args);
    }
    public static void main(String[] args) throws Exception {
        //reflectOpe2();
        //reflectOpe3();
        Properties properties = new Properties();
        invokeAny(properties, "setProperty", new Class[]{String.class,String.class}, "username", "张三");
        System.out.println(properties);
    }

在这里插入图片描述

使用反射获取类的属性

	//5使用反射获取类的属性
    public static void reflectOpe4() throws Exception{
        //(1)获取类对象
        Class<?> aClass = Class.forName("com.reflect.Person");
        //(2)获取属性(字段) 公开的字段,父类继承的字段
//        Field[] fields = aClass.getFields();
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        //(3)获取name属性
        Field name = aClass.getDeclaredField("name");
        name.setAccessible(true);
        System.out.println(name);
        //(4)赋值
        Person zhangsan = (Person) aClass.newInstance();
        name.set(zhangsan, "张三");
        //(5)获取值
        System.out.println(name.get(zhangsan));
    }

在这里插入图片描述

四、设计模式

4.1 什么是设计模式

一套被反复调用、多数人知晓的、经过分类编目的、代码设计经验的总结。

  • 好处:使用设计模式为了可重用代码、让代码更容易被他人理、保证代码可靠性、重用性。

4.2 工厂设计模式

  • 工厂设计模式主要负责对象创建的问题
  • 开发中有一个非常重要的原则“开闭原则”,对扩展开放、对修改关闭。
  • 可通过反射进行工厂模式的设计,完成动态的对象创建。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
添加实现类时只需修改配置文件
在这里插入图片描述

4.3 单例模式

  • 单例:只允许创建一个该类的对象。

方式1:饿汉式(类加载时创建,天生线程安全)

class Singleton {
	private static final Singleton = new Singleton();
	private Singleton();
	public static Singleton getInstance(){
		return instance;
	};
}

方式2:懒汉式(使用时创建,线程不安全,加同步,效率低)

class Singleton {
	private static final Singleton = null;
	private Singleton();
	public static synchronized Singleton getInstance(){
		if(instance==null){
			instance = new Singleton();
		}
		return instance;
	};
}

方式3:懒汉式(使用时创建,线程安全)

class Singleton {
	private Singleton();
	private static class Holder() {
		static Singleton s = new Singleton();
	}
	public static Singleton instance(){
		return Holder.s;
	};
}

五、枚举

5.1 什么是枚举

枚举是一个引用类型,枚举是一个规定了取值范围的数据类型。

  • 枚举变量不能使用其他的数据,只能使用枚举中常量赋值,提高程序安全性。
  • 定义枚举使用enum关键字。
  • 枚举的本质:
    • 枚举是一个终止类,并集成enum抽象类
    • 枚举中常量是当前类型的静态常量

六、注解

6.1 什么是注解

注解是代码里的特殊标记,程序可以读取注解,一般用于替代配置文件。

  • 开发人员可以通过注解告诉类如何运行。

    • 在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。
  • 常见注解:@Override、@Deprecated

  • 定义注解使用@interface关键字,注解中只能包含属性

定义一个注解:
在这里插入图片描述
使用
在这里插入图片描述

6.2 注解属性类型

  • String类型
  • 基本数据类型
  • Class类型
  • 枚举类型
  • 注解类型
  • 以上类型的一堆数组

6.3 元注解

  • 元注解:用来描述注解的注解

  • @Retention:用于指定注解可以保留的域。

    • RetentionPolicy.CLASS:注解记录在class文件中,运行java程序时,JVM不会保留
    • RetentionPolicy.RUNTIME:注解记录在class文件中,运行java程序时,JVM会保留,程序可以通过反射获取该注释
    • RetentionPolicy.SOURCE:编译时直接丢弃这种策略的注释。
  • @Target:指定注解用于修饰类的哪个成员

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值