作為一個前端,我很慶幸js是動態(tài)語言叹哭,這樣就減少了類型檢查卑惜,而java這種高級語言是如何做的呢?
1 什么是java反射機制袋倔?
當程序運行時荚恶,允許改變程序結構或變量類型晰赞,這種語言稱為動態(tài)語言谈秫。我們認為java并不是動態(tài)語言惕橙,但是它卻有一個非常突出的動態(tài)相關機制雇庙,俗稱:反射谓形。
話不多說看代碼:
package Reflect;
/**
* 通過一個對象獲得完整的包名和類名
* */
class Demo{
//other codes...
}
class hello{
public static void main(String[] args) {
Demo demo=new Demo();
System.out.println(demo.getClass().getName());
}
}
等等demo.getClass()是干嘛的。状共。套耕。
在面向對象的世界里,萬事萬物皆是對象峡继。而在java語言中冯袍,static修飾的東西不是對象,但是它屬于類。普通的數(shù)據(jù)類型不是對象康愤,例如:int a = 5;它不是面向對象儡循,但是它有其包裝類 Integer 或者分裝類來彌補了它。除了以上兩種不是面向對象征冷,其余的包括類也有它的面向對象择膝,類是java.lang.Class的實例化對象(注意Class是大寫)。也就是說:Class A{}當我創(chuàng)建了A類检激,那么類A本身就是一個對象肴捉,誰的對象?java.lang.Class的實例對象叔收。
這里的F的實例化對象就可以用f表達出來齿穗。同理F類也是一個實例化對象,Class類的實例化對象饺律。我們可以理解為任何一個類都是Class類的實例化對象窃页,這種實例化對象有三種表示方法:
public class Demo(){
F f=new F();
//第一種表達方式
Class c1=F.class;//這種表達方式同時也告訴了我們任何一個類都有一個隱含的靜態(tài)成員變量class
//第二種表達方式
Class c2=f.getClass();//這種表達方式在已知了該類的對象的情況下通過getClass方法獲取
try {
c3 = Class.forName("com.text.F");//類的全稱
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
class F{}
到這里想必大家也知道反射到底是干嘛的,就是運行時獲取該類本身的Class复濒。
看一下如何通過c1創(chuàng)建F類的實例
Public class Demo1{
try {
Foo foo = (Foo)c1.newInstance();//foo就表示F類的實例化對象
foo.print();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}}
class F{
void print(){
}
}
注意這里c1.newInstance()出來的是Object類型脖卖,需要強轉。
通過無參構造實例化對象
package Reflect;
class Person{
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString(){
return "["+this.name+" "+this.age+"]";
}
private String name;
private int age;
}
class hello{
public static void main(String[] args) {
Class<?> demo=null;
try{
demo=Class.forName("Reflect.Person");
}catch (Exception e) {
e.printStackTrace();
}
Person per=null;
try {
per=(Person)demo.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
per.setName("Rollen");
per.setAge(20);
System.out.println(per);
}
}
但是注意一下巧颈,當我們把Person中的默認的無參構造函數(shù)取消的時候畦木,比如自己定義只定義一個有參數(shù)的構造函數(shù)之后,會出現(xiàn)錯誤:
public Person(String name, int age) {
this.age=age;
this.name=name;
}
上述代碼會報錯砸泛。馋劈。。那么問題來了晾嘶,一個類總有有參構造函數(shù)把妓雾,碰到這種情況如何使用反射呢?
package Reflect;
import java.lang.reflect.Constructor;
class Person{
public Person() {
}
public Person(String name){
this.name=name;
}
public Person(int age){
this.age=age;
}
public Person(String name, int age) {
this.age=age;
this.name=name;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString(){
return "["+this.name+" "+this.age+"]";
}
private String name;
private int age;
}
class hello{
public static void main(String[] args) {
Class<?> demo=null;
try{
demo=Class.forName("Reflect.Person");
}catch (Exception e) {
e.printStackTrace();
}
Person per1=null;
Person per2=null;
Person per3=null;
Person per4=null;
//取得全部的構造函數(shù)
Constructor<?> cons[]=demo.getConstructors();
try{
per1=(Person)cons[0].newInstance();
per2=(Person)cons[1].newInstance("Rollen");
per3=(Person)cons[2].newInstance(20);
per4=(Person)cons[3].newInstance("Rollen",20);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(per1);
System.out.println(per2);
System.out.println(per3);
System.out.println(per4);
}
}
其實問題的關鍵是動態(tài)獲取Person的構造函數(shù)垒迂,他是一個數(shù)組對象械姻。
下面來幾個例子給大家講解下反射的實際應用:
1.獲取類的全部屬性
import java.lang.reflect.Modifier //這個類需要引入
class hello {
public static void main(String[] args) {
Class<?> demo = null;
try {
demo = Class.forName("Reflect.Person");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("===============本類屬性========================");
// 取得本類的全部屬性
Field[] field = demo.getDeclaredFields();
for (int i = 0; i < field.length; i++) {
// 權限修飾符
int mo = field[i].getModifiers();
String priv = Modifier.toString(mo);
// 屬性類型
Class<?> type = field[i].getType();
System.out.println(priv + " " + type.getName() + " "
+ field[i].getName() + ";");
}
}
同時再看java.lang.Class類中也有同樣的一個方法:
int getModifiers()
返回此類或接口以整數(shù)編碼的 Java語言修飾符。
2.獲取類的全部方法
public class ClassUtil {
public static void printClassMethodMessage(Object obj){
//要獲取類的信息》》首先我們要獲取類的類類型
Class c = obj.getClass();
System.out.println("類的名稱是:"+c.getName());
//如果我們要獲得所有的方法机断,可以用getMethods()方法楷拳,這個方法獲取的是所有的Public的函數(shù),包括父類繼承而來的吏奸。如果我們要獲取所有該類自己聲明的方法欢揖,就可以用getDeclaredMethods()方法,這個方法是不問訪問權限的奋蔚。
Method[] ms = c.getMethods();//c.getDeclaredMethods()
//接下來我們拿到這些方法之后干什么她混?我們就可以獲取這些方法的信息烈钞,比如方法的名字。
//首先我們要循環(huán)遍歷這些方法
for(int i = 0; i < ms.length;i++){
//然后可以得到方法的返回值類型的類類型
Class returnType = ms[i].getReturnType();
//得到方法的返回值類型的名字
System.out.print(returnType.getName()+" ");
//得到方法的名稱
System.out.print(ms[i].getName()+"(");
//獲取參數(shù)類型--->得到的是參數(shù)列表的類型的類類型
Class[] paramTypes = ms[i].getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
}
obj.getClass().getMethods() 與 getDeclaredMethods()的區(qū)別坤按,一個是獲取所有包括繼承的毯欣,一個是自身有的方法。
class Person{
public void eat(string str,integert ){
}
public string sing(string str){
}
}
printClassMethodMessage(new Person())
1 .java.lang.string eat java.lang.string java.lang.interger
2 .java.lang.string sing java.lang.string
3.如何動態(tài)調用方法
public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception {
Class ownerClass = owner.getClass();
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Method method = ownerClass.getMethod(methodName,argsClass);
return method.invoke(owner, args);
}
我們上面定義了一個呼喚類實例方法的方法臭脓。
class Person{
public void say(string name,integert age ){
print 'i am' + name + 'age is' + age
}
}
invokeMethod(new Person(),'say',['sumail',18])
// i am sumail age is 18
注意傳參列表為數(shù)組酗钞,獲取方法是通過ownerClass.getMethod(methodName,argsClass)來得到。
最后來看一下一個用反射的工廠模式例子:
原始版本
interface fruit{
public abstract void eat();
}
class Apple implements fruit{
public void eat(){
System.out.println("Apple");
}
}
class Orange implements fruit{
public void eat(){
System.out.println("Orange");
}
}
// 構造工廠類
// 也就是說以后如果我們在添加其他的實例的時候只需要修改工廠類就行了
class Factory{
public static fruit getInstance(String fruitName){
fruit f=null;
if("Apple".equals(fruitName)){
f=new Apple();
}
if("Orange".equals(fruitName)){
f=new Orange();
}
return f;
}
}
class hello{
public static void main(String[] a){
fruit f=Factory.getInstance("Orange");
f.eat();
}
}
反射版本
package Reflect;
interface fruit{
public abstract void eat();
}
class Apple implements fruit{
public void eat(){
System.out.println("Apple");
}
}
class Orange implements fruit{
public void eat(){
System.out.println("Orange");
}
}
class Factory{
public static fruit getInstance(String ClassName){
fruit f=null;
try{
f=(fruit)Class.forName(ClassName).newInstance();
}catch (Exception e) {
e.printStackTrace();
}
return f;
}
}
class hello{
public static void main(String[] a){
fruit f=Factory.getInstance("Reflect.Apple");
if(f!=null){
f.eat();
}
}
}
好處就是我省略了一些判斷語句来累,更加簡潔砚作。
好了,今天的講解就到這里嘹锁。偎巢。。