Heim >类库下载 >java类库 >Klassenlademechanismus und Reflexion

Klassenlademechanismus und Reflexion

高洛峰
高洛峰Original
2016-10-20 11:14:511806Durchsuche

Klassenlademechanismus und Reflexion

Dieses Kapitel konzentriert sich auf die Schnittstellen und Klassen unter dem java.lang.reflect-Paket

Klassenlademechanismus und Reflexion

Wenn ein Programm eine bestimmte Klasse verwendet , wenn Die Klasse wurde nicht in den Speicher geladen, dann initialisiert das System die Klasse durch drei Schritte: Laden, Verbinden und Initialisieren.

Beim Laden einer Klasse bedeutet dies, dass die Klassendatei der Klasse eingelesen wird den Speicher und erstellen Sie ein java.lang.class-Objekt dafür. Das heißt, wenn eine Klasse im Programm verwendet wird, erstellt das System ein java.lang.Class-Objekt dafür (fast alle Klassen sind Instanzen von java.lang.Class );

Klassenlademechanismus und Reflexion

Klassenlademechanismus und Reflexion

Klassenlademechanismus und Reflexion

Klassenlademechanismus und Reflexion

So Die JVM wird zuerst initialisiert. Immer die Klasse java.long.Object.

In Java wird eine Klasse durch ihren vollständig qualifizierten Klassennamen identifiziert (einschließlich Paketname und Klassenname); in der JVM ist dies jedoch der Fall identifiziert durch seinen vollständig qualifizierten Klassennamen und den Klassenlader als seinen eindeutigen Bezeichner

Klassenlademechanismus und Reflexion

Stammklassenlader drucken:

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

Erweiterter Klassenlader, das kann zu Ihrem eigenen JAR-Paket hinzugefügt werden, macht ziemlich viel Spaß.

Klassenlademechanismus und Reflexion

Systemklassenlader: Dies ist der übergeordnete Lader unserer üblichen benutzerdefinierten Klassen

Klassenlademechanismus und Reflexion

Klassenlademechanismus und Reflexion

Klassenlademechanismus und Reflexion

Entwickler müssen benutzerdefinierte Klassenlader implementieren, indem sie ClassLoader erben.

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());
    }

Klassenlademechanismus und Reflexion

Klassenlademechanismus und Reflexion

Beispiel eines benutzerdefinierten Klassenladers:

Da der URLClassLoader von Java8.0.51 die folgende Methode in der ClassLoader-Klasse überschreibt,

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

Also, Ich habe den Grund noch nicht herausgefunden. Wenn wir findClass (String-Name) selbst umschreiben, ruft das Programm zuerst die findClass-Methode in URLClassLoader auf (Stringname).

Zum Beispiel möchten wir eine bestimmte Klasse dynamisch laden (wenn diese Klasse im Speicher vorhanden ist, dann holen Sie sie aus dem Speicher; wenn sie nicht im Speicher vorhanden ist, dann laden Sie sie java-Datei dieser Klasse und kompiliere sie (ich bin mir nicht sicher, ob ich zuerst prüfen muss, ob eine Klassendatei vorhanden ist. Wenn ich mir den Quellcode und meine eigenen Testergebnisse ansehe, stellt sich heraus, dass es keine Prüfung gibt)).

Beispiel

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 kongyeeku@163.com
 * @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);
    }
}

Das Ausgabeergebnis ist

tes22t2
运行Hello的参数:java疯狂讲义w

Der Haltepunkt ist sichtbar und unsere benutzerdefinierte findClass(String tmpName)-Methode wird nicht aufgerufen.

当我们把
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);

Ausgabeergebnis:

Hello1/Users/liuxin/work/workspace2/learnJava/src/Hello.java
tes22t2
运行Hello的参数:java疯狂讲义w

Hier haben wir Wie Sie sehen können, müssen Sie die findClass-Methode nicht selbst überschreiben, sondern nur die folgender Code:

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-Klasse

