Java反射机制

静态语言 与 动态语言

动态语言:

  • 运行时可以根据自身条件改变数据结构
  • 动态语言举例:Object - c,JavaScript,C#,PHP,Python 等。
  • eg(JavaScript):

静态语言:

  • 运行时不可改变自身数据结构。
  • 静态语言举例:Java,C,C++。
  • Java不是动态语言,但Java是一门 “ 准动态语言 ” 。即可使用反射机制使Java获得类似于动态语言的特性。

Reflection(反射)

Reflection(反射)允许程序在执行期间借助于 Reflection API 获取任何类的内部信息,并能够直接操作任意对象的内部属性及方法。

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

在加载完类之后,会在堆内存的方法区生成一个Class类型的对象。这个Class对象包含了完整的类的结构信息。我们可以通过这个Class对象看到这个类的全部信息。这个Class对象就像是一面镜子,通过这个镜子看到这个类的结构。这个过程就是:Refletion(反射)

Reflection的优点和缺点:

  • 优点:可以实现动态创建对象和编译,体现出很大的灵活性。
  • 缺点:对性能有影响。反射是一种解释性操作,告诉JVM我们需要做什么。

Reflection主要API

Class c1 = Class.forName("FanShe.TestFS");
Class c2 = Class.forName("FanShe.TestFS");
System.out.println(c1);
System.out.println(c1.hashCode() == c2.hashCode());
  • 一个类在内存中只有一个Class对象,及c1 == c2。
  • 一个类在加载后,类的整个结构都会被封装在Class对象中。

Class类

简介:

  • Class本身即是一个类。
  • 每个类有且仅有一个Class对象,由系统生成。我们不能通过new去创建,只能通过方法获取。
  • 一个Class类即对应了一个加载到JVM中的 .class 文件。

拥有Class对象的数据类型(基本所有类型都有):

Class API

获取当前 Class:

  • Class c = Object.class;
Class c = TestFS.class;
  • Class c = object.getClass();
TestFS testFS = new TestFS();
Class c4 = testFS.getClass();
  • Class c = Class.forName(" 包名 . 类名");
Class c = Class.forName("FanShe.TestFS");
  • 内置包装类特有:Class c = Object.Type;
Class c4 = Integer.TYPE;

获取父类Class:

Class getSuperClass();

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

class Student extends Person {
    public Student(String name) {
        super(name);
    }
}

public static void main(String[] args) {
    Class c_student = Student.class;
    Class c_person = Person.class;

    System.out.println("Student类的父类为:" + c_student.getSuperclass());
    System.out.println("Person类的父类为:" + c_person.getSuperclass());
}

获取类名:

  • getName() 获得完整类名。(包名 + 类名)
  • getSimpleName()获得简单类名。
System.out.println(c.getName());
System.out.println(c.getSimpleName());

获得属性(Field):

  • getField() 获得public属性。
  • getField(String name) 获得已知名字的public属性。
  • getDeclaredField() 获得所有属性。
  • getDeclaredField(String name) 获得已知名字的属性。
Field[] fields =  c.getFields();
for (Field field : fields) {
    System.out.println(field);
}

System.out.println();

fields = c.getDeclaredFields();
for (Field field : fields) {
    System.out.println(field);
}

获得方法(Method):

  • getMethod() 获得本类及父类的全部 public 方法。
  • getMethod(String name,Class 参数类型)获取指定方法。
  • getDeclaredMethod()获取本类的全部方法。
  • getDeclaredMethod(String name,Class 参数类型)获取指定方法。

因为有重载函数,所以在获取指定方法时,需要将参数类型传入。

Method[] methods = c.getMethods();

for (Method method : methods) {
    System.out.println(method);
}

System.out.println("\n");

methods = c.getDeclaredMethods();

for (Method method : methods) {
    System.out.println(method);
}

System.out.println(c.getMethod("getName", null));
System.out.println(c.getMethod("setName", String.class));

获得构造器:

  • getConstructor()获取 public 构造器。
  • getConstructor(Class 参数类型)获取指定 public 构造器。
  • getDeclaredConstructor()获取构造器。
  • getDeclaredConstructor()获取指定构造器。

通过Class创造对象

  • class . newInstance() 调用Class的无参构造方法。(没有就会抛异常)
Class c = Class.forName("TestNewObj.User");
User user = (User)c.newInstance(); //调用无参构造函数。
System.out.println(user);
  • constructor . newInstance()通过获取的构造器,构造对象。
Constructor constructor = c.getDeclaredConstructor(String.class, int.class, int.class);
User user = (User)constructor.newInstance("杨然", 398, 20);
System.out.println(user);

通过获取的Method使用方法

  • method . invoke(Object obj ,参数 ……)激活方法。
Method setName = c.getDeclaredMethod("setName", String.class);
setName.invoke(user,"YR");

开启 / 关闭安全检测(setAccessible)

  • setAccessible(bool flag)设置无障碍。
  • 对于Constructor,Method,Field。
  • 如果为true,那么就可以访问 private 。
  • 如果为false,则实施Java语言访问检测。
// Class 中name为私有属性。
private String name;

// 获取Field name,如需使用则需修改权限。
name.setAccessible(true);

通过获取Field操作属性

  • field . set(Object object,参数)设置属性。

public属性:

Field name = c.getField("name");
name.set(user, "YR");

private属性:

