Java基础学习第二十七天——类加载器和反射

来源:互联网 发布:淘宝白菜价网站 编辑:程序博客网 时间:2024/06/16 22:54
文档版本 开发工具 测试平台 工程名字 日期 作者 备注 V1.0 2016.04.05 lutianfei none

第十三章 类加载器和反射

类的加载

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

  • 加载

    • 就是指将class文件读入内存,并为之创建一个Class对象。
    • 任何类被使用时系统都会建立一个Class对象。
  • 连接
    • 验证 是否有正确的内部结构,并和其他类协调一致
    • 准备 负责为类的静态成员分配内存,并设置默认初始化值
    • 解析 将类的二进制数据中的符号引用替换为直接引用
  • 初始化 就是我们以前讲过的初始化步骤

类初始化时机

  • 创建类的实例
  • 访问类的静态变量,或者为静态变量赋值
  • 调用类的静态方法
  • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
  • 初始化某个类的子类
  • 直接使用java.exe命令来运行某个主类

类加载器

  • 负责将.class文件加载到内存中,并为之生成对应的Class对象
  • 虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
  • 类加载器的组成
    • Bootstrap ClassLoader 根类加载器
    • Extension ClassLoader 扩展类加载器
    • Sysetm ClassLoader 系统类加载器

类加载器的作用

  • Bootstrap ClassLoader 根类加载器
    • 也被称为引导类加载器,负责Java核心类的加载
      • 比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
  • Extension ClassLoader 扩展类加载器
    • 负责JRE的扩展目录jar包的加载。
      • 在JDK中JRE的lib目录ext目录
  • Sysetm ClassLoader 系统类加载器
    • 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径


反射

  • JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制
  • 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象。

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

  • 获取构造方法
    • getConstructors
    • getDeclaredConstructors
  • 创建对象
    • newInstance()
    • con.newInstance(“zhangsan”, 20);
  • 获取所有成员
    • getFields,getDeclaredFields
  • 获取单个成员
    • getField,getDeclaredField
  • 修改成员的值
    • set(Object obj,Object value)
      • 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
  • 获取所有方法
    • getMethods
    • getDeclaredMethods
  • 获取单个方法
    • getMethod
    • getDeclaredMethod
  • 暴力访问

    • method.setAccessible(true);

    • 获取class文件对象的方式:

      • A:Object类的getClass()方法
      • B:数据类型的静态属性class
      • C:Class类中的静态方法
        • public static Class forName(String className)
    • 一般我们使用谁呢?

      • A:自己玩 任选一种,第二种比较方便
      • B:开发 第三种
      • 为什么呢?因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中。
  • 通过反射获取无参构造方法并使用

/* * 反射:就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。 * Class类: *      成员变量    Field *      构造方法    Constructor *      成员方法    Method *  */public class ReflectDemo {    public static void main(String[] args) throws ClassNotFoundException {        // 方式1        Person p = new Person();        Class c = p.getClass();        Person p2 = new Person();        Class c2 = p2.getClass();        System.out.println(p == p2);// false        System.out.println(c == c2);// true        // 方式2        Class c3 = Person.class;        // int.class;        // String.class;        System.out.println(c == c3);        // 方式3        // ClassNotFoundException        Class c4 = Class.forName("cn.itcast_01.Person");        System.out.println(c == c4);    }}public class Person {    private String name;    int age;    public String address;    public Person() {    }    private Person(String name) {        this.name = name;    }    Person(String name, int age) {        this.name = name;        this.age = age;    }    public Person(String name, int age, String address) {        this.name = name;        this.age = age;        this.address = address;    }    public void show() {        System.out.println("show");    }    public void method(String s) {        System.out.println("method " + s);    }    public String getString(String s, int i) {        return s + "---" + i;    }    private void function() {        System.out.println("function");    }    @Override    public String toString() {        return "Person [name=" + name + ", age=" + age + ", address=" + address                + "]";    }}


  • 通过反射获取无参构造方法的使用
/* * 通过反射获取构造方法并使用。 */public class ReflectDemo {    public static void main(String[] args) throws Exception {        // 获取字节码文件对象        Class c = Class.forName("cn.itcast_01.Person");        // 获取构造方法        // public Constructor[] getConstructors():所有公共构造方法        // public Constructor[] getDeclaredConstructors():所有构造方法        // Constructor[] cons = c.getDeclaredConstructors();        // for (Constructor con : cons) {        // System.out.println(con);        // }        // 获取单个构造方法        // public Constructor<T> getConstructor(Class<?>... parameterTypes)        // 参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象        Constructor con = c.getConstructor();// 返回的是构造方法对象        // Person p = new Person();        // System.out.println(p);        // public T newInstance(Object... initargs)        // 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。        Object obj = con.newInstance();        System.out.println(obj);        // Person p = (Person)obj;        // p.show();    }}


  • 获取带参构造方法
/* * 需求:通过反射去获取该构造方法并使用: * public Person(String name, int age, String address) *  * Person p = new Person("林青霞",27,"北京"); * System.out.println(p); */public class ReflectDemo2 {    public static void main(String[] args) throws Exception {        // 获取字节码文件对象        Class c = Class.forName("cn.itcast_01.Person");        // 获取带参构造方法对象        // public Constructor<T> getConstructor(Class<?>... parameterTypes)        Constructor con = c.getConstructor(String.class, int.class,                String.class);        // 通过带参构造方法对象创建对象        // public T newInstance(Object... initargs)        Object obj = con.newInstance("林青霞", 27, "北京");        System.out.println(obj);    }}


  • 获取私有构造方法并使用
/* * 需求:通过反射获取私有构造方法并使用 * private Person(String name){} *  * Person p = new Person("风清扬"); * System.out.println(p); */public class ReflectDemo3 {    public static void main(String[] args) throws Exception {        // 获取字节码文件对象        Class c = Class.forName("cn.itcast_01.Person");        // 获取私有构造方法对象        // NoSuchMethodException:每有这个方法异常        // 原因是一开始我们使用的方法只能获取公共的,下面这种方式就可以了。        Constructor con = c.getDeclaredConstructor(String.class);        // 用该私有构造方法创建对象        // IllegalAccessException:非法的访问异常。        // 暴力访问        con.setAccessible(true);// 值为true则指示反射的对象在使用时应该取消Java语言访问检查。        Object obj = con.newInstance("风清扬");        System.out.println(obj);    }}


  • 获取成员变量并使用
/* * 通过反射获取成员变量并使用 */public class ReflectDemo {    public static void main(String[] args) throws Exception {        // 获取字节码文件对象        Class c = Class.forName("cn.itcast_01.Person");        // 获取所有的成员变量        // Field[] fields = c.getFields();        // Field[] fields = c.getDeclaredFields();        // for (Field field : fields) {        // System.out.println(field);        // }        /*         * Person p = new Person(); p.address = "北京"; System.out.println(p);         */        // 通过无参构造方法创建对象        Constructor con = c.getConstructor();        Object obj = con.newInstance();        System.out.println(obj);        // 获取单个的成员变量        // 获取address并对其赋值        Field addressField = c.getField("address");        // public void set(Object obj,Object value)        // 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。        addressField.set(obj, "北京"); // 给obj对象的addressField字段设置值为"北京"        System.out.println(obj);        // 获取name并对其赋值        // NoSuchFieldException        Field nameField = c.getDeclaredField("name");        // IllegalAccessException        nameField.setAccessible(true);        nameField.set(obj, "林青霞");        System.out.println(obj);        // 获取age并对其赋值        Field ageField = c.getDeclaredField("age");        ageField.setAccessible(true);        ageField.set(obj, 27);        System.out.println(obj);    }}


  • 获取无参无返回值成员方法并使用
  • 获取带参数返回值成员方法并使用
public class ReflectDemo {    public static void main(String[] args) throws Exception {        // 获取字节码文件对象        Class c = Class.forName("cn.itcast_01.Person");        // 获取所有的方法        // Method[] methods = c.getMethods(); // 获取自己的包括父亲的公共方法        // Method[] methods = c.getDeclaredMethods(); // 获取自己的所有的方法        // for (Method method : methods) {        // System.out.println(method);        // }        Constructor con = c.getConstructor();        Object obj = con.newInstance();        /*         * Person p = new Person(); p.show();         */        // 获取单个方法并使用        // public void show()        // public Method getMethod(String name,Class<?>... parameterTypes)        // 第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型        Method m1 = c.getMethod("show");        // obj.m1(); // 错误        // public Object invoke(Object obj,Object... args)        // 返回值是Object接收,第一个参数表示对象是谁,第二参数表示调用该方法的实际参数        m1.invoke(obj); // 调用obj对象的m1方法        System.out.println("----------");        // public void method(String s)        Method m2 = c.getMethod("method", String.class);        m2.invoke(obj, "hello");        System.out.println("----------");        // public String getString(String s, int i)        Method m3 = c.getMethod("getString", String.class, int.class);        Object objString = m3.invoke(obj, "hello", 100);        System.out.println(objString);        // String s = (String)m3.invoke(obj, "hello",100);        // System.out.println(s);        System.out.println("----------");        // private void function()        Method m4 = c.getDeclaredMethod("function");        m4.setAccessible(true);        m4.invoke(obj);    }}


反射应用举例

  • 通过配置文件运行类中的方法
public class Student {    public void love() {        System.out.println("爱生活,爱Java");    }}public class Teacher {    public void love() {        System.out.println("爱生活,爱青霞");    }}public class Worker {    public void love() {        System.out.println("爱生活,爱老婆");    }}/* * 通过配置文件运行类中的方法 *  * 反射: *      需要有配置文件配合使用。 *      用class.txt代替。 *      并且你知道有两个键。 *          className *          methodName */public class Test {    public static void main(String[] args) throws Exception {        // 反射前的做法        // Student s = new Student();        // s.love();        // Teacher t = new Teacher();        // t.love();        // Worker w = new Worker();        // w.love();        // 反射后的做法        // 加载键值对数据        Properties prop = new Properties();        FileReader fr = new FileReader("class.txt");        prop.load(fr);        fr.close();        // 获取数据        String className = prop.getProperty("className");        String methodName = prop.getProperty("methodName");        // 反射        Class c = Class.forName(className);        Constructor con = c.getConstructor();        Object obj = con.newInstance();        // 调用方法        Method m = c.getMethod(methodName);        m.invoke(obj);    }}


  • 我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?
public class ArrayListDemo {    public static void main(String[] args) throws NoSuchMethodException,            SecurityException, IllegalAccessException,            IllegalArgumentException, InvocationTargetException {        // 创建集合对象        ArrayList<Integer> array = new ArrayList<Integer>();        // array.add("hello");        // array.add(10);        Class c = array.getClass(); // 集合ArrayList的class文件对象        Method m = c.getMethod("add", Object.class);        m.invoke(array, "hello"); // 调用array的add方法,传入的值是hello        m.invoke(array, "world");        m.invoke(array, "java");        System.out.println(array);    }}


  • 写一个方法,
    • public void setProperty(Object obj, String propertyName, Object value){},
    • 此方法可将obj对象中名为propertyName的属性的值设置为value。
public class Tool {    public void setProperty(Object obj, String propertyName, Object value)            throws NoSuchFieldException, SecurityException,            IllegalArgumentException, IllegalAccessException {        // 根据对象获取字节码文件对象        Class c = obj.getClass();        // 获取该对象的propertyName成员变量        Field field = c.getDeclaredField(propertyName);        // 取消访问检查        field.setAccessible(true);        // 给对象的成员变量赋值为指定的值        field.set(obj, value);    }}public class ToolDemo {    public static void main(String[] args) throws NoSuchFieldException,            SecurityException, IllegalArgumentException, IllegalAccessException {        Person p = new Person();        Tool t = new Tool();        t.setProperty(p, "name", "林青霞");        t.setProperty(p, "age", 27);        System.out.println(p);        System.out.println("-----------");        Dog d = new Dog();        t.setProperty(d, "sex", '男');        t.setProperty(d, "price", 12.34f);        System.out.println(d);    }}class Dog {    char sex;    float price;    @Override    public String toString() {        return sex + "---" + price;    }}class Person {    private String name;    public int age;    @Override    public String toString() {        return name + "---" + age;    }}


动态代理

  • 代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。
    • 举例:春季回家买票让人代买
  • 动态代理:在程序运行过程中产生的这个对象

    • 而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理
  • 在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib

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

    • public static Object newProxyInstance(ClassLoader loader,Class
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 result = method.invoke(target, args);        System.out.println("日志记录");        return result; // 返回的是代理对象    }}public interface StudentDao {    public abstract void login();    public abstract void regist();}public class StudentDaoImpl implements StudentDao {    @Override    public void login() {        System.out.println("登录功能");    }    @Override    public void regist() {        System.out.println("注册功能");    }}/* * 用户操作接口 */public interface UserDao {    public abstract void add();    public abstract void delete();    public abstract void update();    public abstract void find();}public class UserDaoImpl 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("查找功能");    }}public class Test {    public static void main(String[] args) {        UserDao ud = new UserDaoImpl();        ud.add();        ud.delete();        ud.update();        ud.find();        System.out.println("-----------");        // 我们要创建一个动态代理对象        // 我准备对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();        System.out.println("-----------");        StudentDao sd = new StudentDaoImpl();        MyInvocationHandler handler2 = new MyInvocationHandler(sd);        StudentDao proxy2 = (StudentDao) Proxy.newProxyInstance(sd.getClass()                .getClassLoader(), sd.getClass().getInterfaces(), handler2);        proxy2.login();        proxy2.regist();    }}




模版设计模式(抽象类)

  • 模版方法模式就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现
  • 优点
    • 使用模版方法模式,在定义算法骨架的同时,可以很灵活的实现具体的算法,满足用户灵活多变的需求
  • 缺点
    • 如果算法骨架有修改的话,则需要修改抽象类
public class ForDemo extends GetTime {    @Override    public void code() {        for (int x = 0; x < 100000; x++) {            System.out.println(x);        }    }}public abstract class GetTime {    // 需求:请给我计算出一段代码的运行时间    public long getTime() {        long start = System.currentTimeMillis();        code();        long end = System.currentTimeMillis();        return end - start;    }    public abstract void code();}public class GetTimeDemo {    public static void main(String[] args) {        // GetTime gt = new GetTime();        // System.out.println(gt.getTime() + "毫秒");        GetTime gt = new ForDemo();        System.out.println(gt.getTime() + "毫秒");        gt = new IODemo();        System.out.println(gt.getTime() + "毫秒");    }}public class IODemo extends GetTime{    @Override    public void code() {        try {            BufferedInputStream bis = new BufferedInputStream(                    new FileInputStream("a.avi"));            BufferedOutputStream bos = new BufferedOutputStream(                    new FileOutputStream("b.avi"));            byte[] bys = new byte[1024];            int len = 0;            while ((len = bis.read(bys)) != -1) {                bos.write(bys, 0, len);            }            bos.close();            bis.close();        } catch (IOException e) {            e.printStackTrace();        }    }}


装饰设计模式

  • 装饰模式就是使用被装饰类的一个子类的实例,在客户端将这个子类的实例交给装饰类。是继承的替代方案
  • 优点
    • 使用装饰模式,可以提供比继承更灵活的扩展对象的功能,它可以动态的添加对象的功能,并且可以随意的组合这些功能
  • 缺点
    • 正因为可以随意组合,所以就可能出现一些不合理的逻辑
public interface Phone {    public abstract void call();}public class IPhone implements Phone {    @Override    public void call() {        System.out.println("手机可以打电话了");    }}public class MusicPhoneDecorate extends PhoneDecorate {    public MusicPhoneDecorate(Phone p) {        super(p);    }    @Override    public void call() {        super.call();        System.out.println("手机可以听音乐");    }}public class RingPhoneDecorate extends PhoneDecorate {    public RingPhoneDecorate(Phone p) {        super(p);    }    @Override    public void call() {        System.out.println("手机可以听彩铃");        super.call();    }}public abstract class PhoneDecorate implements Phone {    private Phone p;    public PhoneDecorate(Phone p) {        this.p = p;    }    @Override    public void call() {        this.p.call();    }}public class PhoneDemo {    public static void main(String[] args) {        Phone p = new IPhone();        p.call();        System.out.println("------------");        // 需求:我想在接电话前,听彩铃        PhoneDecorate pd = new RingPhoneDecorate(p);        pd.call();        System.out.println("------------");        // 需求:我想在接电话后,听音乐        pd = new MusicPhoneDecorate(p);        pd.call();        System.out.println("------------");        // 需求:我要想手机在接前听彩铃,接后听音乐        // 自己提供装饰类,在打电话前听彩铃,打电话后听音乐        pd = new RingPhoneDecorate(new MusicPhoneDecorate(p));        pd.call();        System.out.println("----------");        // 想想我们在IO流中的使用        // InputStream is = System.in;        // InputStreamReader isr = new InputStreamReader(is);        // BufferedReader br = new BufferedReader(isr);        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        BufferedWriter bw = new BufferedWriter((new OutputStreamWriter(                System.out)));        Scanner sc = new Scanner(System.in);    }}




1 0