【达内课程】Java反射介绍

news/2024/5/19 2:26:55 标签: 反射

文章目录

  • 什么是反射
  • 举个栗子:通过new和反射分别创建类
  • 反射常用API
    • 获取 Class 类对象有三种方法
    • 通过反射创建类对象的两种方法
    • 方法介绍
  • 一些疑问
    • getFields() 方法和 getDeclaredFields () 的区别
    • 使用 new 和 newInstance() 创建类的区别

什么是反射

一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。例如:

Person person = new Person();
person.setId(22);

反射则是一开始并不知道要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。我们使用 JDK 提供的反射 API 进行反射调用:

Class clz = Class.forName("com.example.testapplication.Person");
Method method = clz.getMethod("setId", int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 22);;

面两段代码的执行结果,其实是完全一样的。但是其思路完全不一样,第一段代码在未运行时就已经确定了要运行的类(Person),而第二段代码则是在运行时通过字符串值才得知要运行的类(com.example.testapplication.Person)。

所以 Java 反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。

所谓反射其实是获取类的字节码文件,也就是.class文件,那么我们就可以通过 Class 这个对象进行获取。

举个栗子:通过new和反射分别创建类

首先创建 Person 类

class Person {
    private int id;
    private String name;

    public Person() {
        super();
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

		Person person = new Person();
        person.setId(22);
        Log.d("TTT",person.getId()+"");

        try {
            Class clz = Class.forName("com.example.testapplication.Person");
            Method method = clz.getMethod("setId", int.class);
            Constructor constructor = clz.getConstructor();
            Object object = constructor.newInstance();
            method.invoke(object, 22);

            Method getPriceMethod = clz.getMethod("getId");
            Log.d("TTT",getPriceMethod.invoke(object)+"");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

输出结果:
在这里插入图片描述
从这个简单的例子可以看出,一般情况下我们使用反射获取一个对象的步骤

1、获取类的 Class 对象实例

Class clz = Class.forName("com.example.testapplication.Person");

2、根据 Class 对象实例获取 Constructor 对象

Constructor constructor = clz.getConstructor();

3、使用 Constructor 对象的 newInstance 方法获取反射类对象

Object object = constructor.newInstance();

而如果要调用某一个方法,则需要经过下面的步骤

1、获取方法的 Method 对象

Method method = clz.getMethod("setId", int.class);

2、利用 invoke 方法调用方法

method.invoke(object, 22);

反射常用API

获取 Class 类对象有三种方法

反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象。
在 Java API 中,获取 Class 类对象有三种方法:

第一种,使用 Class.forName 静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。

Class clz = Class.forName("java.lang.String");

第二种,使用 .class 方法。这种方法只适合在编译前就知道操作的 Class。

Class clz = String.class;

第三种,使用类对象的 getClass() 方法。

String str = new String("Hello");
Class clz = str.getClass();

通过反射创建类对象的两种方法

通过反射创建类对象主要有两种方式:通过 Class 对象的 newInstance() 方法、通过 Constructor 对象的 newInstance() 方法。

第一种:通过 Class 对象的 newInstance() 方法。

Class clz = Person.class;
Object object = clz.newInstance();

第二种:通过 Constructor 对象的 newInstance() 方法。

Class clz = Person.class;
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();

通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。

Class clz = Person.class;
Constructor constructor = clz.getConstructor(int.class,String.class);
Object object = constructor.newInstance(22,"Errol");

方法介绍

创建一个 Person 类

class Person {
    public int id;
    private String name;

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

MainActivity 中

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Person person = new Person(22, "Errol");
        //获取Class对象
        Log.d("TTT", person.getClass() + "");//class com.example.testapplication.Person
        //获取Class对象所表示的实体
        Log.d("TTT", person.getClass().getName() + "");//com.example.testapplication.Person
        //得到类的简写名称
        Log.d("TTT", person.getClass().getSimpleName() + "");//Person

        Field[] fields = person.getClass().getFields();
        for (int i = 0; i < fields.length; i++) {
            //获取字段的名称,无法获取私有属性
            //getDeclaredFields()方法则可以获取包括私有属性在内的所有属性
            Log.d("TTT", fields[i].getName());//id
            //获取字段修饰符。什么都不加:0;public:1;private:2;protected:4;static:8;final:16
            Log.d("TTT", fields[i].getModifiers() + "");//1
            //与某个具体的修饰符进行比较
            Log.d("TTT", Modifier.isStatic(fields[i].getModifiers()) + "");//false
            //获取字段的声明类型
            Log.d("TTT", fields[i].getType() + "");//int
            //与某个类型进行比较
            Log.d("TTT", (fields[i].getType() == int.class) + "");//true
            //获取字段的值
            try {
                Object fieldObject = fields[i].get(person);
                Log.d("TTT", fieldObject.toString());//22
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            //判断Class是否为原始类型(boolean、char、byte、short、int、long、float、double)
            Log.d("TTT", (fields[i].getType().isPrimitive()) + "");//true
            //isAccessible()值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查
            //setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问
            Log.d("TTT", fields[i].isAccessible() + "");//false
        }
    }
}          

tips:由于JDK的安全检查耗时较多。所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的

一些疑问

getFields() 方法和 getDeclaredFields () 的区别

关于获取类的字段有两种方式:getFields()getDeclaredFields(),区别如下:

getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段
getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段

同样类似的还有getConstructors()getDeclaredConstructors()getMethods()getDeclaredMethods(),这两者分别表示获取某个类的方法、构造函数

使用 new 和 newInstance() 创建类的区别

在初始化一个类,生成一个实例的时候,newInstance()方法和new关键字除了一个是方法,一个是关键字外,最主要有什么区别?

它们的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类。newInstance() 生成对象只能调用无参的构造函数,首先调用Class.forName("")返回的是类来加载某个类,然后使用Class.forName("").newInstance()返回的是 object 来实例化。只能调用无参的构造函数。是使用newInstance()方法的时候,就必须保证:1、这个类已经加载;2、这个类已经连接了。

而使用new关键字生成对象没有这个限制。new 创建一个类的时候,这个类可以没有被加载。

简言之:
newInstance(): 弱类型,低效率,只能调用无参构造。你可以对该类一无所知。
new: 强类型,相对高效,能调用任何 public 构造。必须要知道一个明确的类才能使用。

参考:
大白话说Java反射:入门、使用、原理


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

相关文章

按位取反和逻辑非

对于-1 -1的16进制表示&#xff1a;FF FF FF FF对它按位取反得到&#xff1a;00 00 00 00则在控制条件中&#xff1a; 1. x -1 <> !~xx -1 <> ~x 0 <> !~x 1 <> !~x2. x ! -1 <> ~xx ! -1 <> ~x ! 0 <> ~x 按位取反和逻辑非 …

【达内课程】ThinkAndroid数据库框架使用

thinkAndroid数据库框架 ThinkAndroid Git地址 下载jar包放入libs中 新建MyApplication继承TAApplication public class MyApplication extends TAApplication {Overridepublic void onCreate() {super.onCreate();} }AndroidManifest中注册MyApplication <applicatio…

Linux软件安装的流程

开发商做的 在固定的硬件平台和操作系统平台上将需要安装或升级的软件编译好&#xff0c;然后将这个软件的所有相关文件打包成为一个特殊格式的文件。在这个软件安装文件内包含有预先检测系统与依赖软件的脚本&#xff0c;并提供记录该软件提供的所有文件信息等&#xff0c;最…

【达内课程】Eclipse中的junit测试

文章目录简介使用测试1测试2生成测试报告简介 使用 下载junit 新建一个java项目&#xff0c;把junit jar包放入项目&#xff0c;右键项目&#xff0c;选择properties&#xff0c;把jar包加进来 测试1 创建如下文件 在这里插入代码片如果出错 如果成功 测试2 新建Htt…

VMware的更新怎么这么恶心

VMware 的更新怎么这么恶心&#xff01;更新完之后搞得原来的所有虚拟机都不能用了。不是说向后兼容吗&#xff1f;哪有什么向后兼容&#xff1f;

【达内课程】Android中的junit测试

文章目录介绍什么是单元测试&#xff0c;为什么要用主要方法常用注解常用匹配器小试牛刀参数化测试assertThat用法Rule用法介绍 什么是单元测试&#xff0c;为什么要用 从名字上看&#xff0c;单元测试就是参与项目开发的工程师在项目中为了测试某一个代码单元而写的测试代码…

Filesystem Hierarchy Standard (FHS)

Linux目录配置的依据&#xff1a;FHS&#xff0c;即文件系统层次结构标准。 FHS的主要目的是让用户了解已安装的软件放置在哪个目录下&#xff0c;即FHS的重点在于规范每个特定的目录下应该放置什么样的数据。 FHS依据文件系统使用的频繁程度与是否允许用户随意修改&#xff…

Android Studio3.5:Default Activity not found

升级AS需要勇气&#xff0c;因为升级后项目可能会出各种问题… 这次升级了Android Studio3.5以后&#xff0c;发现其中一个项目不能运行&#xff0c;app处是个❌&#xff0c;然后运行提示 按照网上的解决办法&#xff0c;无非是 1、检查AndroidManifest是否有启动页&#xff0…