码迷,mamicode.com
首页 > 其他好文 > 详细

第十二章 类加载器和反射机制

时间:2015-11-19 07:22:58      阅读:242      评论:0      收藏:0      [点我收藏+]

标签:类加载器、反射机制

12 类加载器和反射机制

12.1 类加载器

    负责将.class文件加载到内存中,并为之生成对应的Class对象。

1、类的加载

        当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三个步骤来实现对这个类的初始化。

加载

  • 就是指将calss文件读入到内存,并为之穿件一个Class对象。

  • 任何类被使用时系统都会建立一个Class对象。

连接

  • 验证    是否有正确的内部结构,并和其他类协调一致

  • 准备    负责为类的静态成员分配内存,并设置默认初始化值

  • 解析    将类的二进制数据中的符号引用替换为直接引用

初始化

  • 为栈开辟空间

  • 默认初始化

  • 显示初始化

  • 构造初始化


2、类的加载时机

  • 创建类的实例

  • 访问类的静态变量,或者为静态变量赋值

  • 调用类的静态方法

  • 使用反射方式来强制创建某个类或者接口对应的java.lang.Class对象

  • 初始化某个类的子类

  • 直接使用java.exe命令来运行某个主类


3、类加载器特点

  • 并非一次性加载

  • 需要的时候加载

  • static语句块在加载后执行一次

  • 动态(dynamic)语句块每次new新的对象都会执行(等同于构造方法中的语句)


4、类加载器的组成

Bootstrap ClassLoader根类加载器

    也被称为引导类加载器,负责Java核心类的加载。(在JDK中JRE的lib目录下rt.jar文件中)

Extension ClassLoader扩展类加载器

    负责JRE的扩展目录中jar包的加载。(在JDK中JRE的lib目录下的ext目录中)

System ClassLoader系统加载器

    负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所制定的jar包和类路径。


12.2 反射机制


1、介绍

        JAVA反射机制实在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为JAVA反射机制。

        要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象。

        通俗来讲,反射就是通过class文件对象,去使用该文件中的成员变量、构造方法、成员方法。

        Class类:

                成员变量:Field

                构造函数:Constructor

                成员方法:Method


2、获取calss文件对象的方式

  • Object类的getClass()方法

  • 数据类型的静态属性class

  • Class类中的静态方法:public static Class forName(String className)

    在开发中一般使用第三种,因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中。


public class ReflectDemo {

    public static void main(String[] args) throws ClassNotFoundException {

        //方式1
        Person p1 = new Person();
        Class c1 = p1.getClass();
        Person p2 = new Person();
        Class c2 = p1.getClass();
        System.out.println(p1==p2);
        System.out.println(c1==c2);
        
        //方式2
        Class c3 = Person.class;
        System.out.println(c1==c3);
        
        //方式3
        Class c4 = Class.forName("Person");
        System.out.println(c1==c4);
    }

}


public class Person {

    private String name;
    int age;
    public String sex;
    
    public Person() {
        
    }
    
    private Person(String name) {
        this.name = name;
    }
    
    public Person(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    
    public void function(){
        System.out.println("function");
    }
    
    public String function(String s,int a){
    
        return s+"-----"+a;
    }
    
    private void show(){
        System.out.println("show");
    }
}


12.2.1、通过反射获取构造方法并使用


1、获取构造方法:

  • public Constructor[] getConstructors();所有公共构造方法

  • public Constructor[] getDeclaredConstructors();所有构造方法

  • public Constructor<T> getConstructor(Class... parameterTypes);单个公共构造方法

         参数表示:要获取的构造方法的构造参数的个数及数据类型的class字节码文件对象。、

  • public Constructor<T> getDeclaredConstructor(Class... parameterTypes);单个任意构造方法


2、获取无参构造方法并使用

package cn.ref;

import java.lang.reflect.Constructor;

public class ReflectDemo {

    public static void main(String[] args) throws Exception {

        //获取字节码对象
        Class c = Class.forName("cn.ref.Person");
        //获取单个构造方法
        Constructor con = c.getConstructor();//返回的是构造方法对象
        
        //通过获取的构造方法创建一个新实例
        Object obj = con.newInstance();
        System.out.println(obj);
        Person p = (Person)obj;
        p.function();
    }
}


3、获取带参构造方法并使用

package cn.ref;

import java.lang.reflect.Constructor;

public class ReflectDemo {

