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() | 只需要类的全限定名 |
类声明时会包含一个或多能够影响他运行时行为的修饰符:
不是所有的修饰符都可以应用到所有的类上,比如,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()
下面这些例子是在用注解是会遇到到的一些常见问题:
调用方法的时候会检查参数类型比做类型转换,如下示例:
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)