Java教程

2.Java-反射

本文主要是介绍2.Java-反射,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1.反射概述

image-20211222100141606

Java反射机制:是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增加程序的灵活性,程序不用在编译期就完成确定,在运行期任然可以扩展。

2.获取Class类的对象

想通过反射去使用一个类,首先需要获取到该类的字节码文件对象,也就是类型为Class类型的对象

三种获取Class类型的对象

  • 使用类的class()属性来获取该类对应的Class对象。举例:Student.class将会返回Student
  • 调用对象的getClass()方法,返回该对象所属类对应的Class对象
    • 该方法是Object类中的方法,所有的Java对象都可以调用该方法
  • 使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径。

代码示例:

  • Student类
package Study02;

public class Student {
    //成员变量:一个私有,一个默认,一个公共
    private String name;
    int age;
    public String address;
    //构造方法:一个私有,一个默认,两个公共
    private Student(String name){
        this.name = name;
    }
    Student(String name,int age){
        this.name = name;
        this.age = age;
    }

    public Student() {
    }

    public Student(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    //成员方法:一个私有,四个公共
    private void function(){
        System.out.println("function");
    }
    public void method1(){
        System.out.println("method");
    }
    public void method2(String s){
        System.out.println("method"+s);
    }
    public String method3(String s,int n){
      return s+","+n;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}
  • ReflectDemo
package Study02;
/*
三种获取`Class`类型的对象
- 使用类的`class()`属性来获取该类对应的`Class`对象。举例:`Student.class`将会返回`Student`
- 调用对象的`getClass()`方法,返回该对象所属类对应的`Class`对象
	- 该方法是`Object`类中的方法,所有的`Java`对象都可以调用该方法
- 使用`Class`类中的静态方法`forName(String className)`,该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径。
*/
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //- 使用类的`class()`属性来获取该类对应的`Class`对象。举例:`Student.class`将会返回`Student`
        //最方便
        Class<Student> c1 = Student.class;
        System.out.println(c1);//class Study02.Student
        Class<Student> c2 = Student.class;
        System.out.println(c1==c2);//true
        System.out.println("========================");

        //- 调用对象的`getClass()`方法,返回该对象所属类对应的`Class`对象
        Student s = new Student();
        Class<? extends Student> c3 = s.getClass();
        System.out.println(c1==c3);//true
        System.out.println("========================");

        //- 使用`Class`类中的静态方法`forName(String className)`
        //最灵活
        Class<?> c4 = Class.forName("Study02.Student");
        System.out.println(c1==c4);
    }
}

3.反射获取构造方法并使用

1.知识讲解

Class中用于获取构造方法的方法

  • Constructor<?>[]getConstructors():返回所有公共构造方法对象的数组
  • Constructor<?>[]getDelcaredConstructors():返回所有构造方法对象的数组
  • Constructor<T>getConstructor(Class<?>…parameterTypes):返回单个公共构造方法对象
  • Constructor<T>getDelcaredConstructor(Class<?>…parameterTypes):返回单个构造方法对象

Constructor类中用于创建对象的方法

  • T newInstance(Object…initargs):根据指定的构造方法创建对象

代码示例:

  • Student类
package Study02;

public class Student {
    //成员变量:一个私有,一个默认,一个公共
    private String name;
    int age;
    public String address;
    //构造方法:一个私有,一个默认,两个公共
    private Student(String name){
        this.name = name;
    }
    Student(String name,int age){
        this.name = name;
        this.age = age;
    }

    public Student() {
    }

