【17-类加载与反射】


类加载与反射



Java程序与JVM

•不管Java程序有多么复杂、该程序启动了多少个线程,它们都处于该Java虚拟机进程里。正如前面

介绍的,同一个JVM的所有线程、所有变量都处于同一个进程里,它们都使用该JVM进程的内存区。

当系统出现以下几种情况时,JVM进程将被终止:

–程序运行到最后正常结束。

–程序运行到使用System.exit()或Runtime.getRuntime().exit()代码结束程序。

–程序执行过程中遇到未捕获的异常或错误而结束。

–程序所在平台强制结束了JVM进程。 



类加载

•当程序主动使用某个类时,如果该类还未被加载到内存中,系统会通过加载、连接、初始化三个步骤

来对该类进行初始化,如果没有意外,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称

为类加载或类初始化。

•类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说当程序

使用任何类时,系统都会为之建立一个java.lang.Class对象。



 类数据的来源

•通过使用不同的类加载器,可以从不同来源加载类的二进制数据,通常有如下几种来源:

–从本地文件系统来加载class文件,这是前面绝大部分示例程序的类加载方式。

–从JAR包中加载class文件,这种方式也是很常见的,前面介绍JDBC编程时用到的数据库驱动类就是放在JAR文件中,JVM可以从JAR文件中直接加载该class文件。

–通过网络在加载class文件。

–把一个Java源文件动态编译、并执行加载。 



 类的连接

•当类被加载之后,系统为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段将会负责把类的二进制数据合并到JRE中。类连接又可分为如下三个阶段:

–验证:验证阶段用于检验被加载的类是否有正确的内部结构,并和其他类协调一致。

–准备:类准备阶段则负责为类的静态属性分配内存,并设置默认初始值。

–解析:将类的二进制数据中的符号引用替换成直接引用。



 类的初始化 

•在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对静态属性进行初始化。在Java类中对静态属性指定初始值有两种方式:

–(1)声明静态属性时指定初始值;

–(2)使用静态初始化块为静态属性指定初始值。 



 JVM初始化类的步骤

•(1)假如这个类还没有被加载和连接,程序先加载并连接该类。

•(2)假如该类的直接父类还没有被初始化,则先初始化其直接父类。

•(3)假如类中有初始化语句,则系统依次执行这些初始化语句。 



类的初始化时机

•创建类的实例。为某个类创建实例的方式包括使用new操作符来创建实例,通过反射来创建实例,通过反序列化的方式来创建实例。

•调用某个类的静态方法。

•访问某个类或接口的静态属性,或为该静态属性赋值。

•使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。例如代码:Class.forName("Person")。

•初始化某个类的子类,当初始化某个类的子类时,该子类的所有父类都会被初始化。

•直接使用java.exe命令来运行某个主类,当运行某个主类时,程序会先初始化该主类。

•final型的静态属性,如果该属性可以在编译时就得到属性值,则可认为该属性可被当成编译时常量。当程序使用编译时常量时,系统会认为这是对该类的被动使用,所以不会导致该类的初始化。 



类加载器

•类加载器负责将.class文件(可能在磁盘上,也可能在网络上)加载到内存中,并为之生成对应的java.lang.Class对象。

•当JVM启动时,会形成由三个类加载器组成的初始类加载器层次结构:

–Bootstrap ClassLoader:根类加载器。

–Extension ClassLoader:扩展类加载器。

–System ClassLoader:系统类加载器。



类加载机制 

•JVM的类加载机制主要有如下三种机制:

–全盘负责:所谓全盘负责,就是说当一个类加载器负责加载某个Class的时候,该Class所依赖的

和引用的其他Class也将由该类加载器负责载入,除非显式使用另外一个类加载器来载入。

–父类委托:所谓父类委托则是先让parent(父)类加载器试图加载该Class,只有在父类加载器

无法加载该类时才尝试从自己的类路径中加载该类。

–缓存机制:缓存机制将会保证所有被加载过的Class都会被缓存,当程序中需要使用某个Class

时,类加载器先从缓存中搜寻该Class,只有当缓存中不存在该Class对象时,系统才会读取该类

对应的二进制数据,并将其转换成Class对象,并存入cache。这就是为什么我们修改了Class

后,程序必须重新启动JVM,程序所作的修改才会生效的原因。 



 实现自定义类加载器

•ClassLoader类有如下三个关键方法:

–loadClass(String name, boolean resolve):该方法为ClassLoader的入口点,根据指定的二进制名称来加载类,系统就是调用ClassLoader的该方法来获取指定类对应的Class对象。

–findClass(String name):根据二进制名称来查找类。

•如果需要实现自定义的ClassLoader,可以通过重写以上两个方法来实现,当然我们推荐重写findClass()方法,而不是重写loadClass()方法。 



自定义的类加载器的常见功能

•执行代码前自动验证数字签名。

•根据用户提供的密码解密代码,从而可以实现代码混淆器来避免反编译class文件。

•根据用户需求来动态地加载类。

•根据应用需求把其他数据以字节码的形式加载到应用中。



URLClassLoader

•Java为ClassLoader提供了一个URLClassLoader实现类,该类也是系统类加载器和扩展类加载器

的父类(此处是父类,而不是父类加载器,这里是类与类之间的继承关系),URLClassLoader功能

比较强大,它既可以从本地文件系统获取二进制文件来加载类,也可以从远程主机获取二进制文件来加

载类。

•实际上应用程序中可以直接使用URLClassLoader来加载类,URLClassLoader类提供了如下两个

构造器:

–URLClassLoader(URL[] urls):使用默认的父类加载器创建一个ClassLoader对象,该对象

将从urls所指定的系列路径来查询、并加载类。

–URLClassLoader(URL[] urls, ClassLoader parent):使用指定的父类加载器创建一个ClassLoader对象,其他功能前一个构造器相同。 



 通过反射获取Class对象

•Java程序中获得Class对象通常有如下三种方式:

–使用Class类的forName()静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定类名(必须添加完整包名)。

–调用某个类的class属性来获取该类对应的Class对象。例如Person.class将会返回Person类对应的Class对象。

–调用某个对象的getClass()方法,该方法是java.lang.Object类中的一个方法,所以所有Java对象都可以调用该方法,该方法将会返回该对象所属类对应的Class对象。



 从Class中获取信息 

•获取构造器

•访问Class对应的类所包含的方法

•访问Class对应的类所包含的属性(Field)

•访问Class对应的类上所包含的注释。

•访问该Class对象对应类包含的内部类。

•访问该Class对象对应类所在的外部类。

•访问该Class对象所对应类所继承的父类、所实现的接口等。



Java 8新增的方法参数反射

•Java 8在java.lang.reflect包下新增了一个Executable抽象基类,该对象代表可执行的类成员,该类派生了Constructor、Method两个子类。

•Executable基类提供了大量方法来获取修饰该方法或构造器的注解信息;还提供了isVarArgs()方法用于判断该方法或构造器是否包含数量可变的形参,以及通过getModifiers()方法来获取该方法或构造器的修饰符。除此之外,Executable提供了如下两个方法来获取该方法或参数的形参个数及形参名。

–int getParameterCount():获取该构造器或方法的形参个数。

–Parameter[] getParameters():获取该构造器或方法的所有形参。

•上面第二个方法返回了一个Parameter[]数组,Parameter也是Java 8新增的API,每个Parameter对象代表方法或构造器的一个参数。Parameter也提供了大量方法来获取声明该参数的泛型信息。



通过反射执行代码

•通过反射调用构造器创建对象。

•通过反射调用方法

•通过反射来访问Field值

•通过反射操作数组



动态代理

•java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和

接口可以生成JDK动态代理类或动态代理对象。

•Proxy 提供用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。如果我们在

程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建的动态代理类;如果需要为一个

或多个接口动态地创建实例,也可以使用Proxy来创建动态代理实例。



 Proxy

•Proxy提供了如下两个方法来创建动态代理类和动态代理实例:

–static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces):创

建一个动态代理类所对应的Class对象,该代理类将实现interfaces所指定的多个接口。第一个

ClassLoader指定生成动态代理类的类加载器。

–static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,

InvocationHandler  h):直接创建一个动态代理对象,该代理对象的实现类实现了interfaces

指定的系列接口,执行代理对象的每个方法时都会被替换执行InvocationHandler对象的invoke

方法。



 动态代理和AOP 

•动态代理在AOP(Aspect Orient Program,即面向切面编程)里被称为AOP代理,AOP代理可

代替目标对象,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差

异:AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理。 



反射的泛型 

•动态代理在AOP(Aspect Orient Program,即面向切面编程)里被称为AOP代理,AOP代理可

代替目标对象,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差

异:AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理。 



 动态代理和AOP 

•从JDK1.5之后,Java的Class类增加了泛型功能,从而允许使用泛型来限制Class类,例如,

String.class 的类型实际上是Class<String>。 使用Class<T>泛型可以避免强制类型转换。



使用反射获取泛型 

•获得了Field对象后,就可以很容易地获得该Field的数据类型,即使用如下代码即可获得指定Field的类型:

–//获取Field对象f的类型

–Class<?> a = f.getType();

•通过这种方式只对普通类型的Field有效。但如果该Field的类型是有泛型限制的类型,如Map<String , Integer>类型,则不能准确的得到该Field的泛型参数。

•为了获得指定Field的泛型类型,应先使用如下方法来获取指定Field的泛型类型:

–//获得Field实例f的泛型类型

–Type gType = f.getGenericType();

•然后将Type对象强制类型转换为ParameterizedType对象,ParameterizedType代表被参数化的类型,也就是增加了泛型限制的类型。ParameterizedType类提供了两个方法:

–getRawType():返回被泛型限制的类型。

–getActualTypeArguments():返回泛型参数类型。



public class A {
    // 定义该类的类变量
    public static int a = 6;
}

public class ATest1 {
    public static void main(String[] args) {
        // 创建A类的实例
        A a = new A();
        // 让a实例的类变量a的值自加
        a.a++;
        System.out.println(a.a);
    }
}

public class ATest2 {
    public static void main(String[] args) {
        // 创建A类的实例
        A b = new A();
        // 输出b实例的类变量a的值
        System.out.println(b.a);
    }
}

class Tester {
    static {
        System.out.println("Tester类的静态初始化块...");
    }
}

public class ClassLoaderTest {
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        // 下面语句仅仅是加载Tester类
        cl.loadClass("Tester");
        System.out.println("系统加载Tester类");
        // 下面语句才会初始化Tester类
        Class.forName("Tester");
    }
}

class MyTest {
    static {
        System.out.println("静态初始化块...");
    }
    // 使用一个字符串直接量为static final的类变量赋值
    static final String compileConstant = "疯狂Java讲义";
}

public class CompileConstantTest {
    public static void main(String[] args) {
        // 访问、输出MyTest中的compileConstant类变量
        System.out.println(MyTest.compileConstant); // ①
    }
}

public class Test {
    static {
        // 使用静态初始化块为变量b指定出初始值
        b = 6;
        System.out.println("----------");
    }
    // 声明变量a时指定初始值
    static int a = 5;
    static int b = 9; // ①
    static int c;

    public static void main(String[] args) {
        System.out.println(Test.b);
    }
}



public class BootstrapTest {
    public static void main(String[] args) {
        // 获取根类加载器所加载的全部URL数组
        URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
        // 遍历、输出根类加载器加载的全部URL
        for (int i = 0; i < urls.length; i++) {
            System.out.println(urls[i].toExternalForm());
        }
    }
}

public class ClassLoaderPropTest {
    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());
    }
}

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方法
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class clazz = null;
        // 将包路径中的点(.)替换成斜线(/)。
        String fileStub = name.replace(".", "/");
        String javaFilename = fileStub + ".java";
        String classFilename = fileStub + ".class";
        File javaFile = new File(javaFilename);
        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(name, raw, 0, raw.length);
            } catch (IOException ie) {
                ie.printStackTrace();
            }
        }
        // 如果clazz为null,表明加载失败,则抛出异常
        if (clazz == null) {
            throw new ClassNotFoundException(name);
        }
        return clazz;
    }

    // 定义一个主方法
    public static void main(String[] args) throws Exception {
        // 如果运行该程序时没有参数,即没有目标类
        if (args.length < 1) {
            System.out.println("缺少目标类,请按如下格式运行Java源文件:");
            System.out.println("java CompileClassLoader ClassName");
        }
        // 第一个参数是需要运行的类
        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);
    }
}

public class Hello {
    public static void main(String[] args) {
        for (String arg : args) {
            System.out.println("运行Hello的参数:" + arg);
        }
    }
}

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 {
        System.out.println(getConn("jdbc:mysql://localhost:3306/mysql", "root",
                "32147"));
    }
}



// 定义可重复注解
@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<ClassTest> 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());
    }
}

class Test {
    public void replace(String str, List<String> list) {
    }
}

public class MethodParameterTest {
    public static void main(String[] args) throws Exception {
        // 获取String的类
        Class<Test> clazz = Test.class;
        // 获取String类的带两个参数的replace()方法
        Method replace = clazz.getMethod("replace", String.class, List.class);
        // 获取指定方法的参数个数
        System.out.println("replace方法参数个数:" + replace.getParameterCount());
        // 获取replace的所有参数信息
        Parameter[] parameters = replace.getParameters();
        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());
            }
        }
    }
}



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

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

public class ExtendedObjectPoolFactory {
    // 定义一个对象池,前面是对象名,后面是实际对象
    private Map<String, Object> objectPool = new HashMap<>();
    private Properties config = new Properties();

    // 从指定属性文件中初始化Properties对象
    public void init(String fileName) {
        try (FileInputStream fis = new FileInputStream(fileName)) {
            config.load(fis);
        } catch (IOException ex) {
            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("extObj.txt");
        epf.initPool();
        epf.initProperty();
        System.out.println(epf.getObject("a"));
    }
}

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

public class ObjectPoolFactory {
    // 定义一个对象池,前面是对象名,后面是实际对象
    private Map<String, Object> objectPool = new HashMap<>();

    // 定义一个创建对象的方法,
    // 该方法只要传入一个字符串类名,程序可以根据该类名生成Java对象
    private Object createObject(String clazzName)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        // 根据字符串来获取对应的Class对象
        Class<?> clazz = Class.forName(clazzName);
        // 使用clazz对应类的默认构造器创建实例
        return clazz.newInstance();
    }

    // 该方法根据指定文件来初始化对象池,
    // 它会根据配置文件来创建对象
    public void initPool(String fileName) throws InstantiationException,
            IllegalAccessException, ClassNotFoundException {
        try (FileInputStream fis = new FileInputStream(fileName)) {
            Properties props = new Properties();
            props.load(fis);
            for (String name : props.stringPropertyNames()) {
                // 每取出一对key-value对,就根据value创建一个对象
                // 调用createObject()创建对象,并将对象添加到对象池中
                objectPool.put(name, createObject(props.getProperty(name)));
            }
        } catch (IOException ex) {
            System.out.println("读取" + fileName + "异常");
        }

    }

    public Object getObject(String name) {
        // 从objectPool中取出指定name对应的对象。
        return objectPool.get(name);
    }

    public static void main(String[] args) throws Exception {
        ObjectPoolFactory pf = new ObjectPoolFactory();
        pf.initPool("obj.txt");
        System.out.println(pf.getObject("a")); // ①
        System.out.println(pf.getObject("b")); // ②
    }
}



public interface Dog {
    // info方法声明
    void info();

    // run方法声明
    void run();
}

public class DogUtil {
    // 第一个拦截器方法
    public void method1() {
        System.out.println("=====模拟第一个通用方法=====");
    }

    // 第二个拦截器方法
    public void method2() {
        System.out.println("=====模拟通用方法二=====");
    }
}

public class GunDog implements Dog {
    // 实现info()方法,仅仅打印一个字符串
    public void info() {
        System.out.println("我是一只猎狗");
    }

    // 实现run()方法,仅仅打印一个字符串
    public void run() {
        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();
    }
}



interface Person {
    void walk();

    void sayHello(String name);
}

class MyInvokationHandler implements InvocationHandler {
    /*
     * 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法 其中: proxy:代表动态代理对象 method:代表正在执行的方法
     * args:代表调用目标方法时传入的实参。
     */
    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("孙悟空");
    }
}



public class CrazyitArray {
    // 对Array的newInstance方法进行包装
    @SuppressWarnings("unchecked")
    public static <T> T[] newInstance(Class<T> componentType, int length) {
        return (T[]) Array.newInstance(componentType, length); // ①
    }

    public static void main(String[] args) {
        // 使用CrazyitArray的newInstance()创建一维数组
        String[] arr = CrazyitArray.newInstance(String.class, 10);
        // 使用CrazyitArray的newInstance()创建二维数组
        // 在这种情况下,只要设置数组元素的类型是int[]即可。
        int[][] intArr = CrazyitArray.newInstance(int[].class, 5);
        arr[5] = "疯狂Java讲义";
        // intArr是二维数组,初始化该数组的第二个数组元素
        // 二维数组的元素必须是一维数组
        intArr[1] = new int[] { 23, 12 };
        System.out.println(arr[5]);
        System.out.println(intArr[1][1]);
    }
}

