当前位置: 移动技术网 > IT编程>开发语言>Java > 8千字干货教程|java反射精讲

8千字干货教程|java反射精讲

2020年03月30日  | 移动技术网IT编程  | 我要评论
java反射机制精讲 目录 1. 反射机制的概念 2. 反射的基础Class类 3. 反射的用法 4. 反射的应用示例 作者简介:全栈学习笔记,一个正在努力的人 反射机制的概念: 在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包 ...

java反射机制精讲

目录
1. 反射机制的概念
2. 反射的基础class类
3. 反射的用法
4. 反射的应用示例

作者简介:全栈学习笔记,一个正在努力的人

反射机制的概念:

在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。反射被视为动态语言的关键。简单来说反射就是java的各种成分映射成对应的java类。

通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。包括构造方法,属性,方法。

java反射机制提供的功能

在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成动态代理。

这其实也涉及到了语言的动态与静态,java语言本身不算是动态语言,但是他有一个非常突出的动态机制,就是我们所说的反射机制。
什么是动态语言呢?就是说,程序在运行的时候,(注意是运行的时候,不是编译的时候)允许改变程序结构或者变量类型。反之静态就是没有这些特点了。

反射的基础class类

class类是反射实现的基础,所以想要学会反射,必须先掌握class类的一些基本的概念。
类是什么?类是class类的实例对象,所以说class类是所有类的类。

要想解剖一个类,必须先获取到该类的字节码文件对象。而解剖使用的就是class 类中的方法,所以先要获取每一个字节码文件对应的class类型的对象。

class类没有公共的构造方法,class对象是在类加载的时候由java虚拟机以及通过调用类加载器中的 defineclass 方法自动构造的,因此不能显式地声明一个class对象。这里又涉及到一个东西,类的加载

简要的说明一下:

类加载器:当程序需要是用某个类时,如果该类还没有被加载到内存中,则,系统会通过加载,连接,初始化 这三步来对类进行初始化

加载:就是指将class文件读入内存(编译之后的文件是.class文件),并为之创建一个class对象
任何类被使用时,系统都会建立一个class对象,第一次的时候会,第二次则会判断这个类是否存在。

连接:验证是否有正确的内部结构,并和其他类协调一致
准备为类的静态成员分配内存,并设置默认初始化值
并做一个解析:将类的二进制数据中的字符引用替换为直接引用。

上面说到class对象是不能直接创建的,但是我们可以通过其他方式得到class类的,目前有三种方式可以得到我们想要的class类,得到class类之后就能正常的使用反射了。

获取class的三种方式(获取一个类的字节码对象):

第一种:使用对象获取,使用对象的getclass获取

person person = new person();
class clazz = person.getclass();

第二种:使用静态属性class

class clazz = person.class

第三种:使用class类的静态方法forname(字符串的类名)
注;类名要写全包名

class clazz = calss.forname("…….");

好了,重点来了,反射怎么玩才有趣!

反射的用法

上面说了通过反射可以得到任意一个类的什么什么,下面来看看是不是真的。

第一步要干啥?当然是通过之前的哪三种方法来得到这个可以为所欲为的class类。有三种方法,我们先都做个示例吧!
上代码

//获取class第一种方法
student student = new student();
class clazz = student.getclass();
//获取class第二种方法
class clazztwo = student.class;
//获取class第三种方法
class clazzthree = class.forname("demo.qzxxbj.entity.student");

system.out.println("第一个"+clazz+"\n第二个"+clazztwo+"\n第三个"+clazzthree);

结果

第一个class demo.qzxxbj.entity.student
第二个class demo.qzxxbj.entity.student
第三个class demo.qzxxbj.entity.student

可以看到三种方法得到的class对象是一样的,没有区别。
第三种方法是会有一个找不到类的异常抛出的。
其中student就是一个简单的类,可以是任何类。

通过class获取任意一个类的属性
student类的代码

package demo.qzxxbj.entity;

/**
 * @author 微信公众号:全栈学习笔记
 * @date 2020/3/29
 * @description
 */