Java bietet ClassLoader Eine URLClassLoader-Implementierungsklasse, die auch die übergeordnete Klasse des Systemklassenladers und des Erweiterungsklassenladers (die übergeordnete Klasse) ist bezieht sich hier auf die Vererbungsbeziehung zwischen Klassen. URLClassLoader ist leistungsstark und kann von lokalen oder Remote-Hosts abgerufen werden. Binärdateien zum Laden von Klassen Die vorherige dynamisch geladene Klasse kann mit URLClassLoader wie folgt umgeschrieben werden: (Um den Pfad zu verstehen, fügen Sie Hello.java in das LearnJava-Paket ein)

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 kongyeeku@163.com
 * @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 (&#39;java8&#39;, &#39;2&#39;)";
                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();
            }
    }
}

18.3 Klasseninformationen anzeigen durch Reflexion
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);
    }

Wann wird Reflexion eingesetzt?Klassenlademechanismus und Reflexion

Informationen aus der Klasse einholen, die Methoden sind in folgende Kategorien unterteilt:

1. Holen Sie sich den KonstruktorKlassenlademechanismus und Reflexion

2. Holen Sie sich die Methode

3 .Get Attribute

Die oben genannten drei Arten von Methoden, jeder Typ ist in 4 Methoden unterteilt, zum Beispiel: (einzelne , mehrfach) * (mit Erlaubnis, unabhängig von der Erlaubnis)

4. Holen Sie sich Anmerkungen, es gibt zu viele:

Klassenlademechanismus und Reflexion

5. Holen Sie sich interne Kurse

Class>[] getDeclaredClasses():返回该Class对象对应类包含的全部内部类

6.获取外部类

Class>[] getDeclaringClasse():返回该Class对象对应类所在的外部类

7.获取接口

Class>[] getInterfaces():返回该Class对象对应类所实现的全部接口

8.其他的如下:

Klassenlademechanismus und Reflexion

 例子:

// 定义可重复注解
@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 新增的方法参数反射

Klassenlademechanismus und Reflexion

关于反射方法的参数的名字,这个比较麻烦,如下的例子

Klassenlademechanismus und Reflexion

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 使用反射生成并操作对象

Klassenlademechanismus und Reflexion

Klassenlademechanismus und Reflexion

例子:这个个别地方没理解,但是这是一个反射的典型应用,需要好好理解

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 kongyeeku@163.com
 * @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);
    }
}

Klassenlademechanismus und Reflexion

18.4.3 访问成员变量值

Klassenlademechanismus und Reflexion

例子:

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类操作数组

Klassenlademechanismus und Reflexion

例子:

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动态代理或动态代理对象.

Klassenlademechanismus und Reflexion

动态代理的例子:

