java反射基础

java反射是什么

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

一般功能性的类,都会采用动态加载的方式

一.Class类简单介绍

1.什么是Class类

Class类简单说就是任何一个类的类型,因为我们常说,java是面向对象的,一切都是对象,而对象又是它属于所属类的一个实例,那么它所属的类又是谁的实例呢?答案就是Class类。
简而言之,Class类就是java中所有类的类,或者说java中所有的类都是Class类的对象。
下面我们一起来看Class类的简单使用。

2.创建Class的对象的三种方法

注:MyClassTest 类声明

package fanshe;

public class MyClassTest {

    MyClassTest() {}

    public void print() {
        System.out.println("MyClassTest.print()");
    }

// 该静态方法用于验证疑问
    public static void staticPrint() {
        System.out.println("MyClassTest.staticPrint()");
    }
}
// 1.通过类的隐含的静态属性class创建
**此方法说明每一个类默认有一个隐含的静态属性class**
        Class c1 = MyClassTest.class;
// 2. 通过对象的getClass方法创建
        MyClassTest t1 = new MyClassTest();
        Class c2 = t1.getClass();
// 3. 通过 CLass类的Class.forName()方法创建
//     由参数指定要创建的类型
        Class c3 = null;
        try {
            c3 = Class.forName("fanshe.MyClassTest");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

验证:
这里写图片描述

3.Class类的对象的使用

我们通过前面三种方式得到了Class类的对象,那么得到这个对象有什么用呢?
看如下代码:

try {
            MyClassTest t4 = (MyClassTest)c1.newInstance();
            // newInstance()方法需要该类有无参的构造方法支持
            // 而且new出来的对象需要强转
            t4.print();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

简单分析上述代码的作用
1)c1是一个Class类的关于MyTest类型的对象,我们想创建一个MyTest类型的对象可以通过new的方式
2)想通过c1这个对象创建MyTest类型的对象则需要调用Class类的newInstance()方法创建,但是创建的是模板类型 T ,所以要强转成MyTest类型,然后才能调用MyTest类型的print() 方法
附newInstance()方法的源代码的首行:

     @CallerSensitive
    public T newInstance() throws InstantiationException, IllegalAccessException {

4.一个疑问

我们通过前面三种方式得到了Class类的对象,而Class类是一切类的类型,从某种层面上来讲,似乎Class类的关于MyTest类型的对象和MyTest类是同一个层面上的,通常情况下,我们得到一个类的时候,我们可以直接使用这个类的一些静态方法和属性,那么我们这种方式得到的Class类的关于MyTest类型的对象行不行呢?
验证:
这里写图片描述
显然答案是不可以!!!

因为Class类的对象本质是属于Class类型的,只能调用Class类的方法和属性,这是面向对象的基础!

那么怎么才能通过某一个类的Class类的对象调用该类的方法呢?
请看第七部分,实战2,。

5.基本的内置类型,包括void都存在class属性

        Class c5 = int.class;
        Class c6 = double.class;
        Class c7 = void.class;
        Class c8 = String.class;
        System.out.println(c5.getName());
        System.out.println(c6.getName());
        System.out.println(c7.getName());
        System.out.println(c8.getName());

运行结果:

int
double
void
java.lang.String

6.实战1 —— 通过反射获得类的所有信息,包括类的成员方法、成员变量、构造函数

package fanshe;

import java.lang.reflect.Method;

public class ClassUtil {
    /**
     *
     * @param o 传递一个对象
     *
     */
    public static void getClassMessage(Object o ) {
        Class c = o.getClass(); //得到的是所传递的对象的类类型

        // 得到类的名称
        System.out.println("类的名称:"+c.getName());

        // 获得类的方法
        Method[] methods = c.getMethods(); //获得所有public的方法,包括继承自父类的方法
                                           //c.getDeclaredMethods()获得所有的自己声明的方法

        for (int i =0;i<methods.length;i++) {
            Class  rt = methods[i].getReturnType(); //得到返回值的类类型,比如java.leng.Strig
            System.out.print(rt.getSimpleName()+" "); //得到类名 比如String
            System.out.print(methods[i].getName()+"("); //得到方法名
            Class[] parmType = methods[i].getParameterTypes();
            for(int j=0;j<parmType.length;j++) {
                System.out.print(parmType[j].getSimpleName()+",");
            }
            System.out.println(")");
        }

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

        // 同样的,我们来获取成员变量
        Field[] fields = c.getDeclaredFields();
        for(int i =0;i<fields.length;i++) {
            Class returnType = fields[i].getType();
            System.out.print(returnType.getSimpleName()+" ");
            System.out.println(fields[i].getName());
        }

    }
    /**
     * 获取构造函数的信息
     */
    public static void printConstructorMessage(Object o) throws NoSuchMethodException {
        Class c = o.getClass();
        Constructor[] cs = c.getDeclaredConstructors();
        for (int i = 0; i <cs.length ; i++) {
            System.out.print(cs[i].getName()+" (");
            Class[] returnType = cs[i].getParameterTypes();
            for (int j = 0; j < returnType.length ; j++) {
                System.out.print(returnType[j].getSimpleName()+",");
            }
            System.out.println(")");
        }

    }
}

测试类:

public class Test {
    public static void main(String[] args){
        String s = "test";
        ClassUtil.getClassMessage(s);
        try {
            ClassUtil.printConstructorMessage(s);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

运行结果(部分):

类的名称:java.lang.String
boolean equals(Object,)
String toString()
int hashCode()
int compareTo(String,)
int compareTo(Object,)
int indexOf(String,int,)
int indexOf(String,)
int indexOf(int,int,)
int indexOf(int,)
String valueOf(int,)
String valueOf(long,)
String valueOf(float,)
String valueOf(boolean,)
String valueOf(char[],)
String valueOf(char[],int,int,)
String valueOf(Object,)
String valueOf(char,)
String valueOf(double,)
char charAt(int,)
int codePointAt(int,)
int codePointBefore(int,)
int codePointCount(int,int,)
...
...
##############################
char[] value
int hash
long serialVersionUID
ObjectStreamField[] serialPersistentFields
Comparator CASE_INSENSITIVE_ORDER
##############################
java.lang.String (byte[],int,int,)
java.lang.String (byte[],Charset,)
java.lang.String (byte[],String,)
java.lang.String (byte[],int,int,Charset,)
java.lang.String (byte[],int,int,String,)
java.lang.String (char[],boolean,)
java.lang.String (StringBuilder,)
java.lang.String (StringBuffer,)
java.lang.String (byte[],)
java.lang.String (int[],int,int,)
java.lang.String ()
java.lang.String (char[],)
java.lang.String (String,)
java.lang.String (char[],int,int,)
java.lang.String (byte[],int,)
java.lang.String (byte[],int,int,int,)

7.实战2 —— 通过反射调用指定类的对象的方法

package fanshe;

import java.lang.reflect.InvocationTargetException;

public class InvokeMethod {

    /**
     * 通过反射调用类的对象的方法
     */
    public static void main(String[] args) {
        A a = new A();
        Class c = a.getClass();
        try {
            c.getMethod("print",int.class,int.class).invoke(a,10,20);
            c.getMethod("print",String.class,String.class)
                    .invoke(a,"hello","world");
            c.getMethod("print").invoke(a);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }
}

class A {
    public void print(int a,int b) {
        System.out.println(a+"+"+b+"="+a+b);
    }

    public void print(String a,String b) {
        System.out.println(a+",,"+b);
    }
    public void print() {
        System.out.println("null");
    }
}

8.实战3 —— 通过反射实现同一个集合存放不同类型的元素

首先看这样一个代码:
这里写图片描述
显然,l1并不能存放10,因为10 不是String类型,那么我们怎么解决呢?
先看这样的一个测试:
这里写图片描述
运行结果为true!!!
这说明编译之后集合的泛型是去泛型化的,也就是说在编译之前,语法检查的时候,才会对集合将要存放的类型进行审查,而编译之后他们属于同一个类型!

下面看反射实现:

package fanshe;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class Demo {


    public static void main(String[] args) {

        List<String> l1 = new ArrayList<>();
        List<Integer> l2 = new ArrayList<>();
        l1.add("test");
        // l1.add(100);

        Class c1 = l1.getClass();
        Class c2 = l2.getClass();

        System.out.println(c1==c2);//运行结果为true

        try {
            Method m = c1.getMethod("add",Object.class);
            // 现在向l1中添加一个100
            m.invoke(l1,100);
            System.out.println("l1中的元素:"+l1);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

运行结果:
这里写图片描述

9.总结

<1>.得到指定类的类类型常用方法(三种)
<2>.类的类类型对象的常用方法(getMethods()、getReturnType()、getParameterTypes()…..等)
<3>反射是在运行时加载对象,而不是编译时,所以可以绕过编译实现同一个结合存放不同类型数据

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页