實現java動態機制

作為一個前端,我很慶幸js是動態語言,這樣就減少了類型檢查,而java這種高級語言是如何做的呢?

1 什么是java反射機制?

當程序運行時,允許改變程序結構或變量類型,這種語言稱為動態語言。我們認為java并不是動態語言,但是它卻有一個非常突出的動態相關機制,俗稱:反射。

話不多說看代碼:

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修飾的東西不是對象,但是它屬于類。普通的數據類型不是對象,例如:int a = 5;它不是面向對象,但是它有其包裝類 Integer 或者分裝類來彌補了它。除了以上兩種不是面向對象,其余的包括類也有它的面向對象,類是java.lang.Class的實例化對象(注意Class是大寫)。也就是說:Class A{}當我創建了A類,那么類A本身就是一個對象,誰的對象?java.lang.Class的實例對象。

這里的F的實例化對象就可以用f表達出來。同理F類也是一個實例化對象,Class類的實例化對象。我們可以理解為任何一個類都是Class類的實例化對象,這種實例化對象有三種表示方法:

public class Demo(){
F f=new F();
//第一種表達方式
Class c1=F.class;//這種表達方式同時也告訴了我們任何一個類都有一個隱含的靜態成員變量class
//第二種表達方式
Class c2=f.getClass();//這種表達方式在已知了該類的對象的情況下通過getClass方法獲取
try {
c3 = Class.forName("com.text.F");//類的全稱
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
class F{}

到這里想必大家也知道反射到底是干嘛的,就是運行時獲取該類本身的Class。

看一下如何通過c1創建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中的默認的無參構造函數取消的時候,比如自己定義只定義一個有參數的構造函數之后,會出現錯誤:

public Person(String name, int age) {
        this.age=age;
        this.name=name;
    }

上述代碼會報錯。。。那么問題來了,一個類總有有參構造函數把,碰到這種情況如何使用反射呢?

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;
        //取得全部的構造函數
        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);
    }
}

其實問題的關鍵是動態獲取Person的構造函數,他是一個數組對象。

下面來幾個例子給大家講解下反射的實際應用:
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()
返回此類或接口以整數編碼的 Java語言修飾符。

2.獲取類的全部方法

public class ClassUtil {
public static void printClassMethodMessage(Object obj){
//要獲取類的信息》》首先我們要獲取類的類類型
Class c = obj.getClass();

System.out.println("類的名稱是:"+c.getName());

//如果我們要獲得所有的方法,可以用getMethods()方法,這個方法獲取的是所有的Public的函數,包括父類繼承而來的。如果我們要獲取所有該類自己聲明的方法,就可以用getDeclaredMethods()方法,這個方法是不問訪問權限的。
Method[] ms = c.getMethods();//c.getDeclaredMethods()
//接下來我們拿到這些方法之后干什么?我們就可以獲取這些方法的信息,比如方法的名字。
//首先我們要循環遍歷這些方法
for(int i = 0; i < ms.length;i++){
//然后可以得到方法的返回值類型的類類型
Class returnType = ms[i].getReturnType();
//得到方法的返回值類型的名字
System.out.print(returnType.getName()+" ");
//得到方法的名稱
System.out.print(ms[i].getName()+"(");
//獲取參數類型--->得到的是參數列表的類型的類類型
Class[] paramTypes = ms[i].getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
}

obj.getClass().getMethods() 與 getDeclaredMethods()的區別,一個是獲取所有包括繼承的,一個是自身有的方法。

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.如何動態調用方法

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

注意傳參列表為數組,獲取方法是通過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();
        }
    }
}

好處就是我省略了一些判斷語句,更加簡潔。
好了,今天的講解就到這里。。。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,779評論 18 399
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,993評論 19 139
  • 一:java概述:1,JDK:Java Development Kit,java的開發和運行環境,java的開發工...
    ZaneInTheSun閱讀 2,691評論 0 11
  • 那一年,我21歲 想過自己是風, 再也無所畏懼。 想過自己是云, 飄到...
    讀楊小莫有感閱讀 216評論 0 0
  • “山清水秀”“鳥語花香”,這是我記憶里小學時最喜歡的兩個成語,喜歡清晨的微風,喜歡穿過樹葉縫隙的陽光,大自然總有使...
    伊心心心心閱讀 486評論 0 0