java反射

Posted hometown

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java反射相关的知识,希望对你有一定的参考价值。

一、Class类的使用

在面向对象的世界里,万事万物皆对象。所以一个具体的类(比如我们创建的User类)也是一个对象,那么类是谁的对象呢?类是java.lang.Class的对象,通过源码我们发现Class是私有的,不能通过new创建。但是可以通过以下三种方法实现。

我们首先创建一个User类,然后实现创建此类类型的方法:

package com.ht;

public class User {
    public void print(){
        System.out.println("hello world!");
    }
}
View Code
import com.ht.User;

public class Main {

    public static void main(String[] args) {
        User user=new User();
        user.print();

        //第一种获取类的方法(通过类的一个实例创建)
        Class u1=user.getClass();

        //第二种获取类的方法(通过类的隐藏的静态class属性创建)
        Class u2=User.class;

        //第三种获取类的方法 (这里有一个异常的抛出,通过Ctrl+Alt+t,快捷键直接打出代码)
        Class u3=null;
        try {
            u3=Class.forName("com.ht.User");//必须是类的完整路径名
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //以上u1,u2,u3表示User这个类的类类型
        //通过比较我们知道这三个值是相等的,而且通过类类型也可以实例化出其具体的对象
        System.out.println(u1==u2); //true
        System.out.println(u2==u3); //true

        try {
            User xiaoming=(User)u2.newInstance(); //需要有无参的构造函数
            xiaoming.print();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
View Code

 

二、 动态加载类

通过Class.forName(“类的全名”)这种情况,我们就可以对类进行动态加载。

设计出一个公共的接口类,然后让我们需要的类实现这个接口,每次编译我们实现的类,然后运行主程序就可。

//ioffice作为接口,定义了start()方法,凡是实现了这个方法的类,我们都认为是office程序。

package com.ht;

public interface Ioffice {
    public void start();
}
View Code

//Word的实现

package com.ht;

public class Word implements Ioffice{
    public void start() {
        System.out.println("word...start...");
    }
}
View Code

//主程序

import com.ht.Ioffice;

public class Main {

    public static void main(String[] args) {
        try {
            Class c=Class.forName(args[0]);
            Ioffice ioffice=(Ioffice)c.newInstance();
            ioffice.start();
        } catch (Exception e){
        }

    }
}
View Code

经过编译后,输入具体的类名,就可以动态的加载了。

 三、获取类的方法信息

我们通过一个对象,就能够拿到此对象的类类型,然后通过类类型获取此类的内部方法

//我们实现一个方法,传递一个对象,获取信息

class ClassUnit{
    public static void getDetails(Object obj){
        System.out.println(obj.getClass()); //打印出传递进来对象的类类型

        Class c=obj.getClass(); //通过获取相应的类类型,来获取类名,方法名,参数名等等
        System.out.println(c.getName());
        Method[] methods=c.getDeclaredMethods();//c.getMethods(); //获取所有的方法的集合
        //以下通过循环拿到每个方法的名称
        for (int i=0;i<methods.length;i++){
            Class returnType= methods[i].getReturnType(); //得到一个方法的返回类型
            System.out.print("方法为:"+returnType.getName()+" "); //得到一个方法返回值类型的名称
            System.out.print(methods[i].getName()+" (");//得到一个方法名
            Class[] pataTypes=methods[i].getParameterTypes();//获取参数的类类型
            for (Class patatype:pataTypes) {
                System.out.print(patatype.getName()+","); //每个方法的类型名
            }
            System.out.println(")");
        }
    }
}
View Code

//此处传递的对象,既可以是系统基础类型,也可以是我们自己创建的类对象

public class Main {
    public static void main(String[] args) {
        int a=9;
        ClassUnit.getDetails(a);

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

        Word word=new Word();
        ClassUnit.getDetails(word);
    }
}
View Code

//获取到的结果如下

同样我们也可以获取成员变量信息和构造函数的信息

 

四、方法的反射

我们通过Method.invoke(Object obj对象, Object... args参数列表)方法,可以进行方法反射的操作

比如我们有以下类,类中有不同的方法实现:

class ClassUnit{
    public void print(){
        System.out.println("hello");
    }

    public int print(int a,int b){
        return a+b;
    }

    public void printf(String a,String b){
        System.out.println(a+b);
    }
}
View Code
 public static void main(String[] args) {
        ClassUnit c=new ClassUnit();//假设我们已经有了这么个对象

        Class aClass= c.getClass(); //首先通过对象,获取其类类型

        try {
            Method m=aClass.getMethod("print",new Class[]{int.class,int.class});//此方法需要有try操作,万一没有这个方法或者参数不对的情况呢
            Method m2=aClass.getMethod("print");
            Method m3=aClass.getMethod("printf",new Class[]{String.class,String.class});
            try {
                Object o= m.invoke(c,new Object[]{10,20});
                Object o2=m2.invoke(c);
                Object o3=m3.invoke(c,new Object[]{"hello","world"});

                System.out.println(o);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
View Code

 

五、通过反射了解集合泛型的本质

java中集合的泛型是防止错误的输入,只在编译阶段有效。验证:我们通过方法的反射,绕过编译,就可以将不同的类型数据插入到集合中。

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

public class Main {
    public static void main(String[] args) {
        ArrayList arrayList1=new ArrayList();
        ArrayList<String> arrayList2=new ArrayList<String>();
        arrayList2.add("hekko");
        //arrayList2.add(200);//错误

        Class c1=arrayList1.getClass();
        Class c2=arrayList2.getClass();
        System.out.println(c1==c2);  //true

        try {
            Method m2= c2.getMethod("add", Object.class);
            try {
                m2.invoke(arrayList2,200); //这样就能将200加进String的集合中
                System.out.println(arrayList2.size());
                System.out.println(arrayList2);
                    

            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}
View Code

以上代码得到的结果如下:

以上是关于java反射的主要内容,如果未能解决你的问题,请参考以下文章

反射机制入门

反射机制入门

反射机制入门

使用反射在外部JAR / CLASS上调用包含Hibernate事务的方法(Java EE)

为啥我的 Ray March 片段着色器反射纹理查找会减慢我的帧速率?

OpenGL片段着色器不照亮场景