【Java核心基础知识】09 - Java反射

多线程知识点目录

多线程并发(1)- https://www.jianshu.com/p/8fcfcac74033
多线程并发(2)-https://www.jianshu.com/p/a0c5095ad103
多线程并发(3)-https://www.jianshu.com/p/c5c3bbd42c35
多线程并发(4)-https://www.jianshu.com/p/e45807a9853e
多线程并发(5)-https://www.jianshu.com/p/5217588d82ba
多线程并发(6)-https://www.jianshu.com/p/d7c888a9c03c

一、动态语言

动态语言,是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结构上的变化。比如常见的JavaScript就是动态语言,除此之外RybyPython等也属于动态语言,而C、C++则不属于动态语言。从反射角度说Java属于半动态语言。

二、反射机制概念

Java反射机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。

三、反射的应用场合

3.1 编译时类型和运行时类型

在Java程序中许多对象在运行时会出现两种类型:编译时类型运行时类型

编译时类型(Compile-time Type)通常是你声明变量时所使用的类型。这个类型在编译时期确定,并且会用于编译期的检查,比如类型检查和转换。

运行时类型(Runtime Type)是对象实际的生命周期中的具体类型。这个类型在运行时确定,并且可以动态地改变。

例如:

Person p = new Student

在这里,p的编译时类型是Person,这是你在声明变量时指定的类型。然而,p的运行时类型实际上是Student,因为p实际上指向的是一个Student类型的对象。

3.2 编译时类型无法获取具体方法

编译时类型无法获取具体方法的原因是Java的类型擦除机制。在Java中,泛型类型的信息是在编译期确定的,但这些信息在运行时会被擦除,以便支持Java的动态类型安全和性能优化。

因此,尽管你可以在编译时获取变量的类型信息,但是在运行时,Java虚拟机(JVM)会忽略这些类型信息,而只关注对象的实际运行时类型。这意味着在运行时,你无法通过编译时类型来获取具体方法,因为编译时类型的信息在运行时被擦除了。

如果你想在运行时获取具体方法的信息,你可以使用Java的反射API。反射API允许你在运行时检查类、接口、字段和方法的信息,甚至可以调用对象的方法。通过反射API,你可以动态地获取对象的运行时类型信息,并据此进行相应的操作

四、Java反射API

Java反射API是一组在Java编程语言中用于在运行时检查和操作类、接口、字段和方法的功能强大的API。通过反射API,你可以在运行时获取类的信息,创建对象,调用方法,获取和设置字段值等

以下是一些常用的反射API:

  1. Class类:反射的核心类,代表一个类或接口,提供了一系列方法用于获取类的信息,如名称、注解、成员变量和方法等。
  2. Field类:代表一个字段,提供了一系列方法用于获取字段的类型、名称、访问修饰符等信息,并可以获取和设置字段的值。
  3. Method类:代表一个方法,提供了一系列方法用于获取方法的参数、返回值、访问修饰符等信息,并可以调用该方法。
  4. Constructor类:代表一个构造方法,提供了一系列方法用于创建对象实例。

五、反射使用步骤

  1. 获取想要操作的Class对象,这是反射的核心,可以通过Class对象任意调用类的方法。

  2. 调用Class类的方法是反射的使用阶段。

  3. 使用反射API来操作这些信息。

    import java.lang.reflect.Method;  
      
    public class ReflectionExample {  
        public static void main(String[] args) {  
            try {  
                // 获取String类的Class对象  
                Class stringClass = String.class;  
      
                // 获取一个构造方法  
                Constructor stringConstructor = stringClass.getConstructor(String.class);  
      
                // 创建新的String对象  
                Object stringObject = stringConstructor.newInstance("Hello World");  
      
                // 获取并调用String类的length()方法  
                Method lengthMethod = stringClass.getMethod("length");  
                int length = (Integer) lengthMethod.invoke(stringObject);  
                System.out.println("Length: " + length);  
      
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
    }
    

六、获取Class对象的3种方法

6.1 调用某个对象的getClass方法

Person p = new Persion();
Class clazz = p.getClass();

6.2 调用某个类的class属性来获取该类对应的Class对象

Class clazz = p.class;

6.3 使用class类中的forName()静态方法(最安全、性能最好)

Class clazz = Class.forName("com.example.MyClass");

注意:使用forName()方法时,需要确保类的完全限定名是正确的,否则会抛出ClassNotFoundException异常。


6.4 通过Class对象获取类中方法和属性

// 获取Person类的Class对象
Class clazz = Class.forName("com.example.Person");
// 获取Person类的所有方法信息
Method[] methodArr = clazz.getDeclaredMethods();
for(Method m : methodArr){
    System.out.println(m.toString());
}
// 获取Person类的所有成员属性
Field[] fieldArr = clazz.getDeclaredFields();
for(Field f : fieldArr){
    System.out.println(f.toString());
}
// 获取Person类的所有构造方法信息
Constructor[] constructorArr = clazz.getDeclaredConstructors();
for(Constructor c : constructorArr){
    System.out.println(c.toString());
}

七、创建对象的两种方法

7.1 Class对象的newInstance()

使用Class对象的newInstance()方法来创建该Class对象对应类的实例,但是,这种方法要求该Class对象对应的类由默认的空构造器。

Class clazz = Class.forName("com.example.Person");
Person p = (Person) clazz.newInstance();

7.2 调用Constructor对象的newInstance()

先试用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建Class对象对应类的实例,通过这种方法可以选定构造方法创建实例。

Class clazz = Class.forName("com.example.Person");
// 获取构造方法并创建对象
Constructor c = clazz.getDeclaredConstructor(String.class,String.class,int class);
// 创建对象并设置属性
Person p = (Person) c.newInstance("王梅梅","女",18);

你可能感兴趣的:(【Java核心基础知识】09 - Java反射)