Java中的反射到底是个啥?

    科技2024-10-15  17

    目录

    前言一、反射是什么二、类对象三、获取类对象3.1 通过类的对象,获取类对象3.2 通过类名获取类对象3.3 通过静态方法获取类对象(推荐)3.4 常用方法 四、设计模式4.1 什么是设计模式4.2 工厂设计模式4.3 单例模式 五、枚举5.1 什么是枚举 六、注解6.1 什么是注解6.2 注解属性类型6.3 元注解

    前言

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

    一、反射是什么

    反射到底是个啥?《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:指定注解用于修饰类的哪个成员

    Processed: 0.020, SQL: 8