栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Java

Java反射

Java 更新时间:发布时间: 百科书网 趣学号
反射(第一版)

本人可能写的不太好,但是我会不断从书中找到新的
点进行不断更新,


文章目录
  • 反射(第一版)
  • 前言
  • 一、Class类
    • 类的加载过程
    • 类的加载与ClassLoader的理解
  • 二、资源 (这个地方写的不太好)
    • 2.利用类反射
    • 反射的使用
    • 反射在泛型上的操作
  • 总结

反射: 是指能够分析类能力的程序
反射:是Java被称为动态语言的的关键,反射机制允许程序在执行期间借助于反射取得任何类的内部信息,并能直接操作任意对象的内部属性以及方法


在加载完类之后,在堆内存的方法区就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息,我们可以通过这个看到类的结构,这个对象就像一面镜子,通透过这个镜子看到类的结构所以我们称为反射

前言
方法明说明
static ClassforName(String name)返回指定类名name的Class对象
Object newInstance()调用缺省构造函数,返回一个Class对象的实例
getName()返回此Class对象所表示的实体(类、接口、数组类型)的名称
Class getSuperClass()放回当前Class对象的父类的Class对象
Class getinterfaces()获取当前Class对象的接口
ClassLoader getClassLoader()返回该类的类加载器
Constructor[] getConstructors()返回一个包含某些Constructor对象的数组
Method getMothed()返回一个Method对象
Field[] getDecalredFields()返回一个Field对象的一个数组

内置的基本类型使用 .Type

一、Class类

在程序运行期间,java运行时系统始终为所有的对象维护一个运行时类型标识

对象照镜子后可以得到的信息:某一个类的属性、方法、构造器、实现了那些接口,对于每一个类来说,在JRE都为其保留了一个不变的Class对象

  1. Class类本身也是一个类
  2. Clas对象只能由系统建立对象
  3. 一个加载的类在JVM中只有以个Class实例
  4. 每个类的实例都会记得自己是由那一个Class实例所生成的
  5. 通过Class可以完整的得到一个类中所有被加载的结构
  6. Class类是反射的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
  7. 一个Class对象对应的是一个加载到JVM中的一个.class文件
public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        A a = new A();
        Class c1 = a.getClass();
        System.out.println(
                c1   // class com.company.A
                + "      c1.getName():  " + c1.getName()  // c1.getName():  com.company.A
                + "  c1.getSimpleName():   " + c1.getSimpleName());  // c1.getSimpleName():   A

        //根据全限定类名获取对应的Class对象

        Class c2 = Class.forName("com.company.A");
        System.out.println(
                c2   // class com.company.A
                        + "      c2.getName():  " + c2.getName()  // c2.getName():  com.company.A
                        + "  c2.getSimpleName():   " + c2.getSimpleName());  // c2.getSimpleName():   A
        
        // 使用 .class 方法,这个方法能用尽量用
        Class c3 = A.class;
        System.out.println(
                c3   // class com.company.A
                        + "      c3.getName():  " + c3.getName()  // c3.getName():  com.company.A
                        + "  c3.getSimpleName():   " + c3.getSimpleName());  // c3.getSimpleName():   A
          
          Class c4 = Interager.Type;
    
   }
    
    
}

class A {

}

类的加载过程

类的加载与ClassLoader的理解

加载: 将class文件字节码内容加载到内存中,并将这些静态数据装换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象
链接: 将Java类的二进制代码合并到JVM的运行状态之中的过程

  1. 验证: 确保加载的类信息符合JVM规范,没有安全方面的问题
  2. 准备: 正式为类变量分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区进行分配
  3. 解析: 虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
    初始化:
  4. 执行类构造器()方法的过程,类构造器()方法时由编译器自动收集类中所有变量的赋值动作和静态代码块中的语句合并产生的(类构造器是构造类信息的,不是构造该类对象的构造器)
  5. 当初始化有一个类的时候如果发现父类还没有进行初始化,则需要先触发分类的初始化
  6. 虚拟机会保证一个类的()方法在多线程环境中被正确的加锁和同步

