首页 > 编程语言 >Java 反射

Java 反射

时间:2023-02-23 19:36:02浏览次数:33  
标签:反射 Java System println 加载 Class out

image-20220829085156261

1.小demo 快速上手

读取配置文件信息,实现调用方法

image-20220828111706445

@SuppressWarnings({"all"})   //取消显示指定的编译器警告
public class ReflectionQuestion {

    public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        //传统方式  new 对象 -》 调用方法
//        Cat cat = new Cat();
//        cat.hi();

        //尝试使用配置文件读信息 然后做  -》 通过过程理解反射

        // 1. 使用Properties 类, 可以读写配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("E:\\code\\Java\\reflection\\src\\re.properties"));
        String classfullpath = properties.get("classfullpath").toString();
        String method = properties.get("method").toString();

        System.out.println("classfullpath= " + classfullpath);
        System.out.println("method= " + method);

        // 2. 创建对象   传统的方法, 行不通  =》  反射机制
        //new classfullpath();

        // 3. 使用反射机制解决
        // (1)加载类, 返回名字叫Class类型的对象cls
        Class cls = Class.forName(classfullpath);

        // (2)通过 cls 得到你加载的类 com.hspedu.Cat 的对象实例
        Object o = cls.newInstance();
        System.out.println("o的运行类型= " + o.getClass());

        // (3)通过 cls 得到你加载的类 com.hspedu.Cat 的 methodName"hi" 的方法
        //      即:在反射中,可以把方法视为对象(万物皆对象)
        Method method1 = cls.getMethod(method);


        System.out.println("==========================");
        // (4)通过method1 调用方法:即通过方法对象来实现调用方法
        method1.invoke(o);  //传统方法  对象.方法(),  反射机制 方法.invoke(对象)

    }
}

2.反射机制

运行状态中知道类所有的属性和方法

image-20220828113155267在 Java 中的反射机制是指 **在运行状态,对于任意一个类都知道这个类所有的属性和方法; 并且对于任意一个对象,都能够调用它的任意一个方法; **这种动态获取信息以及动态调用对象方法的功能成为 Java 语言的反射机制。

2.1.Java Reflection

    1. 反射机制允许程序在执行期借助于Reflecttion API 取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都有使用。
    2. 加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class类),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。类似于一面镜子,所以将其称之为:反射。

2.2.Java 反射机制原理示意图

image-20220828114059749

2.3.反射相关的主要类:

image-20220828114837781

2.4.反射优缺点

1. 优点:可以动态的创建和使用对象(也是框架底层核心)使用灵活,没有反射机制,框架技术就会失去底层支撑。
2. 缺点:使用反射基本是解释执行,对执行速度有影响。

2.5.反射调用的优化 - 关闭访问检查

image-20220828215642072

image-20220828215720041

image-20220828215738394

3.Class 类

image-20220829085950619

3.1 基本介绍

  1. Class也是类,因此也是继承Object类
  2. Class类对象不是new出来的,而是系统创建的
  3. 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
  4. 每个类的实例都会记得自己是由那个Class实例所生成
  5. 通过Class对象可以完整地得到一个类的完整结构,通过一系列API
  6. Class对象是存放在堆的
  7. 类的字节码的二进制数据,是放在方法区的

3.2 常用方法

/**
     * 演示Clas类的常用方法
     */
public class Class02 {
        public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
            String classAllPath = "com.hspedu.Car";
            // 1. 获取到Car类 对应的 Class 对象
            //<?> 表示不确定的Java类型
            Class<?> cls = Class.forName(classAllPath);
            // 2. 输出cls
            System.out.println(cls);  // 显示cls对象,是哪个类的Class对象  com.hspedu.Car
            System.out.println(cls.getClass()); // 输出cls运行类型 java.lang.Class
            // 3. 得到包名
            System.out.println(cls.getPackage().getName());
            // 4. 得到全类名
            System.out.println(cls.getName());
            // 5. 通过cls创建对象实例
            Car car = (Car) cls.newInstance();  // 实质:Car实例 可以强转
            System.out.println(car);
            // 6. 通过反射获取属性 brand
            Field brand = cls.getField("brand");
            System.out.println(brand.get(car));  //宝马  私有属性不能获取
            // 7. 通过反射给属性赋值
            brand.set(car, "奔驰");
            System.out.println(brand.get(car));
            // 8. 获取所有的属性(字段)
            System.out.println("=======所有的字段属性=======");
            Field[] fields = cls.getFields();
            for(Field f : fields)
                System.out.println(f.getName());  //名称
        }
}

3.3 获取Class对象的六种方式

不同阶段的方法不同

image-20220829154214464

① Class.forName

前提:已知一个类的全类名,且该类在类路径下,可以通过Class类的静态方法forName() 获取, 可能抛出ClassNotFoundException

应用场景:多用于配置文件,读取类全路径,加载类

//1. Class.forName
  String classAllPath = "com.hspedu.Car";  //一般是通过读取配置文件获取
  Class<?> cls1 = Class.forName(classAllPath);
  System.out.println(cls1);

②类名.class

前提:若已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高

应用场景:多用于参数传递,比如通过反射得到对应构造器对象

     //2. 类名.class, 应用于参数传递
     Class cls2 = Car.class;
     System.out.println(cls2);

③对象.getClass()

前提:已知某个类的实例,调用该实例的getClass() 方法获取Class对象

应用场景:通过创建好的对象,获取Class对象

//3. 对象.getClass(), 应用场景,有对象实例
     Car car = new Car();
     Class cls3 = car.getClass();
     System.out.println(cls3);

④通过类加载器【4种】获取到类的Class对象

     //4. 通过类加载器【4种】获取到类的Class对象
     //(1) 先得到类加载器 car
     ClassLoader classLoader = car.getClass().getClassLoader();
     //(2) 通过类加载器得到Class类
     Class<?> cls4 = classLoader.loadClass(classAllPath);
     System.out.println(cls4);

⑤基本数据(int, char, boolean ...)获取Class类对象

Class cls = 基本数据类型.class

     //5. 基本数据(Int, char, boolean, double, byte, long, short) 按如下方式得到Class类对象
     Class<Integer> integerClass = int.class;
     Class<Character> characterClass = char.class;
     Class<Boolean> booleanClass = boolean.class;
     System.out.println(integerClass); // int
     System.out.println(characterClass); // char
     System.out.println(booleanClass); // boolean

⑥基本数据类型对应的包装类

Class cls = 包装类.TYPE

     //6. 基本数据类型对应的包装类,可以通过 .TYPE 得到Class类对象
     Class<Integer> type = Integer.TYPE;
     Class<Character> type1 = Character.TYPE;
     System.out.println(type);
     System.out.println(type1);

4.类加载

  • 基本说明
    • 反射机制是java 实现动态语言的关键, 也就是通过反射实现类动态加载
      1. 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强,
      2. 动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低依赖性
  • 类加载时机
    1. 当创建对象时(new) // 静态加载
    2. 当子类被加载时,父类也加载 // 静态加载
    3. 调用类中的静态成员时 // 静态加载
    4. 通过反射 // 动态加载

4.1 类加载过程图

image-20220829191630821

image-20220829191841788

4.2 类加载的五个阶段

①加载阶段