interface Person
{
    void walk();
    void sayHello(String name);
}
class MyInvokationHandler implements InvocationHandler
{
    /*
    执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法
    其中:
    proxy:代表动态代理对象
    method:代表正在执行的方法
    args:代表调用目标方法时传入的实参。
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
    {
        System.out.println("----正在执行的方法:" + method);
        if (args != null)
        {
            System.out.println("下面是执行该方法时传入的实参为:");
            for (Object val : args)
            {
                System.out.println(val);
            }
        }
        else
        {
            System.out.println("调用该方法没有实参!");
        }
        return null;
    }
}
public class ProxyTest
{
    public static void main(String[] args)
        throws Exception
    {
        // 创建一个InvocationHandler对象
        InvocationHandler handler = new MyInvokationHandler();
        // 使用指定的InvocationHandler来生成一个动态代理对象
        Person p = (Person)Proxy.newProxyInstance(Person.class.getClassLoader()
            , new Class[]{Person.class}, handler);
        // 调用动态代理对象的walk()和sayHello()方法
        p.walk();
        p.sayHello("孙悟空");
    }
}

18.5.2 动态代理和AOP

一个AOP的实现例子:

public interface Dog
{
    // info方法声明
    void info();
    // run方法声明
    void run();
}
public class GunDog implements Dog
{
    // 实现info()方法,仅仅打印一个字符串
    public void info()
    {
        System.out.println("我是一只猎狗");
    }
    // 实现run()方法,仅仅打印一个字符串
    public void run()
    {
        System.out.println("我奔跑迅速");
    }
}
public class DogUtil
{
    // 第一个拦截器方法
    public void method1()
    {
        System.out.println("=====模拟第一个通用方法=====");
    }
    // 第二个拦截器方法
    public void method2()
    {
        System.out.println("=====模拟通用方法二=====");
    }
}
public class MyInvokationHandler implements InvocationHandler
{
    // 需要被代理的对象
    private Object target;
    public void setTarget(Object target)
    {
        this.target = target;
    }
    // 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Exception
    {
        DogUtil du = new DogUtil();
        // 执行DogUtil对象中的method1。
        du.method1();
        // 以target作为主调来执行method方法
        Object result = method.invoke(target , args);
        // 执行DogUtil对象中的method2。
        du.method2();
        return result;
    }
}
public class MyProxyFactory
{
    // 为指定target生成动态代理对象
    public static Object getProxy(Object target)
        throws Exception
        {
        // 创建一个MyInvokationHandler对象
        MyInvokationHandler handler =
        new MyInvokationHandler();
        // 为MyInvokationHandler设置target对象
        handler.setTarget(target);
        // 创建、并返回一个动态代理
        return Proxy.newProxyInstance(target.getClass().getClassLoader()
            , target.getClass().getInterfaces() , handler);
    }
}
public class Test
{
    public static void main(String[] args)
        throws Exception
    {
        // 创建一个原始的GunDog对象,作为target
        Dog target = new GunDog();
        // 以指定的target来创建动态代理
        Dog dog = (Dog)MyProxyFactory.getProxy(target);
        dog.info();
        dog.run();
    }
}
--------------结果-----------------
=====模拟第一个通用方法=====
我是一只猎狗
=====模拟通用方法二=====
=====模拟第一个通用方法=====
我奔跑迅速
=====模拟通用方法二=====

Klassenlademechanismus und Reflexion

18.6 反射和泛型

例子1:泛型工厂类

public class CrazyitObjectFactory2
{
    public static <T> T getInstance(Class<T> cls)
    {
        try
        {
            return cls.newInstance();
        }
        catch(Exception e)
        {
            e.printStackTrace();
            return null;
        }
    }
    public static void main(String[] args)
    {
        // 获取实例后无须类型转换
        Date d = CrazyitObjectFactory2.getInstance(Date.class);
        JFrame f = CrazyitObjectFactory2.getInstance(JFrame.class);
    }
}

例子2:使用反射来获取泛型信息

public class GenericTest
{
    private Map<String , Integer> score;
    public static void main(String[] args)
        throws Exception
    {
        Class<GenericTest> clazz = GenericTest.class;
        Field f = clazz.getDeclaredField("score");
        // 直接使用getType()取出的类型只对普通类型的成员变量有效
        Class<?> a = f.getType();
        // 下面将看到仅输出java.util.Map
        System.out.println("score的类型是:" + a);
        // 获得成员变量f的泛型类型
        Type gType = f.getGenericType();
        // 如果gType类型是ParameterizedType对象
        if(gType instanceof ParameterizedType)
        {
            // 强制类型转换
            ParameterizedType pType = (ParameterizedType)gType;
            // 获取原始类型
            Type rType = pType.getRawType();
            System.out.println("原始类型是:" + rType);
            // 取得泛型类型的泛型参数
            Type[] tArgs = pType.getActualTypeArguments();
            System.out.println("泛型信息是:");
            for (int i = 0; i < tArgs.length; i++)
            {
                System.out.println("第" + i + "个泛型类型是:" + tArgs[i]);
            }
        }
        else
        {
            System.out.println("获取泛型类型出错!");
        }
    }
}


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn