java第九弹 java反射机制

news/2024/5/19 6:03:32 标签: java, 反射

通常情况下,我们做项目时,设计一个类,根据类去创建这个类的对象,根据对象名调用方法、属性和构造方法。

反射与这种情况不一样。

什么是反射

(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

反射是框架设计的灵魂。

框架定义:半成品软件,可以在框架的基础上进行软件开发,简化代码,解耦。

下面程序执行过程。

在这里插入图片描述

反射的关键是获取Class对象(类的字节码文件)。

获取Class类对象有三种方式。
1.源代码阶段:Class.forName("全类名"):将字节码文件加载到内存,返回该字节码文件的Class类对象
     ---多用在配置文件,把类的定义放在配置文件里,读取配置文件,加载类。
2.Class类对象阶段:通过类名.class获取Class类对象
     ---多用于参数传递。
3.运行阶段:通过对象名.getClass()获取,此方法生命在Object类里的方法
     ---多用于获取字节码文件。
java">package com.hpe.java;


public class TestReflection {

    public static void main(String[] args) throws ClassNotFoundException {

        //通常的做法
        //Person person = new Person();
        //person.setName("张三");
        //person.setAge(20);
        //System.out.println(person);
        //person.eat();

        //1.源代码阶段:Class.forName("全类名");
        Class clazz = Class.forName("com.hpe.java.Person");
        System.out.println(clazz);//class com.hpe.java.Person

        //2.Class类对象阶段:通过类名.class获取Class类对象
        Class<Person> clazz1 = Person.class;
        System.out.println(clazz1);//class com.hpe.java.Person

        //运行阶段:通过对象名.getClass()获取
        Person person = new Person();
        Class clazz2 = person.getClass();
        System.out.println(clazz2);//class com.hpe.java.Person

        //通过==比较三个Class类对象
        System.out.println(clazz == clazz1);//true
        System.out.println(clazz == clazz2);//true

        Class<Student> studentClass = Student.class;
        System.out.println(studentClass == clazz);//false
    }
}

通过上面三个Class对象的比较,可以知道一个类在 JVM 中只会有一个Class实例,无论使用哪一种方式获取Class类对象,获取到的都是同一个。

那获取到Class对象之后要做什么呢?

Class类对象的功能
1.获取成员变量
2.获取成员方法
3.获取构造方法
4.获取类名

首先来说一下涉及到成员变量的几个方法。

获取成员变量:
1.Field[] getFields():获取所有public修饰的成员方法。
2.Field getField():根据变量名获取public修饰的成员变量。
3.Field[] getDeclareFields():获取所有的成员变量。
4.Field getDeclareField(String name):根据变量名获取成员变量。
对成员变量设置值和获取值
set(Object obj, Object value):设置成员变量的值。
Object get(Object obj):获取成员变量值。
int getModifiers():获取每个属性的访问修饰符
Class getModifiers():获取属性的类型

先写一个Person类。

java">package com.hpe.java;

public class Person {

    private String name;
    private int age;
    public int id;
    int a;
    protected int b;
    private int c;

    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;
    }

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

    public Person() {
    }

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

    public void eat() {
        System.out.println("人在吃饭");
    }

    public void eat(String food){
        System.out.println("人在吃饭,吃的" + food);
    }

    private void show() {
        System.out.println("show方法");
    }

    public String sayHello(){
        return "Hello";
    }

    public static void info(){
        System.out.println("静态info方法");
    }
}
java">package com.hpe.java;

import java.lang.reflect.Field;

public class TestField {

    public static void main(String[] args) throws Exception {

        //获取Class对象
        Class<Person> personClass = Person.class;
        //1.Field[] getFields():获取所有public修饰的成员方法
        Field[] fields = personClass.getFields();
        for(Field field : fields){
            System.out.println(field);//public int com.hpe.java.Person.id
        }

        System.out.println("===================================");
        //2.Field getField():根据变量名获取public修饰的成员变量
        Field id = personClass.getField("id");
        //System.out.println(name);//public int com.hpe.java.Person.id

        //Object get(Object obj):获取成员变量值。
        //创建personClass对应的运行时对象
        //直接通过Class对象创建实例
        Person p = personClass.newInstance();
        Object o = id.get(p);
        System.out.println(o);//0
        //设置值
        id.set(p, 10);
        System.out.println(id.get(p));//10
        System.out.println("===================================");

        //3.Field[] getDeclareFields():
        Field[] dlf = personClass.getDeclaredFields();
        for(Field field : dlf){
            //1.获取每个属性的访问修饰符
            //缺省-0   public-1     private-2    protected-4
            int modifiers = field.getModifiers();
            System.out.println(modifiers);

            //2.获取属性的类型
            Class type = field.getType();
            String name = type.getName();
            System.out.println(name);


            //System.out.println(field);
        }

        Field name = personClass.getDeclaredField("c");
        System.out.println(name);//private java.lang.String com.hpe.java.Person.name
        //忽略访问权限的安全检查机制
        name.setAccessible(true);
        Object c = name.get(p);//报错
        System.out.println(c);
    }
}
注意:
1.getFields和getField只能获取到public修饰的变量;getDeclareField和getDeclareFields能获取所有的变量。
2.getModifiers会返回int类型的数据,缺省-0、public-1、private-2、protected-4。
3.当用get获取private修饰的变量的值的时候,要忽略访问权限的安全检查机制,即需要先执行 属性.setAccessible(true);然后再使用get获取值。

