原文链接:https://www.codemore.top/cates/Backend/post/2018-04-26/reflect-class
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
函数。
如果可以获取到一个类的实例,最简单获取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.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
相同。
获取原生类型的Class
既可以通过.class
方式,也可以通过原生类型的包装类的TYPE字段,对于void,java.lang.Void
同样提供了这样的一个字段。
Class c = Double.TYPE;
Double.TYPE 就相当于double.class
Class c = Void.TYPE;
等同于void.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() | 只需要类的全限定名 |
类声明时会包含一个或多能够影响他运行时行为的修饰符:
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()
下面这些例子是在用注解是会遇到到的一些常见问题:
调用方法的时候会检查参数类型比做类型转换,如下示例:
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();
如果通过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)
如对本文有疑问, 点击进行留言回复!!
网友评论