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 类加载和双亲委派机制

参考文章:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值