下面看一下获取构造方法。

构造方法的作用就是创建对象。

获取构造方法
1.Constructor[] getConstructors():返回所有的public修饰的构造方法。
2.Constructor getConstructor(参数类型.class,...):根据参数列表获取对应的public修饰的构造方法。
3.Constructor[] getDeclareConstructor(参数类型.class,...):获取所有构造方法。
4.Constructor getDeclareConstructor(参数类型.class,.):根据参数列表获取对应的构造方法。
java">package com.hpe.java;

import java.lang.reflect.Constructor;

public class TestConstructor {

    public static void main(String[] args) throws Exception {
        //1.获取Class类对象
        Class<Person> personClass = Person.class;

        //Constructor getConstructor(参数类型.class,...):根据参数列表获取对应的构造方法。
        Constructor<Person> c = personClass.getConstructor(String.class, int.class);
        System.out.println(c);//public com.hpe.java.Person(java.lang.String,int)
        //构造方法的作用就是创建对象
        //通过有参构造方法创建对象
        Person person = c.newInstance("张三", 18);
        System.out.println(person);

        //通过无参构造方法创建对象,创建对象的常用方式都是无参构造方法
        Constructor<Person> c1 = personClass.getConstructor();
        Person person1 = c1.newInstance();
        System.out.println(person1);
        
        //简化的方式:用Class类对象的newInstance()创建对象
        Person person2 = personClass.newInstance();
        System.out.println(person2);
    }
}
说明:
1.获取属性、构造函数时,凡是方法里带有Declare的都可以获取所有的。
2.获取构造函数的目的是创建对象,可以通过有参构造函数和无参构造函数创建对象,也可以直接通过Class对象创建对象,通过Class对象创建对象相当于调用了无参构造函数创建对象。

下面说一下获取方法。

获取方法
1.Method[] getMethods():获取所有的public方法,包括父类的。
2.Method getMethod(方法名):返回指定的public方法。
3.Method[] getDeclareMethods():获取所有方法,包含私有的,不包含父类的。
4.Method getDeclareMethod(方法名):返回指定的方法。
java">package com.hpe.java;


public class Person {

    private String name;
    private int age;
    public int id;
    int a;
    protected int b;
    private int c;

    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;
    }

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

    public Person() {
    }

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

    public void eat() {
        System.out.println("人在吃饭");
    }

    public void eat(String food){
        System.out.println("人在吃饭,吃的" + food);
    }

    private void show() {
        System.out.println("show方法");
    }

    public String sayHello(){
        return "Hello";
    }

    public static void info(){
        System.out.println("静态info方法");
    }
}
java">package com.hpe.java;

import java.lang.reflect.Field;

public class TestMethod {

    public static void main(String[] args) throws Exception {

        Class<Person> personClass = Person.class;
        //1.方法[] getMethods():获取所有的public方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("==================================");

        //2.方法[] getDeclareMethods():获取所有方法,包含私有的,不包含父类的
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method);
        }

        System.out.println("==================================");
        //创建运行时对象
        Person person = personClass.newInstance();
        //操作属性
        //获取属性
        Field name = personClass.getDeclaredField("name");
        Field age = personClass.getDeclaredField("age");
        name.setAccessible(true);
        age.setAccessible(true);
        //设置属性值
        name.set(person, "张三");
        age.set(person, 18);
        Object name1 = name.get(person);
        Object age1 = age.get(person);
        System.out.println("name: " + name1 + ",age: " + age1);

        //3.方法 getMethods(String name,...):返回指定的public方法
        Method eat = personClass.getMethod("eat");
        //invoke(指定的对象, 形参列表)
        eat.invoke(person);//方法属于对象

        //调用有返回值的方法
        Method sayHello = personClass.getMethod("sayHello");
        Object returnValue = sayHello.invoke(person);
        System.out.println(returnValue);

        //调用静态方法
        Method info = personClass.getMethod("info");
        //info.invoke(person);
        //info.invoke(personClass);
        info.invoke(Person.class);

        //调用有参数的方法
        Method eat1 = personClass.getMethod("eat", String.class);
        eat1.invoke(person, "辣子鸡");
    }
}
注意:
1.getMethods不仅获取本类的public方法,也能获取到父类的public方法。
2.getDeclareMethods只能获取本类的所有方法。
3.调用有返回值的方法的时候,需要创建Object类型变量来接受返回值。
4.调用静态方法的时候,invoke的参数是本类的Class对象。
5.调用有参数的方法的时候,invoke的第一个参数为对象名,后几个参数为实参。

练习:模拟一个框架,在不改变该类的任何代码的前提下,可以帮我们创建类的对象,并且可以执行该对象的任何方法。

如何实现?

1.配置文件
2.反射

具体步骤。

1.创建配置文件,在配置文件里定义需要创建类的全类名和需要执行的方法或者属性。
2.在java程序中加载配置文件,读配置文件里的相关信息。
3.使用反射技术加载文件到内存中。
4.创建对象。
5.执行方法或者属性。
java">package com.hpe.java;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;


public class TestFrame {

    public static void main(String[] args) throws Exception {
        
        //1.创建Properties对象
        Properties properties = new Properties();

        通过流的方式读取配置文件
        //FileInputStream fis = new FileInputStream(new File("proclass.properties"));
        将输入流加载到Properties对象中
        //properties.load(fis);
        //fis.close();

        //2.通过类加载器去加载配置文件
        //类加载器的作用就是把文件加载到内存中,我们就可以通过类加载器获取所有的字节码文件,
        //同样也可以获取项目中的配置文件。

        //2.1获取当前类字节码文件的类加载器(所有的字节码文件都是同一个类加载器)
        ClassLoader classLoader = TestFrame.class.getClassLoader();
        //2.2通过类加载器加载配置文件输入流
        InputStream is = classLoader.getResourceAsStream("proclass.properties");
        properties.load(is);

        //3.获取配置文件里的相关配置
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        System.out.println(user + ":" + password);

        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");

        //4.通过反射获取Class对象(把字节码文件加载到内存中)
        Class clazz = Class.forName(className);

        //5.创建对象
        Object o = clazz.newInstance();
        //6.获取对象的方法
        Method method = clazz.getMethod(methodName);
        //7.调用方法
        method.invoke(o);
    }
}
读取配置文件有两种方式,第一种是流的方式读取配置文件,第二种是通过类加载器读取配置文件。
我们主要来说明一下第二种方式:通过类加载器读取配置文件。
一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。

http://www.niftyadmin.cn/n/1521695.html

相关文章

idea的debug调试按钮

idea debug 只对debug的简单调试功能的使用进行了说明&#xff0c;其实也已经够用了。 没有对深层次的debug调试功能进行说明。 一、code 1.1.项目结构 1.2.项目代码 MainApplication&#x1f447; package com.java2001;public class MainApplication {public static vo…

Vue 学习0——API的使用

render内置方法 前言&#xff1a;在页面上渲染数据&#xff0c;我们可以通过new Vue然后在components声明组件名称&#xff0c;也可以通过render内置方法&#xff0c;效果等同。 demo App.vue <template><div id"app">{{hello}}</div> </temp…

Vue——表单元素demo watch 例子

目录 ㋀㏫ ㍩ ✎ input 元素 ◆ v-model 修饰符 ❶ lazy ❷ number ◆ v-model 和 v-bind 区别 ✎ 单选框和多选框 ❶ radio和checkbox ✎ 下拉框 ❶ select ❷ select 和 v-for ✎ watch ◆ 前言 ❶ 练习&#xff08;利用监听v-model绑定的myValue&#xff0c…

Vue 学习11——Vue过渡与动画(二)【动态组件如何实现transition】/ 如何在vue中进入第三方依赖 / vue插件

目录 ㋀㏭ ☪ App.vue中如何进行组件A和组件B的切换 ◆ transition ❶ a.vue ❷ b.vue ❸ App.vue ㊫ 跟我写代码 ➫ 总结 ☪ CSS过渡 ☪ JS过渡 ◆ 内容 Ⅰ 内容截图 Ⅱ 方法 Ⅲ 例子 ◆ 实战 1.实现上面同样的效果只有监听三个阶段 2.引入jquery第三方库&am…

vuex——状态管理插件(转)

我在网上看到一篇很好关于vuex使用的文章&#xff08;转&#xff09;所以本知识要点按照那篇文章操作。我们在使用vuex插件的时候&#xff0c;一定要使用npm install --save vuex&#xff0c;不然程序会报错&#xff0c;并且提示你请安装。 目录 ☪ &#xff1f;数组状态管理…

a.vue基础入门项目实战——(项目初始化、修改根组件、首页页面、三要素基本搭建和json-server)实战01

目录 ​ 百度网盘源代码 ​ my-sz-one项目CLI创建 ◆ 前提依赖构建 1、router-view 2、ajax&#xff08;菜鸟教程&#xff09; 3、typicode/json-server 4、json-server文章 ◆ 基础项目构建 ​ my-sz-one项目内容实现 ◆ 首页三要素&#xff08;header、content、foot…