什么是動態(tài)語言
我們都知道Java是動態(tài)語言符匾,但什么是動態(tài)語言呢祈坠?大致認同的定義是:
程序運行時害碾,允許改變程序結(jié)構(gòu)或變量類型,這種語言稱為動態(tài)語言赦拘。
從這個觀點看慌随,Perl,Python另绩,Ruby是動態(tài)語言儒陨,C++,Java笋籽,C#不是動態(tài)語言蹦漠。
什么是反射
JAVA有著一個非常突出的動態(tài)相關(guān)機制:Reflection(反射)。先看一下反射的概念:
在運行狀態(tài)中车海,對于任意一個類笛园,都能夠知道這個類的所有屬性和方法;對于任意一個對象侍芝,都能夠調(diào)用它的任意方法和屬性研铆;這種動態(tài)獲取信息以及動態(tài)調(diào)用對象方法的功能稱為java語言的反射機制。
換句話說州叠,Java程序可以加載一個運行時才得知名稱的class棵红,獲悉其完整構(gòu)造(但不包括methods定義),并生成其對象實體咧栗、或?qū)ζ鋐ields設(shè)值逆甜、或喚起其methods。
反射機制能做什么
- 在運行時判斷任意一個對象所屬的類致板;
- 在運行時構(gòu)造任意一個類的對象交煞;
- 在運行時判斷任意一個類所具有的成員變量和方法;
- 在運行時調(diào)用任意一個對象的方法斟或;
- 生成動態(tài)代理素征。
Class
Class 類繼承自O(shè)bject,其實體用以表達Java程序運行時的classes和interfaces,也用來表達enum御毅、array根欧、primitive Java types(boolean, byte, char, short, int, long, float, double)以及關(guān)鍵詞void。
當(dāng)一個class被加載端蛆,或當(dāng)加載器(class loader)的defineClass()被JVM調(diào)用咽块,JVM 便自動產(chǎn)生一個Class 對象。
Class是Reflection故事起源欺税。針對任何您想探勘的類,唯有先為它產(chǎn)生一個Class 對象揭璃,接下來才能經(jīng)由后者喚起為數(shù)十多個的Reflection APIs晚凿。
獲取Class三種方式
1. Object類中的getClass()
Date date = new Date();
Class c = date.getClass();
2. static method-Class.forName()
Class c = Class.forName("java.util.Date");
3. T.class
Class c = Date.class;
創(chuàng)建對象的五種方式
1. 使用new(最常用)
Date d = new Date();
2. 使用Class類的newInstance方法
newInstance方法調(diào)用無參的構(gòu)造函數(shù)創(chuàng)建對象
Date d = Date.class.newInstance();
3. 使用Constructor類的newInstance方法
java.lang.reflect.Constructor類里也有一個newInstance方法可以創(chuàng)建對象。
可以通過這個newInstance方法調(diào)用有參數(shù)的和私有的構(gòu)造函數(shù)瘦馍。
Constructor<Date> constructor= Date.class.getConstructor();
Date d = constructor.newInstance();
4. 使用clone
用clone方法創(chuàng)建對象并不會調(diào)用任何構(gòu)造函數(shù)歼秽。
要使用clone方法,我們需要先實現(xiàn)Cloneable接口并實現(xiàn)其定義的clone方法情组。
Date d1 = new Date();
Date d2 = (Date)d1.clone();
5. 使用反序列化
ObjectInputStream is = new ObjectInputStream(new FileInputStream("D:/user.obj"));
User userTemp = (User) is.readObject();
Reflection API
Class的方法
1. 獲取Field
Field[] getFields() 獲取所有public域
Field[] getDeclaredFields() 獲取所有域
Class c = Class.forName("java.util.Date");
Field[] fields0 = c.getFields();
Field[] fields1 = c.getDeclaredFields();
Field getField(String name) 獲取指定名稱的域
Field getDeclaredField(String name) 獲取指定名稱的域
Field的方法
Object get(Object obj) 返回obj對象中用Field對象表示的域值
void set(Object obj,Object newValue) 設(shè)置Obj對象中Field對象表示的域的值
User user = new User("001","winson","w5566");
Class c = user.getClass();
Field[] fields = c.getDeclaredFields();
for (Field field: fields) {
field.setAccessible(true);
Object object = field.get(user); //獲取user對象中Field對象表示的域的對象
System.out.println(object.toString());
field.set(user,new String("test")); //設(shè)置user對象中Field對象表示的域的值
}
System.out.println(user);
2. 獲取Method
Method[] getMethods() 獲取所有public方法
Method[] getDeclaredMethods() 獲取所有域
Class c = Class.forName("java.util.Date");
Method[] methods0 = c.getMethods();
Method[] methods1 = c.getDeclaredMethods();
3. 獲取Constructor
Constructor[] getConstructors() 獲取所有public構(gòu)造器
Constructor[] getDeclaredConstructors() 獲取所有構(gòu)造器
Class c = Class.forName("java.util.Date");
Constructor[] constructors0 = c.getConstructors();
Constructor[] constructors1 = c.getDeclaredConstructors();
Field燥筷、Method、Constructor的通用的方法
1. 獲取Class對象
Class getDeclaringClass()
2.獲取方法或構(gòu)造器拋出的異常類型 (Contructor|Method)
Class[] getExceptionTypes()
3. 返回修飾權(quán)限的整型值,使用MOdifier類可以分析這個返回值
int getModifiers()
4. 返回一個用于描述構(gòu)造器院崇、方法或域名的字符串
String getName()
5. 返回一個用于描述參數(shù)類型的Class對象數(shù)組 (Contructor|Method)
Class[] getParameterTypes()
5. 返回一個用于描述返回類型的的Class對象 (Method)
Class getReturnTypes()
Modifier的方法
public class ReflectTestDemo {
public static void main(String[] args) throws ClassNotFoundException {
Class c = Class.forName("java.util.Date");
System.out.println(c.getModifiers()); //輸出1
System.out.println(Modifier.toString(c.getModifiers())); //輸出public
Method[] methods = c.getDeclaredMethods();
for (Method m: methods) {
Class aClass= m.getDeclaringClass();
Class[] exceptionClass = m.getExceptionTypes(); //獲取拋出的異常類型
System.out.println(Modifier.toString(m.getModifiers())); //獲取方法的權(quán)限
String name = m.getName(); //獲取方法名
Class[] parameterClass = m.getParameterTypes(); //獲取參數(shù)的類型
Class returnClass = m.getReturnType(); //獲取返回值類型
}
}
}
AccessibleObject的方法
1. 為反射對象設(shè)置可訪問標(biāo)志肆氓,flag為true表明屏蔽Java語言的訪問檢查,使私有屬性也可以被查詢或設(shè)置底瓣。
void setAccessible(boolean flag)
2.返回反射對象的可訪問標(biāo)準的值
boolean isAccessible()
3.設(shè)置對象數(shù)組可訪問標(biāo)志的快捷方法
static void setAccessible(AccessibleObject[] array,boolean flag)
Class c0 = Class.forName("java.util.Date");
Field[] fields = c.getDeclaredFields();
for (Field field: fields) {
field.setAccessible(true); //如果不設(shè)置Accessible為true谢揪,則下面代碼會拋出IllegalAccessException異常
field.set(user,new String("test"));
}
Method的invoke方法
調(diào)用對象所描述的方法,傳給定參數(shù)捐凭,并返回方法的返回值拨扶。
在使用包裝器傳遞基本類型的值時,基本類型的返回值必須是未包裝的
public Object invoke(Object implicitParameter,Object[] explicitParamenters)
第一個參數(shù)是隱式參數(shù)傳入被獲取方法的對象茁肠,如果是靜態(tài)方法則傳入null
public class MethodDemo {
public static void main (String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method method0 = MethodDemo.class.getMethod("square", double.class);
Method method1 = Math.class.getMethod("sqrt", double.class);
double d0 = (Double)method0.invoke(null,3.3); //如果是static第一個參數(shù)可以為null
double d1 = (Double)method1.invoke(null,4.4);
System.out.println(d0);
System.out.println(d1);
User user = new User("007","Winson","w5566");
Method method2 = user.getClass().getMethod("getName");
String name = (String)method2.invoke(user,new Object[0]); //如果沒有參數(shù)患民,則傳入new Object[0]
System.out.println(name);
}
public static double square(double x){
return x*x;
}
}