当前位置:首页 > 技术 > 正文内容

【Java面试指北】反射(1) 初识反射

Lotus2022-12-19 10:19技术

如果你被问到:什么是反射?为什么需要反射、以及反射的应用?你会如何回答呢?
本篇会带大家初识反射,了解反射概念和基本应用。反射的原理以及深入源码的探究将会在后面几篇介绍。

一、什么是反射?

要理解什么是反射,我们先看看什么是「正射」,一个常见的获取Student的正射如下:

Student student = new Student();

通常 我们都是直接声明,或者通过 new Student() 直接获取一个 Student 类,然后再使用。而一个反射的例子如下:

// 这里的“com.demo.Student”是需要反射的类的全限定名(包名+类名)
Class clz = Class.forName("com.demo.Student")	
Object stu = clz.newInstance();

先获取实例的Class类,然后再通过其Class类生成一个Student的Instance。以上两种方式(new Student和clz.newInstance)是效果是等价的,都是获取到了一个Student 的实例。

那么什么是反射呢?反射是Java中的一个重要的特性,使用反射可以在运行时动态生成对象、获取对象属性以及调用对象方法。
Oracle 官方对反射的解释是:

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。

反射的问题:
这里先简单提一下:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的 Java 代码要慢很多。

二、为什么需要反射?

举一个直观的例子(仅为了说明其中一种用法):
如果我让你写一个根据运行时输入的名字进行打印输出,你会写出类似下面的代码:

public void sayHello(String name) {
    // 在运行前根本不知道 name 是什么,只有在运行时 name 才会被确认并打印出来
    System.out.println("hello, " + name);
}

那么同样的,在写代码时可能也不知道要用什么类,运行时才知道。比如加载数据库驱动的时候,你可以直接 new 出来具体的驱动类,但要是换了数据库呢,还要修改源码重新打包更新么?

new com.mysql.jdbc.Driver();

那你可能会说,我多写几个 if else 不就行了,类似下面这样:

if ( xxx == "mysql") {
    new com.mysql.jdbc.Driver();
else if ( xxx == "redis" ) {
    new com.redis.jdbc.Driver();
else if ( ... ){
}

这样的问题是,在编译期就要凑齐所有的 jdbc 连接库,甭管用不用这些都会被加载到内存中,数据库类型多了会有极大的浪费。
那么这种情况,就可以用反射来解决,在运行时才去动态的加载对应类。你也可以在配置文件中指明要使用哪种数据库类,连接不同的数据库都可以使用这一份程序。

// 反射的方式动态加载类
Class.forName("com.mysql.jdbc.Driver");

三、反射的基本使用

下面介绍通过反射都能做什么:

一)获得 Class 对象

// 1 使用 Class 类的 forName 静态方法
 Class.forName(driver);

// 2 直接获取某一个对象的 class
Class<?> cl = int.class;

// 3 调用某个对象的 getClass() 方法
StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();

二)判断是否为某个类的实例

public static void displayObjectClass(Object o) {
    if (o instanceof Vector)
   		System.out.println("对象是 java.util.Vector 类的实例");
  	else if (o instanceof ArrayList)
   		System.out.println("对象是 java.util.ArrayList 类的实例");
   	else
   		System.out.println("对象是 " + o.getClass() + " 类的实例");
}

三)创建实例

Class<?> c = String.class;
Object str = c.newInstance();

四)获取方法

getDeclaredMethods() 方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
getMethods() 方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。
getMethod() 方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象。

public class ReflectDemo {
	public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
	    Class<?> c = MyClass.class;

	    Method[] methods = c.getMethods();
	    Method[] declaredMethods = c.getDeclaredMethods();
	    Method method = c.getMethod("add", int.class, int.class);

        System.out.println("getMethods获取的方法:");
        for(Method m:methods)
            System.out.println(m);

        System.out.println("getDeclaredMethods获取的方法:");
        for(Method m:declaredMethods)
            System.out.println(m);
    }
}

class MyClass {
    public int add(int a, int b) {
        return a + b;
    }
    public int sub(int a, int b) {
        return a - b;
    }
}

// 输出
/*
getMethods获取的方法:
public int com.shuofxz.basic.ReflectDemo$MyClass.add(int,int)
public int com.shuofxz.basic.ReflectDemo$MyClass.sub(int,int)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

getDeclaredMethods获取的方法:
public int com.shuofxz.basic.ReflectDemo$MyClass.add(int,int)
public int com.shuofxz.basic.ReflectDemo$MyClass.sub(int,int)
*/

五)调用方法

当我们从类中获取了一个方法后,我们就可以用 invoke() 来调用这个方法。

public class ReflectDemo {
	public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> mc = MyClass.class;
        Object obj = mc.newInstance();
        //获取methodClass类的add方法
        Method method = mc.getMethod("add", int.class, int.class);
        //调用method对应的方法 => add(1,4)
        Object result = method.invoke(obj, 1, 4);
        System.out.println(result);
    }
}

六)获取构造器、类的成员变量(字段)信息

  • 通过 Class 类的 getConstructor 方法得到 Constructor 类的一个实例
  • getFiled:访问公有的成员变量
  • getDeclaredField:所有已声明的成员变量,但不能得到其父类的成员变量

四、小结

本篇文章初步介绍了反射机制。让大家了解了反射是什么,为什么会有反射这个功能,以及一些基本使用方式。后续文章将会反射的机制和原理做进一步的讲解。

原文链接

扫描二维码推送至手机访问。

版权声明:本文来源于网络,仅供学习,如侵权请联系站长删除。

本文链接:https://news.layui.org.cn/post/1174.html

分享给朋友:

“【Java面试指北】反射(1) 初识反射” 的相关文章

多功能手持VH501TC采集仪如何设置振弦传感器的激励方法和激励电压

河北稳控科技多功能手持VH501TC采集仪如何设置振弦传感器的激励方法和激励电压 VH501TC 提供多种振弦传感器激励方法,以最大限度兼容所有厂家和型号的振弦传感器。 振弦传感器激励方法参数位于实时数据窗口右侧,共有 5 种方法可选,分别用 MODTH0~MODTH4 表示。各方法说明如下: 激励电压数据在屏幕上显示为 xxx/xxx 的形式,其中前面的数字表示实际的激励电压,后面的数字...

基础知识(1) --Matlab基础知识

前言: 前两次文章有读者私信说Matlab初学,基础较差,本次分享一下Matlab的基础内容,熟练者可以跳过本文,后续的文章也会在文后加上一些基础内容分享。 关键字:Matlab、数组、函数、计算、绘图 正文 1、MATLAB 产品 1.1 MATLAB 产品描述 全世界数超过百万的工程师和科学家们使用 MATLAB 来分析和设计改变世界的系统和产品。MATLAB 应用于汽车安全系统、宇宙...

【微信小程序】认识小程序页面

????系列专栏:微信小程序 ????欢迎关注????点赞????收藏⭐留言???? ✅个人主页:​​hacker_demo的51CTO博客​​ ????个人格言:不断的翻越一座又一座的高山,那样的人生才是我想要的。这一马平川,一眼见底的活,我不想要,我的人生,我自己书写,余生很长,请多关照,我的人生,敬请期待???????????? 新建小程序页面 只需要在app.json->...

C++浅拷贝深拷贝

1. C++ 浅拷贝 什么是浅拷贝? 一般比较形象的理解,浅拷贝可以理解"值"层面的拷贝,深拷贝可以理解成"内存"上的拷贝,特别是类里面含有指针类型的。 // .h文件class HasPtrMem{public:HasPtrMem();HasPtrMem(const HasPtrMem& h);~HasPtrMem();void print();int *p;};// .cpp 文件H...

Android平台实现mp4文件实时推送RTMP|轻量级RTSP服务|GB28181平台

好多开发者有这样的诉求,想把本地录制的MP4文件,以实时流数据的形式,推送到RTMP服务器,注入轻量级RTSP服务,或者对接到GB28181平台,这块前几年我们就有对接。 本次以MediaExtractor为例,先利用MediaExtractor,把mp4文件的音视频数据分离,然后调用我们publisher模块,实现编码后的数据对接到RTMP服务器、轻量级RTSP服务或GB28181平台即可,废...

前端三剑客快速入门(二)

前言 本文书接上回,继续css的知识,序号就重新开始了。上篇内容:前端三剑客快速入门(一) CSS 盒子模型 盒子模型属性: border外框 margin外边距 padding内边距 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <me...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。