[Java基础] 反射机制汇总

引言

初学Java反射机制的时候,只是感觉很神奇,但是不知道学了该怎么用,所以过了一段时间就忘得差不多了;最近接触到了框架,在学习中遇到了反射,深深体会到了反射机制的神奇,回来复习了一下反射机制,写一个小总结;

概念

反射机制是什么?百度百科很好的解释了:

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。
这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

在这里我也就不细说概念了,在使用中你就可以慢慢明白了;

下面我来展示一下Java反射机制的基本操作;
注:为了使代码简明易看,所有的异常处理直接抛出Exception(实际应用不要这样!!)

获取类型

在Java中有一种类型叫做Class,这是一种类的类型,和String、Object一个意思,所以不要和类弄混了;
在这里插入图片描述
我们可以通过反射机制获取一个类的类型,返回值类型就是Class

获取类型有三种方法:

  • Class静态方法forName()
  • 对象.getClass
  • 类名.class

下面是代码演示:

// 获取不同类型(Class)的三种方式:Class静态方法forName、对象.getClass()、类名.class
// Class也是一个类类型,和String、Object一样
// java.lang.Class:Class代表整个字节码,代表一个类型,代表整个类
public class ReflectTest01 {
    
    
    public static void main(String[] args) throws Exception {
    
    

        // 第一种:通过Class的静态方法:forName获取Class类型
        Class c1 = Class.forName("java.lang.String"); // String类型
        Class c2 = Class.forName("java.lang.Object"); // Object类型
        System.out.println(c1 == c2); // String类型和Object类型不同, false
        //==============================================================

        // 第二种:(对象.getClass())java中任何一个对象都有这个方法:getClass(),返回值类型是Class类型
        Class c3 = "444".getClass(); // c5是String类型
        Class c4 = new Object().getClass(); // c6是Object类型
        System.out.println(c1 == c3); // true
        System.out.println(c2 == c4); // true
        //=========================================================

        // 第三种:(类名.class)java中任何一种类型,包括基本数据类型,都有一个.class属性
        Class c5 = String.class; // String类型
        Class c6 = Object.class; // Object类型
        System.out.println(c1 == c5); // true
        System.out.println(c2 == c6); // true
    }
}

这三种方法中主要留意的就是第一种,forName()方法的使用规则是:
在这里插入图片描述
这里需要记住forName()方法可以使对应的类加载,类加载就会执行静态代码块;
所以如果只想执行静态代码块,那么可以使用forName()方法;
为什么说这一点呢?因为JDBC注册驱动中将会用到这一点;

下面来展示一下这个特性:
forName()传入的类:

public class MyClass {
    
    
    static {
    
    
        System.out.println("静态代码块执行了!!");
    }
}
// 结论:如果只想要一个类的静态代码块执行,其余代码都不执行,可以使用:
// Class.forName("完整类名")
// 这个方法的执行会导致类加载,类加载时静态代码快执行
// *****这一点会在后面JDBC中用到*****
public class ReflectTest03 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        // Class.forName()导致类加载,静态代码快执行
        Class.forName("reflect.test03.MyClass");

    }
}

输出:
在这里插入图片描述

当然这里只是简单模拟了一下,但是原理都是一样的;
结论:如果只想要一个类的静态代码块执行,其余代码都不执行,可以使用:Class.forName(“完整类名”)

实例化对象

当获取到对象类型后就可以通过该类型实例化对应的对象,用到newInstance()方法
在这里插入图片描述

在框架一般都是需要通过反射机制获取配置文件内容并进行操作的,通过配置文件内容可以获取不同信息,下面演示一下如何通过配置文件实例化对象;
注:这里使用.properties文件,简单易懂

创建一个.properties文件:
![在这里插入图片描述](https://img-blog.csdnimg.cn/ba4e6a3b4cab491e855af93441592531.png

这个配置文件需要放到该项目的目录下,不能放到类路径(src)或者其他目录下;

import java.io.FileInputStream;
import java.util.Properties;

// 通过反射机制获取配置文件内容并实例化对象
// 反射机制让程序更加灵活,符合OPC开闭原则
// 高级框架(Spring等)底层原理都是反射机制
public class ReflectTest02 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        FileInputStream fis = new FileInputStream("classname.properties"); // 创建输入流
        Properties properties = new Properties(); // 底层实现是Map,继承了HashTable,key和Value都是String类型
        properties.load(fis); // 加载Java的配置文件classname.properties到内存中
        fis.close(); // 关闭输入流

        String classname = properties.getProperty("first"); // 通过key得到Value
        Class<?> clazz = Class.forName(classname); // 获取classname(Value值)的类型
        Object obj = clazz.newInstance(); // 创建对象实例
        System.out.println(obj);
    }
}

因为这里的key传入的是first,所以输出为:
在这里插入图片描述
可以看出实例化了一个Object对象

还有另外一种方法,是比较常用简单的方法:
java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容;
在这里插入图片描述
需要使用该类的两个方法:
在这里插入图片描述
在这里插入图片描述

创建一个配置文件:
在这里插入图片描述

使用这种方式的时候,配置文件必须放到类路径(src)下

import java.util.ResourceBundle;

// 使用以下这种方式的时候,属性配置文件xxx.properties必须放到类路径下。
// 实现和ReflectTest02一样的功能,但是又通过新的获取绝对路径的方法,而且以后常用这个方法
public class ReflectTest05 {
    
    
    public static void main(String[] args) throws Exception {
    
    
        // 资源绑定器,只能绑定xxx.properties文件。并且这个文件必须在类路径(src文件夹)下。文件扩展名也必须是properties
        // 并且在写路径的时候,路径后面的扩展名.properties不能写。
        ResourceBundle bundle = ResourceBundle.getBundle("absolute_path"); // 绑定配置文件
        String classname = bundle.getString("second"); // 通过key得到Value
        Class c = Class.forName(classname);
        Object obj = c.newInstance();
        System.out.println(obj);
    }
}

这次key传入的是second,输出为:
在这里插入图片描述

反射机制的一些操作

下面展现反射机制的一些操作,其实就是各种方法的调用,会查文档就好;
这里先定义一个类,下面所有的操作都基于这个类;

public class TestClass01 {
    
    
    int age;
    public String name;
    private final static int num = 4;
    static protected boolean flag;

    public void firstMethod(String s) {
    
    
        System.out.println("第一个方法的调用!");
    }
    public void secondMethod(String s, int i) {
    
    
        System.out.println("第二个方法的调用!");
    }
}

反射TestClass类中所有Field(属性)

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class ReflectTest06 {
    
    
    public static void main(String[] args) throws Exception {
    
    
        // 获取整个类
        Class testClass = Class.forName("reflect.test06.TestClass01");

        // 获取类名
        String classname = testClass.getName(); // 完整类名
        System.out.println(classname);
        System.out.println(testClass.getSimpleName()); // 简单类名

        // 获取TestClass类的属性
        // getFields()只会获取所有public修饰的属性
        Field[] fields01 = testClass.getFields();
        Field f = fields01[0];
        String filename = f.getName(); // 获取属性名
        System.out.println(filename);

        System.out.println("=====================");

        // 获取所有属性
        Field[] fields02 = testClass.getDeclaredFields();
        for (Field i : fields02) {
    
    
            System.out.println(i.getName()); // 获取属性名
            // 获取属性的类型
            System.out.println(i.getType().getName()); // 完整类型
            System.out.println(i.getType().getSimpleName()); // 简单类型
            // 获取修饰符(用到了Modifier类的toString方法)
            System.out.println(Modifier.toString(i.getModifiers()));
            System.out.println("------------");
        }
    }
}

输出:

reflect.test06.TestClass01
TestClass01
name
=====================
age
int
int

------------
name
java.lang.String
String
public
------------
num
int
int
private static final
------------
flag
boolean
boolean
protected static
------------

获取对象属性中的值

import java.lang.reflect.Field;

public class ReflectTest07 {
    
    
    public static void main(String[] args) throws Exception {
    
    
        Class className = Class.forName("reflect.test06.TestClass01"); // 获取类
        Object obj = className.newInstance(); // 通过类创建对象
        Field ageFiled = className.getDeclaredField("age"); // 获取age属性
        // 给属性赋值需要有实例化的对象obj
        ageFiled.set(obj, 111); // 给obj对象的age属性复制111
        System.out.println(ageFiled.get(obj)); // 获取obj对象的age属性的值
    }
}

输出:
在这里插入图片描述

获取方法并调用(重要)

这个相对重要,获取方法用到了Class类的getMethod()方法或者getMethods()
在这里插入图片描述
返回类型Method:
在这里插入图片描述
同样获取方法后需要调用,使用Method类的一个方法:invoke()
在这里插入图片描述
下面是具体使用方法:

import java.lang.reflect.Method;

// 通过反射机制调用方法
public class ReflectTest08 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        // 先获取类
        Class c = Class.forName("reflect.test06.TestClass01");
        // 通过类实例化对象
        Object obj = c.newInstance();
        // 通过类获取方法
        Method method01 = c.getMethod("firstMethod", String.class);
        Method method02 = c.getMethod("secondMethod", String.class, int.class);

        // 通过获取的方法来调用(通过invoke方法)
        // 需要:1,该方法; 2,一个对象; 3,对应参数
        Object retValue01 = method01.invoke(obj, "这是一个没有什么用的参数");
        Object retValue02 = method02.invoke(obj, "这也是一个没有什么用的参数,后面1也是", 1);
    }
}

输出:
在这里插入图片描述

总结

这是反射机制一些基本常用操作,但是实际应用并不会这么简单,所以基础的一定要掌握好,这样在以后学习spring等框架时就能看懂学习底层源码了;

Guess you like

Origin blog.csdn.net/YXXXYX/article/details/122325925