程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> Java基礎學習第二十七天——類加載器和反射

Java基礎學習第二十七天——類加載器和反射

編輯:JAVA綜合教程

Java基礎學習第二十七天——類加載器和反射


文檔版本 開發工具 測試平台 工程名字 日期 作者 備注 V1.0       2016.04.05 lutianfei none

第十三章 類加載器和反射

類的加載

當程序要使用某個類時,如果該類還未被加載到內存中,則系統會通過加載連接初始化三步來實現對這個類進行初始化。

加載

就是指將class文件讀入內存,並為之創建一個Class對象。 任何類被使用時系統都會建立一個Class對象。 連接
驗證 是否有正確的內部結構,並和其他類協調一致 准備 負責為類的靜態成員分配內存,並設置默認初始化值 解析 將類的二進制數據中的符號引用替換為直接引用 初始化 就是我們以前講過的初始化步驟

類初始化時機

創建類的實例 訪問類的靜態變量,或者為靜態變量賦值 調用類的靜態方法 使用反射方式來強制創建某個類或接口對應的java.lang.Class對象 初始化某個類的子類 直接使用java.exe命令來運行某個主類

類加載器

負責將.class文件加載到內存中,並為之生成對應的Class對象。 雖然我們不需要關心類加載機制,但是了解這個機制我們就能更好的理解程序的運行。 類加載器的組成
Bootstrap ClassLoader 根類加載器 Extension ClassLoader 擴展類加載器 Sysetm ClassLoader 系統類加載器

類加載器的作用

Bootstrap ClassLoader 根類加載器
也被稱為引導類加載器,負責Java核心類的加載
比如System,String等。在JDK中JRE的lib目錄下rt.jar文件中 Extension ClassLoader 擴展類加載器
負責JRE的擴展目錄jar包的加載。
在JDK中JRE的lib目錄ext目錄 Sysetm ClassLoader 系統類加載器
負責在JVM啟動時加載來自java命令的class文件,以及classpath環境變量所指定的jar包和類路徑

 

反射

JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。 要想解剖一個類,必須先要獲取到該類的字節碼文件對象。而解剖使用的就是Class類中的方法。所以先要獲取到每一個字節碼文件對應的Class類型的對象。

通過反射獲取構造方法並使用

獲取構造方法
getConstructors getDeclaredConstructors 創建對象
newInstance() con.newInstance(“zhangsan”, 20); 獲取所有成員
getFields,getDeclaredFields 獲取單個成員
getField,getDeclaredField 修改成員的值
set(Object obj,Object value)
將指定對象變量上此 Field 對象表示的字段設置為指定的新值。 獲取所有方法
getMethods getDeclaredMethods 獲取單個方法
getMethod getDeclaredMethod

暴力訪問

method.setAccessible(true);

獲取class文件對象的方式:

A:Object類的getClass()方法 B:數據類型的靜態屬性class C:Class類中的靜態方法
public static Class forName(String className)

一般我們使用誰呢?

A:自己玩 任選一種,第二種比較方便 B:開發 第三種 為什麼呢?因為第三種是一個字符串,而不是一個具體的類名。這樣我們就可以把這樣的字符串配置到配置文件中。

通過反射獲取無參構造方法並使用

/*
 * 反射:就是通過class文件對象,去使用該文件中的成員變量,構造方法,成員方法。
 * Class類:
 *      成員變量    Field
 *      構造方法    Constructor
 *      成員方法    Method
 * 
 */
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方式1
        Person p = new Person();
        Class c = p.getClass();

        Person p2 = new Person();
        Class c2 = p2.getClass();

        System.out.println(p == p2);// false
        System.out.println(c == c2);// true

        // 方式2
        Class c3 = Person.class;
        // int.class;
        // String.class;
        System.out.println(c == c3);

        // 方式3
        // ClassNotFoundException
        Class c4 = Class.forName("cn.itcast_01.Person");
        System.out.println(c == c4);
    }
}




public class Person {
    private String name;
    int age;
    public String address;