public class CrazyitObjectFactory {
    public static Object getInstance(String clsName) {
        try {
            // 创建指定类对应的Class对象
            Class cls = Class.forName(clsName);
            // 返回使用该Class对象所创建的实例
            return cls.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

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

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

时间: 2024-10-23 01:59:54

【17-类加载与反射】的相关文章

第16篇-JAVA 类加载与反射

第16篇-JAVA 类加载与反射 每篇一句 :敢于弯曲,是为了更坚定的站立 初学心得: 追求远中的欢声笑语,追求远中的结伴同行 (笔者:JEEP/711)[JAVA笔记 | 时间:2017-05-12| JAVA 类加载与反射 ] 1.类加载 类加载器负责将 .class 文件(可能在磁盘上, 也可能在网络上) 加载到内存中, 并为之生成对应的 java.lang.Class 对象 当程序主动使用某个类时,如果该类还未被加载到内存中,系统会通过加载.连接.初始化三个步骤来对该类进行初始化,如果没

JAVA程序类加载及其反射机制

[IT168 技术]当调用java命令运行某个Java程序时,该命令将启动一条Java虚拟机进程,同一个JVM的所有线程,所有变量都处于同一进程里,它们都是用该JVM进程的内存区. 程序运行到最后正常结束. 程序运行到使用System.exit()或Runtime().exit()代码结束程序. 程序执行过程中遇到未捕获的异常或错误而结束. 程序所在平台强制结束了JVM进程. 类的加载 类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象,而系统中所有的类,

33_反射(类加载、反射)_讲义

今日内容介绍 1.类加载器 2.反射构造方法 3.反射成员变量 4.反射成员方法 5.反射配置文件运行类中的方法 01类加载器 A.类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. * a 加载 * 就是指将class文件读入内存,并为之创建一个Class对象. * 任何类被使用时系统都会建立一个Class对象 * b 连接 * 验证 是否有正确的内部结构,并和其他类协调一致 * 准备 负责为类的静态成员分配内存,并设置默认

Java类加载、反射及练习整理

类加载器 1.1      类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. 加载 就是指将class文件读入内存,并为之创建一个Class对象. 任何类被使用时系统都会建立一个Class对象 连接 验证 是否有正确的内部结构,并和其他类协调一致 准备 负责为类的静态成员分配内存,并设置默认初始化值 解析 将类的二进制数据中的符号引用替换为直接引用 初始化 就是我们以前讲过的初始化步骤 1.2      类初始化时机 1.

复习java基础第七天(反射)

一:目标 Ø理解 Class 类 Ø理解 Java 的类加载机制 Ø学会使用 ClassLoader 进行类加载 Ø理解反射的机制 Ø掌握 Constructor.Method.Field 类的用法 Ø理解并掌握动态代理 1.Class类 –对象照镜子后可以得到的信息:某个类的数据成员名.方法和构造器.某个类到底实现了哪些接口. 对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象. 一个 Class 对象包含了特定某个类的有关信息. –  Class 对象只能由系统建立对象.

黑马程序员----java基础--反射机制

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 一.反射的基本描述 Java反射机制是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象方法的功能称为java语言的反射机制.动态获取类中信息,就是java反射.可以理解为对类的解剖.如果想要对指定名称的字节码文件进行加载并获取其中的内容并调用,这时就使用到了反射技术. 简单一

Java反射学习:深入学习Java反射机制

一.Java反射的理解(反射是研究框架的基础之一) Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制. 二.逐步分析 参考:https://blog.csdn.net/u012585964/article/details/52011138 1.关于Class 1.Class是一个类,一个描述类的类(也就是描述类本身),封装了描述方法的Met

重踏学习Java路上_Day27(反射,模式设计,jdk新特性)

1:反射(理解) (1)类的加载及类加载器 (2)反射: 通过字节码文件对象,去使用成员变量,构造方法,成员方法 (3)反射的使用 A:通过反射获取构造方法并使用 B:通过反射获取成员变量并使用 C:通过反射获取成员方法并使用 (4)反射案例 A:通过反射运行配置文件的内容 B:通过反射越过泛型检查 C:通过反射给任意的一个对象的任意的属性赋值为指定的值 (5)动态代理 2:设计模式 (1)装饰设计模式 BufferedReader br = new BufferedReader(new Inp

java反射+java泛型,封装BaseDaoUtil类。供应多个不同Dao使用

当项目是ssh框架时,每一个Action会对应一个Service和一个Dao.但是所有的Ation对应的Dao中的方法是相同的,只是要查的表不一样.由于封装的思想,为了提高代码的重用性.可以使用java中的泛型+反射去实现最终的封装,将所有的Dao层的共同部分写一个BaseDaoUtil.而所有的Dao都继承这个类. 思路: ----->(1)反射+泛型 ----->(2)当生成子类对象(UserDao),调用空构造时(new UserDao()),子类的空构造会默认调用父类的空构造器(new