反射機制呢就是在程序運行時旗们,動態(tài)的獲取類(class),類的方法(method)屬性(field)等。主要的注意點就是程序運行時動態(tài)的獲取。
這里主要是從代碼的角度來講解Java反射坝锰。在使用中我們用的較多的幾個類有Class
,Method
重付,Field
顷级,Constructor
,Annotation
等。
下面我們分別介紹下确垫。
獲取Class
獲取class對象有如下三種方式:
- cat.getClass
- Cat.class
- Class.forName("xyz.magicer.Cat")
//方式1 調用對象的getClass()方式弓颈,該方法屬于Object類
Class<? extends Cat> aClass = cat.getClass();
//方式2 直接類.class獲取。
Class<Cat> catClass = Cat.class;
//方式3 通過Class類的靜態(tài)方法forName獲取删掀,參數為想要獲取的類的全名(含包名翔冀,不然會報ClassNotFoundException)
try {
Class<?> cat2 = Class.forName("xyz.magicer.Cat");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
在使用中根據實際情況選擇,獲取Class
對象是我們使用反射的第一步披泪。不過還是很簡單的纤子。獲取了Class
對象后,怎么創(chuàng)建一個實體呢款票?catClass.newInstance()
不過改方法會拋出兩個異常:InstantiationException
和IllegalAccessException
- InstantiationException :當該Class不能被實例化的時候拋出該異常控硼,例如,為抽象類艾少、接口卡乾、數組類、基本類缚够、Void等時幔妨。
- IllegalAccessException:當無參構造為私有時
還有一種創(chuàng)建實體的方式是使用Constructor
,在下邊在介紹潮瓶。
Method
獲取Method
類或接口的方法對應這Method
這個類。我們通過Class對象來獲取钙姊。主要方法有
-
getMethods()
: 返回Method[]
返回所有public
的方法毯辅。包含
父類或父接口的方法。 -
getDeclaredMethods()
: 返回Method[]
返回所有的方法煞额。包括private
public
default
和protected
的思恐。不包含
父類或父接口的方法 -
getMethod(String name, Class<?>... parameterTypes)
: 返回Method
, 根據方法名和類型獲取Method膊毁。類或接口的及父類父接口公共成員方法胀莹。 -
getDeclaredMethod(String name, Class<?>... parameterTypes)
: 返回Method
。根據方法名和類型返回Method婚温。類和接口的所有方法描焰。
//sleep是private的
Method[] methods = catClass.getMethods();
for (Method method : methods) {
//method.getName() 返回方法名,不包括修飾符,參數和返回值荆秦。
System.out.printf(method.getName()+" ");
}
// 打印toString getName setName setColor eat eat getAge setAge getColor wait wait wait equals hashCode getClass notify notifyAll
System.out.println();
Method[] declaredMethods = catClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.printf(declaredMethod.getName()+" ");
}
//打印 toString getName setName sleep setColor eat eat getAge setAge getColor
//拋出NoSuchMethodException 因為sleep的訪問權限為private
//Method sleep1 = catClass.getMethod("sleep", null);
Method hashCode = catClass.getMethod("hashCode", null);
//拋出NoSuchMethodException篱竭,因為hashCode是父類的方法。
//Method hashCode1 = catClass.getDeclaredMethod("hashCode", null);
Method eat2 = catClass.getMethod("eat",null);
Method sleep1 = catClass.getDeclaredMethod("sleep", null);
通過上面的代碼我們能看到步绸,getMethods()可以獲取父類的方法掺逼,但是不能獲取私有方法,而getDeclaredMethod()方法不可以獲取父類的方法瓤介,但是可以獲取私有的方法吕喘。getMethod也是一樣,可以獲取父類父接口方法刑桑,但是無法獲取私有方法氯质,而getDeclaredMethod可以獲取私有方法不能獲取父類父接口方法。
而帶s和不帶s的區(qū)別是帶s返回值為Method[]
漾月,不帶返回的是Method
病梢。
調用Method
既然我們獲取到了方法(Method)當然是想調用它,那么怎么調用呢梁肿?Method有個方法invoke(Object obj, Object... args)
.
invoke接收兩個參數蜓陌,第一個是調用該方法的實體,第二個是方法的參數吩蔑。參數類型多個時順序要跟方法參數相同钮热。
Method eat1 = catClass.getMethod("eat");
eat1.invoke(catInstance,null); //打印:我只吃小魚干烛芬。
Method eat = catClass.getDeclaredMethod("eat");
eat.invoke(catInstance,null); //打铀砥凇: 我只吃小魚干。
Method sleep = catClass.getDeclaredMethod("sleep");
//IllegalAccessException
sleep.invoke(catInstance,null);
我們會發(fā)現當私有方法invoke調用時會拋出IllegalAccessException赘娄,不是可以獲取么為什么不能調用仆潮?因為方法有權限修飾符,我們需要設置成我們可以調用的遣臼。如下:
sleep.setAccessible(true);
sleep.invoke(catInstance,null);
在調用前設置為可以調用就解決了性置。
在前面我們接觸到了兩個Method類的方法了(getName,和invoke)揍堰。
Method常用方法
- getModifiers(): 返回方法修飾符
- getAnnotations(): 可以獲取父類的注解
- getDeclaredAnnotations(): 不可以返回父類的注解
- getAnnotation(Class<T> annotationClass)
- getDeclaredAnnotation(Class<T> annotationClass)
- getParameters(): 返回Parameter[] (java1.8加入)
Field
獲取Field對象的方式跟Method一樣鹏浅,用法和規(guī)律都一樣。無非是現在方法改為getFields()屏歹、getDeclaredFields()隐砸、getField(String)、getDeclaredField(String)蝙眶。
設置Field
可以通過set方法來設置值 public void set(Object obj, Object value)
每種基本數據類型都有的setxxx()方法季希。
//name 為私有
Field name = catClass.getDeclaredField("name");
name.setAccessible(true);
name.set(cat,"啦啦啦");
System.out.println("\n"+cat.toString());
在使用反射設置屬性的時候一定要注意,可能在代碼中用到該屬性的地方較多,改變了值之后引起一些意想不到的效果胖眷。
Constructor
獲取Constructor對象的方式跟Field和Method一樣武通。有四個方法:
getConstructors(),getDeclaredConstructors珊搀,getConstructor(Class<?>... parameterTypes)冶忱, getDeclaredConstructor(Class<?>... parameterTypes)。
得到了Constructor對象后我們就可以調用newInstance(Object ... initargs)
方法進行初始化了境析,注意參數的順序囚枪。
Constructor<Cat> constructor1 = catClass.getDeclaredConstructor(String.class, int.class, String.class);
Cat cat1 = constructor1.newInstance("喵喵2", 3, "white");
System.out.println(cat1.toString());
到這里我們看到,通過反射創(chuàng)建一個對象有兩種方式:(1)class.newInstance()和(2)consturctor.newInstance(Object ... initargs)劳淆。那么他們有什么區(qū)別呢链沼?
方式(1)只能調用無參構造創(chuàng)建對象,并且無參構造不能為私有沛鸵,而方式(2)可以調用所有
Constructor<Cat> constructor1 = catClass.getDeclaredConstructor(String.class, int.class, String.class);
Cat cat1 = constructor1.newInstance("喵喵2", 3, "white");
System.out.println(cat1.toString());
//調用私有構造
Constructor<Cat> constructor2 = catClass.getDeclaredConstructor(String.class);
constructor2.setAccessible(true);
Cat miaomiao = constructor2.newInstance("miaomiao");
System.out.println(miaomiao.toString());