public class student {
private string name;
private integer age;
private string sex;
public int number;

public int getnumber() {
    return number;
}

public void setnumber(int number) {
    this.number = number;
}

public string getname() {
    return name;
}

public void setname(string name) {
    this.name = name;
}

public integer getage() {
    return age;
}

public void setage(integer age) {
    this.age = age;
}

public string getsex() {
    return sex;
}

public void setsex(string sex) {
    this.sex = sex;
}

}

以下获取class的方法都采用第二种,比较简洁

	//获取class第二种方法
    class clazztwo = student.class;

    //获取该类指定属性名的public成员变量,包括父类的
    field field = clazztwo.getfield("number");

    //field   public int demo.qzxxbj.entity.student.number
    system.out.println("该类指定属性名的public成员变量,包括父类的"+field);

    //获取该类指定名称声明的变量,即不包括父类的
    field defield = clazztwo.getdeclaredfield("name");

    // defield  private java.lang.string demo.qzxxbj.entity.student.name
    system.out.println("该类所有声明的变量,即不包括父类的"+defield);

    //获取该类所有的public声明的成员变量
    field fields[] = clazztwo.getfields();

    system.out.println("public声明的变量:");
    //public int demo.qzxxbj.entity.student.number
    for (field field1:fields){
        system.out.println(field1);
    }

    //获取该对象的所有成员变量
    field defields[] = clazztwo.getdeclaredfields();
    system.out.println("该对象的所有成员变量");
    //private java.lang.string demo.qzxxbj.entity.student.name
    //private java.lang.integer demo.qzxxbj.entity.student.age
    //private java.lang.string demo.qzxxbj.entity.student.sex
    //public int demo.qzxxbj.entity.student.number
    for (field field1:defields){
        system.out.println(field1);
    }

记住getfields(),getfield(string name),getdeclaredfields(),getdeclaredfield(string name)的区别,你就能好好掌握这个知识点!

通过class获取任意成员方法

还是看代码吧!

获取成员方法method

	//获取class第二种方法
    class clazztwo = student.class;
    //根据方法名以及参数类型获取,只能获取public声明的方法,包括父类的
    method method = clazztwo.getmethod("setage",integer.class);

    //public java.lang.integer demo.qzxxbj.entity.student.getage()
    system.out.println(method);

    //根据方法名以及参数名称获取该类声明的所有的属性方法,不包括父类的
    method demethod = clazztwo.getdeclaredmethod("setage", integer.class);

    system.out.println(demethod);

    //获取该对象声明的所有的public方法,包括父类的
    method methods[] = clazztwo.getmethods();

    //获取该对象声明的所有的方法,但是不包含父类的方法
    method demethods[] = clazztwo.getdeclaredmethods();

一个method方法打印出来是什么呢?上面代码中也包含了

public void demo.qzxxbj.entity.student.setage(java.lang.integer)

和之前讲的field是不是很相似。
既然说到了方法,那么就肯定涉及到了方法调用,我们得到了这些方法,又该怎么调用这个类里面的方法呢?使用invoke函数,method这个类里面包含了一个invoke函数,英语好的就知道了,这个invoke的中文意思就是“调用”。
怎么用呢?

    //获取class第二种方法
    class clazztwo = student.class;
    //根据方法名以及参数类型获取,只能获取public声明的方法,包括父类的
    method method = clazztwo.getmethod("setage",integer.class);

    //public java.lang.integer demo.qzxxbj.entity.student.getage()
    system.out.println(method);

    //利用class创建一个对象的实例
    student student = (student) clazztwo.newinstance();

    //函数调用
    object value = method.invoke(student,20);
    //null
    system.out.println(value);

以上的代码,你可能会看不懂,我来讲一下,首先,我们获取一个类的class,然后我们通过这个class获取该类的一个setage方法,获取到这个方法后继续调用这个方法,调用方法是不是应该调用一个实例对象里面的方法?所以我们需要先实例化一个对象,通过什么方法呢,通过class里面的newinstance(),创建一个实例,这种方法需要该实例化的类具有一个无参构造方法。还有其他方法也能创建一个实例,后面我们会说。创建出一个实例对象之后,我们就能开始调用方法了。
通过invoke对方法进行调用,invoke的第一个参数就是一个实例化对象,不然我去哪找这个方法。第二个参数,或者第三个,等等,后面的所有参数都是我调用的该方法所具有的参数,按照顺序填进去就ok了。然后这个函数返回的是一个object对象,你都能想到,我调用一个方法是不是要让他做一些事,做了这些事需要返回一个东西,不知道这个东西是啥,就用object获取嘛。由于我们调用的这个方法不需要返回值,所以就是null了。很简单是不是。学到了记得给我点个关注哦!精彩美文第一时间推送到你的手中。

