背景
反射是java的一大特性,所有的java框架或多或少都会使用到反射。Class类型是java反射中最核心的概念之一,下面阐述一下。
Class原理
在java的世界,所有内容皆是对象,这样能有一些特殊的操作(对比c语言)。其中根本的一个概念就是Class(类类型)。类也有自己的类型。
实战class获取
类型的获取有三种方式:
- MyDemo.class,通过类直接获取
- new MyDemo().getClass(),通过对象获取
- Class.forname(String),通过string来获取
只有这三种方法,而且也全部覆盖了所有的需求。细想一下,我们如果知道类的话,可以直接通过类来获取。如果在运行时,我们获得了一个对象,而我们又不知道它的类,我们同样可以通过对象获取。再有就是最终极的方案,只要我们知道类的全路径,就可以去获取,Class.forname,不过这种获取方法,需要捕获异常,毕竟我们传的全路径参数可能是不对的嘛。。。
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(MyDemo.class);
System.out.println(new MyDemo().getClass());
System.out.println(Class.forName("MyDemo").toString());
}
}
输出如下:
class MyDemo
class MyDemo
class MyDemo
三种方法获得的都一样
Class应用
获取到class对象,大家都知道么个对象里面都应该用属性、方法,那么这个Class对象的属性和方法都是来干嘛的?答案是,用来获取一个类的信息。
获取
package classdemo;
public class Main {
public Main() {
class MainDefinedClass {
}
}
public static void main(String[] args) throws NoSuchMethodException {
Class demoClass = MyDemo.class;
// 获取类的注解
System.out.println(demoClass.getAnnotation(MyAnnotationDemo.class));
// 获取类的所有注解,包括父类的
System.out.println(demoClass.getAnnotations().toString());
// 获取按照java规范的输出格式
System.out.println(demoClass.getCanonicalName());
// getClasses得到该类及其父类所有的public的内部类
System.out.println(demoClass.getClasses());
// getDeclaredClasses得到该类所有的内部类,除去父类的
System.out.println(demoClass.getDeclaredClasses());
// 获取类加载器
System.out.println(demoClass.getClassLoader());
// 返回数组类型中的元素的类型(说白了,数组类型是另外一个类型了,通过个class和getClass获取的是整体的类型,如果只想获取元素类型,那就用下面的)
System.out.println(new Integer[]{1, 2}.getClass().getComponentType());
// 根据参数列表获取构造函数(如果没有就抛出异常)
System.out.println(demoClass.getConstructor());
// 获取所有构造函数
System.out.println(demoClass.getConstructors());
// 获取所有public的构造函数
System.out.println(demoClass.getDeclaredConstructors());
// 获取所有注解,不包括父类的
System.out.println(demoClass.getDeclaredAnnotations());
// 获取所有方法,不包括父类的
System.out.println(demoClass.getDeclaredMethods().length);
// 获取所有属性,不包括父类的(去掉Declare就可以同时获取父类的属性了)
System.out.println(demoClass.getDeclaredFields());
// 获取内部类的声明出处的类
System.out.println(demoClass.getDeclaringClass());
System.out.println(MyDeclaringDemo.class.getDeclaringClass());
// 该类是在那个类中定义的, 比如直接定义的内部类或匿名内部类
System.out.println(demoClass.getEnclosingClass());
System.out.println(MyDeclaringDemo.class.getEnclosingClass());
// 该类是在哪个构造函数中定义的,比如构造方法中定义的匿名内部类
new MyDeclaringDemo();
// 该类是在哪个方法中定义的,比如方法中定义的匿名内部类
System.out.println(MyDeclaringDemo.class.getEnclosingMethod());
// 获取所有实现的接口的类型(即Type,这个类包含泛型信息)
System.out.println(demoClass.getGenericInterfaces().length);
System.out.println(MyDeclaringDemo.class.getGenericInterfaces().length);
// 获取所有实现的接口类(即Class类,这个类不含有泛型信息)
System.out.println(demoClass.getInterfaces().length);
// 获取包名
System.out.println(demoClass.getPackage());
// 获取方法列表
System.out.println(demoClass.getMethods().length);
// 返回修饰符,是一个int的值,这个int的定义在java.lang.reflect.Modifier中定义
System.out.println(demoClass.getModifiers());
// 暂时不太知道。。。
System.out.println(demoClass.getProtectionDomain());
//getResource是java.lang.Class的方法,也就是由字节码对象调用。
//getResource接受一个字符串参数,如果以”/”开头,就在classpath根目录下找(不会递归查找子目录),如果不以”/”开头,就在调用getResource的字节码对象所在目录下找(同样不会递归查找子目录)
//————————————————
//版权声明:本文为CSDN博主「crazyboy12138」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
//原文链接:https://blog.csdn.net/crazyboy12138/article/details/80717271
System.out.println(demoClass.getResource("/"));
System.out.println(demoClass.getSuperclass());
// 此类的标记,若无标记则返回 null。特别地,如果此对象表示一个基本类型或 void,则此方法返回 null。
System.out.println(demoClass.getSigners());
System.out.println(demoClass.getTypeParameters().length);
System.out.println(demoClass.getAnnotatedInterfaces().length);
// 获取类的全路径名
System.out.println(demoClass.getTypeName());
// 根据注解名称获取注解类
System.out.println(demoClass.getAnnotationsByType(MyAnnotationDemo.class).length);
}
public static class MyDeclaringDemo implements MyInterfaceDemo {
public MyDeclaringDemo() {
class DefinedClass {
}
// 获取该类是在哪个构造方法定义的
System.out.println(new DefinedClass().getClass().getEnclosingConstructor());
}
}
}
通过class能获取的信息非常立体。
- 我是谁
- 我是哪来的
- 我有什么东东
- 我能干啥
- 等等
判断
package classdemo;
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(MyDemo.class);
System.out.println(new MyDemo().getClass());
System.out.println(Class.forName("classdemo.MyDemo").toString());
Class demoClass = MyDemo.class;
// 是否是注解类
System.out.println(demoClass.isAnnotation());
// 是否存在一个注解
System.out.println(demoClass.isAnnotationPresent(MyAnnotationDemo.class));
// 是否为匿名类
System.out.println(demoClass.isAnonymousClass());
// 是否为匿名类
System.out.println(new MyInterfaceDemo() {}.getClass().isAnonymousClass());
// 是否为某类的父类
System.out.println(demoClass.isAssignableFrom(Main.class));
// 是否为某类的子类
System.out.println(demoClass.isInstance(MyDemo.class));
// 是否为成员类
System.out.println(demoClass.isMemberClass());
// 是否为成员类(在某个类中,再定义的类)
System.out.println(InnerClass.class.isMemberClass());
class LocalClass {
}
// 是否为局部类(局部类:在一个局部方法中定义的类)
System.out.println(LocalClass.class.isLocalClass());
// 是否为原始类型boolean、char、byte、short、int、long、float、double
System.out.println(demoClass.isPrimitive());
// 是否为java编译器引入的类(这里还不是太理解。。。。??之后看机会吧)
System.out.println(demoClass.isSynthetic());
}
public class InnerClass {
}
}
能对没个类进行全方位的属性判断,设置包括局部类、内部类、动态类(动态加载)。
其中有一个是否为java编译器引入的判断,现在还不太明白。。。
根据class创建对象
Class demoClass = MyDemo.class;
Object object = demoClass.newInstance();
// 通过class反射获得一个对象
System.out.println(object.getClass());
这种创建对象的方法是反射的灵魂所在。试想一下,在new一个对象之前,并不一定知道这个类是什么,很灵活吧。
总结
单单通过自己的demo还不够,应该从更多框架中加深对Class反射的理解。