类加载器的作用: 将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后再堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口
类缓存: 标准的JavaSE类加载器可以按照要求进行查找类,一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间,不过JVM垃圾回收机制可以回收这些Class对象

类加载器:

  1. 引导类加载器: 无法直接获取
  2. 拓展类加载器: 负责将指定目录下的jar包装入工作库
  3. 系统类加载器:所指的的项目类加载
public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取系统类加载器
        var systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        // 获取系统类加载器的父类加载器 -- 》 拓展类加载器
        var parent = systemClassLoader.getParent();
        System.out.println(parent);

        // 获取拓展类加载器的父类路径 --》 根加载器
        var parent1 = parent.getParent();
        System.out.println(parent1);

        // 测试当前类是哪一个加载器  getClassLoader 获取类加载器
        var classLoader = Class.forName("com.company.Test").getClassLoader();
        System.out.println(classLoader);

        // 如何获得类加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));
    }
}

注释: 在这里有一个双亲委派机制,当我们创建一个类并进行加载时,类加载器会先在系统类加载器中查找看能否找到一个相同的类,如果没有则在拓展类加载器中查找,然后在引导类加载器中查找,只要右移个环节查找到了相同的那么这个类无法进行加载(这个可能是与JVM中只能有一个.class文件有关,我也不太清楚以后会修改)

二、资源 (这个地方写的不太好)

获取指定路径下的资源
URL getResource(String name)
InputStream getResource(String name)
找到与类位与同一位置的资源

public class Test {
    public static void main(String[] args) throws IOException {
        //找到与类位与同一位置的资源
        Class test = Test.class;
        URL resource = test.getResource("123.jpg");
        ImageIcon imageIcon = new ImageIcon(resource);
        InputStream resourceAsStream = test.getResourceAsStream("123.txt");
        String s = new String(resourceAsStream.readAllBytes(), "UTF-8");
        System.out.println(s);
    }
}
2.利用类反射

代码如下(示例):

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException {
        //获取一个类
        var stu = Student.class;
        // 获取无参构造器(只能获取公共的)
        var constructor = stu.getConstructor();
        System.out.println(constructor);
        // 获取本类除私有的构造器,并返回一个构造器数组
        var constructors = stu.getConstructors();
        System.out.println(Arrays.toString(constructors));
        // 获取无参构造器(可以获取私有构造器)
        var declaredConstructor = stu.getDeclaredConstructor();
        System.out.println(declaredConstructor);
        // 获取本类的所有构造器,返回一个构造器数组
        var declaredConstructors = stu.getDeclaredConstructors();
        System.out.println(Arrays.toString(declaredConstructors));
        // 获取指定的构造器
        var constructor1 = stu.getConstructor(int.class);
        System.out.println(constructor1);


        // 获取指定名称的域 仅限于公有的
        Field field = stu.getField("Type");
        System.out.println(field);
        // 获取全部的域返回一个数组,仅限于公有的
        var fields = stu.getFields();
        System.out.println(Arrays.toString(fields));
        // 可以获取所指定的域,
        var name = stu.getDeclaredField("name");
        System.out.println(name);
        // 获取全部的域返回一个数组,不受权限的限制
        var declaredFields = stu.getDeclaredFields();
        System.out.println(Arrays.toString(declaredFields));

        // 获取指定名称的方法(受权限的影响)
        var getId = stu.getMethod("getId",int.class); // 获取参数类型为int.class的方法
        var getId1 = stu.getMethod("getId");  // 获取参数为空的方法
        System.out.println(getId);
        // 获取全部公共的方法包括父类的方法
        var methods = stu.getMethods();
        System.out.println(Arrays.toString(methods));
        var declaredMethods = stu.getDeclaredMethods();
        // 获取全部的方法(不受权限的影响)包括父类的方法
        System.out.println(Arrays.toString(declaredMethods));


    }
}

class Student extends person {
    private String name;
    protected int id;
    public String Type;

    private Student() {
    }

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

