zoukankan      html  css  js  c++  java
  • java8--类加载机制与反射(java疯狂讲义3复习笔记)

      

    本章重点介绍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 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 ('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("		");
                       }
                    System.out.println();
                       //显示表格内容
                       while(rs.next())
                       {
                        for(int i=1;i<=columns;i++)
                        {
                         System.out.print(rs.getString(i));
                         System.out.print("		");
                        }
                        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("获取泛型类型出错!");
            }
        }
    }
  • 相关阅读:
    oracle客户端plsql设置字符集
    命令导入导出oracle库
    java初级开发一系列的工具安装配置
    docker学习笔记-5.数据共享
    docker学习笔记-4.harbor
    flask学习笔记2
    socket编程学习
    docker学习笔记-3.docker镜像制作
    docker学习笔记-2.docker镜像制作
    docker学习笔记-1.docker基础
  • 原文地址:https://www.cnblogs.com/lakeslove/p/5978382.html
Copyright © 2011-2022 走看看