反射

关于反射的基本概念这里不多介绍,记得自己开始学习Java的时候,是在dos下执行命令来运行程序。下面来重温下:


在记事本中写一个简单的类,然后在dos下执行,我想基本的命令使用过得都很清楚:

文件夹中会看到a.class文件,编译后生成的Java字节码文件,那么里面到底是啥呢?用Java提供的一个反编译工具javap命令查看:


上面生成的Java字节码,可以被JVM加载运行。

学习异常可以很清楚的看到一张图:


关于Error异常不多说,是系统内部错误,我们是解决不了的,比如资源耗尽。但是Exception下面分为检查异常和不检查异常,检查异常比如IOExceptionJAXBExceptionJMExceptionKeySelectorException等,具体的可以去API上查看,

常见的是文件不存在等。不检查异常就是运行时异常,比如空指针,数组越界等,下面通过简单的来用代码测试下:

简单的修改:

public class a{
	
	public static void main(String []args){
		System.out.println("hello world");
		D d = new D();
	}
}
编译:


说明编译都通不过,找不到类D,属于检查异常

再简单修改下:

public class a{
	
	public static void main(String []args){
		System.out.println(args[0]);
	}
}
然后编译运行:


发现上面编译没有报错,但是运行时就报数组越界错误,上面很直观的说明什么是检查异常和非检查异常了吧!

当然我们并不是奔着异常,但是想通过次说明Java中编译和运行是两个不同的概念。

java编译后生成的.class文件,然后在JVM中加载运行,在JVM中每个Java类都表现为一个Class对象,但是Class对象又是java.lang.Class一个对象,表示类的类型信息,那么如何获取类的Class对象呢:

public class test {
    public void print(){
        System.out.println("test");
    }
}
public class Main {
    public static void main(String[] args) {
        Class a = test.class;  //第一种方法
        try {
            ((test) a.newInstance()).print();//通过类的Class对象可以生成类的实例,前提是类有默认的非private空构造器
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        try {
            Class b = Class.forName("com.java.c.test");//第二种 forName  动态加载类
            try {
                ((test) b.newInstance()).print();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        test t = new test();
        Class c = t.getClass(); //第三种 getClass   静态加载类,在编译时就需要加载所有需要用到的类
        try {
            ((test) c.newInstance()).print();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
只有类有类类型么?当然不是,看API中的注释:

/**
 * Instances of the class {@code Class} represent classes and
 * interfaces in a running Java application.  An enum is a kind of
 * class and an annotation is a kind of interface.  Every array also
 * belongs to a class that is reflected as a {@code Class} object
 * that is shared by all arrays with the same element type and number
 * of dimensions.  The primitive Java types ({@code boolean},
 * {@code byte}, {@code char}, {@code short},
 * {@code int}, {@code long}, {@code float}, and
 * {@code double}), and the keyword {@code void} are also
 * represented as {@code Class} objects.
基本的数据类型以及void都有类类型,int.class,short.class等等

反射是发生在运行时,当我们编写一个类时,JVM就会帮我们编译成class对象,存放在同名的.class文件中。在运行时,当需要生成这个类的对象,JVM就会检查此类是否已经装载内存中,若没有装载,则把.class文件装入到内存中。若是装载,则根据class文件生成实例对象。

下面通过反射,再运行时获取类信息,比如类名称,类成员变量,成员函数,构造函数,以及注解。还会介绍在泛型中如何使用反射,跳过编译,往容器中放入不同类型的值。下面涉及到自定义注解,后续文章会涉及。

@Target({TYPE,METHOD,FIELD,CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserAnn {
    public int id() default 1;
    public String description() default "no description";
}

/**
 * Created by diy_os on 2016/11/5.
 */
@UserAnn(id = 1, description = "类注解")
public class msg {
    @UserAnn(id = 2, description = "成员变量注解")
    private int i = 1;  //私有成员变量,通过反射取不到该变量信息
    @UserAnn(id = 1, description = "成员变量注解")
    private int j = 2;
    @UserAnn(id = 1, description = "成员变量注解")
    public int z = 3;

    @UserAnn(id = 3, description = "构造函数注解")
    public msg() {

    }

    @UserAnn(id = 3, description = "构造函数注解")
    public msg(int i, int j) {

    }

    @UserAnn(id = 4, description = "成员函数注解")
    public int getI() {
        return i;
    }

    @UserAnn(id = 5, description = "成员函数注解")
    public void setI(int i) {
        this.i = i;
    }

    @UserAnn(id = 6, description = "成员函数注解")
    public int getJ() {
        return j;
    }

    @UserAnn(id = 7, description = "成员函数注解")
    public void setJ(int j) {
        this.j = j;
    }

    @UserAnn(id = 8, description = "成员函数注解")
    private int getij() {  //私有成员函数,通过反射取不到该函数信息
        return i + j;
    }

    @UserAnn(id = 9, description = "成员函数注解")
    public static void main(String[] args) {
        Class<msg> s = msg.class;
        System.out.println("类名称:" + s.getName());
        Method[] methods = s.getMethods();
        for (Method c : methods) {
            System.out.print("方法" + c.getName() + ",");
        }
        System.out.print("\n");
        Constructor constructor[] = s.getConstructors();
        for (Constructor c : constructor) {
            System.out.print("构造函数" + c.getName() + ",");
        }
        System.out.print("\n");
        Field[] field = s.getFields();
        for (Field c : field) {
            System.out.print("成员变量" + c.getName());
        }

        System.out.print("\n");
        Annotation annotation[] = s.getAnnotations();
        for (Annotation c : annotation) {
            UserAnn userAnn = (UserAnn) c;
            System.out.print(userAnn.description() + " " + userAnn.id());
        }
        System.out.print("\n");
        for (Method msg : methods) {
            boolean isAnn = msg.isAnnotationPresent(UserAnn.class);
            if (isAnn) {
                UserAnn userAnn = msg.getAnnotation(UserAnn.class);
                System.out.print(userAnn.description() + userAnn.id() + ",");
            }
        }
        System.out.print("\n");
        for (Constructor con : constructor) {
            boolean isAnn = con.isAnnotationPresent(UserAnn.class);
            if (isAnn) {
                UserAnn userAnn = (UserAnn) con.getAnnotation(UserAnn.class);
                System.out.print(userAnn.description() + userAnn.id() + ",");
            }
        }
        System.out.print("\n");
        for (Field f : field) {
            boolean isAnn = f.isAnnotationPresent(UserAnn.class);
            if (isAnn) {
                UserAnn userAnn = f.getAnnotation(UserAnn.class);
                System.out.print(userAnn.description() + userAnn.id() + ",");
            }
        }
        System.out.print("\n");

        msg msg = new msg();
        try {
            Method mm = s.getMethod("getI");
            try {
                System.out.print(mm.invoke(msg));  //通过方法的反射操作,用方法的对象调用函数
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        System.out.print("\n");
        ArrayList<String> list1 = new ArrayList<>();
        ArrayList list2 = new ArrayList();
        System.out.print(list1.getClass() == list2.getClass());
        Class class1 = list1.getClass();
        System.out.print("\n");
        try {
            Method method1 = class1.getMethod("add", Object.class);
            method1.invoke(list1, 1);
        } catch (Exception E) {
            E.printStackTrace();
        }
        System.out.println(list1.size());
        // System.out.println(list1.get(0));//如果取出通过反射放入的int值,会导致错误


    }
}

上面需要简单介绍:

通过方法对象,来调用方法,首先是类类型通过方法名获取方法对象,然后方法对象调用invoke调用方法,其实和通过类对象调用方法是一样的。

然后是泛型在容器中,利用反射可以动态的绕过编译,往容器中写入不同类型的值,上面的list1 == list2返回时true,说明编译之后集合的泛型是去泛型化的,JAVA中集合泛型为了防止错误输入,只是在编译阶段有效,绕过编译就无效了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值