java基础-反射

java基础-反射

1 什么是反射

在程序运行期间,动态获取信息(类,属性,方法),动态操作对象的一种功能。能够将java类的类型,构造方法,成员变量,方法映射成一个java对象并进行操作。

优点:运行时获取类和对象的内容,提高了系统的灵活性和扩展性(可以利用多态,在运行时获取需要的对象)

缺点:性能比普通方法差,jvm无法优化代码,破坏类的封装性

2 反射相关的API

2.1 Class

Class用于封装java类型的相关信息,构造方法为private,只能由jvm类加载器加载

Class位于java.lang包下,Constructor,Method,Field等位于java.lang.reflect包下

常用的方法

创建Class对象的三种方式

  1. Class<?> clazz = Class.forName(String);
  2. Class clazz = T.class
  3. Class<? extends Obj> clazz = obj.getClass();

举个栗子:

package bean;

/**
 * 创建一个Product类放在bean这个包下
 */
@Getter
@Setter
public class Product {
    private String  productName;
    private Integer count;

    public Product() {}

    public Product(String productName, Integer count) {
        this.productName = productName;
        this.count = count;
    }
    
    public void queryCount() {
        System.out.println("count:" + this.getCount());
    }
}
@Test
public void test1() {
    // 1.入参需要传入类的全路径 并且处理已检查异常
    try {
        Class<?> p = Class.forName("bean.Product");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    // 2.获取Class的第二种方式
    Class<Product> productClass = Product.class;
    // 3.获取Class的第三种方式
    Product product = new Product();
    Class<? extends Product> pClass = product.getClass();
}

类分析相关的方法

  1. getName 获取类的名称
  2. getConstructor(s)\Field(s)\Method(s)\Annotation(s)\Class(es) 获取该类,父类,父接口的公有的构造器、属性、方法、注解、内部类
  3. getDeclareConstructor(s)\Field(s)\Method(s)\Annotation(s)\Class(es) 获取该类所有的构造器、属性、方法、注解、内部类
  4. isInstance() 判断是否是该类的实例

方法带Declare说明获取的不止是公有,否则获取的只能是公有

方法结尾带(s)说明是获取类中全部,否则获取的是指定的,需要传入参数列表来指定

举个栗子:

// 获取公有的构造方法
Constructor<?>[] constructors = p.getConstructors();
// 获取全部的构造方法
Constructor<?>[] declaredConstructors = p.getDeclaredConstructors();
// 获取指定的构造方法 入参决定获取的是哪个构造方法
// 空参构造方法
Constructor<?> constructor = p.getConstructor();
// 初始化商品名称和数量的构造方法
Constructor<?> constructor2 = p.getConstructor(String.class,Integer.class);
// Method
Method[] methods = p.getMethods();
Method[] declaredMethods = p.getDeclaredMethods();
Method setCount = p.getMethod("setCount", Integer.class);
Method queryCount = p.getMethod("queryCount");
// Field
Field[] fields = p.getFields();
Field[] declaredFields = p.getDeclaredFields();
Field count = p.getField("count");

创建实例

newInstance()

使用该方法时,类中必须有空参构造方法。如果想用带参数的构造方法创建实例,需要先获取指定参数的构造方法对象,然后利用Constructor.newInstance()生成类的实例

举个栗子:

Class<?> p = Class.forName("bean.Product");
// 空参构造方法
Product o1 = (Product) p.newInstance();

Class<Product> productClass = Product.class;
Product product1 = productClass.newInstance();

Product product = new Product();
Class<? extends Product> pClass = product.getClass();
Product product2 = pClass.newInstance();

// 初始化商品名称和数量的构造方法
Constructor<?> constructor2 = p.getConstructor(String.class,Integer.class);
Product mobile = (Product) constructor2.newInstance("手机", 10);

获取类加载器

getClassLoader()

类加载器与双亲委派机制(TODO)

2.2 Constructor

java类中构造方法的抽象,可以获取名称,参数类型,访问修饰符(getModifiers),注解

newInstance() 生成一个类的实例

举个栗子:

Class<?> p = Class.forName("bean.Product");
// 获取指定的构造方法 入参决定获取的是哪个构造方法
// 空参构造方法
Constructor<?> constructor = p.getConstructor();
Product o = (Product) constructor.newInstance();
// 初始化商品名称和数量的构造方法
Constructor<?> constructor2 = p.getConstructor(String.class,Integer.class);
Product mobile = (Product) constructor2.newInstance("手机", 10);

2.3 Method

java类中方法的抽象,可以获取名称,参数类型,访问修饰符(getModifiers),返回值类型,注解等

invoke() 方法调用,需要有对象实例。

举个栗子:

Class<?> p = Class.forName("bean.Product");
Product product = (Product) p.newInstance();
Method setProductName = p.getMethod("setProductName", String.class);
setProductName.invoke(product,"手机");
Method getProductName = p.getMethod("getProductName");
// console输出手机
System.out.println(getProductName.invoke(product));

2.4 Field

java类中属性的抽象

getType() 获取属性类型

get() 获取属性值

set() 设置属性

setAccessible()设置能否访问 方法和类的反射api也有这个方法

举个栗子:

// 反射生成一个Product实例
Class<?> p = Class.forName("bean.Product");
Product product = (Product) p.newInstance();
// 获取属性
Field productName = p.getDeclaredField("productName");
// productName是私有属性 要想直接访问需要setAccessible为true
productName.setAccessible(true);
productName.set(product,"手机");
System.out.println(productName.get(product));

2.5 Modifier

封装了访问修饰符的方法,可以查看是否是public,是否是private,是否是static,final等

举个栗子:

Class<?> p = Class.forName("bean.Product");
Product product = (Product) p.newInstance();
Method setProductName = p.getMethod("setProductName", String.class);
setProductName.invoke(product,"手机");
Field productName = p.getDeclaredField("productName");
// false
System.out.println(Modifier.isPublic(productName.getModifiers()));
// true
System.out.println(Modifier.isPrivate(productName.getModifiers()));

3 问题

3.1 Class.forName()和.class获取Class对象有什么区别?

Class.forName()会进行静态初始化 .class不会

Class.forName()有两个重载的方法,一个只传字符串,另一个传字符串,boolean,classLoader,这个boolean代表是否初始化这个类。只传字符串的方法里boolean默认是true。

3.2 用instanceof 可以和父类比较吗,且会返回true吗?

可以 会

3.3 用getClass并用== 可以和T.class比较吗,且会返回true吗?

不可以 编译报错 返回值不同 见2.1

3.4 用getClass并用.equals可以和父类比较吗,且会返回true吗?

可以比较 返回false

3.5 getDeclaredXXX 有哪几种?

  • 注解Annotation
  • 内部类Classed
  • 构造方法Construcotor
  • 字段Field
  • 方法Method

3.6 getMethods()返回哪些方法, getDeclaredMethods()会返回哪些方法

getMethods()获取本类,父类,父接口public方法,getDeclaredMethods() 获取本类所有方法

3.7 Filed、Method、Constructor如何使用

Filed get,set赋值

Method invoke方法调用

Constructor newInstance实例化

setAccessible 设置能否访问

3.8 如何提高反射效率

  • 使用高性能反射包,例如ReflectASM(ReflectASM 使用字节码生成的方式实现了更为高效的反射机制)
  • 缓存反射的对象,避免每次都要重复去字节码中获取。(缓存!缓存!)
  • method反射可设置method.setAccessible(true)来关闭安全检查。
  • 尽量不要getMethods()后再遍历筛选,而直接用getMethod(methodName)来根据方法名获取方法
  • 利用hotspot虚拟机中的反射优化技术(jit技术)

3.9 用反射获取到的method对象, 是返回一个method引用,还是返回1个拷贝的method对象?

拷贝的 可以翻翻源码,如下:

 getReflectionFactory().copyMethod(res)

3.10 getMethods()后自己做遍历获取方法,和getMethod(methodName) 直接获取方法, 为什么性能会有差异?

因为返回的method是copy一份再返回的,copy多次返回,和copy一次返回 效率肯定有差异(开发时要注意)

3.11 获取方法时,jvm内部其实有缓存,但是返回给外部时依然会做拷贝。那么该method的缓存是持久存在的吗?

不是 缓存的属性是个软引用,内存不足时可能回收,也可以设置参数发生GC时回收

-XX:SoftRefLRUPolicyMSPerMB

3.12 反射是线程安全吗?

是 获取反射数据时利用cas

3.13 普通方法调用,反射调用,关闭安全检查调用性能差异(setAccessibile)

普通方法>关闭安全检查反射调用>反射调用

4 延伸

4.1 静态代理和动态代理

4.2 解释型语言和编译型语言

解释型:代码逐行翻译执行 性能比编译型差

编译型:通过编译器编译后再执行

java编译为字节码,jvm再将字节码解释执行

4.3 静态语言和动态语言

静态:编译时确定变量类型

动态:运行时确定变量类型,及对应的方法调用

4.4 类加载和双亲委派机制

参考文章:

<think>嗯,用户之前问过关于`int`和`integer`的区别,现在转向Java基础中的反射。可能用户正在学习Java,之前的问题涉及基本数据类型和包装类,现在深入到了更高级的主题。我需要先确定用户对Java基础掌握程度,反射通常是面向对象之后的内容,所以用户可能已经有一定的Java基础。 用户的需求是了解Java反射基础知识,可能需要知道反射是什么、为什么用它、以及如何使用。深层需求可能包括实际应用场景或常见问题,比如性能影响或者安全性考虑。需要结构清晰,分点解释,帮助用户逐步理解。 首先,反射的定义,必须用简单明了的语言,避免术语过多。然后核心类,比如Class、Constructor、Method、Field,这些是反射的关键类,需要举例说明。应用场景部分,用户可能想知道反射的实际用途,比如框架开发或动态加载,要举常见的例子如Spring的IoC。 接下来要讲实现步骤,获取Class对象的三种方式,实例化对象的方法,访问字段和调用方法。每个步骤都需要代码示例,但用户可能对代码的细节不太清楚,所以需要解释每个步骤的作用。例如,`Class.forName`可能会抛出异常,需要提及异常处理,但可能不需要深入,保持基础知识的介绍。 优缺点部分需要平衡,用户可能想知道为什么有时候不推荐使用反射,所以性能和安全问题是重点。最后,实际案例,比如动态加载类,或者简单的框架应用,帮助用户将理论联系实际。 还要注意用户可能存在的误区,比如滥用反射导致代码难以维护,或者性能问题。需要提醒在必要时使用,并遵循最佳实践。此外,检查是否需要提到Java版本的变化对反射的影响,比如模块化系统带来的访问限制,但可能用户刚入门,暂时不需要深入。 最后总结,强调反射的灵活性和代价,帮助用户权衡使用场景。整体结构需要逻辑清晰,从定义到实现再到应用,逐步深入,确保用户能够跟随思路,逐步掌握反射基础知识。</think>好的!以下是关于 **Java反射(Reflection)** 的基础知识介绍,以逐步分析的方式展开: --- ### 一、反射的定义 **反射(Reflection)** 是Java提供的一种**运行时动态机制**,允许程序在**不预先知道类结构**的情况下,通过`Class`对象获取类的信息(如字段、方法、构造器等),并操作这些成员。简单来说:**通过反射,程序可以“观察”和修改自身的行为**。 --- ### 二、反射的核心类 Java反射通过以下核心类实现: 1. **`Class`**:表示类或接口的元数据。 2. **`Constructor`**:表示类的构造方法。 3. **`Method`**:表示类的方法。 4. **`Field`**:表示类的字段(属性)。 5. **`Modifier`**:解析成员的修饰符(如`public`、`private`)。 --- ### 三、反射的核心用途 1. **动态获取类信息** 例如:获取类名、父类、接口、注解等。 ```java Class<?> clazz = String.class; System.out.println("类名:" + clazz.getName()); // 输出:java.lang.String ``` 2. **动态创建对象** 即使没有显式定义构造方法,也可以通过反射实例化对象。 ```java Class<?> clazz = Class.forName("java.util.ArrayList"); Object list = clazz.newInstance(); // 创建ArrayList实例(已过时,推荐用构造器) ``` 3. **动态调用方法/访问字段** 可以绕过访问权限(如调用私有方法)。 ```java Method method = clazz.getDeclaredMethod("privateMethod"); method.setAccessible(true); // 突破private限制 method.invoke(obj); ``` 4. **实现通用框架** 如Spring的依赖注入(IoC)、MyBatis的ORM映射等。 --- ### 四、反射的实现步骤(以调用方法为例) 1. **获取`Class`对象** 三种方式: ```java Class<?> clazz1 = Class.forName("全限定类名"); // 通过类路径加载 Class<?> clazz2 = Object.class; // 通过类字面量 Class<?> clazz3 = obj.getClass(); // 通过对象实例 ``` 2. **获取方法或字段** ```java Method method = clazz.getDeclaredMethod("方法名", 参数类型.class); // 包括私有方法 Field field = clazz.getDeclaredField("字段名"); // 包括私有字段 ``` 3. **设置访问权限(可选)** ```java method.setAccessible(true); // 突破private限制 ``` 4. **执行操作** ```java Object result = method.invoke(对象实例, 参数值); // 调用方法 field.set(对象实例, 值); // 修改字段值 ``` --- ### 五、反射的优缺点 | **优点** | **缺点** | |------------------------------|-----------------------------------| | 动态性高(如插件化开发) | 性能较差(比直接调用慢约10-100倍)| | 可访问私有成员(灵活性强) | 代码可读性降低 | | 通用性高(框架开发必备) | 绕过类型安全检查,可能引发异常 | --- ### 六、实际应用案例 1. **动态加载类**(如插件机制): ```java Class<?> pluginClass = Class.forName("com.example.Plugin"); Object plugin = pluginClass.newInstance(); Method runMethod = pluginClass.getMethod("run"); runMethod.invoke(plugin); ``` 2. **Spring框架的依赖注入**: ```java // 通过反射创建Bean并注入属性 Class<?> beanClass = Class.forName("com.example.UserService"); Object bean = beanClass.newInstance(); Field daoField = beanClass.getDeclaredField("userDao"); daoField.setAccessible(true); daoField.set(bean, new UserDaoImpl()); // 注入依赖 ``` --- ### 七、注意事项 1. **性能优化**:避免在频繁调用的代码中使用反射。 2. **安全限制**:Java 9+的模块系统可能限制反射访问(需在`module-info.java`中声明`opens`)。 3. **代码健壮性**:反射调用需处理大量异常(如`ClassNotFoundException`、`NoSuchMethodException`)。 --- ### 总结 反射Java中强大的动态编程工具,**核心价值是灵活性**,但需谨慎使用。掌握反射后,可以深入理解框架设计原理(如Spring、Hibernate),但日常开发中应优先考虑直接调用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值