Field name = c.getDeclaredField("name");
name.setAccessible(true);
name.set(user, "YR");
  • field . get(Object object)获取属性值。(同样如果是private属性需要设置setAccessible(true)。
String str = (String)name.get(user);

创造对象性能对比

  • 普通创建对象,并调用方法。
  • 反射创建对象,并调用方法。
  • 反射创建对象,设置Accessible,并调用方法。
public class TestXL {
    
    public void test01() {
        User user = new User();
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("普通方式执行1000000000次方法时间:" + (endTime - startTime));
    }

    public void test02() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class c = Class.forName("TestNewObj.User");
        Constructor constructor = c.getDeclaredConstructor();
        User user = (User) constructor.newInstance();
        Method getName = c.getDeclaredMethod("getName");
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user);
        }

        long endTime = System.currentTimeMillis();
        System.out.println("通过反射方式执行1000000000次方法时间:" + (endTime - startTime));
    }
    
    public void test03() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class c = Class.forName("TestNewObj.User");
        Constructor constructor = c.getDeclaredConstructor();
        User user = (User) constructor.newInstance();
        Method getName = c.getDeclaredMethod("getName");
        getName.setAccessible(true);
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user);
        }

        long endTime = System.currentTimeMillis();
        System.out.println("通过反射方式,并设置Accessible后,执行1000000000次方法时间:" + (endTime - startTime));


    }
    
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        TestXL testXL = new TestXL();
        testXL.test01();
        testXL.test02();
        testXL.test03();
    }
    
}

获取方法参数,返回值,异常类型

参考函数:

public Map<String, Double> test01(Map<String, Integer> map, List<User> list, int num) throws ClassNotFoundException {
    Class c = Class.forName("TestFX.TestFX");
    return null;
}
  • method . getGenericParameterTypes() 获取传参类型。
Method method01 = TestFX.class.getMethod("test01", Map.class, List.class, int.class);
Type[] genericParameterTypes = method01.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
    System.out.println(genericParameterType);
}

  • method . getGenericExceptionTypes()获取抛出异常类型。
Method method01 = TestFX.class.getMethod("test01", Map.class, List.class, int.class);
Type[] genericExceptionTypes = method01.getGenericExceptionTypes();
for (Type genericExceptionType : genericExceptionTypes) {
    System.out.println(genericExceptionType);
}

  • method . getGenericReturnType()获取返回值类型。
Method method01 = TestFX.class.getMethod("test01", Map.class, List.class, int.class);
Type genericReturnType = method01.getGenericReturnType();
System.out.println(genericReturnType);

获取Type泛型的类型

  • 先判断 type 是否是一个 参数化类型(ParameterizedType)。
  • instanceof(实例):左边为对象,右边为类(接口)。如果该对象为该类的实例则为true,否则为false。
  • 在将 type 强转为一个 ParameterizedType类型后,调用getActualTypeArguments()后返回一个Type [ ] ,即为该 type 的泛型类型
Method method01 = TestFX.class.getMethod("test01", Map.class, List.class, int.class);
Type[] genericParameterTypes = method01.getGenericParameterTypes();

for (Type genericParameterType : genericParameterTypes) {
    if (genericParameterType instanceof ParameterizedType) {
        Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
        for (Type actualTypeArgument : actualTypeArguments) {
            System.out.println(genericParameterType + "  " + actualTypeArgument);
        }
    }
}

获取注解及注解的传参

先创建注释与作用的类:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface db_Student{
    String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface db_SX{
    String name();
    String type();
    int length();
}

@db_Student("Student")
public class TestGetZJ {

    @db_SX(name = "id",type = "char(10)",length = 10)
    String id;

    @db_SX(name = "name",type = "nchar(5)",length = 5)
    String name;

    @db_SX(name = "age",type = "int",length = 3)
    int age;

}
获取类与属性的注解:
Class c = TestGetZJ.class;
Annotation[] annotations = c.getAnnotations();
for (Annotation annotation : annotations) {
    System.out.println(annotation);
}

System.out.println();

Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
    Annotation[] annotations1 = field.getAnnotations();
    for (Annotation annotation : annotations1) {
        System.out.println(annotation);
    }
}

获取Annotation中传参的值:
Class c = TestGetZJ.class;
Field name = c.getDeclaredField("name");

db_SX annotation = name.getAnnotation(db_SX.class);

System.out.println(annotation.name());
System.out.println(annotation.type());
System.out.println(annotation.length());

https://www.bilibili.com/video/BV1p4411P7V3

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: Java反射机制是指在运行时动态地获取一个类的信息,并可以操作类的属性、方法和构造器等。Java反射机制可以使程序员在运行时动态地调用类的方法和属性,扩展类的功能,并可以实现注解、工厂模式以及框架开发等。 Java反射机制的原理如下:首先,Java编译器将Java源代码编译为字节码文件,字节码文件中包含着类的信息,这些信息包括类的名称、方法、属性和构造器等等。接着,Java虚拟机将字节码文件加载到内存中,然后通过类加载器将类加载到内存中形成一个类对象,这个类对象可以操作字节码文件中的信息。 使用Java反射机制的过程如下:首先获取类对象,通过类对象来获取类的构造器、属性、方法等信息,然后调用构造器来创建对象,通过属性获取和设置类的成员属性,通过方法调用类的方法等。 Java反射机制的优点是可以在运行时动态地得到类的信息,使得程序员在程序运行时能够对类进行更加灵活的操作,并可以使得程序更加通用化,同时也存在着一定的性能问题,因为Java反射机制需要Java虚拟机进行一定的额外处理,所以在程序运行时需要进行额外的时间和资源消耗。 总之,Java反射机制Java语言的一项重要特性,在Java开发中广泛应用,在代码编写、框架开发以及API开发中具有重要作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ryan kk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值