JVM 在该阶段的主要目的是将字节码从不同的数据源(可能是class 文件、也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的 java.lang.Class 对象

②连接阶段-验证

  1. 目的是为了确保Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
  2. 包括:文件格式验证(是否以魔数 oxcafebabe 开头)、元数据验证,字节码验证和符号引用验证
  3. 可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。

③连接阶段-准备

JVM 会在该阶段对静态变量,分配内存并初始化(对应数据类型的默认初始值如 0、0L、null、false等)。这些变量所使用的内存都将在方法区中进行分配。

image-20220830160934446

④连接阶段-解析

虚拟机将常量池内的符号引用替换为直接引用我的过程

A类 -》 B类

⑤初始化(Initalization)

  1. 到初始化阶段,才真正开始执行类中定义的Java 程序代码,此阶段是执行() 方法的过程
  2. () 方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。
  3. 虚拟机会保证一个类的() 方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的() 方法,其他线程都需要阻塞等待,直到活动线程执行() 方法完毕。

5.通过反射获取类的结构信息

①第一组:java.lang.Class 类

image-20220830164630541

②第二组:java.lang.reflect.Field 类

image-20220830165310570

③第三组:java.lang.reflect.Method 类

image-20220830165920712

④第四组:java.lang.reflect.Constructor 类

image-20220830170459182

6.反射爆破创建实例

6.1 通过反射创建对象

image-20220830172028158

package com.hspedu.reflection;

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

/**
 * 演示通过反射机制创建实例
 */
public class ReflecCreatInstance {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        //1. 先获取到User类的Class对象
        Class<?> userClass = Class.forName("com.hspedu.reflection.User");
        //2. 通过public 的无参构造创建实例
        Object o = userClass.newInstance();
        System.out.println(o);
        //3. 通过public 的有参构造创建实例
        /*
             constructor  对象就是
             public  User(String name){  //public 的有参构造器
                    this.name = name;
            }

        */

        //3.1  先得到对应构造器
        Constructor<?> constructor = userClass.getConstructor(String.class);
        //3.2  创建实例,并传入实参
        Object user1 = constructor.newInstance("名字");
        System.out.println("user1 =" + user1);

        //4. 通过非public 的有参构造创建实例
        //4.1 得到private 的构造器
        Constructor<?> constructor1 = userClass.getDeclaredConstructor(int.class, String.class);
        //4.2 创建实例
        constructor1.setAccessible(true);  //爆破, 使用反射可以访问private 构造器
        Object user2 = constructor1.newInstance(100, "张三");
        System.out.println("user2 =" + user2);
    }
}


class User{
    private int age = 10;

    private String name = "这是名字";

    public User() {
    }

    public  User(String name){  //public 的有参构造器
        this.name = name;
    }

    private User(int age, String name) {  // private 有参构造器
        this.age = age;
        this.name = name;
    }

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

image-20220830174240419

6.2 通过反射访问类中的成员

image-20220830174204931

6.3 通过反射访问类中的成员

image-20220830175110553

标签:反射,Java,System,println,加载,Class,out
From: https://www.cnblogs.com/cx330-957/p/17149135.html

相关文章

  • java基础语法
    java基础语法注释,标识符,关键字注释注释的意思注释不会被执行,是给我们写代码的人看的,让同行能看懂你写的这个语句是什么意思,和完成进度等等....书写注释是一个非......
  • Java异常
    Java异常目录Java异常异常分类常见的异常运行时异常编译时异常异常抓抛异常处理try-catch-finallythrows自定义异常异常分类Exception是程序可以捕获并处理的异常,分为......
  • Java基础语法
    基础语法1.单行注释//2.多行注释/**/3.文档注释/***/4.标识符所有的标识符都应该以字母,或者美元符号$,或者下划线(_)开始标识符大小写敏感5.八大基本数据类......
  • java实现发送邮件
    java发送邮件需要先申请邮箱的授权码,对应配置文件中的password。  1.引入jar<dependency><groupId>javax.mail</groupId><artifactId>mail</artifactId......
  • JAVA解析xml
    <?xmlversion="1.0"encoding="UTF-8"standalone="no"?><contacts><tr><td>4</td><td>广州市鼎和机械设备有限公司44</td><td>东莞......
  • java
    publicclass代表类名称和文件名字一致基本模板:    ......
  • 「趣学前端」JavaScript标准库
    背景最近睡前习惯翻会书,重温了《JavaScript权威指南》这本书。这本书,文字小,内容多。两年了,我才翻到第十章。因为书太厚,平时都充当电脑支架。JavaScript标准库今天阅读的章节......
  • 网站出现java.lang.NullPointerException怎么解决
    今日小结:要解决这个问题,需要找到引发异常的代码行并确定哪个对象为空。这可以通过检查异常堆栈跟踪来完成,堆栈跟踪将告诉你异常在哪个方法中引发以及哪个对象为空。一旦......
  • Java国际化号码验证方法,国内手机号正则表达式
    Java国际化号码验证方法,国内手机号正则表达式 中国电信号段133、149、153、173、177、180、181、189、199中国联通号段130、131、132、145、155、156、166、175、17......
  • java--BigDecimal 类型介绍
      BigDecimal.add();    //加法BigDecimal.subtract();//减法BigDecimal.multiply();//乘法BigDecimal.divide();  //除法在这些BigDecimal调用的这......