>  기사  >  类库下载  >  클래스 로딩 메커니즘과 반영

클래스 로딩 메커니즘과 반영

高洛峰
高洛峰원래의
2016-10-20 11:14:511769검색

클래스 로딩 메커니즘과 반영

이 장에서는 java.lang.reflect 패키지의 인터페이스와 클래스에 중점을 둡니다.

클래스 로딩 메커니즘과 반영

프로그램이 특정 클래스를 사용할 때 , 클래스가 메모리에 로드되지 않은 경우 시스템은 로드, 연결 및 초기화의 세 단계를 거쳐 클래스를 초기화합니다.

클래스를 로드할 때 클래스의 클래스 파일을 읽어들이는 것을 의미합니다. 즉, 프로그램에서 어떤 클래스가 사용될 때 시스템은 이에 대한 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를 작성합니다. (문자열 이름).

예를 들어 특정 클래스를 동적으로 로드하려고 합니다(이 클래스가 메모리에 있으면 메모리에서 가져오고, 메모리에 없으면 해당 클래스를 로드합니다). (먼저 클래스 파일이 있는지 확인해야 할지 잘 모르겠습니다. 소스코드와 제가 직접 테스트한 결과를 보면 체크가 없는 것으로 나타났습니다.)

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

출력 결과는

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

이전에 동적으로 로드된 클래스는 다음과 같이 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 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);
    }
}

클래스 로딩 메커니즘과 반영

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);
}
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();
    }
}
--------------结果-----------------
=====模拟第一个通用方法=====
我是一只猎狗
=====模拟通用方法二=====
=====模拟第一个通用方法=====
我奔跑迅速
=====模拟通用方法二=====

클래스 로딩 메커니즘과 반영

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("获取泛型类型出错!");
        }
    }
}


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.