java8--類載入機制與反射(java瘋狂講義3複習筆記)

来源:http://www.cnblogs.com/lakeslove/archive/2016/10/19/5978382.html
-Advertisement-
Play Games

本章重點介紹java.lang.reflect包下的介面和類 當程式使用某個類時,如果該類還沒有被載入到記憶體中,那麼系統會通過載入,連接,初始化三個步驟來對該類進行初始化. 類的載入時指將類的class文件讀入記憶體,併為之創建一個java.lang.class對象,也就是說,當程式中使用任何類時,系 ...


  

本章重點介紹java.lang.reflect包下的介面和類

當程式使用某個類時,如果該類還沒有被載入到記憶體中,那麼系統會通過載入,連接,初始化三個步驟來對該類進行初始化.

類的載入時指將類的class文件讀入記憶體,併為之創建一個java.lang.class對象,也就是說,當程式中使用任何類時,系統都會為之建立一個java.lang.Class對象.(幾乎所有的類都是java.lang.Class的實例);

所以JVM最先初始化的總是java.long.Object類.

在java中,一個類用其全限定類名(包括包名和類名)作為標識;但在JVM中,一個類用其全限定類名和類載入器作為其唯一標識.

列印根類載入器:

public class BootstrapTest
{
    public static void main(String[] args)
    {
        // 獲取根類載入器所載入的全部URL數組
        for (URL url : sun.misc.Launcher.getBootstrapClassPath().getURLs()) {
            // 遍歷、輸出根類載入器載入的全部URL
            System.out.println(url.toExternalForm());
        }
    }
}

----------------------------------------------------------------------
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/resources.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/rt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/jsse.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/jce.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/charsets.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/jre/lib/jfr.jar

擴展類載入器,這個可以加入自己的jar包,挺好玩的.

系統類載入器:這個就是我們平常自定義類的父載入器了

開發者實現自定義的類載入器需要通過繼承ClassLoader來實現.

public static void main(String[] args)
        throws IOException
    {
        // 獲取系統類載入器
        ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
        System.out.println("系統類載入器:" + systemLoader);
        /*
        獲取系統類載入器的載入路徑——通常由CLASSPATH環境變數指定
        如果操作系統沒有指定CLASSPATH環境變數,預設以當前路徑作為
        系統類載入器的載入路徑
        */
        Enumeration<URL> em1 = systemLoader.getResources("");
        while(em1.hasMoreElements())
        {
            System.out.println(em1.nextElement());
        }
        // 獲取系統類載入器的父類載入器:得到擴展類載入器
        ClassLoader extensionLader = systemLoader.getParent();
        System.out.println("擴展類載入器:" + extensionLader);
        System.out.println("擴展類載入器的載入路徑:"
            + System.getProperty("java.ext.dirs"));
        System.out.println("擴展類載入器的parent: "
            + extensionLader.getParent());
    }

 自定義類載入器的例子:

由於java8.0.51的URLClassLoader里重寫了ClassLoader這個類里的下麵這個方法,

protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

所以,現在還沒弄明白原因,如果我們自己重寫findClass(String name),結果就是程式會先調用URLClassLoader里的findClass方法,如果這個方法找不到類,才會調用我們自己寫的findClass(String name).

比如我們要動態載入某個類(如果記憶體存在這個類,那麼閑從記憶體取;如果記憶體中不存在,那麼載入那個類的java文件並編譯(我不確定是不是要先檢查是否有class文件,看源碼和自己的測試結果是沒有檢查)).

例子

public class Hello
{
    public static void main(String[] args)
    {
        System.out.println("tes22t2");
        for (String arg : args)
        {
            System.out.println("運行Hello的參數:" + arg);
        }
    }
}
import java.io.*;
import java.lang.reflect.*;
import java.net.URL;
import java.security.CodeSigner;
import java.security.CodeSource;
import java.util.jar.Manifest;

import sun.misc.Resource;
import sun.misc.URLClassPath;

import com.sun.xml.internal.bind.annotation.OverrideAnnotationOf;
/**
 * Description:
 * <br/>網站: <a href="http://www.crazyit.org">瘋狂Java聯盟</a>
 * <br/>Copyright (C), 2001-2016, Yeeku.H.Lee
 * <br/>This program is protected by copyright laws.
 * <br/>Program Name:
 * <br/>Date:
 * @author Yeeku.H.Lee [email protected]
 * @version 1.0
 */
public class CompileClassLoader extends ClassLoader
{
    // 讀取一個文件的內容
    private byte[] getBytes(String filename)
        throws IOException
    {
        File file = new File(filename);
        long len = file.length();
        byte[] raw = new byte[(int)len];
        try(
            FileInputStream fin = new FileInputStream(file))
        {
            // 一次讀取class文件的全部二進位數據
            int r = fin.read(raw);
            if(r != len)
            throw new IOException("無法讀取全部文件:"
                + r + " != " + len);
            return raw;
        }
    }
    // 定義編譯指定Java文件的方法
    private boolean compile(String javaFile)
        throws IOException
    {
        System.out.println("CompileClassLoader:正在編譯 "
            + javaFile + "...");
        // 調用系統的javac命令
        Process p = Runtime.getRuntime().exec("javac " + javaFile);
        try
        {
            // 其他線程都等待這個線程完成
            p.waitFor();
        }
        catch(InterruptedException ie)
        {
            System.out.println(ie);
        }
        // 獲取javac線程的退出值
        int ret = p.exitValue();
        // 返回編譯是否成功
        return ret == 0;
    }
    // 重寫ClassLoader的findClass方法
    @Override
    protected Class<?> findClass(String tmpName)
        throws ClassNotFoundException
    {
        System.out.println(tmpName);
        Class clazz = null;
        // 將包路徑中的點(.)替換成斜線(/);
        String className = tmpName.replace("." , "/").replace("1" , "");
     // 這裡因為我是在eclipse里編輯的,而java文件統一放到src里,class文件統一放到了bin里, 所以我需要加首碼, String javaFilename
= "src/"+className + ".java"; String classFilename = "bin/"+className + ".class"; File javaFile = new File(javaFilename); System.out.println(javaFile.getAbsolutePath()); File classFile = new File(classFilename); // 當指定Java源文件存在,且class文件不存在、或者Java源文件 // 的修改時間比class文件修改時間更晚,重新編譯 if(javaFile.exists() && (!classFile.exists() || javaFile.lastModified() > classFile.lastModified())) { try { // 如果編譯失敗,或者該Class文件不存在 if(!compile(javaFilename) || !classFile.exists()) { throw new ClassNotFoundException( "ClassNotFoundExcetpion:" + javaFilename); } } catch (IOException ex) { ex.printStackTrace(); } } // 如果class文件存在,系統負責將該文件轉換成Class對象 if (classFile.exists()) { try { // 將class文件的二進位數據讀入數組 byte[] raw = getBytes(classFilename); // 調用ClassLoader的defineClass方法將二進位數據轉換成Class對象 clazz = defineClass(className,raw,0,raw.length); } catch(IOException ie) { ie.printStackTrace(); } } // 如果clazz為null,表明載入失敗,則拋出異常 if(clazz == null) { throw new ClassNotFoundException(className); } return clazz; } // 定義一個主方法 public static void main(String[] args) throws Exception { // 如果運行該程式時沒有參數,即沒有目標類 args = new String[]{"Hello","java瘋狂講義w"}; // 第一個參數是需要運行的類 String progClass = args[0]; // 剩下的參數將作為運行目標類時的參數, // 將這些參數複製到一個新數組中 String[] progArgs = new String[args.length-1]; System.arraycopy(args , 1 , progArgs , 0 , progArgs.length); CompileClassLoader ccl = new CompileClassLoader(); // 載入需要運行的類 Class<?> clazz = ccl.loadClass(progClass); // 獲取需要運行的類的主方法 Method main = clazz.getMethod("main" , (new String[0]).getClass()); Object[] argsArray = {progArgs}; main.invoke(null,argsArray); } }

輸出結果是

tes22t2
運行Hello的參數:java瘋狂講義w

打斷點可見,並沒有調用我們自定義的findClass(String tmpName)方法.

當我們把
args = new String[]{"Hello","java瘋狂講義w"};

改為
args = new String[]{"Hello1","java瘋狂講義w"};

 時,由於URLClassLoader並沒有找到被載入的類Hello1,所以最後會調用我們自定義的findClass方法.

當然,更規範的是把
 Class<?> clazz = ccl.loadClass(progClass);
改為
Class<?> clazz = ccl.findClass(progClass);

輸出結果:

Hello1
/Users/liuxin/work/workspace2/learnJava/src/Hello.java
tes22t2
運行Hello的參數:java瘋狂講義w

這裡我們可以看到,如果要動態載入某個類,不用自己覆寫findClass方法,只要如下代碼就好:

import java.io.*;
import java.lang.reflect.*;
import java.net.URL;
import java.security.CodeSigner;
import java.security.CodeSource;
import java.util.jar.Manifest;

import sun.misc.Resource;
import sun.misc.URLClassPath;

import com.sun.xml.internal.bind.annotation.OverrideAnnotationOf;
public class CompileClassLoader extends ClassLoader
{
    public static void main(String[] args) throws Exception
    {
        // 如果運行該程式時沒有參數,即沒有目標類
        args = new String[]{"Hello","java瘋狂講義w"};
        // 第一個參數是需要運行的類
        String progClass = args[0];
        // 剩下的參數將作為運行目標類時的參數,
        // 將這些參數複製到一個新數組中
        String[] progArgs = new String[args.length-1];
        System.arraycopy(args , 1 , progArgs
            , 0 , progArgs.length);
        CompileClassLoader ccl = new CompileClassLoader();
        // 載入需要運行的類
        Class<?> clazz = ccl.loadClass(progClass);
        // 獲取需要運行的類的主方法
        Method main = clazz.getMethod("main" , (new String[0]).getClass());
        Object[] argsArray = {progArgs};
        main.invoke(null,argsArray);
    }
}

----------------輸出結果--------------------
tes22t2
運行Hello的參數:java瘋狂講義w

 

18.2.4 URLClassLoader類

java為ClassLoader提供了一個URLClassLoader實現類,該類也是系統類載入器和擴展類載入器的父類(此處的父類,就是指類與類之間的繼承關係).URLClassLoader功能強大,可以從本地或遠程主機獲取二進位文件來載入類.

下麵是一個例子

import java.sql.*;
import java.util.*;
import java.net.*;
/**
 * Description:
 * <br/>網站: <a href="http://www.crazyit.org">瘋狂Java聯盟</a>
 * <br/>Copyright (C), 2001-2016, Yeeku.H.Lee
 * <br/>This program is protected by copyright laws.
 * <br/>Program Name:
 * <br/>Date:
 * @author Yeeku.H.Lee [email protected]
 * @version 1.0
 */
public class URLClassLoaderTest
{
    private static Connection conn;
    // 定義一個獲取資料庫連接方法
    public static Connection getConn(String url ,
        String user , String pass) throws Exception
    {
        if (conn == null)
        {
            // 創建一個URL數組
            URL[] urls = {new URL(
                "file:mysql-connector-java-5.1.30-bin.jar")};
            // 以預設的ClassLoader作為父ClassLoader,創建URLClassLoader
            URLClassLoader myClassLoader = new URLClassLoader(urls);
            // 載入MySQL的JDBC驅動,並創建預設實例
            Driver driver = (Driver)myClassLoader.
                loadClass("com.mysql.jdbc.Driver").newInstance();
            // 創建一個設置JDBC連接屬性的Properties對象
            Properties props = new Properties();
            // 至少需要為該對象傳入user和password兩個屬性
            props.setProperty("user" , user);
            props.setProperty("password" , pass);
            // 調用Driver對象的connect方法來取得資料庫連接
            conn = driver.connect(url , props);
        }
        return conn;
    }
    public static void main(String[] args)throws Exception
    {
        Connection temconn = getConn("jdbc:mysql://localhost:3306/mybatis"
                , "root" , "password");
            try{
                String sql = "INSERT INTO `mybatis`.`users` (`NAME`, `age`) VALUES ('java8', '2')";
                java.sql.PreparedStatement stmt = temconn.prepareStatement(sql);
                stmt.execute();
                stmt.close();
                String sql2 = "select * from `mybatis`.`users`";
                java.sql.PreparedStatement stmt2 = temconn.prepareStatement(sql2);
                ResultSet rs = stmt2.executeQuery();
                ResultSetMetaData m=rs.getMetaData();
                //顯示列,表格的表頭
                int columns=m.getColumnCount();
                for(int i=1;i<=columns;i++)
                   {
                    System.out.print(m.getColumnName(i));
                    System.out.print("\t\t");
                   }
                System.out.println();
                   //顯示表格內容
                   while(rs.next())
                   {
                    for(int i=1;i<=columns;i++)
                    {
                     System.out.print(rs.getString(i));
                     System.out.print("\t\t");
                    }
                    System.out.println();
                   }
                stmt2.close();
            }catch(Exception e){
                e.printStackTrace();
            }finally{
                temconn.close();
            }
    }
}

所以前面的動態載入類,可以用URLClassLoader改寫如下:(為了弄明白路徑,把Hello.java放到了learnJava包里去了)

public static void main(String[] args) throws Exception
    {
        args = new String[]{"learnJava.Hello","java瘋狂講義w"};
        String progClass = args[0];
        String[] progArgs = new String[args.length-1];
        System.arraycopy(args , 1 , progArgs, 0 , progArgs.length);
        URL[] urls = {new URL("file:")};
        Class<?> clazz = (new URLClassLoader(urls)).loadClass(progClass);
        // 獲取需要運行的類的主方法
        Method main = clazz.getMethod("main" , (new String[0]).getClass());
        Object[] argsArray = {progArgs};
        main.invoke(null,argsArray);
    }

18.3 通過反射來查看類的信息

什麼時候會用到反射?

從Class中獲取信息,方法分以下幾類:

1.獲取構造器

2.獲取方法

3.獲取屬性

上面這三類方法,每一類都分4個方法,例如:(單個,多個) * (按許可權,不顧許可權)

4.獲取註解,這個太多:

5.獲取內部類

Class<?>[] getDeclaredClasses():返回該Class對象對應類包含的全部內部類

6.獲取外部類

Class<?>[] getDeclaringClasse():返回該Class對象對應類所在的外部類

7.獲取介面

Class<?>[] getInterfaces():返回該Class對象對應類所實現的全部介面

8.其他的如下:

 例子:

// 定義可重覆註解
@Repeatable(Annos.class)
@interface Anno {}
@Retention(value=RetentionPolicy.RUNTIME)
@interface Annos {
    Anno[] value();
}
// 使用4個註解修飾該類
@SuppressWarnings(value="unchecked")
@Deprecated
// 使用重覆註解修飾該類
@Anno
@Anno
public class ClassTest
{
    // 為該類定義一個私有的構造器
    private ClassTest()
    {
    }
    // 定義一個有參數的構造器
    public ClassTest(String name)
    {
        System.out.println("執行有參數的構造器");
    }
    // 定義一個無參數的info方法
    public void info()
    {
        System.out.println("執行無參數的info方法");
    }
    // 定義一個有參數的info方法
    public void info(String str)
    {
        System.out.println("執行有參數的info方法"
            + ",其str參數值:" + str);
    }
    // 定義一個測試用的內部類
    class Inner
    {
    }
    public static void main(String[] args)
        throws Exception
    {
        // 下麵代碼可以獲取ClassTest對應的Class
        Class<?> clazz = ClassTest.class;
        // 獲取該Class對象所對應類的全部構造器
        Constructor[] ctors = clazz.getDeclaredConstructors();
        System.out.println("ClassTest的全部構造器如下:");
        for (Constructor c : ctors)
        {
            System.out.println(c);
        }
        // 獲取該Class對象所對應類的全部public構造器
        Constructor[] publicCtors = clazz.getConstructors();
        System.out.println("ClassTest的全部public構造器如下:");
        for (Constructor c : publicCtors)
        {
            System.out.println(c);
        }
        // 獲取該Class對象所對應類的全部public方法
        Method[] mtds = clazz.getMethods();
        System.out.println("ClassTest的全部public方法如下:");
        for (Method md : mtds)
        {
            System.out.println(md);
        }
        // 獲取該Class對象所對應類的指定方法
        System.out.println("ClassTest裡帶一個字元串參數的info()方法為:"
            + clazz.getMethod("info" , String.class));
        // 獲取該Class對象所對應類的上的全部註解
        Annotation[] anns = clazz.getAnnotations();
        System.out.println("ClassTest的全部Annotation如下:");
        for (Annotation an : anns)
        {
            System.out.println(an);
        }
        System.out.println("該Class元素上的@SuppressWarnings註解為:"
            + Arrays.toString(clazz.getAnnotationsByType(SuppressWarnings.class)));
        System.out.println("該Class元素上的@Anno註解為:"
            + Arrays.toString(clazz.getAnnotationsByType(Anno.class)));
        // 獲取該Class對象所對應類的全部內部類
        Class<?>[] inners = clazz.getDeclaredClasses();
        System.out.println("ClassTest的全部內部類如下:");
        for (Class c : inners)
        {
            System.out.println(c);
        }
        // 使用Class.forName方法載入ClassTest的Inner內部類
        Class inClazz = Class.forName("ClassTest$Inner");
        // 通過getDeclaringClass()訪問該類所在的外部類
        System.out.println("inClazz對應類的外部類為:" +
            inClazz.getDeclaringClass());
        System.out.println("ClassTest的包為:" + clazz.getPackage());
        System.out.println("ClassTest的父類為:" + clazz.getSuperclass());
    }
}

 

18.3.3 java8 新增的方法參數反射

關於反射方法的參數的名字,這個比較麻煩,如下的例子

class Test
{
    public void replace(String str, List<String> list){}
}
public class MethodParameterTest
{
    public static void main(String[] args)throws Exception
    {
        // 獲取Tesy的類
        Class<Test> clazz = Test.class;
        // 獲取Test類的帶兩個參數的replace()方法
        Method replace = clazz.getMethod("replace"
            , String.class, List.class);
        // 獲取指定方法的參數個數
        System.out.println("replace方法參數個數:" + replace.getParameterCount());
        // 獲取replace的所有參數信息
        Parameter[] parameters = replace.getParameters();
        System.out.println((new File("")).getAbsolutePath());
        int index = 1;
        // 遍歷所有參數
        for (Parameter p : parameters)
        {
            if (p.isNamePresent())
            {
                System.out.println("---第" + index++ + "個參數信息---");
                System.out.println("參數名:" + p.getName());
                System.out.println("形參類型:" + p.getType());
                System.out.println("泛型類型:" + p.getParameterizedType());
            }
        }
    }
}

 所以,上面這個例子在用eclipse編譯時,總是找不到參數名.沒找到設置的地方,只能用javac編譯

編譯命令如下:
javac -parameters -encoding GBK  -d . MethodParameterTest.java 

因為瘋狂java講義里的源碼都是GBK編碼,而一般的電腦預設utf-8,所以需要指定編碼方式

運行:
java MethodParameterTest
----------------------結果--------------------------
replace方法參數個數:2
/Users/liuxin/work/workspace2/learnJava/src
---第1個參數信息---
參數名:str
形參類型:class java.lang.String
泛型類型:class java.lang.String
---第2個參數信息---
參數名:list
形參類型:interface java.util.List
泛型類型:java.util.List<java.lang.String>

 

18.4 使用反射生成並操作對象

 

例子:這個個別地方沒理解,但是這是一個反射的典型應用,需要好好理解

import java.util.*;
import java.io.*;
import java.lang.reflect.*;
/**
 * Description:
 * <br/>網站: <a href="http://www.crazyit.org">瘋狂Java聯盟</a>
 * <br/>Copyright (C), 2001-2016, Yeeku.H.Lee
 * <br/>This program is protected by copyright laws.
 * <br/>Program Name:
 * <br/>Date:
 * @author Yeeku.H.Lee [email protected]
 * @version 1.0
 */
public class ExtendedObjectPoolFactory
{
    // 定義一個對象池,前面是對象名,後面是實際對象
    private Map<String ,Object> objectPool = new HashMap<>();
    private Properties config = new Properties();
    // 從指定屬性文件中初始化Properties對象
    public void init(String fileName)
    {
//        File tmpFi = new File(fileName);
//        System.out.println(tmpFi.getAbsolutePath());
        try(FileInputStream fis = new FileInputStream(fileName))
        {
            config.load(fis);
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
            System.out.println("讀取" + fileName + "異常");
        }
    }
    // 定義一個創建對象的方法,
    // 該方法只要傳入一個字元串類名,程式可以根據該類名生成Java對象
    private Object createObject(String clazzName)
        throws InstantiationException
        , IllegalAccessException , ClassNotFoundException
    {
        // 根據字元串來獲取對應的Class對象
        Class<?> clazz =Class.forName(clazzName);
        // 使用clazz對應類的預設構造器創建實例
        return clazz.newInstance();
    }
    // 該方法根據指定文件來初始化對象池,
    // 它會根據配置文件來創建對象
    public void initPool()throws InstantiationException
        ,IllegalAccessException , ClassNotFoundException
    {
        for (String name : config.stringPropertyNames())
        {
            // 每取出一對key-value對,如果key中不包含百分號(%)
            // 這就標明是根據value來創建一個對象
            // 調用createObject創建對象,並將對象添加到對象池中
            if (!name.contains("%"))
            {
                objectPool.put(name ,
                    createObject(config.getProperty(name)));
            }
        }
    }
    // 該方法將會根據屬性文件來調用指定對象的setter方法
    public void initProperty()throws InvocationTargetException
        ,IllegalAccessException,NoSuchMethodException
    {
        for (String name : config.stringPropertyNames())
        {
            // 每取出一對key-value對,如果key中包含百分號(%)
            // 即可認為該key用於控制調用對象的setter方法設置值,
            // %前半為對象名字,後半控制setter方法名
            if (name.contains("%"))
            {
                // 將配置文件中key按%分割
                String[] objAndProp = name.split("%");
                // 取出調用setter方法的參數值
                Object target = getObject(objAndProp[0]);
                // 獲取setter方法名:set + "首字母大寫" + 剩下部分
                String mtdName = "set" +
                objAndProp[1].substring(0 , 1).toUpperCase()
                    + objAndProp[1].substring(1);
                // 通過target的getClass()獲取它實現類所對應的Class對象
                Class<?> targetClass = target.getClass();
                // 獲取希望調用的setter方法
                Method mtd = targetClass.getMethod(mtdName , String.class);
                // 通過Method的invoke方法執行setter方法,
                // 將config.getProperty(name)的值作為調用setter的方法的參數
                mtd.invoke(target , config.getProperty(name));
            }
        }
    }
    public Object getObject(String name)
    {
        // 從objectPool中取出指定name對應的對象。
        return objectPool.get(name);
    }
    public static void main(String[] args)
        throws Exception
    {
        ExtendedObjectPoolFactory epf = new ExtendedObjectPoolFactory();
        epf.init("src/extObj.txt");
        epf.initPool();
        epf.initProperty();
        System.out.println(epf.getObject("a"));
    }
}

 

例子二:使用指定的構造器來構造對象.

public class CreateJFrame
{
    public static void main(String[] args)
        throws Exception
    {
        // 獲取JFrame對應的Class對象
        Class<?> jframeClazz = Class.forName("javax.swing.JFrame");
        // 獲取JFrame中帶一個字元串參數的構造器
        Constructor ctor = jframeClazz
            .getConstructor(String.class);
        // 調用Constructor的newInstance方法創建對象
        Object obj = ctor.newInstance("測試視窗");
        // 輸出JFrame對象
        System.out.println(obj);
    }
}

18.4.3 訪問成員變數值

例子:

class Person
{
    private String name;
    private int age;
    public String toString()
    {
        return "Person[name:" + name +
        " , age:" + age + " ]";
    }
}
public class FieldTest
{
    public static void main(String[] args)
        throws Exception
    {
        // 創建一個Person對象
        Person p = new Person();
        // 獲取Person類對應的Class對象
        Class<Person> personClazz = Person.class;
        // 獲取Person的名為name的成員變數
        // 使用getDeclaredField()方法表明可獲取各種訪問控制符的成員變數
        Field nameField = personClazz.getDeclaredField("name");
        // 設置通過反射訪問該成員變數時取消訪問許可權檢查
        nameField.setAccessible(true);
        // 調用set()方法為p對象的name成員變數設置值
        nameField.set(p , "Yeeku.H.Lee");
        // 獲取Person類名為age的成員變數
        Field ageField = personClazz.getDeclaredField("age");
        // 設置通過反射訪問該成員變數時取消訪問許可權檢查
        ageField.setAccessible(true);
        // 調用setInt()方法為p對象的age成員變數設置值
        ageField.setInt(p , 30);
        System.out.println(p);
    }
}

用java.lang.reflect包下的Array類操作數組

例子:

public class ArrayTest1
{
    public static void main(String args[])
    {
        try
        {
            // 創建一個元素類型為String ,長度為10的數組
            Object arr = Array.newInstance(String.class, 10);
            // 依次為arr數組中index為5、6的元素賦值
            Array.set(arr, 5, "瘋狂Java講義");
            Array.set(arr, 6, "輕量級Java EE企業應用實戰");
            // 依次取出arr數組中index為5、6的元素的值
            Object book1 = Array.get(arr , 5);
            Object book2 = Array.get(arr , 6);
            // 輸出arr數組中index為5、6的元素
            System.out.println(book1);
            System.out.println(book2);
        }
        catch (Throwable e)
        {
            System.err.println(e);
        }
    }
}

操作多維數組的例子:

public class ArrayTest2
{
    public static void main(String args[])
    {
        /*
          創建一個三維數組。
          根據前面介紹數組時講的:三維數組也是一維數組,
          是數組元素是二維數組的一維數組,
          因此可以認為arr是長度為3的一維數組
        */
        Object arr = Array.newInstance(String.class, 3, 4, 10);
        // 獲取arr數組中index為2的元素,該元素應該是二維數組
        Object arrObj = Array.get(arr, 2);
        // 使用Array為二維數組的數組元素賦值。二維數組的數組元素是一維數組,
        // 所以傳入Array的set()方法的第三個參數是一維數組。
        Array.set(arrObj , 2 , new String[]
        {
            "瘋狂Java講義",
            "輕量級Java EE企業應用實戰"
        });
        // 獲取arrObj數組中index為3的元素,該元素應該是一維數組。
        Object anArr  = Array.get(arrObj, 3);
        Array.set(anArr , 8  , "瘋狂Android講義");
        // 將arr強制類型轉換為三維數組
        String[][][] cast = (String[][][])arr;
        // 獲取cast三維數組中指定元素的值
        System.out.println(cast[2][3][8]);
        System.out.println(cast[2][2][0]);
        System.out.println(cast[2][2][1]);
    }
}

18.5 使用反射生成JDK動態代理

在java的java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler介面,通過使用這個類和介面,可生成JDK動態代理或動態代理對象.

動態代理的例子:

interface Person
{
    void walk();
    void sayHello(String name);
}

              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JavaWeb_day01 HTTP協議 HTTP(HyperText Transfer Protocol)超文本傳輸協議,是TCP/IP的應用層協議,用於定義WEB瀏覽器與WEB伺服器之間交換數據的過程以及數據本身的格式. Http協議版本號 : HTTP/1.0 HTTP/1.1 交互步驟 : ...
  • JList想添加元素,可以執行將所有元素在JList初始化時加入的靜態操作,也可以利用“列表模型”DefaultListModel處理所有列表修改細節的動態操作。 ...
  • Java Labmda表達式的一個重要用法是簡化某些匿名內部類(Anonymous Classes)的寫法。實際上Lambda表達式並不僅僅是匿名內部類的語法糖,JVM內部是通過invokedynamic指令來實現Lambda表達式的。本篇我們首先感受一下使用Lambda表達式帶來的便利之處。 ...
  • 筆者在一家互聯網公司做JavaEE開發,公司開發了移動端的產品,唯獨沒有PC端的產品,於是領導將任務分配給筆者。 使用Java開發PC客戶端,我的第一反應是使用swing API。但是,產品的需求是客戶端內嵌一個瀏覽器引擎,能夠渲染網頁內容。於是,筆者通過百度無意間發現和瞭解到JavaFX。 經過編 ...
  • 一、 hibernate是什麼 (一)hibernate 是一個orm框架,orm (object relation mapping) 對象關係映射框架 o object -> 業務層(只對對象操作) r relation-> 關係資料庫 m mapping 對象關係映射文件 Hibernate有六 ...
  • 一、插入排序 1 #-*- coding:utf-8 -*- 2 ''' 3 描述 4 插入排序的基本操作就是將一個數據插入到已經排好序的有序數據中,從而得到一個新的、個數加一的有序數據,演算法適用於少量數據的排序,時間複雜度為O(n^2)。 5 是穩定的排序方法。插入演算法把要排序的數組分成兩部分:第 ...
  • 轉載於:http://www.cnblogs.com/767355675hutaishi/p/3873770.html 題目大意:眾所周知冒泡排序演算法多數情況下不能只掃描一遍就結束排序,而是要掃描好幾遍。現在你的任務是求1~N的排列中,需要掃描K遍才能排好序的數列的個數模20100713。註意,不同 ...
  • 大家好啊,今天為大家帶來的是自己實現的用C++編寫的簡單進位轉換器,用於10進位數和8進位數,16進位數,2進位數的相互轉換. 首先,說明一下什麼是進位.n進位就是一種用來表示數值的方法,n進位,顧名思義,逢n進1.我們日常生活中使用的基本都是10進位數,逢10進1;現代電腦處理器所能處理的只能是 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...