java初步了解反射机制

李亦华的博客      2022-05-10     311

关键词:

反射:框架设计的灵魂

  • 框架:

    半成品软件。可以在框架的基础上进行软件开发,简化编码

  • 反射:

    将类的各个组成部分封装为其他对象,这就是反射机制

    • 好处:

      可以在程序运行过程中,操作这些对象。

      可以解耦,提高程序的可扩展性。

Java在计算机中经历的三个阶段:

img

获取字节码Class对象的三种方式

定义一个Person.java,用于下面的讲解。

package view.study.demo44;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

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

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

Class.forName("类的全称,包括所在包")

Class.forName("全类名"):将字节码文件加载进内存,返回Class对象。

常用之处:多用于配置文件,将类名定义在配置文件中。读取文件,加载类。

package view.study.demo44;

public class DemoForName {

    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> aClass = Class.forName("view.study.demo44.Person");
        System.out.println(aClass);
    }

}

运行程序,控制台输出:

class view.study.demo44.Person

[类名].class

类名.class:通过类名的属性class获取。

常用之处:多用于参数的传递。

package view.study.demo44.test;

import view.study.demo44.Person;

public class DemoClass {

    public static void main(String[] args) {
        Class<Person> personClass = Person.class;
        System.out.println(personClass);
    }

}

运行程序,控制台输出:

class view.study.demo44.Person

[对象].getClass()

对象.getClass():getClass()方法在Object类中定义着。

常用之处:多用于对象的获取字节码的方式。

package view.study.demo44;

public class DemoGetClass {

    public static void main(String[] args) {
        Person person = new Person();
        Class<? extends Person> personClass = person.getClass();
        System.out.println(personClass);
    }

}

运行程序,控制台输出:

class view.study.demo44.Person

结论

上面的三种方式,在一个程序中输出,其实是一样的。

package view.study.demo44.test;

import view.study.demo44.Person;

public class Demo01Class {

    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> class1 = Class.forName("view.study.demo44.Person");

        Class<Person> class2 = Person.class;

        Person person = new Person();
        Class<? extends Person> class3 = person.getClass();

        System.out.println(class1 == class2);
        System.out.println(class1 == class3);
    }

}

运行程序,控制台输出:

true
true

同一个字节码文件(如Person.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

Class对象获取功能:获取成员变量

Field[] getFields()
// 获取所有public修饰的成员变量

Field getField(String name)
// 获取指定名称的 public修饰的成员变量

Field[] getDeclaredFields()
// 获取所有的成员变量,不考虑修饰符

Field getDeclaredField(String name) 
// 获取指定名称的成员变量,不考虑修饰符

定义一个Person.java,用于下面方法的使用:

package view.study.demo45;

public class Person {
    private String name;
    private int age;
    public int a;
    public int b;
    public int c;

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a=" + a +
                ", b=" + b +
                ", c=" + 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 int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

    public int getB() {
        return b;
    }

    public void setB(int b) {
        this.b = b;
    }

    public int getC() {
        return c;
    }

    public void setC(int c) {
        this.c = c;
    }
}

getFields()方法

public class Demo01Reflection {

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

        // 获取Person的Class对象
        Class<?> personClass = Person.class;

        // 获取所有public修饰的成员变量
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

    }

}

运行程序,控制台输出:

public int view.study.demo45.Person.a
public int view.study.demo45.Person.b
public int view.study.demo45.Person.c

假如想要获取:成员变量的初始值(如上例中)

Object o = field.get(new Person());

类似的,也可以使用java.lang.Class中的set()方法,为成员变量设置值。

* Field:成员变量
* 操作:
1. 设置值
    * void set(Object obj, Object value)  
2. 获取值
    * get(Object obj) 

3. 忽略访问权限修饰符的安全检查
    * setAccessible(true):暴力反射

getField(String name)方法

public class Demo01Reflection {

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

        // 获取Person的Class对象
        Class<?> personClass = Person.class;

        // 获取指定名称的 public修饰的成员变量
        Field a = personClass.getField("a");
        System.out.println(a);

    }

}

运行程序,控制台输出:

public int view.study.demo45.Person.a

getDeclaredFields()方法

public class Demo01Reflection {

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

        // 获取Person的Class对象
        Class<?> personClass = Person.class;

        // 获取所有的成员变量,不考虑修饰符
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

    }

}

运行程序,控制台输出:

private java.lang.String view.study.demo45.Person.name
private int view.study.demo45.Person.age
public int view.study.demo45.Person.a
public int view.study.demo45.Person.b
public int view.study.demo45.Person.c

getDeclaredField(String name)方法

public class Demo01Reflection {

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

        // 获取Person的Class对象
        Class<?> personClass = Person.class;

        // 获取指定名称的成员变量,不考虑修饰符
        Field name = personClass.getDeclaredField("name");
        Field b = personClass.getDeclaredField("b");
        System.out.println(name);
        System.out.println(b);

    }

}

运行程序,控制台输出:

private java.lang.String view.study.demo45.Person.name
public int view.study.demo45.Person.b

Class对象获取功能:获取构造方法

Constructor<?>[] getConstructors()
// 返回一个构造函数对象数组,该构造函数对象数组每个元素,都反映Class对象表示的类所对应公共构造函数。

Constructor<T> getConstructor(类<?>... parameterTypes)
// 返回一个构造函数对象,该构造函数对象反映此Class对象表示的类所指定公共构造函数。

Constructor<?>[] getDeclaredConstructors()
// 返回一个构造函数对象数组,该构造函数对象数组每个元素,都反映Class对象(或interface对象)表示的类所对应公共构造函数。

Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
// 返回一个构造函数对象,该构造函数对象反映此Class对象(或interface对象)表示的类所指定公共构造函数。

下面就介绍:getConstructors()方法和getConstructors()方法。剩下两种方法的使用如介绍中方法一致

getConstructor()方法

这里例子中,所使用的Person.java,使用的是“获取成员变量”例子中定义的。

import java.lang.reflect.Constructor;

public class Demo02Reflection {
    public static void main(String[] args) throws NoSuchMethodException {

        // 创建Person.class对象
        Person person = new Person("LeeHua", 22, 6, 6, 6);
        // 获取Person的Class对象
        Class<?> personClass = person.getClass();

        // 获取一个Person.class构造函数对象
        Constructor<?> constructor = personClass.getConstructor();
        System.out.println(constructor);
        
    }
}

运行程序,控制台输出:

public view.study.demo45.Person()

getConstructors()方法

这里例子中,所使用的Person.java,使用的是“获取成员变量”例子中定义的。

import java.lang.reflect.Constructor;

public class Demo02Reflection {
    public static void main(String[] args) throws NoSuchMethodException {

        // 创建Person.class对象
        Person person = new Person("LeeHua", 22, 6, 6, 6);
        // 获取Person的Class对象
        Class<?> personClass = person.getClass();

        // 获取一个Person.class构造函数数组对象
        Constructor<?>[] constructors = personClass.getConstructors();
        for (Constructor<?> constructor1 : constructors) {
            System.out.println(constructor1);
        }
        
    }
}

运行程序,控制台输出:

public view.study.demo45.Person()
public view.study.demo45.Person(java.lang.String,int,int,int,int)

Class对象获取功能:获取成员方法

Method[] getMethods()
// 返回一个包含{@code Method}对象的数组,这些对象反映此{@code Class}对象表示的类或接口的所有公共方法,包括由类或接
// 口声明的方法以及从超类和超接口继承的方法。

Method getMethod(String name, 类<?>... parameterTypes)
// 返回一个指定的{@code Method}对象,该对象反映此{@code Class}对象表示的类或接口的指定公共成员方法。

Method[] getDeclaredMethods()
// 返回一个包含{@code Method}对象的数组,这些对象反映了此{@code Class}对象表示的类或接口的所有已声明方法,包括公共,
// 受保护,默认(程序包)访问和私有方法,但不包括继承的方法。

Method getDeclaredMethod(String name, 类<?>... parameterTypes)
// 返回一个指定的{@code Method}对象,该对象反映此{@code Class}对象表示的类或接口的指定声明方法。

getMethods()方法

package view.study.demo45;

import java.lang.reflect.Method;

public class Demo03Reflection {
    public static void main(String[] args) throws NoSuchMethodException {
        // 创建Person.class对象
        Person person = new Person("LeeHua", 22, 6, 6, 6);
        // 获取Person的Class对象
        Class<?> personClass = person.getClass();

        // 获取一个包含{@code Method}对象的数组
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method.getName());
        }
    }
}

运行程序,控制台输出:

toString
getName
setName
getAge
setAge
getB
setB
getC
setC
getA
setA
wait
wait
wait
equals
hashCode
getClass
notify
notifyAll

其中红色部分是其父类或其继承的接口的方法。

getMethod(String name, 类<?>... parameterTypes)方法

package view.study.demo45;

import java.lang.reflect.Method;

public class Demo03Reflection {
    public static void main(String[] args) throws NoSuchMethodException {
        // 创建Person.class对象
        Person person = new Person("LeeHua", 22, 6, 6, 6);
        // 获取Person的Class对象
        Class<?> personClass = person.getClass();

        // 返回一个指定的{@code Method}对象
        Method getAge = personClass.getMethod("getAge");
        System.out.println(getAge);
    }
}

运行程序,控制台输出:

public int view.study.demo45.Person.getAge()

getDeclaredMethods()方法

package view.study.demo45;

import java.lang.reflect.Method;

public class Demo03Reflection {
    public static void main(String[] args) throws NoSuchMethodException {
        // 创建Person.class对象
        Person person = new Person("LeeHua", 22, 6, 6, 6);
        // 获取Person的Class对象
        Class<?> personClass = person.getClass();

        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
    }
}

运行程序,控制台输出:

public java.lang.String view.study.demo45.Person.toString()
public java.lang.String view.study.demo45.Person.getName()
public void view.study.demo45.Person.setName(java.lang.String)
public int view.study.demo45.Person.getAge()
public void view.study.demo45.Person.setAge(int)
public int view.study.demo45.Person.getB()
public void view.study.demo45.Person.setB(int)
public int view.study.demo45.Person.getC()
public void view.study.demo45.Person.setC(int)
public int view.study.demo45.Person.getA()
public void view.study.demo45.Person.setA(int)

getDeclaredMethod(String name, 类<?>... parameterTypes)方法

package view.study.demo45;

import java.lang.reflect.Method;

public class Demo03Reflection {
    public static void main(String[] args) throws NoSuchMethodException {
        // 创建Person.class对象
        Person person = new Person("LeeHua", 22, 6, 6, 6);
        // 获取Person的Class对象
        Class<?> personClass = person.getClass();

        Method getName = personClass.getDeclaredMethod("getName");
        System.out.println(getName);
    }
}

运行程序,控制台输出:

public java.lang.String view.study.demo45.Person.getName()

案例

需求

写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法

实现之前,定义Person.java、Student.java:

package view.study.demo46;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    
    public void personMethod() {
        System.out.println("我是Person中的方法!!!");
    }

    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;
    }
}
package view.study.demo46;

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

    public Student() {
    }

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

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

    public void studentMethod() {
        System.out.println("我是Student中的方法!!!");
    }

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

实现步骤

  1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
  2. 在程序中加载读取配置文件
  3. 使用反射技术来加载类文件进内存
  4. 创建对象
  5. 执行方法

实现

第一步:创建配置文件——pro.properties,并将需要创建的对象的全类名和需要执行的方法定义在配置文件中

配置文件内容如下:

className=view.study.demo46.Person
methodName=personMethod

第二步:在程序中加载获取配置文件*
*

// 1. 在程序中加载读取配置文件
// 1.1 创建Properties对象
Properties pro = new Properties();
// 1.2 加载配置文件,转换为一个集合
// 1.2.1 获取类的类加载器
ClassLoader classLoader = DemoReflection.class.getClassLoader();
// 1.2.2 读取配置文件
// InputStream inputStream = classLoader.getResourceAsStream("pro.properties");
InputStream inputStream = new FileInputStream("/Users/liyihua/IdeaProjects/Study/src/view/study/demo46/pro.properties");
// 1.2.3 加载配置文件
pro.load(inputStream);

第三步:获取配置文件中的定义数据

// 2. 获取配置文件中定义的数据
// 2.1 获取类名称
String className = pro.getProperty("className");
// 2.2 获取方法名称
String methodName = pro.getProperty("methodName");

第四步:加载该类进内存

// 3. 加载该类进内存
Class<?> aClass = Class.forName(className);

第五步:创建对象

// 4. 创建对象
Object object = aClass.newInstance();

第六步:获取方法对象

// 5. 获取方法对象
Method method = aClass.getMethod(methodName);

第七步:执行方法

// 6. 执行方法
method.invoke(object);

代码总和:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class DemoReflection {
    public static void main(String[] args) throws IOException, ClassNotFoundException,
            IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        // 1. 在程序中加载读取配置文件
        // 1.1 创建Properties对象
        Properties pro = new Properties();
        // 1.2 加载配置文件,转换为一个集合
        // 1.2.1 获取类的类加载器
        ClassLoader classLoader = DemoReflection.class.getClassLoader();
        // 1.2.2 读取配置文件
        // InputStream inputStream = classLoader.getResourceAsStream("pro.properties");
        InputStream inputStream = new FileInputStream("/Users/liyihua/IdeaProjects/Study/src/view/study/demo46/pro.properties");
        // 1.2.3 加载配置文件
        pro.load(inputStream);

        // 2. 获取配置文件中定义的数据
        // 2.1 获取类名称
        String className = pro.getProperty("className");
        // 2.2 获取方法名称
        String methodName = pro.getProperty("methodName");

        // 3. 加载该类进内存
        Class<?> aClass = Class.forName(className);

        // 4. 创建对象
        Object object = aClass.newInstance();

        // 5. 获取方法对象
        Method method = aClass.getMethod(methodName);

        // 6. 执行方法
        method.invoke(object);
    }
}

运行程序,控制台输出:

我是Person中的方法!!!

修改配置文件中的内容:

className=view.study.demo46.Student
methodName=studentMethod

运行程序,控制台输出:

我是Student中的方法!!!

如此,修改配置文件,不用修改代码。实现了创建任意类的对象,并且执行其中任意方法。

objective-c反射机制

...机制可以让oc语言更加的灵活。这句话是对oc反射机制的初步认识,不过具体的怎么类似于java机制,怎么让oc更加的灵活,还得看下面的内容才能了解。   oc反射机制有三个用途:   1.获得Class    查看详情

java反射机制

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

12000+字java反射,一起全面了解java反射机制,为学习框架铺路(代码片段)

文章目录Java反射机制理解Class类获取Class类实例类的加载过程类加载器ClassLoader创建运行时类的对象获取运行时类的结构调用运行时类的指定结构动态代理Java反射机制Reflection是被视为动态语言的关键,反射机制允许程序在执... 查看详情

反射机制

...在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。 查看详情

java反射机制

1.通过例子了解Java反射机制1packagejava_reflect;23publicclassPerson{4publicStringname;5privateintage;678static{9System.out.println("我是static的静态代码块!!");10}11publicPerson(){}12publicPerson(Stringname,intage){13thi 查看详情

java--反射机制

...的一个重要特性。在学习Java反射机制前,大家应该先了解两个概念,编译期和运行期。编译期是指把源码交给编译器编译成计算机可以执行的文件的过程。在Java中也就是把Java代码编成class文件的过程。编译期只是做了一... 查看详情

深入理解java的反射机制

...演示java反射的应用场景1,java反射是什么首先大家应该先了解两个概念,编译期和运行期,编译期就是编译器帮你把源代码翻译成机器能识别的代码,比如编译器把java代码编译成jvm识别的字节码文件,而运行期指的是将可执行... 查看详情

java中的反射机制

...能在设计模式中使用过,但是感觉对它没有一个较深入的了解,这次重新学习了一下,感觉还行吧!       一,先看一下反射的概念: 查看详情

java中的反射机制(代码片段)

...反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法;对于任意一个对象,都能够调用它的任... 查看详情

java中的反射机制

...能在设计模式中使用过,但是感觉对它没有一个较深入的了解,这次重新学习了一下,感觉还行吧!&nb 查看详情

转载:java中的反射机制

...能在设计模式中使用过,但是感觉对它没有一个较深入的了解,这次重新学习了一下,感觉还行吧!       一,先看一下反射的概念:              查看详情

java的反射机制,理解一下

1,java反射是什么首先应该先了解两个概念,编译期和运行期,编译期就是编译器帮你把源代码翻译成机器能识别的代码,比如编译器把java代码编译成jvm识别的字节码文件,而运行期指的是将可执行文件交给操作系统去执行,JAV... 查看详情

java基础-反射(代码片段)

...获取Class类的实例哪些类型可以有Class对象Java的内存分析了解:类的加载过程与ClassLoader的理解什么时候会发生类初始化类加载器的作用双亲委派机制获取运行时类的完整结构有了Clas 查看详情

java基础-反射(代码片段)

...获取Class类的实例哪些类型可以有Class对象Java的内存分析了解:类的加载过程与ClassLoader的理解什么时候会发生类初始化类加载器的作用双亲委派机制获取运行时类的完整结构有了Clas 查看详情

强哥说java--反射

...​​​​4.Class实例可以是哪些结构的说明​​​​三、了解ClassLoader​​​​1.类 查看详情

面试官问:“说一下java的反射机制吧”。

...作任意对象的内部属性及方法。透过反射的简介我们可以了解到反射的一个比较重要的应用场景:在实际开发中,我们往往不会把项目中所有用到的类都一下子加载到内存中。有些类ÿ 查看详情

面试官问:“说一下java的反射机制吧”。

...作任意对象的内部属性及方法。透过反射的简介我们可以了解到反射的一个比较重要的应用场景:在实际开发中,我们往往不会把项目中所有用到的类都一下子加载到内存中。有些类ÿ 查看详情

java基础部分复习(java反射)

关于反射:1.需要了解jvm类的加载机制(java高级部分会详细介绍)2.反射的API其实只要多看看API和源码,很容易就懂了。下面是代码,简单讲解反射的使用:importjava.lang.reflect.Constructor;importjava.lang.reflect.Field;importjava.lang.reflect.Meth... 查看详情