首页 > 其他分享 >反射(Reflection)

反射(Reflection)

时间:2022-10-31 13:45:17浏览次数:38  
标签:反射 Class String class name 获取 public Reflection

反射(Reflection

所谓反射就是在Java运行期间,程序可以拿到一个对象的所有信息。

反射是为了解决在对一个对象完全未知的情况下调用其方法还产生的。

通过Class实例获取class信息的方法称为反射(Reflection)。

通过反射读写字段是一种非常规方法,它会破坏对象的封装。

class类

获取class实例

1、通过class静态变量class获取

Class cls = String.class;

2、实例变量的getClass()方法获取

String s = "Hello";
Class cls = s.getClass();

3、Class.forName("class完整类名")获取

Class cls = Class.forName("java.lang.String");

class实例获取基本信息

public class Main {
    public static void main(String[] args) {
        printClassInfo("".getClass());
        printClassInfo(Runnable.class);
        printClassInfo(java.time.Month.class);
        printClassInfo(String[].class);
        printClassInfo(int.class);
    }

    static void printClassInfo(Class cls) {
        System.out.println("Class name: " + cls.getName());
        System.out.println("Simple name: " + cls.getSimpleName());
        if (cls.getPackage() != null) {
            System.out.println("Package name: " + cls.getPackage().getName());
        }
        System.out.println("is interface: " + cls.isInterface());
        System.out.println("is enum: " + cls.isEnum());
        System.out.println("is array: " + cls.isArray());
        System.out.println("is primitive: " + cls.isPrimitive());
    }
}

创建class实例

1、可通过class实例创建class实例(实例-->实例)

// 获取String的Class实例:
Class cls = String.class;
// 创建一个String实例:
String s = (String) cls.newInstance();

通过newInstance创建实例存在局限性:只能访问public的无参构造方法,带参构造或者非public构造方法都无法访问。

2、new创建实例

String s = new String()

3、动态加载

jvm对于java程序的执行并非是一次性加载完所有的class类,而是只有首次使用class时才会加载。

如下所示:在create方法调用之前,Person.class类不会加载

// Main.java
public class Main {
    public static void main(String[] args) {
        if (args.length > 0) {
            create(args[0]);
        }
    }

    static void create(String name) {
        Person p = new Person(name);
    }
}

访问字段(实例>class>class信息)

对于一个object实例来说,只要获得它的class,就能获取它的所有信息

获取class类字段

  • Field getField(name):根据字段名获取某个publicfield(包括父类)
  • Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
  • Field[] getFields():获取所有publicfield(包括父类)
  • Field[] getDeclaredFields():获取当前类的所有field(不包括父类)

注:Declared不包含父类,即要获取父类的字段信息需要使用不含Declared相关方法,而Declared可以来获取非public的字段信息,故无法获取父类中的非public字段信息

public class Main {
    public static void main(String[] args) throws Exception {
        Class stdClass = Student.class;
        // 获取public字段"score":
        System.out.println(stdClass.getField("score")); //public int Student.score
        // 获取继承的public字段"name":
        System.out.println(stdClass.getField("name")); //public java.lang.String Person.name
        // 获取private字段"grade":
        System.out.println(stdClass.getDeclaredField("grade")); //private int Student.grade
    }
}

class Student extends Person {
    public int score;
    private int grade;
}

class Person {
    public String name;
}

Filed对象相关方法

  • get(Object obj):返回指定对象obj上此field表示的字段值,例如,"zhangsan"

    1、字段不是静态字段的话,要传入反射类的对象

    Field field=A.class.getDeclaredField("fild"); 
    // a表示A class上fild字段的字段值
    int a= (Integer)field.get(new A());
    

    2、字段是静态字段的话,传入任何对象都是可以的,包括null

    Field static field=A.class.getDeclaredField("staticFild");
    // staticFild字段是静态字段 static private staticFild
    int b= (Integer)staticfield.get("") ; 
    int d= (Integer)staticfield.get(null) ;
    
  • set(Object obj, Object value):将指定对象变量上此field 对象表示的字段设置为指定的新值,第一个Object参数是指定的实例,第二个Object参数是待修改的值。

  • getName():返回字段名称,例如,"name"

  • getType():返回字段类型,也是一个Class实例,例如,String.class

  • getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。

public final class String {
    private final byte[] value;
}

Field f = String.class.getDeclaredField("value");

f.getName(); // "value"
f.getType(); // class [B 表示byte[]类型
int m = f.getModifiers();

Modifier.isFinal(m); // true
Modifier.isPublic(m); // false
Modifier.isProtected(m); // false
Modifier.isPrivate(m); // true
Modifier.isStatic(m); // false

获取字段值

// reflection
// Filed描述的是类的属性信息,功能:获取当前对象的成员变量的类型,对成员变量重新设置值
import java.lang.reflect.Field;
public class Main {

    public static void main(String[] args) throws Exception {
        // new 一个person实例
        Object p = new Person("Xiao Ming");
        // getClass() 获取p的class实例
        Class c = p.getClass();
        // 设置为true允许访问private变量
        Field.setAccessible(true)
        // 根据class实例获取name的Filed实例,getDeclaredField获取指定字段Field
        Field f = c.getDeclaredField("name");
        // 获取
        Object value = f.get(p);
        System.out.println(value); // "Xiao Ming"
    }
}

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }
}

取值流程:获取Class实例—>获取filed实例—>Field.get(Object)获取实例指定字段的值。

为了保证JVM核心库的安全,setAccessible(true)jvm运行期存在SecurityManager,可能会被检查规则检查导致不生效。

设置字段值

方法:Field.set(Object, Object),其中第一个Object参数是class实例,第二个Object是需要更改的值

// reflection
import java.lang.reflect.Field;

public class Main {

    public static void main(String[] args) throws Exception {
        Person p = new Person("new a");
        System.out.println(p.getName()); // "new a"
        Class c = p.getClass();
        Field f = c.getDeclaredField("name");
        // 修改非public的值时需要调用setAccessible(true)
        // 如下,name是private变量的值,需要调用setAccessible,在使用set赋值
        f.setAccessible(true);
        f.set(p, "new a");
        System.out.println(p.getName()); // "new a"
    }
}

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

调用方法

访问字段是通过class实例获取获取所有Field对象,调用方法就是获取所有Method信息。

使用反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法(如果存在)。

有如下几种方式:

  • Method getMethod(name, Class...):获取某个publicMethod(包括父类)
  • Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
  • Method[] getMethods():获取所有publicMethod(包括父类)
  • Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)

一个Method对象包含一个方法的所有信息:

  • getName():返回方法名称,例如:"getScore"
  • getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class
  • getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class}
  • getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
public class Main {
    public static void main(String[] args) throws Exception {
        // String对象:
        String s = "Hello world";
        // 获取String substring(int)方法,参数为int:
        Method m = String.class.getMethod("substring", int.class);
        // 在s对象上调用该方法并获取结果
        // 调用invake相当于调用方法substring,第一个参数表示对象实例,第二个参数是是方法substring的参数
        // 调用静态方法无需第一个参数,第一个参数永远为null,即invoke(null, "12345")
        String r = (String) m.invoke(s, 6);
        // 打印调用结果:
        System.out.println(r);
        <----调用非public方法---->
        // 1、非public方法使用getDeclaredMethod获取方法实例
        // 2、Method.setAccessible(true)允许其调用
        Method m = p.getClass().getDeclaredMethod("setName", String.class);
        m.setAccessible(true);
        m.invoke(p, "Bob");
    }
}

调用构造方法

newInstance()

// newInstance()只能调用该类无参构造方法,对于有参构造和非public构造不能通过newInstance访问
Person p = Person.class.newInstance();

Constructor对象

Constructor是当前定义类的构造函数,与父类无关,不存在多态问题

  • getConstructor(Class...):获取某个publicConstructor
  • getDeclaredConstructor(Class...):获取某个Constructor
  • getConstructors():获取所有publicConstructor
  • getDeclaredConstructors():获取所有Constructor

调用非publicConstructor时,必须首先通过setAccessible(true)设置允许访问,setAccessible(true)可能失败

import java.lang.reflect.Constructor;

public class Main {
    public static void main(String[] args) throws Exception {
        // 获取构造方法Integer(int):
        Constructor cons1 = Integer.class.getConstructor(int.class);
        // 调用构造方法:
        Integer n1 = (Integer) cons1.newInstance(123);
        System.out.println(n1);

        // 获取构造方法Integer(String)
        Constructor cons2 = Integer.class.getConstructor(String.class);
        Integer n2 = (Integer) cons2.newInstance("456");
        System.out.println(n2);
    }
}

获取继承关系

  • Class getSuperclass():获取父类类型
  • Class[] getInterfaces():获取当前类实现的所有接口
  • 通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否可以实现
public class Main {
    public static void main(String[] args) throws Exception {
        Class i = Integer.class;
        Class n = i.getSuperclass();
        System.out.println(n);
        Class o = n.getSuperclass();
        System.out.println(o);
        System.out.println(o.getSuperclass());
    }
}

Integer的父类类型是NumberNumber的父类是ObjectObject的父类是null。除Object外,其他任何非interfaceClass都必定存在一个父类类型。

动态代理

Java标准库提供了一种动态代理(Dynamic Proxy)的机制:可以在运行期动态创建某个interface的实例

静态代理创建

// 定义接口
public interface Hello {
    void morning(String name);
}
// 实现类
public class HelloWorld implements Hello {
    public void morning(String name) {
        System.out.println("Good morning, " + name);
    }
}
// 创建实例,转型为接口并调用
Hello hello = new HelloWorld();
hello.morning("Bob");

动态代理

拿上面的例子举例:先定义Hello接口,中间跳过实现类的编写,通过调用Proxy.newProxyInstance()创建接口对象,这种没有实现类但是在运行期动 态创建了一个接口对象的方式,我们称为动态代码

JDK提供的动态创建接口对象的方式,就叫动态代理

动态代理在spring等框架中大量使用

运行期动态创建一个interface实例的方法:

  1. 定义一个InvocationHandler实例,它负责实现接口的方法调用;
  2. 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
    1. 使用的ClassLoader,通常就是接口类的ClassLoader
    2. 需要实现的接口数组,至少需要传入一个接口进去;
    3. 用来处理接口方法调用的InvocationHandler实例。
  3. 将返回的Object强制转型为接口。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method);
                if (method.getName().equals("morning")) {
                    System.out.println("Good morning, " + args[0]);
                }
                return null;
            }
        };
        Hello hello = (Hello) Proxy.newProxyInstance(
            Hello.class.getClassLoader(), // 传入ClassLoader
            new Class[] { Hello.class }, // 传入要实现的接口
            handler); // 传入处理调用方法的InvocationHandler
        hello.morning("Bob");
    }
}

interface Hello {
    void morning(String name);
}

标签:反射,Class,String,class,name,获取,public,Reflection
From: https://www.cnblogs.com/yangguanglei/p/16843969.html

相关文章

  • 反射API
    获取反射类:Student.classstudent.getClass()Class<?>aClass=Class.forName("student")构造器Objecto=aClass.newInstance();//默认构造器Constructor<?>con......
  • 解决 jdk9 及以后的 非法反射 警告
    前言我的jdk是11版的,因此经常会看见如下图所示的警告很多广泛使用的库都有这种非法反射的问题,看起来特别难受,这里给出两种解决方案1.JVM参数将--illegal-acce......
  • 使用Expression代替反射读取IDataReader或IDataRecord给实体类赋值
    ExpressionMapper代码usingSystem;usingSystem.Collections.Concurrent;usingSystem.Collections.Generic;usingSystem.Data;usingSystem.Data.Common;usingSystem.Li......
  • 使用泛型和反射实现一个简单的ORM框架
    什么是ORM框架?ORM框架是连接数据库与实体类帮助程序对接数据库的框架,类似于三层架构的数据访问层,但ORM框架可以根据数据库生成实体类,或者根据实体类生成数据库,解决了......
  • 菲涅尔反射 Fresnel Reflectance
    入射光射到平面上后会被分为反射和折射两个部分,反射向量\(\mathbf{r}_i\)和入射向量\(\mathbf{l}\)关于法线\(\mathbf{n}\)有相同的夹角\(\theta_i\),反射向量\(\ma......
  • C# 反射动态判断转换属性类型值生成类实例
    ///<summary>///为指定对象分配参数///</summary>///<typeparamname="T">待赋值的类型</typeparam>///<paramname="dic">字段/值</param>/......
  • JavaWEB06--book02--利用反射合并 LoginServlet 和 RegistServlet 程序为 UserServlet
    ​​项目源码​​合并LoginServlet和RegistServlet程序为UserServlet程序优化一:使用​​if-else​​优化代码二:使用​​反射​​优化大量elseif代码:protectedvo......
  • Java反射
    Java反射作用原理:反射机制在运行时,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意个对象,都能够调用它的任意一个方法,在Java中,只要给定类的名字,就可以通过反射......
  • 反射Reflection
    1.1简介反射是框架设计的灵魂将类的各个组成部分封装为其他对象Source源代码阶段->Class类对象阶段->Runtime运行时阶段1.2获取Class对象Class.forName("全类名......
  • 笔记(反射)
      第一个:  这个是将字符串转成json;第二个:  这个吧,emmm,就是将类转成class对象(这样子说应该正确吧?)第三个:  这个就是利用类来创建对象啦,就和利用无参构......