本人翻譯并整理自O(shè)racle官方文檔
Java的反射機(jī)制允許在程序運(yùn)行時(shí)去獲取一個(gè)類(class)
的成員變量和方法藕赞。
一杆融、Classes
每個(gè)對(duì)象不是引用類型就是基本類型。引用類型全部繼承自java.lang.Object
亿扁。類如蚜、枚舉類型、數(shù)組和接口都是引用類型挡逼。還有一套固定的基本類型:boolean
, byte
, short
, int
, long
, char
, float
和 double
括改。
對(duì)于每種類型的對(duì)象,Java虛擬機(jī)會(huì)實(shí)例化出一個(gè)不可變的java.lang.Class
對(duì)象的實(shí)例家坎,它提供了一些方法去檢查這個(gè)對(duì)象的運(yùn)行時(shí)屬性包括它的成員和類型信息嘱能。Class類同時(shí)也提供了創(chuàng)造新的類和對(duì)象的能力吝梅。最重要的是它是所有反射API(Reflection APIs)的出發(fā)點(diǎn)。
1.1 獲得Class對(duì)象-反射操作的入口
所有反射操作的出發(fā)點(diǎn)是java.lang.Class
惹骂。因?yàn)橛?code>java.lang.reflect.ReflectPermission這個(gè)異常苏携, java.lang.reflect
下的類都沒有公共的構(gòu)造函數(shù)。為了獲得這些類必須調(diào)用Class里的合適方法对粪。這里有若干種方法來獲得一個(gè)Class對(duì)象右冻,取決于代碼是否能訪問某個(gè)對(duì)象,是否知道這個(gè)類的名稱著拭,類型(TYPE)纱扭,或者一個(gè)已有的Class對(duì)象。
1.1.1 Object.getClass()
如果一個(gè)對(duì)象的實(shí)例是可以獲得的儡遮,那么最簡(jiǎn)單的獲得它的Class是調(diào)用Object.getClass()
跪但。當(dāng)然它只在這個(gè)實(shí)例是繼承自O(shè)bject才可行。下面是一些例子:
Class c = "foo".getClass();
byte[] bytes = new byte[1024];
Class c = bytes.getClass();
1.1.2 .class 語法
如果類型是可以獲得的但是沒有對(duì)象的實(shí)例峦萎,那么可以通過在type后追加".class"來獲得它的Class屡久。這也是最簡(jiǎn)單的方法來獲得一個(gè)基本類型的Class。
boolean b;
Class c = b.getClass(); // 編譯錯(cuò)誤
Class c = boolean.class; // 正確
Class c = int[][][].class;
1.1.3 Class.forName()
如果一個(gè)類的 完全限定名(fully-qualified) 可以獲得爱榔,可以通過靜態(tài)方法Class.forName()
來獲得相應(yīng)的Class被环。不能被用于基本類型。使用這種方法獲得某個(gè)數(shù)組類的語法是Class.getName()
详幽。這個(gè)語法適用于引用和原始類型筛欢。
Class c = Class.forName("com.duke.MyLocaleServiceProvider");
Class cDoubleArray = Class.forName("[D"); // 與double[].class作用相同
1.1.4 基本類型包裝類的TYPE成員變量
除了.class語法外還有一種方式可以獲得基本類型的Class。每種基本類型和void都有一個(gè)包裝類在java.lang
被用來基本類型裝箱成引用類型唇聘。每個(gè)包裝類包含一個(gè)成員變量TYPE相當(dāng)于Class對(duì)基本類型的包裝版姑。
Class c = Double.TYPE;
Class c = Void.TYPE;
1.1.5 方法返回的Class
這里有幾個(gè)Reflection APIs返回Class對(duì)象但是需要某個(gè)Class已經(jīng)被直接或間接的獲得。
Class.getSuperclass() // 返回某個(gè)Class的父類Class
Class.getClasses() // 返回某個(gè)類的所有成員的Class對(duì)象
Class<?>[] c = Character.class.getClasses(); // Character包含2個(gè)成員對(duì)象Character.Subset 和Character.UnicodeBlock
1.2 訪問Class的聲明信息
某個(gè)類可能在被聲明時(shí)使用一個(gè)或多個(gè)影響運(yùn)行行為的修飾符迟郎。
- 訪問修飾符:
public
,protected
和private
- 要求重寫的修飾符:
abstract
- 限制單實(shí)例的修飾符:
static
- 禁止修改值的修飾符:
final
- 強(qiáng)制嚴(yán)格浮點(diǎn)行為的修飾符:
strictfp
- 注解
不是所有的修飾符可以用在所有的類上剥险,例如一個(gè)接口不能被聲明為final
,一個(gè)枚舉類不能是abstract
宪肖。
java.lang.reflect.Modifier
包含了所有可能的修飾符的聲明表制。Class.getModifiers()
方法返回了某個(gè)類的修飾符的集合。
下面這個(gè)例子展示了如何獲得一個(gè)類的聲明組件控乾,包括修飾符么介,泛型類型參數(shù),實(shí)現(xiàn)的接口和集成路徑蜕衡。如果Class實(shí)現(xiàn)了java.lang.reflect.AnnotatedElement
接口壤短,那么也能查詢到運(yùn)行時(shí)注解。
import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
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]); // 根據(jù)輸入的類名通過反射獲得其Class
out.format("Class:%n %s%n%n", c.getCanonicalName()); // 獲得完全限定名
out.format("Modifiers:%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()); // 輸出類型參數(shù)
out.format("%n%n");
} else {
out.format(" -- No Type Parameters --%n%n");
}
out.format("Implemented Interfaces:%n");
Type[] intfs = c.getGenericInterfaces();
if (intfs.length != 0) {
for (Type intf : intfs)
out.format(" %s%n", intf.toString()); // 輸出實(shí)現(xiàn)的接口
out.format("%n");
} else {
out.format(" -- No Implemented Interfaces --%n%n");
}
out.format("Inheritance Path:%n");
List<Class> l = new ArrayList<Class>();
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");
}
// 生產(chǎn)環(huán)境的代碼應(yīng)該更優(yōu)雅的處理這個(gè)異常
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
private static void printAncestor(Class<?> c, List<Class> l) {
Class<?> ancestor = c.getSuperclass(); // 獲得其父類
if (ancestor != null) {
l.add(ancestor);
printAncestor(ancestor, l);
}
}
}
下面看看使用上面程序的實(shí)例:
$ 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 --
這是一個(gè)真實(shí)的類java.util.concurrent.ConcurrentNavigableMap
,它的源代碼是:
public interface ConcurrentNavigableMap<K,V>
extends ConcurrentMap<K,V>, NavigableMap<K,V>
注意久脯,既然這是一個(gè)接口蒜绽,那么它隱式的為abstract
,編譯器會(huì)為每個(gè)接口添加這個(gè)修飾符桶现。