    public static void main(String[] args) throws Exception {
        
        //获取字节码对象
        Class c = Class.forName("cn.ref.Person");
        //获取带参构造方法
        Constructor con = c.getConstructor(String.class,int.class,String.class);//返回的是构造方法对象
        
        //通过获取的构造方法创建一个新实例
        Object obj = con.newInstance("张三",20,"男");
        System.out.println(obj);
        Person p = (Person)obj;
        System.out.println(p.getName());
    }
}


4、获取私有构造方法并使用

package cn.ref;

import java.lang.reflect.Constructor;

public class ReflectDemo {

    public static void main(String[] args) throws Exception {
        
        // 获取字节码对象
        Class c = Class.forName("cn.ref.Person");
        // 获取私有构造方法
        Constructor con = c.getDeclaredConstructor(String.class);// 返回的是构造方法对象
        //值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查
        con.setAccessible(true);//设置为true才能访问私有,否则报java.lang.IllegalAccessException
        // 通过获取的构造方法创建一个新实例
        Object obj = con.newInstance("张三");
        System.out.println(obj);
        Person p = (Person) obj;
        System.out.println(p.getName());
    }
}


12.2.2、通过反射获取成员变量并使用


1、获取成员变量

  • public Field getField(String name);获取单个公共成员变量

  • public Field getDeclaredField(String name);获取单个任意成员变量

  • public Field[] getFields();获取所有公共成员变量

  • public Field[] getDeclaredFields();获取所有成员变量


2、获取公共成员变量并使用

package cn.ref;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class ReflectDemo {

    public static void main(String[] args) throws Exception {
        
        // 获取字节码对象
        Class c = Class.forName("cn.ref.Person");
        //通过构造方法创建一个实例
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();
        // 获取成员变量
        Field f = c.getField("sex");
        //赋值
        f.set(obj, "女");
        
        System.out.println(obj);
        Person p = (Person) obj;
        System.out.println(p.getSex());
    }
}


3、获取私有成员变量并使用

package cn.ref;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class ReflectDemo {

    public static void main(String[] args) throws Exception {
        
        // 获取字节码对象
        Class c = Class.forName("cn.ref.Person");
        //通过构造方法创建一个实例
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();
        // 获取成员变量
        Field f = c.getDeclaredField("name");
        f.setAccessible(true);
        //赋值
        f.set(obj, "张三");
        
        System.out.println(obj);
        Person p = (Person) obj;
        System.out.println(p.getName());
    }
}

12.2.3、通过反射获取成员方法并使用


1、获取成员方法

  • public Method getMethod(String name, Class... parameterTypes);获取单个公共成员方法

  • public Method getDeclaredMethod(String name, Class... parameterTypes);获取单个任意成员方法

  • public Method[] getMethods();获取所有公共成员方法

  • public Method[] getDeclaredMethods();获取所有成员方法


2、获取无参无返回值成员方法并使用

package cn.ref;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectDemo {

    public static void main(String[] args) throws Exception {
        
        // 获取字节码对象
        Class c = Class.forName("cn.ref.Person");
        //通过构造方法创建一个实例
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();
        // 获取无参成员方法

        Method m = c.getMethod("function");
        //调用方法
        m.invoke(obj);
    }
}

3、获取带参带返回值成员方法并使用

package cn.ref;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectDemo {

    public static void main(String[] args) throws Exception {
        
        // 获取字节码对象
        Class c = Class.forName("cn.ref.Person");
        // 通过构造方法创建一个实例
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();
        // 获取带参成员方法
        Method m = c.getMethod("function",String.class,int.class);
        // 调用方法
        Object o = m.invoke(obj,"hello",20);
        System.out.println(o);
    }
}


4、获取私有成员方法并使用

package cn.ref;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectDemo {

    public static void main(String[] args) throws Exception {
        
        // 获取字节码对象
        Class c = Class.forName("cn.ref.Person");
        // 通过构造方法创建一个实例
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();
        // 获取私有成员方法
        Method m = c.getDeclaredMethod("show");
        m.setAccessible(true);
        // 调用方法
        m.invoke(obj);
    }
}

12.2.4、反射机制的使用


1、通过反射运行配置文件的内容

测试类:

package cn.refTest;

import java.io.FileReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;

public class Test {

