类
Java 中每个类型要么是引用类型, 要么是原生类型. 类, 枚举, 数组 (他们都继承于 java.lang.Object) 和接口都是引用类型. 例如: java.lang.String, 所有原生类型的包装类 java.lang.Double, 接口
java.io.Serializable
和枚举
javax.swing.Sortorder
, 都是引用类型. 原生类型的数量是固定的: boolean,byte,short,int,long,char,float,double. 对于每个对象类型, JVM 都会为其初始化一个 java.lang.Class 的实例, 可以检查包括属性和方法在内的对象运行时的属性. Class 同样也可以创建一个新的类和对象. 最重要的是, 他是所有反射 API 的入口.
获取类对象
java.lang.Class 是所有反射操作的的入口. java.lang.reflect 中的所有类都没有公共的构造函数, 所以为了实例化其中的类, 需要通过调用合适的 Class 函数.
Object.getClass()
如果可以获取到一个类的实例, 最简单获取 Class 的方法就是调用 Object.getClass(). 当然这个只适应于全部都继承与 Object 的引用类型. 例如: 返回 String 的 Class
Class c = "foo".getClass();
System.console() 返回的是一个 java.io.Console 类, 所以如下代码然会了 Console 的 Class
Class c = System.console().getClass();
对于枚举类型, E,A 是 E 的实例, 所以 A.getClass() 返回的是 E 的 Class 例如:
- Enum E {A,B}
- Class c = A.getClass();
因为数组也是对象, 所以也可以通过调用其 getClass()获取 Class, 返回的 Class 带有数组的类型信息.
- byte[] bytes = new byte[1024];
- Class c = bytes.getClass();
对于集合类, 同样 s.getClass() 返回的是与 java.util.HashSet 相关的 Class
- import java.util.Set
- import java.util.HashSet
- Set<String> s = new HashSet<String>();
- Class c = s.getClass();
.class 语法
如果类型可用, 但是没有实例, 可以通过. class 获取 Class. 同样, 也可以使用这个方法获取原生类型的 Class.
- boolean b;
- Class = b.getClass(); // 编译错误
- Class = boolean.class; // 正确
注意直接使用 boolean.getClass()会发生编译错误, 因为 boolean 是一个原生类型, 而且不可以被反引用.
Class c = java.io.PrintStream.class;
获取
java.io.PrintStream
相关的 Class
Class c = int[][].class;
获取二位 int 数组相关的 Class
Class.forName()
使用这个方法需要知道类的全限定名. 只能应用于引用类型. 数组类型的全限定名可以由 Class.getName()获取.
Class c = Class.forName("com.duke.MyLocaleServiceProvider");
通过使用全限定名创建 class
- Class cDoubleArray = Class.forName("[D");
- Class cStringArray = Class.forName("[[Ljava.lang.String;");
cDoubleArray 是 double[]的 Class 同 double[].class 相同. cStringArray 则与 String[][].class 相同.
原生类型包装类的 TYPE 字段
获取原生类型的 Class 既可以通过. class 方式, 也可以通过原生类型的包装类的 TYPE 字段, 对于 void,java.lang.Void 同样提供了这样的一个字段.
Class c = Double.TYPE;
Double.TYPE 就相当于 double.class
Class c = Void.TYPE;
等同于 void.class
返回 Class 的方法
如果已经得到了 Class, 那么可以使用 Class 中的 API 来获取其他的类
Class.getSuperClass()
返回指定类的父类
Class c = javax.swing.JButton.class.getSupperClass();
返回其父类
javax.swing.AbstractButton
Class.getClasses() 获取所有内部所有 public 类, 接口, 枚举, 包括本身和继承的成员类.
Class<?>[] c = Character.class.getClasses();
返回 Character 的两个成员类 Character.Subset 和
- Character.UnicodeBlock
- Class.getDeclearedClasses()
返回类中所有的类, 接口, 枚举.
Class<?>[] c Character.class.getDeclearedClasses();
返回 Character 的两个 public 类 Character.Subset 和
Character.UnicodeBlock
和一个 private 类
- Character.CharacterCache
- .
- Class.getDeclearingClass()
- ,
- java.lang.reflect.Field.getDeclearingClass()
- ,
- java.lang.reflect.Method.getDeclearingClass()
- ,
- java.lang.reflect.Constructor.getDeclaringClass()
返回声明这些成员的类返回声明这些成员的类. 匿名类不会有声明类, 有一个外围类(enclosing class).
- import java.lang.reflect.Field;
- Field f = System.class.getField("out");
- Class c = f.getDeclaringClass();
out 是在 System 中声明的.
- public class MyClass{
- static Object o = new Object() {
- public void m(){}
- };
- static Class<c> = o.getClass().getEnclosingClass();
- }
- Class.getEnclosingClass()
返回其外围类
Class c = Thread.State.class.getEnclosingClass();
Thread.State 的外围类 Thread
- public class MyClass {
- static Object o = new Object() {
- public void m() {}
- };
- static Class<c> = o.getClass().getEnclosingClass();
- }
匿名类的外围类是 MyClass.
获取 Class 主要三种方法对比:
方式 | 使用范围 |
---|---|
getClass() | 需要获取对象实例,仅能用于引用类型 |
.class | 无需获取对象实例,既可以是引用类型也可以是原生类型 |
forName() | 只需要类的全限定名 |
检查类修饰符和类型
类声明时会包含一个或多能够影响他运行时行为的修饰符:
访问修饰符: public,private,protected
抽象修饰符: abstract
静态修饰符: static
不可变修饰符: final
强制严格浮点修饰符: strictfp
注解 不是所有的修饰符都可以应用到所有的类上, 比如, interface 不能使用 final,enum 不能使用 abstract.
java.lang.reflect.Modifier
包含了所有的修饰符声明. 方法
Class.getModifiers()
可以返回所有的修饰符. 下面这个例子演示了如果获取一个类的修饰符, 泛型参数, 实现接口, 和集成链. 因为 Class 继承了
java.lang.reflect.AnnotatedElement
所以可以在运行时获取注解信息.
- import java.lang.annotation.Annotation;
- import java.lang.reflect.Modifier;
- import java.lang.reflect.TypeVariable;
- import java.util.ArrayList;
- import java.util.List;
- import static java.lang.System.out;
- public class ClassDeclarationSpy {
- public static void main(String[] args) {
- try{
- Class<?> c = Class.forName(args[0]);
- out.format("Class:%n %s%n%n", c.getCanonicalName());
- out.format("Modifier: %n %s%n%n", Modifier.toString(c.getModifiers()));
- out.format("Type Parameters:%n");
- TypeVariable[] tv = c.getTypeParameters();
- if (tv.length != 0){
- out.format(" ");
- for (TypeVariable t : tv)
- out.format("%s", t.getName());
- out.format("%n%n");
- }else{
- out.format("-- No TypeParameter --%n%n");
- }
- out.format("Inheritance Path:%n");
- List<Class> l = new ArrayList<>();
- printAncestor(c, l);
- if (l.size() != 0) {
- for (Class<?> cl : l)
- out.format("%s%n", cl.getCanonicalName());
- out.format("%n");
- } else {
- out.format("-- No Super Classes --%n%n");
- }
- out.format("Annotations:%n");
- Annotation[] ann = c.getAnnotations();
- if (ann.length != 0) {
- for (Annotation a : ann)
- out.format("%s%n", a.toString());
- out.format("%n");
- } else {
- out.format("-- No Annotations --%n%n");
- }
- }catch (Exception e){
- e.printStackTrace();
- }
- }
- private static void printAncestor(Class<?> c, List<Class> l){
- Class<?> ancestor =c.getSuperclass();
- if (ancestor != null){
- l.add(ancestor);
- printAncestor(ancestor, l);
- }
- }
- }
运行:
$ java ClassDeclarationSpy java.util.concurrent.ConcurrentNavigableMap
- Class:
- java.util.concurrent.ConcurrentNavigableMap
- Modifiers:
- public abstract interface
- Type Parameters:
- K V
- Implemented Interfaces:
- java.util.concurrent.ConcurrentMap<K, V>
- java.util.NavigableMap<K, V>
- Inheritance Path:
- -- No Super Classes --
- Annotations:
- -- No Annotations --
- java.util.concrrent.ConcurrentNavigableMap
的源码为:
- public interface ConcurrentNavigableMap<K,V>
- extends ConcurrentMap<K,V>, NavigableMap<K,V>
注意, 以为他是接口, 编译器默认对所有接口加 abstract. 有两个泛型 K,V. 运行:
- $ java ClassDeclarationSpy "[Ljava.lang.String;"
- Class:
- java.lang.String[]
- Modifiers:
- public abstract final
- Type Parameters:
- -- No Type Parameters --
- Implemented Interfaces:
- interface java.lang.Cloneable
- interface java.io.Serializable
- Inheritance Path:
- java.lang.Object
- Annotations:
- -- No Annotations --]
获取类成员
Class 中有两种获取 field,method,constructor 的方式, 一种是获取全部成员, 一种是获取指定成员. 同样获取成员是也分为从当前类查找和从继承链中查找. 总结如下: Field 方法
API | 列出所有成员 | 列出继承成员 | 私有成员 |
---|---|---|---|
getDeclaredField() | n | n | y |
getField() | n | y | n |
getDeclaredFields() | y | n | y |
getFields() | y | y | n |
Method 方法
API | 列出所有成员 | 列出继承成员 | 私有成员 |
---|---|---|---|
getDeclaredMethod() | n | n | y |
getMethod() | n | y | n |
getDeclaredMethods() | y | n | y |
getMethods() | y | y | n |
constructor 方法:
API | 列出所有成员 | 列出继承成员 | 私有成员 |
---|---|---|---|
getDeclaredConstructor() | n | n | y |
getConstructor() | n | n | n |
getDeclaredConstructors() | y | n | y |
getConstructors() | y | n | n |
构造函数并不继承.
示例如下:
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- import java.lang.reflect.Member;
- import static java.lang.System.out;
- enum ClassMember { CONSTRUCTOR, FIELD, METHOD, CLASS, ALL }
- public class ClassSpy {
- public static void main(String... args) {
- try {
- Class<?> c = Class.forName(args[0]);
- out.format("Class:%n %s%n%n", c.getCanonicalName());
- Package p = c.getPackage();
- out.format("Package:%n %s%n%n",
- (p != null ? p.getName() : "-- No Package --"));
- for (int i = 1; i <args.length; i++) {
- switch (ClassMember.valueOf(args[i])) {
- case CONSTRUCTOR:
- printMembers(c.getConstructors(), "Constructor");
- break;
- case FIELD:
- printMembers(c.getFields(), "Fields");
- break;
- case METHOD:
- printMembers(c.getMethods(), "Methods");
- break;
- case CLASS:
- printClasses(c);
- break;
- case ALL:
- printMembers(c.getConstructors(), "Constuctors");
- printMembers(c.getFields(), "Fields");
- printMembers(c.getMethods(), "Methods");
- printClasses(c);
- break;
- default:
- assert false;
- }
- }
- // production code should handle these exceptions more gracefully
- } catch (ClassNotFoundException x) {
- x.printStackTrace();
- }
- }
- private static void printMembers(Member[] mbrs, String s) {
- out.format("%s:%n", s);
- for (Member mbr : mbrs) {
- if (mbr instanceof Field)
- out.format("%s%n", ((Field)mbr).toGenericString());
- else if (mbr instanceof Constructor)
- out.format("%s%n", ((Constructor)mbr).toGenericString());
- else if (mbr instanceof Method)
- out.format("%s%n", ((Method)mbr).toGenericString());
- }
- if (mbrs.length == 0)
- out.format("-- No %s --%n", s);
- out.format("%n");
- }
- private static void printClasses(Class<?> c) {
- out.format("Classes:%n");
- Class<?>[] clss = c.getClasses();
- for (Class<?> cls : clss)
- out.format("%s%n", cls.getCanonicalName());
- if (clss.length == 0)
- out.format("-- No member interfaces, classes, or enums --%n");
- out.format("%n");
- }
- }
使用如下:
java ClassSpy ClassMember FIELD METHOD
- Class:
- ClassMember
- Package:
- -- No Package --
- Fields:
- public static final ClassMember ClassMember.CONSTRUCTOR
- public static final ClassMember ClassMember.FIELD
- public static final ClassMember ClassMember.METHOD
- public static final ClassMember ClassMember.CLASS
- public static final ClassMember ClassMember.ALL
- Methods:
- public static ClassMember ClassMember.valueOf(java.lang.String)
- public static ClassMember[] ClassMember.values()
- public final int java.lang.Enum.hashCode()
- public final int java.lang.Enum.compareTo(E)
- public int java.lang.Enum.compareTo(java.lang.Object)
- public final java.lang.String java.lang.Enum.name()
- public final boolean java.lang.Enum.equals(java.lang.Object)
- public java.lang.String java.lang.Enum.toString()
- public static <T> T java.lang.Enum.valueOf
- (java.lang.Class<T>,java.lang.String)
- public final java.lang.Class<E> java.lang.Enum.getDeclaringClass()
- public final int java.lang.Enum.ordinal()
- public final native java.lang.Class<?> java.lang.Object.getClass()
- public final native void java.lang.Object.wait(long) throws
- java.lang.InterruptedException
- public final void java.lang.Object.wait(long,int) throws
- java.lang.InterruptedException
- public final void java.lang.Object.wait() hrows java.lang.InterruptedException
- public final native void java.lang.Object.notify()
- public final native void java.lang.Object.notifyAll()
故障排除
下面这些例子是在用注解是会遇到到的一些常见问题:
Compiler Warning: "Note: ... uses unchecked or unsafe operations"
调用方法的时候会检查参数类型比做类型转换, 如下示例:
- import java.lang.reflect.Method;
- public class ClassWarning {
- void m() {
- try {
- Class c = ClassWarning.class;
- Method m = c.getMethod("m"); // warning
- // production code should handle this exception more gracefully
- } catch (NoSuchMethodException x) {
- x.printStackTrace();
- }
- }
- }
运行 结果:
javac ClassWarning.java
Note: ClassWarning.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
$ javac -Xlint:unchecked ClassWarning.java
- ClassWarning.java:6: warning: [unchecked] unchecked call to getMethod
- (String,Class<?>...) as a member of the raw type Class
- Method m = c.getMethod("m"); // warning
- ^
- 1 warning
可以通过使用泛型或者使用注解
@SuppressWarning("unchecked")
解决:
- Class<?> c = warn.getClass();
- Class c = ClassWarning.class;
- @SupressWarning("unchecked")
- Method m = c.getMethod();
- InstantiationException when the Constructor is Not Accessible
如果通过 Class.newInstance()创建类, 但是没有 0 参数的构造器, 会抛出 InstantiationException 异常. 例如:
- class Cls {
- private Cls() {}
- }
- public class ClassTrouble {
- public static void main(String... args) {
- try {
- Class<?> c = Class.forName("Cls");
- c.newInstance(); // InstantiationException
- // production code should handle these exceptions more gracefully
- } catch (InstantiationException x) {
- x.printStackTrace();
- } catch (IllegalAccessException x) {
- x.printStackTrace();
- } catch (ClassNotFoundException x) {
- x.printStackTrace();
- }
- }
- }
运行结果:
java ClassTrouble
java.lang.IllegalAccessException: Class ClassTrouble can not access a member of
- class Cls with modifiers "private"
- at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
- at java.lang.Class.newInstance0(Class.java:349)
- at java.lang.Class.newInstance(Class.java:308)
- at ClassTrouble.main(ClassTrouble.java:9)
来源: https://juejin.im/entry/5ae5c08cf265da0b80708234