    public Student(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public int getId(int i) {
        return id;
    }
    public String getType() {
        return Type;
    }

    private void Create(int a)
    {
        new Student(a);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + ''' +
                ", id=" + id +
                ", Type='" + Type + ''' +
                '}';
    }
}

class person
{
    private int car;

    public person() {
    }

    public person(int car) {
        this.car = car;
    }

    public void setCar(int car) {
        this.car = car;
    }
}

反射的使用

有了类的对象:调用Class对象的newInstance() 方法

  1. 类必须有一个无参数的构造器
  2. 类的构造器的访问权限需要足够
    当我们想要使用有参构造器时,步骤如下:
  3. 通过Class类的getDecalredConstructor(Class…parameterTypes) 取得本类的指定形参类型的构造器
  4. 向构造器的形参中传递一个对象数组进去,里面包含了构造器所需的各个参数
  5. 通过Constructor实例化对象
public class Test {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        var stu = Student.class;

        // 获取默认构造器
        var constructor = stu.getConstructor();
        var student = constructor.newInstance();// 默认调用无参构造器
        System.out.println(student);
        var declaredConstructor = stu.getDeclaredConstructor(String.class, int.class, String.class);
        var student1 = declaredConstructor.newInstance("你好", 1, "学生");
        System.out.println(student1);

        // 调用某个方法
        var setName = stu.getMethod("setName", String.class);
        setName.invoke(student1,"观赏");  // invoke 激活的意思  (对象,值)
        System.out.println(student1);

        // 操作某一个属性
        var name = stu.getDeclaredField("name");
        // 当我们使用的是一个私有域时需要关闭检测
        name.setAccessible(true);
        name.set(student1,"大明");
        System.out.println(student1);


    }
}

class Student {
    private String name;
    protected int id;
    public String Type;

    public Student() {
    }

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


    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

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

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

    public void setType(String type) {
        Type = type;
    }

    public String getType() {
        return Type;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + ''' +
                ", id=" + id +
                ", Type='" + Type + ''' +
                '}';
    }
}

主解:
setAccessible

  1. Method和Filed、Constructor对象都有setAccessible方法
  2. setAccessible 作用是启动和禁止安全检查开关的
  3. 参数值为true则指示反射的对象在使用时应该取消对Java语言的访问检查
    1. 提高反射的效率,如果在代码中必须使用反射,而该句被频繁调用设为true
    2. 使得原本无妨访问的私有成员也可以访问
  4. 参数值为false则指示反射的对象应该实施Java语言访问检查
反射在泛型上的操作
  1. Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器 javac使用的,确保数据的安全性和免去强制类型转换的问题,但是一旦编译完成,所有和泛型有关的类型全部擦除
  2. 为了通过反射操作这些类型,Java新增了ParameterizedType 、GenericArrayType、TypeVariable、WildcardType 几种类型来代表不能背归一到Class类中的类型但是又和原始类型齐名的类型
    1. ParameterizedType : 表示一种参数化类型(列子中只涉及了这一个,其他几个会在以后补充)
    2. GenericArrayType: 表示一种元素类型是参数化类型或者类型变量的数组类型
    3. TypeVariable: 是各种类型变量的父接口
    4. WildcardType :代表一种通配符类型的表达式
public class Test {
    public static void main(String[] args) throws NoSuchMethodException {
        var test01 = Test.class.getMethod("test01", Map.class, List.class);
        // 获取泛型参数信息
        Type[] genericParameterTypes = test01.getGenericParameterTypes();  
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println(genericParameterType);
            System.out.println("_____________________________");
            // 判断泛型参数类型是否属于一个参数化类型
            if(genericParameterType instanceof  ParameterizedType) 
            {
                // 进行强转来获取它的参数信息
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); 
                //class java.lang.String
                //class java.lang.Integer
                //class java.lang.String
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }
        System.out.println("______________________________");
        // 获取返回值类型
        Type genericReturnType = test01.getGenericReturnType();
        if(genericReturnType instanceof ParameterizedType)
        {
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
            //class java.lang.String
            //class java.lang.Integer
        }
    }


    public static Map test01(Map map, List list){
        return map;
    }
    
}

该处使用的url网络请求的数据。


总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/275727.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号