    public Student(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    //成员方法:一个私有,四个公共
    private void function(){
        System.out.println("function");
    }
    public void method1(){
        System.out.println("method");
    }
    public void method2(String s){
        System.out.println("method"+s);
    }
    public String method3(String s,int n){
      return s+","+n;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}
  • 测试类
package Study03;

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

public class ReflectDemo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取Class对象
        Class<?> c4 = Class.forName("Study02.Student");

        //Constructor<?>[] getConstructors() 返回一个包含 Constructor对象的数组,
        // Constructor对象反映了由该 Class对象表示的类的所有公共构造函数。
        Constructor<?>[] cons = c4.getConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
        }
        System.out.println("======================");
        //Constructor<T> getConstructor(Class<?>... parameterTypes)
        // 返回一个 Constructor对象,该对象反映由该 Class对象表示的类的指定公共构造函数。
        Constructor<?> con2 = c4.getConstructor();
        //Constructor提供了一个单个构造函数的信息和访问权限
        //T newInstance(Object... initargs) 使用由此 Constructor对象表示的构造函数,
        // 使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
        Object o = con2.newInstance();
        System.out.println(o);

        System.out.println("=======================");
        //Constructor<?>[] getDeclaredConstructors()
        // 返回反映由该 Class对象表示的类声明的所有构造函数的 Constructor对象的数组。
        Constructor<?>[] con1 = c4.getDeclaredConstructors();
        for (Constructor con : con1) {
            System.out.println(con);
        }
        System.out.println("=======================");

        //Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
        // 返回一个 Constructor对象,该对象反映由此 Class对象表示的类或接口的指定构造函数。
        //参数:你要获取的构造方法的参数的个数和数据类型对应的字节码文件对象
    }
}

2.练习

练习1:通过反射实现以下操作

  • Student s = new Student(“王冰冰”,18,“长春”);
  • System.out.println(s);

注:基本数据类型也可以通过.class来获得对应的Class类型

代码:

  • Student类:同上述Student类
  • 测试类
package Study03;

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

/*练习1:通过反射实现以下操作
- `Student s = new Student(“王冰冰”,18,“长春”);`
- `System.out.println(s);`*/
public class ReflectDemo3 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取Class对象
        Class<?> c = Class.forName("Study02.Student");
        //获取带三个参数的Student的构造方法:public Student(String name, int age, String address)
        //Constructor<T> getConstructor(Class<?>... parameterTypes)
        Constructor<?> con = c.getConstructor(String.class, int.class, String.class);
        //基本数据类型也可以通过.class得到对应的Class类型
        //T newInstance(Object... initargs) 使用由此 Constructor对象表示的构造函数,
        Object obj = con.newInstance("王冰冰", 18, "长春");
        System.out.println(obj);
    }
}

练习2:通过反射实现如下操作

  • Student s = new Student(“王冰冰”);
  • System.out.println(s);

注:public void setAccessible(boolean flag):值为true,取消访问检查—可使用私有构造方法

代码:

  • Student类:同上
  • 测试类
package Study03;

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

/*
练习2:通过反射实现如下操作
- `Student s = new Student(“王冰冰”);`
- `System.out.println(s);`
*/
public class ReflectDemo4 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取Class对象
        Class<?> c = Class.forName("Study02.Student");
        //private Student(String name)
        //Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
        Constructor<?> con = c.getDeclaredConstructor(String.class);
        T newInstance(Object... initargs) 使用由此 Constructor对象表示的构造函数,-----访问私有构造方法会报错
        //Object obj = con.newInstance("王冰冰");
        //System.out.println(obj);


        //public void setAccessible(boolean flag)将此反射对象的accessible标志设置为指示的布尔值。
        // 值为true表示反射对象应该在使用Java语言访问控制时抑制检查。
        // 值为false表示反射对象应该在使用Java语言访问控制时执行检查,并在类描述中指出变体。
        con.setAccessible(true);
        Object obj = con.newInstance("王冰冰");
        System.out.println(obj);
    }
}

4.反射获取成员变量并使用

Class类中用于获取成员变量的方法

  • Filed[] getFileds():返回所有公共成员变量对象的数组
  • Filed[] getDeclaredFileds():返回所有成员变量对象的数组
  • Filed getFiled(String name):返回单个公共成员变量对象
  • Filed getDeclaredFiled(String name):返回单个成员变量对象

代码:

  • Student类:同上
  • 测试类
package Study04;

import Study02.Student;

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

public class ReflectDemo1 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取Class对象
        Class<?> c = Class.forName("Study02.Student");

        //Field[] getFields() 返回一个包含 Field对象的数组, Field对象反映由该 Class对象表示的类或接口的所有可访问的公共字段。
        //Field[] getDeclaredFields() 返回一个 Field对象的数组,反映了由该 Class对象表示的类或接口声明的所有字段。
        //Field[] fields = c.getFields();
            // public java.lang.String Study02.Student.address
        Field[] fields = c.getDeclaredFields();
            //private java.lang.String Study02.Student.name
            //int Study02.Student.age
            //public java.lang.String Study02.Student.address
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("===================");

        //Field getField(String name) 返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定公共成员字段。
        //Field getDeclaredField(String name) 返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定声明字段。
        Field addressField = c.getField("address");
        //获取无参构造方法
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();
        //A Field提供有关类或接口的单个字段的信息和动态访问。
        //void set(Object obj, Object value) 将指定的对象参数中由此 Field对象表示的字段设置为指定的新值。
        addressField.set(obj,"长春");//给obj的成员变量addressField赋值为“长春”
        System.out.println(obj);

//        Student s=new Student();
//        s.address = "长春";
//        System.out.println(s);
    }
}

练习:通过反射实现如下操作

  • Student s = new Student();
  • s.name = “王冰冰”;
  • s.age = “18”;
  • s.address = “长春”;
  • System.out.println(s);

注:对成员变量进行赋值建议统一操作

//s.name = “王冰冰”;
Field nameFiled = c.getDeclaredField("name");//------能获取任意一个成员变量
nameFiled.setAccessible(true);//暴力访问
nameFiled.set(obj,"王冰冰");
System.out.println(obj);

代码示例:

package Study04;

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

/*
练习:通过反射实现如下操作
- `Student s = new Student();`
- `s.name = “王冰冰”;`
- `s.age = “18”;`
- `s.address = “长春”;`
- `System.out.println(s);`
*/
public class ReflectDemo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        //获取Class对象
        Class<?> c = Class.forName("Study02.Student");

        //Student s = new Student();
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();
        System.out.println(obj);

        //s.name = “王冰冰”;
        //Field nameFiled = c.getField("name");//NoSuchFieldException-----只能获取公共的成员变量
        Field nameFiled = c.getDeclaredField("name");//------能获取任意一个成员变量
        nameFiled.setAccessible(true);//暴力访问
        nameFiled.set(obj,"王冰冰");
        System.out.println(obj);

        //s.age = “18”;
        Field ageFiled = c.getDeclaredField("age");
        ageFiled.setAccessible(true);
        ageFiled.set(obj,18);
        System.out.println(obj);

        //s.address = “长春”;
        Field addressFiled = c.getDeclaredField("address");
        addressFiled.setAccessible(true);
        addressFiled.set(obj,"长春");
        System.out.println(obj);
    }
}

5.反射获取成员方法并使用

  • Method[] getMethods() :返回所有公共成员方法对象的数组,包括继承的
  • Method[] getDeclaredMethods():返回所有的成员方法的数组,不包括继承的
  • Method getMethod(String name, Class<?>... parameterTypes):返回单个公共成员方法对象
  • Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回单个成员方法对象

Method 类中用于调用成员方法的方法

  • Object invoke(Object obj, Object... args):调用obj对象的成员方法,参数是args,返回值是Object类型。

代码:

  • Student类:同上
  • 测试类
package Study05;

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

public class ReflectDemo1 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取Class对象
        Class<?> c = Class.forName("Study02.Student");

        //Method[] getMethods() 返回一个包含 方法对象的数组,
        // 方法对象反映由该 Class对象表示的类或接口的所有公共方法,
        // 包括由类或接口声明的对象以及从超类和超级接口继承的类。
//        Method[] methods = c.getMethods();
//        for (Method method:methods){
//            System.out.println(method);
//        }
//        System.out.println("========");

        //Method[] getDeclaredMethods() 返回一个包含 方法对象的数组,
        // 方法对象反映由 Class对象表示的类或接口的所有声明方法,
        // 包括public,protected,default(package)访问和私有方法,但不包括继承方法。
        Method[] declaredMethods = c.getDeclaredMethods();
        for (Method method:declaredMethods){
            System.out.println(method);
        }
        System.out.println("========");

        //Method getMethod(String name, Class<?>... parameterTypes) 返回一个 方法对象,
        // 该对象反映由该 Class对象表示的类或接口的指定公共成员方法。
        //获取:public void method1()
        Method method1 = c.getMethod("method1");
        //获取无参构造方法创建对象
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();
        //方法在类或接口上提供有关单一方法的信息和访问权限。
        //Object invoke(Object obj, Object... args) 在具有指定参数的指定对象上调用此 方法对象表示的基础方法。
        //Object:返回值类型
        //obj:调用方法的对象
        //args:方法需要的参数
        method1.invoke(obj);



        System.out.println("========");
        //Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一个 方法对象,
        // 它反映此表示的类或接口的指定声明的方法 Class对象。

    }
}

练习:通过反射实现如下操作

  • Student s = new Student();
  • s.method1();
  • s.method2();
  • String ss = s.method3(“王冰冰”,18);
  • System.out.println(ss);
  • s.function();

代码:

  • Student类:同上
  • 测试类
package Study05;

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

/*
练习:通过反射实现如下操作
- `Student s = new Student();`
- `s.method1();`
- `s.method2("王冰冰");`
- `String ss = s.method3("王冰冰",18);`
- `System.out.println(ss);`
- `s.function();`
*/
public class ReflectDemo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取Class对象
        Class<?> c = Class.forName("Study02.Student");

        //- `Student s = new Student();`
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

        //- `s.method1();`
        Method method1 = c.getMethod("method1");
        method1.invoke(obj);//method

        //- `s.method2("王冰冰");`
        Method method2 = c.getMethod("method2", String.class);
        method2.invoke(obj,"王冰冰");//method王冰冰

        //- `String ss = s.method3("王冰冰",18);`
        // - `System.out.println(ss);`
        Method method3 = c.getMethod("method3", String.class, int.class);
        Object o = method3.invoke(obj, "王冰冰", 18);
        System.out.println(o);

        //- `s.function();`
        Method function = c.getDeclaredMethod("function");
        function.setAccessible(true);
        function.invoke(obj);
    }
}

6.反射练习

1.练习1:越过泛型检查

有一个ArrayList<Integer>集合,现在想在集合中添加一个字符串数据,如何实现?

代码:

package Study06;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

/*练习1:有一个`ArrayList<Integer>`集合,现在想在集合中添加一个字符串数据,如何实现?*/
public class ReflectTest {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //创建集合
        ArrayList<Integer> arr = new ArrayList<Integer>();
        //获取对象
        Class<? extends ArrayList> c = arr.getClass();
        //获取方法
        Method m = c.getMethod("add", Object.class);
        m.invoke(arr,"hello");
        m.invoke(arr,"world");
        System.out.println(arr);

    }
}

2.练习2:运行配置文件指定内容

通过配置文件运行类的方法

  • 配置文件Class.txt
ClassName = Study06.Student
MethodName = study
  • 代码
  • Student类
package Study06;

public class Student {
    public void study(){
        System.out.println("好好学习,天天向上");
    }
}
  • 测试类
package Study06;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

/*通过配置文件运行类的方法*/
public class ReflectTest1 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//        Student s = new Student();
//        s.study();

//        Teacher t = new Teacher();
//        t.teach();

        /*
        Class.text
        ClassName = xxx;
        MethodName = xxx;
        **/
        //加载数据
        Properties pro = new Properties();
        FileReader fr = new FileReader("Study\\Class.txt");
        pro.load(fr);
        fr.close();
        /*
        ClassName = Study06.ReflectTest1;
          MethodName = study;
          */
        String className = pro.getProperty("ClassName");
        String methodName = pro.getProperty("MethodName");

        //通过反射来使用
        Class<?> c = Class.forName(className);
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

        Method m = c.getMethod(methodName);
        m.invoke(obj);
    }
}
这篇关于2.Java-反射的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!