簡介
Class類表示正在運(yùn)行的Java應(yīng)用程序中的類和接口掘鄙,枚舉和基本數(shù)據(jù)類型院究,我們可以從中獲取到類的一切相關(guān)信息洽瞬,包括字段,方法业汰,名稱伙窃,父類,接口等
常用方法介紹
名稱獲取
此類方法用于獲取類的名稱信息
getName()方法
- 如果是一個(gè)實(shí)體類样漆,則會(huì)返回完整包名路徑名稱,
例如位于com.hj.testclass
包下的student
類为障,則會(huì)返回com.hj.testclass.student
- 如果是一個(gè)數(shù)組類型,則返回內(nèi)部嵌套深度的一個(gè)或多個(gè)"["字符,后面拼接上基本數(shù)據(jù)類型的二進(jìn)制名稱,二進(jìn)制名稱表如下:
Element Type | Encoding |
---|---|
boolean | Z |
byte | B |
char | C |
class or interface | Lclassname |
double | D |
float | F |
int | I |
long | J |
short | S |
示例:
(new long[1][2][3]).getClass().getName()
輸出:
[[[J
- 如果是基本數(shù)據(jù)類型鳍怨,則會(huì)返回?cái)?shù)據(jù)類型的關(guān)鍵字
byte.class.getName()
輸出:
byte
getSimpleName()方法
返回源代碼中給出的基礎(chǔ)類的簡單名稱呻右。 如果基礎(chǔ)類是匿名的,則返回一個(gè)空字符串鞋喇。
Student student = new Student();
Class mClass = student.getClass();
log("getSimpleName:"+mClass.getSimpleName());
輸出:
Student
getPackage()方法
返回包名信息
注解相關(guān)
可獲取此類是否是注解声滥,是否包含某注解并獲取到其對象,獲取全部注解
方法 | 作用 |
---|---|
getAnnotation(Class<A> annotationClass) | 獲取傳入的注解對象侦香,如果不存在落塑,則返回null |
getAnnotations() | 返回此類上的所有注解 |
getAnnotationsByType(Class<A> annotationClass) | 返回傳入的注解對象數(shù)組,與getAnnotation()的區(qū)別是檢測傳入的注解是否是重復(fù)元素 |
isAnnotation() | 判斷這個(gè)類是否是一個(gè)注解類 |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | 是否包含傳入的注解類,效果與getAnnotation()!=null相同 |
舉個(gè)栗子:
新建一個(gè)注解對象:
/**
* Created by hj on 2019/1/10.
* 說明:
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
新建一個(gè)Student并加入@MyAnnotation
注解:
/**
* Created by hj on 2019/1/10.
* 說明:
*/
@MyAnnotation
public class Student{
public static void main(String[] args) {
Student student = new Student();
Class studentClass = student.getClass();
boolean isAnnotation = studentClass.isAnnotation();
log(studentClass.getSimpleName()+"是否是注解類:"+isAnnotation);
boolean isContainAnnotation = studentClass.isAnnotationPresent(MyAnnotation.class);
log(studentClass.getSimpleName()+"是否包含MyAnnotation注解類:"+isContainAnnotation);
Annotation annotation = studentClass.getAnnotation(MyAnnotation.class);
Annotation[] annotations = studentClass.getAnnotations();
if (annotation != null) {
log("獲取指定的MyAnnotation類:"+annotation.toString());
}
if (annotations.length > 0){
log("獲取注解集合中的第一個(gè)元素:"+annotations[0].toString());
}
}
private static void log(String value) {
System.out.print(value);
}
}
打庸藓:
Student是否是注解類:false
Student是否包含MyAnnotation注解類:true
獲取指定的MyAnnotation類:@jie.com.imageoptimize.mclass.MyAnnotation()
獲取注解集合中的第一個(gè)元素:@jie.com.imageoptimize.mclass.MyAnnotation()
構(gòu)造方法相關(guān)
在介紹構(gòu)造方法之前憾赁,先介紹一個(gè)類Constructor
,它的作用是提供一個(gè)類的單個(gè)構(gòu)造方法的信息訪問,如果一個(gè)類有兩個(gè)構(gòu)造方法伴逸,那么這個(gè)類就會(huì)對應(yīng)有兩個(gè)Constructor
類缠沈,它可以使用newInstance
方法來進(jìn)行類的構(gòu)造方法實(shí)現(xiàn)并進(jìn)行擴(kuò)展,但如果發(fā)生縮小轉(zhuǎn)換則會(huì)拋出IllegalArgumentException
異常错蝴,比如這個(gè)類有兩個(gè)構(gòu)造參數(shù)卻只傳入一個(gè)洲愤,就會(huì)拋異常。
獲取Constructor
信息的方法有:
方法 | 作用 |
---|---|
getConstructors() | 返回這個(gè)類的公共構(gòu)造函數(shù)的 Constructor對象的數(shù)組 |
getConstructor(Class<?>..parameterTypes) | 傳入一個(gè)指定的參數(shù)類型來獲取特定的公共構(gòu)造方法類 |
getDeclaredConstructors() | 與getConstructors的區(qū)別是返回所有的構(gòu)造方法數(shù)組顷锰,不限于public protected private |
getDeclaredConstructor(Class<?>..parameterTypes) | 與getConstructor的區(qū)別是會(huì)返回所有類型的構(gòu)造方法 |
接下來介紹一下Constructor
類的newInstance
方法柬赐,這個(gè)方法說白了就是執(zhí)行構(gòu)造函數(shù)的,你傳入一個(gè)當(dāng)前構(gòu)造函數(shù)的類型的值進(jìn)去官紫,那么就會(huì)執(zhí)行這個(gè)類的構(gòu)造方法肛宋。
下面舉一個(gè)反射調(diào)用構(gòu)造方法的栗子:
新建一個(gè)Student類,分別添加兩個(gè)私有構(gòu)造方法束世,一個(gè)公有構(gòu)造方法酝陈,并添加兩個(gè)參數(shù),在構(gòu)造方法處執(zhí)行打印邏輯毁涉,代碼如下:
/**
* Created by hj on 2019/1/10.
* 說明:
*/
public class Student {
private String name;
private int age;
private Student() {
}
public Student(String name) {
this.name = name;
log("公有構(gòu)造方法執(zhí)行了沉帮,打印傳入的名稱為:"+name);
}
private Student(int age){
this.age = age;
log("私有構(gòu)造方法執(zhí)行了,打印傳入的年齡為:"+age);
}
public static void main(String[] args) {
Student student = new Student();
Class mClass = student.getClass();
Constructor[] constructors = mClass.getConstructors();
log("獲取" + mClass.getSimpleName() + "的公共構(gòu)造方法數(shù)量為:" + constructors.length);
try {
Constructor constructor = mClass.getConstructor(String.class);
constructor.newInstance("張三");
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
Constructor[] declaredConstructors = mClass.getDeclaredConstructors();
log("獲取" + mClass.getSimpleName() + "的所有構(gòu)造方法數(shù)量為:" + declaredConstructors.length);
try {
Constructor constructor = mClass.getDeclaredConstructor(int.class);
constructor.newInstance(18);
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
}
private static void log(String value) {
System.out.print(value);
}
}
打印結(jié)果如下:
獲取Student的公共構(gòu)造方法數(shù)量為:1
公有構(gòu)造方法執(zhí)行了贫堰,打印傳入的名稱為:張三
獲取Student的所有構(gòu)造方法數(shù)量為:3
私有構(gòu)造方法執(zhí)行了穆壕,打印傳入的年齡為:18
怎么樣,反射調(diào)用類的構(gòu)造方法技能get到了嗎
字段相關(guān)
此類型方法是用的最多的一種其屏,希望小伙伴們可以熟練掌握
在介紹字段方法之前喇勋,先介紹一個(gè)類Field
,它用于保存,修改字段的信息偎行,甚至可以修改字段的訪問權(quán)限,常用的方法如下:
方法 | 作用 |
---|---|
getName() | 獲取字段名稱 |
get() | 傳入需要獲取的值的類的對象川背,獲取該字段的值,返回object類型贰拿,使用的時(shí)候需要做類型判斷 |
getBoolean(),getInt()... | 獲取指定類型的字段值 |
set(),setBoolean()... | 將指定的類的指定值設(shè)置為新值 |
isAccessible() | 判斷此字段是否有訪問權(quán)限 |
setAccessible() | 設(shè)置字段的權(quán)限渗常,為true代表可以訪問 |
接下來再來看看如何使用Class來獲取字段信息:
方法 | 作用 |
---|---|
getFields() | 獲取所有的公共字段 |
getField() | 傳入字段的名稱壮不,返回公共字段對象 |
getDeclaredFields() | 獲取所有字段,返回一個(gè)Field數(shù)組 |
getDeclaredField | 傳入字段的名稱皱碘,返回字段對象,無訪問限制 |
下面舉個(gè)獲取字段名稱和內(nèi)容的例子询一,并分享一個(gè)常用的套路寫法,一般的都可以按照這個(gè)套路來寫:
/**
* Created by hj on 2019/1/10.
* 說明:
*/
public class Student {
public String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
Student student = new Student("張三", 18);
setAllComponentsName(student);
}
private static void setAllComponentsName(Object f) {
Field[] fields = f.getClass().getDeclaredFields();
for (Field field : fields) {
// 對于每個(gè)屬性癌椿,獲取屬性名
String varName = field.getName();
try {
// 獲取原來的訪問控制權(quán)限
boolean accessFlag = field.isAccessible();
// 修改訪問控制權(quán)限
field.setAccessible(true);
// 獲取在對象f中屬性fields[i]對應(yīng)的對象中的變量
Object o = field.get(f);
if (!"".equals(varName)) {
//這里可以處理相關(guān)邏輯
if (o != null) {
if (o instanceof String) {
String value = (String) o;
log("String類型字段:" + value);
} else if (o instanceof Integer) {
int value = (int) o;
log("int類型字段:" + value);
}
}
}
// 恢復(fù)訪問控制權(quán)限
field.setAccessible(accessFlag);
} catch (IllegalArgumentException | IllegalAccessException ex) {
ex.printStackTrace();
}
}
}
private static void log(String value) {
System.out.print(value);
}
}
打印結(jié)果:
String類型字段:張三
int類型字段:18
一般都是先將權(quán)限修改為true,再去讀取字段內(nèi)容健蕊,隨后進(jìn)行邏輯處理,最后記得將權(quán)限改回來.
方法相關(guān)
此類型方法也是用的比較多的一種踢俄,是反射調(diào)用方法的關(guān)鍵
class中關(guān)于方法的封裝都是給Method
類來操作的缩功,它可以獲取,修改都办,執(zhí)行類的方法嫡锌,核心方法就是invoke()
,作用是執(zhí)行方法琳钉,第一個(gè)參數(shù)是需要執(zhí)行的方法的類對象势木,隨后是需要執(zhí)行的方法參數(shù)值苞笨。
獲取Method
對象有四個(gè)方法:
方法 | 作用 |
---|---|
getMethods() | 獲取類的所有公共方法 |
getMethod() | 獲取指定公共方法离唬,傳入一個(gè)方法的名稱與參數(shù)的類型 |
getDeclaredMethods() | 獲取類的所有方法 |
getDeclaredMethod() | 獲取類的指定方法,傳入?yún)?shù)同getMethod() |
舉個(gè)例子镣屹,將本來為張三的名稱修改為李四:
/**
* Created by hj on 2019/1/10.
* 說明:
*/
public class Student {
public String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
private void setName(String name){
this.name = name;
log("設(shè)置的名稱為:"+name);
}
public static void main(String[] args) {
Student student = new Student("張三", 18);
Class mClass = student.getClass();
try {
Method method = mClass.getDeclaredMethod("setName",String.class);
method.invoke(student,"李四");
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
private static void log(String value) {
System.out.print(value);
}
}
打印結(jié)果:
設(shè)置的名稱為:李四
在項(xiàng)目中使用及皂,可以參考上面的Field
類甫男,其實(shí)都是差不多的,一通百通验烧。
抽象,繼承,接口,泛型相關(guān)
此類方法雖然不常用到板驳,但在某些情況下能發(fā)揮意想不到的作用,在許多框架的源碼中也能看見它們的身影碍拆,如Gson
的$Gson$Types
類,Retrofit
的Util
類等笋庄,來跟我學(xué)習(xí)一下把。
泛型
首先說說獲取類的泛型獲取倔监,先介紹一個(gè)接口Type
,它是java中所有類型的通用接口,類型包括原始類型(class)菌仁,參數(shù)化類型(ParameterizedType)浩习,數(shù)組類型(GenericArrayType),類型變量(TypeVariable)和基本數(shù)據(jù)類型济丘。內(nèi)部實(shí)現(xiàn)了一個(gè)方法getTypeName()
,用于返回類型參數(shù)信息谱秽,基于它向外擴(kuò)展的接口有:
- ParameterizedType
表示一個(gè)參數(shù)化類型洽蛀,說簡單點(diǎn)就是帶有參數(shù)類型的類型,如Collection ,如果帶有了類型疟赊,如Collection<String>郊供,那么就是說String將Collection參數(shù)化 - GenericArrayType
表示一個(gè)參數(shù)化類型或類型變量的數(shù)組類型,簡單說就是泛型A<T>[]或泛型數(shù)組T[] - TypeVariable
所有類型變量的父接口,也就是我們定義抽象類中的那種K,E等泛型變量近哟,可以泛指任何類 - WildcardType
表示一個(gè)通配符表達(dá)驮审,例如?,? extends Integer
Type
可以通過class
的getGenericInterfaces
或getGenericSuperclass()
方法來獲取
這么說或許還有點(diǎn)抽象,ParameterizedType
與TypeVariable
的概念或許還拎不太清吉执。下面通過示例來加深理解
先來說說ParameterizedType
舉個(gè)例子疯淫,先聲明一個(gè)帶泛型的超類Person
/**
* Created by hj on 2019/1/10.
* 說明:
*/
public class Person<T> {
public T feature;
}
隨后定義一個(gè)實(shí)體類Feature
/**
* Created by hj on 2019/1/10.
* 說明:
*/
public class Feature {
}
最后申明一個(gè)Student
并繼承Person
,將Feature
作為T
傳入:
/**
* Created by hj on 2019/1/10.
* 說明:
*/
public class Student extends Person<Feature> {
public static void main(String[] args) {
Student student = new Student();
Class mClass = student.getClass();
Type type = mClass.getGenericSuperclass();
//這里獲取的type為Person<Feature>,所以屬于ParameterizedType
if (type instanceof ParameterizedType) {
//父類的泛型可能有多個(gè)戳玫,這里只寫了一個(gè)熙掺,所以取第一個(gè)就行了,這里是取Person<Feature>中的Feature
Class tClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0];
log(tClass.getSimpleName());
}
}
private static void log(String value) {
System.out.print(value);
}
}
因?yàn)?code>Class類也實(shí)現(xiàn)了Type
接口咕宿,所以是可以強(qiáng)制轉(zhuǎn)化的
打印結(jié)果如下:
Feature
可以看到在此示例中币绩,只要是Person<>
中傳入的參數(shù)是一個(gè)參數(shù)類型,不管是實(shí)體類
府阀,String
都行缆镣,只要是參數(shù)而沒有泛指意義,那么它就屬于ParameterizedType
類型
接下來將代碼修改一下肌似,將Feature
改為T
,修改獲取邏輯:
/**
* Created by hj on 2019/1/10.
* 說明:
*/
public class Student<T> extends Person<T> {
public static void main(String[] args) {
Student student = new Student();
Class mClass = student.getClass();
Type type = mClass.getGenericSuperclass();
//此時(shí)這里的type為Person<T>费就,而不是T,所以依舊屬于ParameterizedType
if (type instanceof ParameterizedType) {
//這里是從Person<T>中獲取T
Type childType = ((ParameterizedType)type).getActualTypeArguments()[0];
if (childType instanceof TypeVariable){
log(((TypeVariable) childType).getName());
}
}
}
private static void log(String value) {
System.out.print(value);
}
}
輸出名稱為
T
不知道小伙伴們有沒有理解了川队,當(dāng)獲取的類型是泛指的力细,如T等,那么就是TypeVariable
類型固额,當(dāng)類型是參數(shù)類型的眠蚂,例如由子類傳入的,具體化的斗躏,那么就是ParameterizedType
類型逝慧。
接下來的GenericArrayType
和WildcardType
就比較好理解了,將以上示例中的Person<T>
改為Person<T[]>
啄糙,將結(jié)果輸出笛臣,代碼如下:
/**
* Created by hj on 2019/1/10.
* 說明:
*/
public class Student<T> extends Person<T[]> {
public static void main(String[] args) {
Student student = new Student();
Class mClass = student.getClass();
Type type = mClass.getGenericSuperclass();
if (type instanceof ParameterizedType) {
//這里獲取到的類型為:T[],是一個(gè)數(shù)組類型,所以是GenericArrayType
Type childType = ((ParameterizedType)type).getActualTypeArguments()[0];
if (childType instanceof GenericArrayType){
log(childType.toString());
}
}
}
private static void log(String value) {
System.out.print(value);
}
}
輸出名稱為:
T[]
WildcardType
就不演示了隧饼,將T
改為帶沈堡?
的類型就可以了,如果封裝類型實(shí)在太復(fù)雜燕雁,教大家一個(gè)技巧诞丽,打個(gè)斷點(diǎn)鲸拥,啥類型都幫你顯示出來了。
一看就知道是ParameterizedType類型了僧免,哈哈哈刑赶,溜不溜。
這里給小伙伴一個(gè)作業(yè):
List<? extends T>[]
中的List<? extends T>
,? extends T
懂衩,T
,List<? extends T>[]
分別代表什么類型呢撞叨?
繼承
獲取父級(jí)類使用Class.getSuperclass()
方法即可獲取父類的class
接口
獲取接口也比較簡單,一個(gè)接口實(shí)際上也是一個(gè)Class類勃痴,通過getInterfaces()
獲取到一個(gè)Class
數(shù)組谒所,而接口里聲明的方法可以通過獲取Method
的方式來獲取,
舉個(gè)栗子,先創(chuàng)建一個(gè)MyInterface
接口
/**
* Created by hj on 2019/1/11.
* 說明:
*/
public interface MyInterface {
void getName();
}
在Student
類里實(shí)現(xiàn)并獲扰嫔辍:
/**
* Created by hj on 2019/1/10.
* 說明:
*/
public class Student implements MyInterface{
public static void main(String[] args) {
Student student = new Student();
Class mClass = student.getClass();
Class interfaces = mClass.getInterfaces()[0];
try {
Method methods = interfaces.getMethod("getName",null);
log(methods.getName());
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
private static void log(String value) {
System.out.print(value);
}
@Override
public void getName() {
}
}
打印出方法名為:
getName
總結(jié)
只要拿到Class類劣领,我們就可以獲取到它的父類,接口铁材,方法尖淘,字段,以及泛型著觉,從而做一些解耦封裝村生,提高類的解耦性”穑或者在不修改源碼的情況下執(zhí)行或修改源碼中的方法趁桃,字段,從而達(dá)到理想中的效果肄鸽。小伙伴們快去練習(xí)一下把卫病。