通过class获取构造方法

这个被我放到了最后来学习,毕竟我觉得用的比例比较少。一起来学习一下怎么用class获取构造方法,并调用他。

public student(string name, int id) {
    this.name = name;
    this.id = id;
}

这里我们在student类里面添加了一个构造方法。

然后我们来获取这个构造方法。

	//获取class第二种方法
    class clazztwo = student.class;

    //获取无参构造方法,public声明的,包括父类,加上参数时就是获取特定的构造方法
    constructor constructor = clazztwo.getconstructor();

    //public demo.qzxxbj.entity.student()
    system.out.println(constructor);

    //获取该类所有的public声明的构造方法
    constructor constructors[] = clazztwo.getconstructors();

    //获取指定参数的构造方法
    constructor deconstructor = clazztwo.getdeclaredconstructor(string.class,integer.class);

    //获取所有的该类的构造方法,不包括父类的
    constructor deconstructors[] =clazztwo.getdeclaredconstructors();

上面代码应该很容易看懂吧,我就不细说了。这里说一下如何使用得到的构造方法,构造方法顾名思义就是来实例化对象的,上面我们也有说到怎么通过class实例化一个对象,现在我们来通过构造方方法实例化一个对象

	student student = (student) deconstructor.newinstance("全栈学习笔记",21);

    //21
    system.out.println(student.getage());

现在知道了吧,我们差不都将反射的功能讲完了,就差一个反射的动态代理,这个比较重要,会专门出一篇博客,码字不易。希望点个关注。微信公众号:全栈学习笔记,精彩美文每天为你推送。

最后我根据我自己以前的经验写了一个java反射的sql语句拼接,相当于是一个反射的应用吧。

反射的应用示例

通过反射动态的生成sql语句,是不是也有点牛逼的感觉?
直接上代码吧,我只发一个sql语句,感兴趣的可以私信我找我拿完整的代码!

public  string insert(object object) throws illegalaccessexception, nosuchmethodexception, invocationtargetexception {`
    //insert into student(id,name,sex) values (1,"全栈学习笔记","男")
    stringbuilder sql = new stringbuilder();
    class clazz = object.getclass();
    sql.append("insert into ");

    sql.append(clazz.getsimplename()+"(");
    field[] fields = clazz.getdeclaredfields();
    for(field field:fields){
        sql.append(field.getname()+",");
    }
    sql.deletecharat(sql.length()-1);
    sql.append(")");
    sql.append(" values (");
    for(field field:fields){
        field.setaccessible(true);
       object value = field.get(object);

       string fieldname = field.getname();
       string str1 = fieldname.substring(0,1).touppercase();
       string str2 = fieldname.substring(1,fieldname.length());
       string strvalue = str1.concat(str2);
		//string strvalue = fieldname.substring(0,1).touppercase().concat(fieldname.substring(1,fieldname.length()));
       method method = clazz.getmethod("get"+strvalue,null);

       object value1 = method.invoke(object,null);

//     if(value1.getclass().equals(string.class))
//     if(field.gettype().equals(string.class))
        if(value1 instanceof string){
            sql.append("\"").append(value1).append("\"").append(",");
        }else {
            sql.append(value1).append(",");
        }
    }
    sql.deletecharat(sql.length()-1);
    sql.append(")");
    system.out.println(sql.tostring());
    return sql.tostring();
}

本期的讲解就到这里,后面应该也会出一期关于注解的文章,如果你发现文章中有错误的地方,欢迎指出来哦!如果你觉得你能学到不少知识,请点个关注哦!欢迎转发!让更多的朋友学到!
微信公众号:公众号日更,精彩美文每天推送
在这里插入图片描述

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网