    public static void main(String[] args) throws Exception {

        //加载配置文件
        Properties p =new Properties();
        FileReader fr =new FileReader("class.properties");
        p.load(fr);
        fr.close();
        
        //获取数据
        String className = p.getProperty("className");
        String methodName = p.getProperty("methodName");
        
        //反射
        Class c = Class.forName(className);
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();
        
        //调用方法
        Method m = c.getMethod(methodName);
        m.invoke(obj);
    }
}


Student类:

package cn.refTest;

public class Student {

    public void show(){
        System.out.println("学习");
    }
}


Teacher类:

package cn.refTest;

public class Teacher {

    public void show(){
        System.out.println("授课");
    }
}


Worker类

package cn.refTest;

public class Worker {

    public void show(){
        System.out.println("工作");
    }
}


配置文件:

className=cn.refTest.Student
methodName=show


2、给定ArrayList<Integer>的一个对象,在这个集合中添加一个字符串。

/*
 * ArrayList原始的add方法是Object类型
 * 所以拿到它的字节码文件对象,通过反射就可以将字符串放进去
 */

package cn.refTest2;

import java.lang.reflect.Method;
import java.util.ArrayList;

public class ArrayListTest {

    public static void main(String[] args) throws Exception {

        ArrayList<Integer> array = new ArrayList<Integer>();
        
        Class c = array.getClass();
        
        Method m = c.getMethod("add", Object.class);
        m.invoke(array, "hello");// 调用array的add方法,传入的值是hello
        m.invoke(array, "java");
        
        System.out.println(array);
    }
}

12.2.4 动态代理


1、代理:本来应该自己做的事,却请了别人来做,被请的人就是代理对象。


2、动态代理:其实就是通过反射来生成的一个代理。

        在Java中java.lang.reflect包下提供了一个Proxy类和InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口来做代理,如果来对类来做,可以使用cglib。

        Proxy类中的方法创建动态代理对象:

  • Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h);

  • 最终会调用InvocationHandle方法:

    Object invoke(Object proxy,Method method,Object[] args);


3、实现动态代理,在登陆、注册等操作之前和之后加入权限校验和日志记录(功能都用输出语句代替)

Test类:

package cn.refTest4;

import java.lang.reflect.Proxy;

public class Test {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        UserDao ud = new UserDaoImp();
        //准备对ud对象做代理
        MyInvocationHandler handler = new MyInvocationHandler(ud);
        //创建动态代理对象
        UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(), ud.getClass().getInterfaces(), handler);
        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.find();
        
        StudentDao sd = new StudentDaoImp();
        //准备对ud对象做代理
        MyInvocationHandler handler2 = new MyInvocationHandler(sd);
        //创建动态代理对象
        StudentDao stu = (StudentDao) Proxy.newProxyInstance(sd.getClass().getClassLoader(), sd.getClass().getInterfaces(), handler2);
        stu.login();
        stu.regist();
    }
}


MyInvocationHandler类:

package cn.refTest4;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {

    private Object target;
    
    public MyInvocationHandler(Object target) {

        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("权限校验");
        Object obj = method.invoke(target, args);
        System.out.println("日志记录");
        return obj;
    }
}


StudentDao接口:

package cn.refTest4;

public interface StudentDao {

    public abstract void login();
    public abstract void regist();
}


UserDao接口:

package cn.refTest4;

public interface UserDao {

    public abstract void add();
    public abstract void delete();
    public abstract void update();
    public abstract void find();
}


StudentDao类:

package cn.refTest4;

public class StudentDaoImp implements StudentDao {

    @Override
    public void login() {
        System.out.println("登录功能");

    }

    @Override
    public void regist() {
        System.out.println("注册功能");

    }

}


UserDaoImp类:

package cn.refTest4;

public class UserDaoImp implements UserDao {

    @Override
    public void add() {
        System.out.println("添加功能");

    }

    @Override
    public void delete() {
        System.out.println("删除功能");

    }

    @Override
    public void update() {
        System.out.println("更新功能");

    }

    @Override
    public void find() {
        System.out.println("查找功能");

    }
}



本文出自 “java基础” 博客,请务必保留此出处http://8163413.blog.51cto.com/8153413/1714425

第十二章 类加载器和反射机制

标签:类加载器、反射机制

原文地址:http://8163413.blog.51cto.com/8153413/1714425

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!