JAVA之反射机制

反射的JAVA的一个重要的机制

静态语言:在编译期间检查类型,所有的类型检查发生在编译时。变量的类型在编译时是已知的,不会随着程序运行时期的变化而变化。典型的静态语言包括 Java、C#、C++等。

动态语言:在运行时检查类型,类型检查发生在运行时。变量的类型可以在运行时随程序的执行而改变。典型的动态语言包括 Python、JavaScript、Ruby等。

虽然java是一个静态语言,但是反射机制让java可以成为一个准动态语言。

正常方式实例化对象的过程:导入包类->new关键字实例化对象->获取实例化对象

反射方式:实例化对象->getClass()方法->得到完整的包类

可以说反射是正常的逆过程,但是因为是逆推导过程,运行速度要比正常的慢不少

以下是java中与反射有关的API

  • java.lang.Class:类
  • java.lang.reflect.Field:类的字段
  • java.lang.reflect.Method:类的方法
  • java.lang.reflect.Constructor:类的构造器
  • java.lang.reflect.Modifier:类的修饰符

反射初体验

//Student
package com.std.www.javaReflection;

public class Student {
    private int id;
    private String name;
    private int age;

    public Student() {
    }

    public Student(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


//ReflectionTest
package com.std.www.javaReflection;

public class ReflectionTest {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c1=Class.forName("com.std.www.javaReflection.Student");
        System.out.println(c1);
        Class c2=Class.forName("com.std.www.javaReflection.Student");
        Class c3=Class.forName("com.std.www.javaReflection.Student");
        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());
    }
}

从上面可以发现,一个类在内存加载中只会有一个Class对象,并且整个类的结构都会保存到该Class对象中

Class类介绍

方法:

  • forName(String className):静态方法,根据类的完全限定名(包括包名)返回对应的 Class 对象。

  • newInstance():通过默认构造函数创建类的新实例(已经过时)。

  • getDeclaredFields():获取类中所有字段的数组,包括公共、受保护、默认(包)访问和私有字段,但不包括继承的字段。

  • getDeclaredMethods():获取类中所有方法的数组,与 getDeclaredFields 类似,包括公共、受保护、默认(包)访问和私有方法,但不包括继承的方法。

  • getDeclaredConstructors():获取类中声明的所有构造函数。

  • getMethod(String name, Class<?>... parameterTypes):获取类中指定方法名和参数类型的公共方法。

  • getField(String name):获取类中指定名称的公共字段。

属性:

  • static final Class<?> TYPEClass 对象表示的类的基本类型,如果该类不是基本类型,则返回 null

  • ClassLoader getClassLoader():获取加载这个类的类加载器。

  • String getName():获取类的名称。

  • Class<?>[] getInterfaces():获取该类实现的接口列表。

  • Class<? super T> getSuperclass():获取该类的父类。

得到Class类的几种方法

package com.std.www.javaReflection;

public class ReflectionTest {
    public static void main(String[] args) throws ClassNotFoundException {
        Student stu=new Student();
        //通过对象直接获取
        Class c1=stu.getClass();
        //forName获取
        Class c2=Class.forName("com.std.www.javaReflection.Student");
        //类名获取
        Class c3=Student.class;
        //包装类独有的获取方法
        Class c4=Integer.TYPE;
        //获取父类的Class类
        Class c5=c1.getSuperclass();
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);

    }
}

哪些类有Class对象

  • 外部类,成员类,局部内部类,匿名内部类
  • 接口
  • 数组
  • 枚举
  • 注解
  • 基本数据类型
  • void

类加载器

类加载器(Class Loader)是 Java 运行时环境的一部分,它负责加载 Java 类文件并将其转换为在 JVM 中运行的 Java 类。类加载器的主要任务是在运行时查找和加载类文件。它按需加载类文件,使得在程序运行时可以动态加载类。

类加载器的主要特点如下:

  1. 加载类:负责将类文件的二进制数据加载到内存中。
  2. 检查类的唯一性:确保加载的类在 JVM 中是唯一的,即对于同一个类文件,不会被加载多次。
  3. 类的链接:在加载类时,进行验证、准备、解析等步骤,最终生成可执行代码。
  4. 加载顺序:类加载器采用双亲委派模型,按照一定的顺序加载类,首先交由父类加载器加载,如果父类加载器无法加载,则由当前类加载器加载。

在 Java 中,有如下几种类加载器:

  1. 引导类加载器(Bootstrap Class Loader):负责加载 Java 核心库,是最顶层的类加载器。
  2. 扩展类加载器(Extension Class Loader):负责加载 Java 的扩展库,例如我们自定义的lib目录下的jar包。
  3. 系统类加载器(System Class Loader):也称为应用类加载器,负责加载应用程序的类。

我们也可以通过扩展自定义类加载器

package com.std.www.javaReflection;

public class ReflectionTest {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取系统加载类可以加载类的路径
       String loaders=System.getProperty("java.class.path");
       String[] str=loaders.split(";");
        for (String s : str) {
            System.out.println(s);
        }
    }
}

 如果类不在上面的路径中就无法加载

反射获取类的结构

package com.std.www.javaReflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1=Class.forName("com.std.www.javaReflection.Student");
        //获取类的公有属性
        System.out.println("==================类的公有属性==================");
        Field[] fields = c1.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        //获取类的全部属性
        System.out.println("==================类的全部属性==================");
        fields=c1.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        //获取指定类的属性
        System.out.println("==================指定类的属性==================");
        Field field=c1.getDeclaredField("name");
        System.out.println(field);
        System.out.println("==================类的公共方法==================");
        Method[] methods = c1.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("==================类的全部方法==================");
        methods=c1.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("==================类的指定方法=================");
        Method method = c1.getMethod("setName", String.class);//这里为参数的类型,因为可能存在参数重载
        System.out.println(method);
        System.out.println("==================类的公共构造器=================");
        Constructor[] constructors = c1.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("==================类的指定构造器=================");
        constructors=c1.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("==================类的指定构造器=================");
        System.out.println(c1.getConstructor(null));
        System.out.println(c1.getConstructor(int.class,String.class,int.class));

    }
}

Class对象的用法

package com.std.www.javaReflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectionTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
       //通过反射获取Class对象
        Class c=Class.forName("com.std.www.javaReflection.Student");
        //获取构造器
        Constructor con = c.getConstructor(int.class, String.class, int.class);
        //通过构造器创建对象
        Object stu = con.newInstance(2,"张三",20);
        System.out.println(stu);
        //通过反射获取方法并调用
        Method setName = c.getDeclaredMethod("setName", String.class);
        //方法.invoke(对象,方法实参),激活,把获取到的方法和实际的对象进行绑定调用
        setName.invoke(stu,"王五");
        System.out.println(stu);
        //通过反射获取属性
        Field name=c.getDeclaredField("name");
        name.setAccessible(true);
        name.set(stu,"程序员");
        System.out.println(stu);
        Field age=c.getDeclaredField("age");
        age.set(stu,40);

    }
}

这里我们可以发现私有属性的方法和值无法直接调用,但是可以通过关闭其的属性检测来进行更改,这里设置为可以访问

这就是反射的基本操作

posted @ 2023-11-03 12:57  突破铁皮  阅读(6)  评论(0编辑  收藏  举报