    public Person() {
    }

    private Person(String name) {
        this.name = name;
    }

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

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

    public void show() {
        System.out.println("show");
    }

    public void method(String s) {
        System.out.println("method " + s);
    }

    public String getString(String s, int i) {
        return s + "---" + i;
    }

    private void function() {
        System.out.println("function");
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", address=" + address
                + "]";
    }

}

 

通過反射獲取無參構造方法的使用
/*
 * 通過反射獲取構造方法並使用。
 */
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 獲取字節碼文件對象
        Class c = Class.forName("cn.itcast_01.Person");

        // 獲取構造方法
        // public Constructor[] getConstructors():所有公共構造方法
        // public Constructor[] getDeclaredConstructors():所有構造方法
        // Constructor[] cons = c.getDeclaredConstructors();
        // for (Constructor con : cons) {
        // System.out.println(con);
        // }

        // 獲取單個構造方法
        // public Constructor getConstructor(Class... parameterTypes)
        // 參數表示的是:你要獲取的構造方法的構造參數個數及數據類型的class字節碼文件對象
        Constructor con = c.getConstructor();// 返回的是構造方法對象

        // Person p = new Person();
        // System.out.println(p);
        // public T newInstance(Object... initargs)
        // 使用此 Constructor 對象表示的構造方法來創建該構造方法的聲明類的新實例,並用指定的初始化參數初始化該實例。
        Object obj = con.newInstance();
        System.out.println(obj);

        // Person p = (Person)obj;
        // p.show();
    }
}

 

獲取帶參構造方法
/*
 * 需求:通過反射去獲取該構造方法並使用:
 * public Person(String name, int age, String address)
 * 
 * Person p = new Person("林青霞",27,"北京");
 * System.out.println(p);
 */

public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        // 獲取字節碼文件對象
        Class c = Class.forName("cn.itcast_01.Person");

        // 獲取帶參構造方法對象
        // public Constructor getConstructor(Class... parameterTypes)
        Constructor con = c.getConstructor(String.class, int.class,
                String.class);

        // 通過帶參構造方法對象創建對象
        // public T newInstance(Object... initargs)
        Object obj = con.newInstance("林青霞", 27, "北京");

        System.out.println(obj);
    }
}

 

獲取私有構造方法並使用
/*
 * 需求:通過反射獲取私有構造方法並使用
 * private Person(String name){}
 * 
 * Person p = new Person("風清揚");
 * System.out.println(p);
 */
public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        // 獲取字節碼文件對象
        Class c = Class.forName("cn.itcast_01.Person");

        // 獲取私有構造方法對象
        // NoSuchMethodException:每有這個方法異常
        // 原因是一開始我們使用的方法只能獲取公共的,下面這種方式就可以了。
        Constructor con = c.getDeclaredConstructor(String.class);

        // 用該私有構造方法創建對象
        // IllegalAccessException:非法的訪問異常。
        // 暴力訪問
        con.setAccessible(true);// 值為true則指示反射的對象在使用時應該取消Java語言訪問檢查。
        Object obj = con.newInstance("風清揚");

        System.out.println(obj);
    }
}

 

獲取成員變量並使用
/*
 * 通過反射獲取成員變量並使用
 */
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 獲取字節碼文件對象
        Class c = Class.forName("cn.itcast_01.Person");

        // 獲取所有的成員變量
        // Field[] fields = c.getFields();
        // Field[] fields = c.getDeclaredFields();
        // for (Field field : fields) {
        // System.out.println(field);
        // }

        /*
         * Person p = new Person(); p.address = "北京"; System.out.println(p);
         */

        // 通過無參構造方法創建對象
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();
        System.out.println(obj);

        // 獲取單個的成員變量
        // 獲取address並對其賦值
        Field addressField = c.getField("address");
        // public void set(Object obj,Object value)
        // 將指定對象變量上此 Field 對象表示的字段設置為指定的新值。
        addressField.set(obj, "北京"); // 給obj對象的addressField字段設置值為"北京"
        System.out.println(obj);

        // 獲取name並對其賦值
        // NoSuchFieldException
        Field nameField = c.getDeclaredField("name");
        // IllegalAccessException
        nameField.setAccessible(true);
        nameField.set(obj, "林青霞");
        System.out.println(obj);

        // 獲取age並對其賦值
        Field ageField = c.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(obj, 27);
        System.out.println(obj);
    }
}

 

獲取無參無返回值成員方法並使用 獲取帶參數返回值成員方法並使用
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 獲取字節碼文件對象
        Class c = Class.forName("cn.itcast_01.Person");

        // 獲取所有的方法
        // Method[] methods = c.getMethods(); // 獲取自己的包括父親的公共方法
        // Method[] methods = c.getDeclaredMethods(); // 獲取自己的所有的方法
        // for (Method method : methods) {
        // System.out.println(method);
        // }

        Constructor con = c.getConstructor();
        Object obj = con.newInstance();

        /*
         * Person p = new Person(); p.show();
         */

        // 獲取單個方法並使用
        // public void show()
        // public Method getMethod(String name,Class... parameterTypes)
        // 第一個參數表示的方法名,第二個參數表示的是方法的參數的class類型
        Method m1 = c.getMethod("show");
        // obj.m1(); // 錯誤
        // public Object invoke(Object obj,Object... args)
        // 返回值是Object接收,第一個參數表示對象是誰,第二參數表示調用該方法的實際參數
        m1.invoke(obj); // 調用obj對象的m1方法

        System.out.println("----------");
        // public void method(String s)
        Method m2 = c.getMethod("method", String.class);
        m2.invoke(obj, "hello");
        System.out.println("----------");

        // public String getString(String s, int i)
        Method m3 = c.getMethod("getString", String.class, int.class);
        Object objString = m3.invoke(obj, "hello", 100);
        System.out.println(objString);
        // String s = (String)m3.invoke(obj, "hello",100);
        // System.out.println(s);
        System.out.println("----------");

        // private void function()
        Method m4 = c.getDeclaredMethod("function");
        m4.setAccessible(true);
        m4.invoke(obj);
    }
}

 

反射應用舉例

通過配置文件運行類中的方法
public class Student {
    public void love() {
        System.out.println("愛生活,愛Java");
    }
}



public class Teacher {
    public void love() {
        System.out.println("愛生活,愛青霞");
    }
}



public class Worker {
    public void love() {
        System.out.println("愛生活,愛老婆");
    }
}




/*
 * 通過配置文件運行類中的方法
 * 
 * 反射:
 *      需要有配置文件配合使用。
 *      用class.txt代替。
 *      並且你知道有兩個鍵。
 *          className
 *          methodName
 */
public class Test {
    public static void main(String[] args) throws Exception {
        // 反射前的做法
        // Student s = new Student();
        // s.love();
        // Teacher t = new Teacher();
        // t.love();
        // Worker w = new Worker();
        // w.love();
        // 反射後的做法

        // 加載鍵值對數據
        Properties prop = new Properties();
        FileReader fr = new FileReader("class.txt");
        prop.load(fr);
        fr.close();

        // 獲取數據
        String className = prop.getProperty("className");
        String methodName = prop.getProperty("methodName");

        // 反射
        Class c = Class.forName(className);

        Constructor con = c.getConstructor();
        Object obj = con.newInstance();

        // 調用方法
        Method m = c.getMethod(methodName);
        m.invoke(obj);
    }
}

 

我給你ArrayList的一個對象,我想在這個集合中添加一個字符串數據,如何實現呢?
public class ArrayListDemo {
    public static void main(String[] args) throws NoSuchMethodException,
            SecurityException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException {
        // 創建集合對象
        ArrayList array = new ArrayList();

        // array.add("hello");
        // array.add(10);

        Class c = array.getClass(); // 集合ArrayList的class文件對象
        Method m = c.getMethod("add", Object.class);

        m.invoke(array, "hello"); // 調用array的add方法,傳入的值是hello
        m.invoke(array, "world");
        m.invoke(array, "java");

        System.out.println(array);
    }
}

 

寫一個方法,
public void setProperty(Object obj, String propertyName, Object value){}, 此方法可將obj對象中名為propertyName的屬性的值設置為value。
public class Tool {
    public void setProperty(Object obj, String propertyName, Object value)
            throws NoSuchFieldException, SecurityException,
            IllegalArgumentException, IllegalAccessException {
        // 根據對象獲取字節碼文件對象
        Class c = obj.getClass();
        // 獲取該對象的propertyName成員變量
        Field field = c.getDeclaredField(propertyName);
        // 取消訪問檢查
        field.setAccessible(true);
        // 給對象的成員變量賦值為指定的值
        field.set(obj, value);
    }
}




public class ToolDemo {
    public static void main(String[] args) throws NoSuchFieldException,
            SecurityException, IllegalArgumentException, IllegalAccessException {
        Person p = new Person();
        Tool t = new Tool();
        t.setProperty(p, "name", "林青霞");
        t.setProperty(p, "age", 27);
        System.out.println(p);
        System.out.println("-----------");

        Dog d = new Dog();

        t.setProperty(d, "sex", '男');
        t.setProperty(d, "price", 12.34f);

        System.out.println(d);
    }
}

class Dog {
    char sex;
    float price;

    @Override
    public String toString() {
        return sex + "---" + price;
    }
}

class Person {
    private String name;
    public int age;

    @Override
    public String toString() {
        return name + "---" + age;
    }
}

 

動態代理

代理:本來應該自己做的事情,卻請了別人來做,被請的人就是代理對象。
舉例:春季回家買票讓人代買

動態代理:在程序運行過程中產生的這個對象

而程序運行過程中產生對象其實就是我們剛才反射講解的內容,所以,動態代理其實就是通過反射來生成一個代理

在Java中java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口,通過使用這個類和接口就可以生成動態代理對象。JDK提供的代理只能針對接口做代理。我們有更強大的代理cglib

Proxy類中的方法創建動態代理類對象

public static Object newProxyInstance(ClassLoader loader,Class
public class MyInvocationHandler implements InvocationHandler {
    private Object target; // 目標對象

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("權限校驗");
        Object result = method.invoke(target, args);
        System.out.println("日志記錄");
        return result; // 返回的是代理對象
    }
}


public interface StudentDao {
    public abstract void login();

    public abstract void regist();
}



public class StudentDaoImpl implements StudentDao {

    @Override
    public void login() {
        System.out.println("登錄功能");
    }

    @Override
    public void regist() {
        System.out.println("注冊功能");
    }

}



/*
 * 用戶操作接口
 */
public interface UserDao {
    public abstract void add();

    public abstract void delete();

    public abstract void update();

    public abstract void find();
}



public class UserDaoImpl implements UserDao {

    @Override
    public void add() {
        System.out.println("添加功能");
    }

    @Override
    public void delete() {
        System.out.println("刪除功能");
    }

    @Override
    public void update() {
        System.out.println("修改功能");
    }

    @Override
    public void find() {
        System.out.println("查找功能");
    }

}



public class Test {
    public static void main(String[] args) {
        UserDao ud = new UserDaoImpl();
        ud.add();
        ud.delete();
        ud.update();
        ud.find();
        System.out.println("-----------");
        // 我們要創建一個動態代理對象
        // 我准備對ud對象做一個代理對象
        MyInvocationHandler handler = new MyInvocationHandler(ud);
        UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass()
                .getClassLoader(), ud.getClass().getInterfaces(), handler);
        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.find();
        System.out.println("-----------");

        StudentDao sd = new StudentDaoImpl();
        MyInvocationHandler handler2 = new MyInvocationHandler(sd);
        StudentDao proxy2 = (StudentDao) Proxy.newProxyInstance(sd.getClass()
                .getClassLoader(), sd.getClass().getInterfaces(), handler2);
        proxy2.login();
        proxy2.regist();
    }
}

 


 

模版設計模式(抽象類)

模版方法模式就是定義一個算法的骨架,而將具體的算法延遲到子類中來實現 優點
使用模版方法模式,在定義算法骨架的同時,可以很靈活的實現具體的算法,滿足用戶靈活多變的需求 缺點
如果算法骨架有修改的話,則需要修改抽象類
public class ForDemo extends GetTime {

    @Override
    public void code() {
        for (int x = 0; x < 100000; x++) {
            System.out.println(x);
        }
    }

}



public abstract class GetTime {
    // 需求:請給我計算出一段代碼的運行時間
    public long getTime() {
        long start = System.currentTimeMillis();
        code();

        long end = System.currentTimeMillis();

        return end - start;
    }

    public abstract void code();
}



public class GetTimeDemo {
    public static void main(String[] args) {
        // GetTime gt = new GetTime();
        // System.out.println(gt.getTime() + "毫秒");

        GetTime gt = new ForDemo();
        System.out.println(gt.getTime() + "毫秒");

        gt = new IODemo();
        System.out.println(gt.getTime() + "毫秒");
    }
}




public class IODemo extends GetTime{

    @Override
    public void code() {
        try {
            BufferedInputStream bis = new BufferedInputStream(
                    new FileInputStream("a.avi"));
            BufferedOutputStream bos = new BufferedOutputStream(
                    new FileOutputStream("b.avi"));
            byte[] bys = new byte[1024];
            int len = 0;
            while ((len = bis.read(bys)) != -1) {
                bos.write(bys, 0, len);
            }
            bos.close();
            bis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

 

裝飾設計模式

裝飾模式就是使用被裝飾類的一個子類的實例,在客戶端將這個子類的實例交給裝飾類。是繼承的替代方案 優點
使用裝飾模式,可以提供比繼承更靈活的擴展對象的功能,它可以動態的添加對象的功能,並且可以隨意的組合這些功能 缺點
正因為可以隨意組合,所以就可能出現一些不合理的邏輯
public interface Phone {
    public abstract void call();
}



public class IPhone implements Phone {

    @Override
    public void call() {
        System.out.println("手機可以打電話了");
    }

}



public class MusicPhoneDecorate extends PhoneDecorate {

    public MusicPhoneDecorate(Phone p) {
        super(p);
    }

    @Override
    public void call() {
        super.call();
        System.out.println("手機可以聽音樂");
    }
}





public class RingPhoneDecorate extends PhoneDecorate {

    public RingPhoneDecorate(Phone p) {
        super(p);
    }

    @Override
    public void call() {
        System.out.println("手機可以聽彩鈴");
        super.call();
    }
}



public abstract class PhoneDecorate implements Phone {

    private Phone p;

    public PhoneDecorate(Phone p) {
        this.p = p;
    }

    @Override
    public void call() {
        this.p.call();
    }
}




public class PhoneDemo {
    public static void main(String[] args) {
        Phone p = new IPhone();
        p.call();
        System.out.println("------------");

        // 需求:我想在接電話前,聽彩鈴
        PhoneDecorate pd = new RingPhoneDecorate(p);
        pd.call();
        System.out.println("------------");

        // 需求:我想在接電話後,聽音樂
        pd = new MusicPhoneDecorate(p);
        pd.call();
        System.out.println("------------");

        // 需求:我要想手機在接前聽彩鈴,接後聽音樂
        // 自己提供裝飾類,在打電話前聽彩鈴,打電話後聽音樂
        pd = new RingPhoneDecorate(new MusicPhoneDecorate(p));
        pd.call();
        System.out.println("----------");
        // 想想我們在IO流中的使用
        // InputStream is = System.in;
        // InputStreamReader isr = new InputStreamReader(is);
        // BufferedReader br = new BufferedReader(isr);
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter((new OutputStreamWriter(
                System.out)));

        Scanner sc = new Scanner(System.in);
    }
}

 


 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved