Class类,反射的概念

一.认识Class类

想要反射,你就必须要了解一个类——Class,我们知道,java程序中的各个java类都属于同一事物,我们通常用Classliability描述对吧,反射这个概念从JDK1.2就出来了,历史算是比较悠久了,这个Class可不是关键字哦,这个是一个类,他代表的是一类事物;

  • 我们归根结底就是拿到字节码对象

这里我们有三种方式是可以得到对应的实例对象(Class类型)

  • 1.类名.class
  • 2.对象.getClass
  • 3.Class.forName(“类名”);—常用

我们写一个小程序来过一遍

<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> CalssTest {

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) throws ClassNotFoundException {
        String str1 = <span class="hljs-string">"liu"</span>;
        Class class1 = str1.getClass();
        Class class2 = String.class;
        Class class3 = Class.forName(<span class="hljs-string">"java.lang.String"</span>);

        System.<span class="hljs-keyword">out</span>.println(class1 == class2);
        System.<span class="hljs-keyword">out</span>.println(class1 == class3);
        System.<span class="hljs-keyword">out</span>.println(class2 == class3);

    }
}
</code>

通过打印的结果我们可以知道我们都是输出同一份字节码,所以结果都是true,我们再来看,我们的Class其实还有一个方法

<code class="hljs avrasm has-numbering">System<span class="hljs-preprocessor">.out</span><span class="hljs-preprocessor">.println</span>(class1<span class="hljs-preprocessor">.isPrimitive</span>())<span class="hljs-comment">;</span>
System<span class="hljs-preprocessor">.out</span><span class="hljs-preprocessor">.println</span>(Integer<span class="hljs-preprocessor">.class</span> == int<span class="hljs-preprocessor">.class</span>)<span class="hljs-comment">;</span>
System<span class="hljs-preprocessor">.out</span><span class="hljs-preprocessor">.println</span>(int<span class="hljs-preprocessor">.class</span> == Integer<span class="hljs-preprocessor">.TYPE</span>)<span class="hljs-comment">;</span></code>

打印的结果是true,false,true,那这三又是什么意思呢?这个就留给大家猜想一下,应该很容易就知道了的

  • isPrimitive是否是基础类型或者原始类型

那我们的数组是什么类型呢?

宗旨,只要是在源程序中出现的类型,都有各自的Class类型,列入int[],Void等

二.反射的概念

好的。了解了Class类的神奇,我们就可以来看看反射的机制了

反射的概念:反射就是把JAVA类中的各种成分映射成相应的JAVA类,例如,一个java类中用一个Class类的对象来表示,一个类中的组成部分,成员变量,方法,构造方法,包等信息,也用一个个的JAVA类来表示,就像汽车是一个类,汽车中的发动机,变速箱也是一个类,表示JAVA的Class类要提供一系列的方法,来获取其中的变量,方法,构造方法,修饰符,包等信息,这些信息是用来相应类的实例对象表示,衙门是Field,Method,Contructor,Package等

一个类中的每一个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class来调用各种方法,我们接下来一个个讲

三.Constructor

代表的是某一个类的构造方法,我们用代码来看

<code class="hljs cs has-numbering">
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> CalssTest {

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args)throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

        <span class="hljs-comment">//得到String的StringBuffer构造方法</span>
        Constructor<String> constructor = String.class.getConstructor(StringBuffer.class);
        String str = constructor.newInstance(<span class="hljs-keyword">new</span> StringBuffer(<span class="hljs-string">"str"</span>));
        System.<span class="hljs-keyword">out</span>.println(str);
    }
}
</code>

通过获得的方法用到上面相同类型的实例,再看newInstance,得到我们设置参数的构造方法,我们再来看下成员变量的反射

四.Field

这里我们就模拟了一个类,作为反射的对象

<code class="hljs cs has-numbering">
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> FieldClass {

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> x;
    publicint y;

    <span class="hljs-keyword">public</span> <span class="hljs-title">FieldClass</span>(<span class="hljs-keyword">int</span> x,<span class="hljs-keyword">int</span> y) {
        <span class="hljs-keyword">this</span>.x = x;
        <span class="hljs-keyword">this</span>.y = y;
    }
}
</code>

那我们实际的操作就是:

<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> CalssTest {

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args){

        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">//首先为x,y 赋值</span>
            FieldClass fClass = <span class="hljs-keyword">new</span> FieldClass(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>);
            <span class="hljs-comment">//反射成员变量</span>
            Field field1 = fClass.getClass().getField(<span class="hljs-string">"x"</span>);
            Field field2 = fClass.getClass().getField(<span class="hljs-string">"y"</span>);
            <span class="hljs-comment">//指定对象</span>
            System.<span class="hljs-keyword">out</span>.println(field1.<span class="hljs-keyword">get</span>(fClass));
            System.<span class="hljs-keyword">out</span>.println(field2.<span class="hljs-keyword">get</span>(fClass));
        } <span class="hljs-keyword">catch</span> (NoSuchFieldException e1) {
            <span class="hljs-comment">// TODO Auto-generated catch block</span>
            e1.printStackTrace();
        } <span class="hljs-keyword">catch</span> (SecurityException e1) {
            <span class="hljs-comment">// TODO Auto-generated catch block</span>
            e1.printStackTrace();
        } <span class="hljs-keyword">catch</span> (IllegalArgumentException e) {
            <span class="hljs-comment">// TODO Auto-generated catch block</span>
            e.printStackTrace();
        } <span class="hljs-keyword">catch</span> (IllegalAccessException e) {
            <span class="hljs-comment">// TODO Auto-generated catch block</span>
            e.printStackTrace();
        }
    }
}
</code>

这样我们就可以直接反射到成员变量的值了,这里我们来做一个小练习来测试一下我们成员变量的反射的理解

练习题

  • 假设我们有一个对象中,有许多的String类型成员变量,我们的需求就是把所有对应的字符串内容”b”改成”a”

题目有了我们整理下思路,首先要做的就是定义一个对象咯

<code class="hljs cs has-numbering">
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> FieldClass {

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> x;
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> y;
    <span class="hljs-keyword">public</span> String str1 = <span class="hljs-string">"boy"</span>;
    <span class="hljs-keyword">public</span> String str2 = <span class="hljs-string">"bbq"</span>;
    <span class="hljs-keyword">public</span> String str3 = <span class="hljs-string">"hello"</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-title">FieldClass</span>(<span class="hljs-keyword">int</span> x,<span class="hljs-keyword">int</span> y) {
        <span class="hljs-keyword">this</span>.x = x;
        <span class="hljs-keyword">this</span>.y = y;
    }
    @Override
    <span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span>() {
        <span class="hljs-keyword">return</span> <span class="hljs-string">"FieldClass [x="</span> + x + <span class="hljs-string">", y="</span> + y + <span class="hljs-string">", str1="</span> + str1 + <span class="hljs-string">", str2="</span>
                + str2 + <span class="hljs-string">", str3="</span> + str3 + <span class="hljs-string">"]"</span>;
    }
}
</code>

好的,这里有三个string类型的成员变量,那我们该怎么去反射呢?

<code class="hljs java has-numbering">
<span class="hljs-javadoc">/**
 * 假设我们有一个对象中,有许多的String类型成员变量,我们
 * 的需求就是把所有对应的字符串内容"b"改成"a"
 *<span class="hljs-javadoctag"> @author</span> LGL
 *
 */</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CalssTest</span> {</span>

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) <span class="hljs-keyword">throws</span> Exception{

        FieldClass fClass = <span class="hljs-keyword">new</span> FieldClass(<span class="hljs-number">5</span>, <span class="hljs-number">10</span>);
        <span class="hljs-comment">//得到所有的成員变量</span>
        Field[] fields = fClass.getClass().getFields();
        <span class="hljs-keyword">for</span>(Field field : fields){
            <span class="hljs-comment">//过滤下String类型</span>
            <span class="hljs-keyword">if</span>(field.getType() == String.class){
                <span class="hljs-comment">//取值</span>
                String oldValue = (String) field.get(fClass);
                String newValue =  oldValue.replace(<span class="hljs-string">"b"</span>, <span class="hljs-string">"a"</span>);
                field.set(fClass, newValue);
            }
        }
        System.out.println(fClass);
    }
}
</code>

这里思路还是比较清晰的,我们一步步的去过滤,最终更改了值

这里写图片描述

五.Method

如果前面的都是不怎么常用的,那这个我相信你绝对会喜欢,也就是方法,Method,我们来看看怎么用吧!

<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> CalssTest {

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) throws Exception{

        String str1 = <span class="hljs-string">"abc"</span>;
        <span class="hljs-comment">//反射charAt方法</span>
        Method method = String.class.getMethod(<span class="hljs-string">"charAt"</span>, <span class="hljs-keyword">int</span>.class);
        <span class="hljs-comment">//作用对象 第几个</span>
        System.<span class="hljs-keyword">out</span>.println(method.invoke(str1, <span class="hljs-number">2</span>));
    }
}
</code>

这里我反射的是String的方法charAt,取第二个char,所以打印出来是c

这其实套用了一种专家模式,谁调用了这个数据,谁就是这个专家,现在我们思考一下,如果我们代码是这样写:

<code class="hljs oxygene has-numbering">System.<span class="hljs-keyword">out</span>.println(<span class="hljs-function"><span class="hljs-keyword">method</span>.<span class="hljs-title">invoke</span><span class="hljs-params">(null, 2)</span>);</span></code>

这里我们不传对象,而是传一个null进去,那我们知道这个Method是调用什么吗?而纵观之下,只有static修饰的静态方法是不需要对象的,那就说明他调用的是对象了;

在JDK1.4和JDK1.5中,invoke的参数是有些区别的,不过我们现在都1.8了,自然而然这个我们不需要去考虑和追究;

六.反射Main方法

我们现在来思考一下,如何通过反射去执行一个类的Main方法,这就比较好玩了,这里就相当于一个练习题了,而题目就是:

  • 写一个程序,这个程序能够根据用户提供的类名,去执行该类的main方法

而我们的在写这段程序之前,我们就要思考一下了,我们观察main方法:

<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) {
    <span class="hljs-comment">//main方法</span>
}</code>

你是否已经发现,这个main方法的参数是一个数组,,如果我们通过反射来调用这个方法,那我们该如何为invoke方法传递参数呢?

在以前的老程序员可能会在纠结JDK1.4和JDK1.5的invoke区别,但是我们倒不用去考虑这些,因为我们的版本是JDK1.8,这样吧,我们先写个需要调用的类:

<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> MainClass {

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) {

        <span class="hljs-keyword">for</span> (String arg : args) {

            System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"Main:"</span> + arg);
        }
    }
}
</code>

OK,这个类就是打印下数组的值,那我们真正的反射应该怎么去写尼?传统的调用方法是这样的:

<code class="hljs javascript has-numbering">MainClass.main(<span class="hljs-keyword">new</span> <span class="hljs-built_in">String</span>[] { <span class="hljs-string">"Hello"</span> });</code>

我们反射的话,就不是这么简单了,我们需要这样:

<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> CalssTest {

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) throws Exception {

        <span class="hljs-comment">// MainClass.main(new String[] { "Hello" });</span>
        <span class="hljs-comment">// 指定类名</span>
        <span class="hljs-comment">// String className = args[0];</span>
        <span class="hljs-comment">// 指定Main方法</span>
        Method main = Class.forName(<span class="hljs-string">"MainClass"</span>).getMethod(<span class="hljs-string">"main"</span>,
                String[].class);
        <span class="hljs-comment">// 调用main方法</span>
        main.invoke(<span class="hljs-keyword">null</span>, (Object) <span class="hljs-keyword">new</span> String[] { <span class="hljs-string">"Hello"</span>, <span class="hljs-string">"LGL"</span> });
    }
}
</code>

这里为什么需要强转为Object?不是说好的传递数组吗?吗是因为JDK为了兼容老版本,如果你传递的是一个数组有多个值会进行拆包,我现在声明一个Object就是告诉他我这里只是一个对象,避免对象异常,我们看下输出的结果:

这里写图片描述

说明我们是调用成功了的;那这里就牵引出数组的反射和Object的关系了,我们继续来看;

七.数组的反射

数组实际上也是一个对象,这点我们一定要清楚,是吧

  • 具有相同维数和元素类型的数组术语同一类型,即具有相同的Class实例对象
  • 代表数组的Class实例对象的getSuperClass()方法返回的父类Object为对象的Class

这里我们以数组为例子就好了,我们来验证一下字节码:

<code class="hljs avrasm has-numbering">int[] arr1 = { <span class="hljs-number">3</span> }<span class="hljs-comment">;</span>
int[] arr2 = { <span class="hljs-number">4</span> }<span class="hljs-comment">;</span>
//true
System<span class="hljs-preprocessor">.out</span><span class="hljs-preprocessor">.println</span>(arr1<span class="hljs-preprocessor">.getClass</span>() == arr2<span class="hljs-preprocessor">.getClass</span>())<span class="hljs-comment">;</span>
//[I
System<span class="hljs-preprocessor">.out</span><span class="hljs-preprocessor">.println</span>(arr1<span class="hljs-preprocessor">.getClass</span>()<span class="hljs-preprocessor">.getName</span>())<span class="hljs-comment">;</span></code>

第一个为true也就验证了第一条,第二个输出的结果是[I,这个代表的是什么意思呢?[代表的是数组,I代表的是Int类型,也就是这个是int类型的数组

这里写图片描述

看一下JDK对照文档就行了

接下来,我们来看下数组的反射具体实践是什么样子的!

八.数组反射实践

我们出道题目好了在这样看起来可能更加生动形象一点

  • 题目:怎么得到数组中的元素?

通过查看API文档,我们知道可以反射这个Array

这里写图片描述

那这个Array要怎么用呢?

假设我现在封装一个打印的方法,这个打印的方法很简单,我定义一个Object参数,你传进来什么,我就打印什么,那要是传递数组,我就一个个的打印,这样的理解应该就比较清晰了,那我们实际的操作你怎样的呢?

<code class="hljs cs has-numbering">
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> CalssTest {

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) throws Exception {

        <span class="hljs-keyword">int</span>[] arr = { <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">9</span>, <span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">6</span>, <span class="hljs-number">8</span>, <span class="hljs-number">10</span> };
        printObject(arr);
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">printObject</span>(Object obj) {
        <span class="hljs-comment">// 拿到字节码</span>
        Class<? extends Object> classs = obj.getClass();
        <span class="hljs-comment">// 判断是否是数组</span>
        <span class="hljs-keyword">if</span> (classs.isArray()) {
            <span class="hljs-comment">// 得到长度</span>
            <span class="hljs-keyword">int</span> len = Array.getLength(obj);
            <span class="hljs-comment">// 取出每一个</span>
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < len; i++) {
                System.<span class="hljs-keyword">out</span>.print(Array.<span class="hljs-keyword">get</span>(obj, i));
            }
        } <span class="hljs-keyword">else</span> {
            System.<span class="hljs-keyword">out</span>.println(obj);
        }
    }
}</code>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值