类加载,反射,动态代理

一.类加载

1.类的加载概述

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

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

2.类的加载时机

(1)创建类的实例
(2)访问类的静态变量,或者为静态变量赋值
(3)调用类的静态方法
(4)使用反射来强制创建某个类或接口对应的字节码文件对象
(5)初始化某个类的子类
(6)直接使用java.exe来运行某个主类

二.类加载器

1.类加载的概述
负责将字节码文件加载到内存中,并为之生成对应的Class对象
2.分类
  • Bootstrap ClassLoader 根类加载器
  • Extension ClassLoader 扩展类加载器
  • Sysetm ClassLoader 系统类加载器
3.类加载的作用
  • 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包和类路径

三.反射

1.反射概述
  • **Java反射机制就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;**这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制。
  • 要想解刨一个类,必须有、先要获取到该类的的字节码文件对象,而解刨使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。
2.获取一个类对象的字节码文件对象的方法
a:Object类的getClass()方法
b:静态属性class
c:Class类中静态方法forName(),public static Class forName(String className): 
className:这个表示的是一个类对应的全类名(就是需要加上包名)
3.通过反射获取构造方法
获取所有构造方法
	public Constructor<?>[] getConstructors() 获取所有的构造方法不包含私有的
	public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法 包括私有的
获取单个构造方法
	public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取单个的构造方法 不包含私有的
	public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取单个的构造方法包含私有的

案例演示

//测试
package com.westmo3.demo4;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class MyDemo9 {
    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        //获取Teacher类的字节码文件对象
        Class<Teacher> teacherClass = Teacher.class;//第一种方式
        //Class<? extends Teacher> aClass = new Teacher().getClass();//第二种方式
        //Class<?> aClass = Class.forName(" com.westmo3.demo4.Teacher");//第三种方式
        Constructor<?>[] constructors = teacherClass.getConstructors();//获取该类中所有非私有构造方法对象的数组
        Constructor<?>[] declaredConstructors = teacherClass.getDeclaredConstructors();//获取该类中所有构造方法对象的数组
        Constructor<Teacher> constructor1 = teacherClass.getConstructor();//获取单个的空参构造方法对象
        System.out.println(constructor1);
        //获取两个参数的私有的构造方法对象
        Constructor<Teacher> declaredConstructor1 = teacherClass.getDeclaredConstructor(String.class, int.class);
        System.out.println(declaredConstructor1);
        //我们可以使用私有构造来创建对象,以前是不行的
        declaredConstructor1.setAccessible(true);//取消权限的语法检测
        Teacher teacher = declaredConstructor1.newInstance("张三", 12);
        System.out.println(teacher);
    }
}

//Teacher类
package com.westmo3.demo4;
public class Teacher {
    public Teacher() {
        System.out.println("公有空参被调用");
    }
    public Teacher(String name){
        System.out.println("公有有参被调用");
    }
    private Teacher(String name,int age){
        System.out.println("私有有参被调用");
    }
}
4.通过反射获取成员变量并使用
获取所有成员变量
	public Field[] getFields() 获取所有的成员变量包含从父类继承过来的
	public Field[] getDeclaredFields() 获取所有的成员变量 包含私有的 也包含从父类继承过来的成员变量
获取单个成员变量
	public Field getField(String name)
	public Field getDeclaredField(String name)

案例演示

//测试用例
package com.westmo3.demo4;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class MyDemo10 {
    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<? extends Teacher> aClass = new Teacher().getClass();//获取类的字节码文件对象
        Field[] fields = aClass.getFields();// 获取所有非私有成员(public 修饰的)变量包含从父类继承过来的
        for (Field field : fields) {
            System.out.println(field);
            //输出:public java.lang.String com.westmo3.demo4.Teacher.name
        }
        Field[] declaredFields = aClass.getDeclaredFields();//获取所有的成员变量包含私有的和父类继承过来的
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        Field field = aClass.getField("name");//获取单个public成员变量
        Teacher teacher = aClass.getConstructor().newInstance();//利用反射空参构造创建对象的简便方法
        field.set(teacher,"张三");//给成员变量赋值
        Field field1 = aClass.getDeclaredField("age");
        field1.setAccessible(true);
        field1.set(teacher,13);
        Field field2 = aClass.getDeclaredField("age");
        field2.setAccessible(true);
        field2.set(teacher,'男');//给私有成员变量赋值
    }
}
//Teacher类
package com.westmo3.demo4;
public class Teacher {
    public Teacher() {
    }
    public String name;
   int age;
   private char sex;
}
5.通过反射获取成员方法并使用
获取所有成员方法
	public Method[] getMethods() //获取所有的公共的成员方法不包含私有的 包含从父类继承过来的过来的公共方法
	public Method[] getDeclaredMethods()//获取自己的所有成员方法 包含私有的
获取单个成员方法
	//参数1: 方法名称  参数2:方法行参的class 对象
	public Method getMethod(String name,Class<?>... parameterTypes) //获取单个的方法 不包含私有的
	public Method getDeclaredMethod(String name,Class<?>... parameterTypes) 获取单个方法包括私有的

案例演示

//测试
package com.westmo3.demo4;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MyTest1 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> aClass = Class.forName("com.westmo3.demo4.Worker");
        Method[] methods = aClass.getMethods();//获取所有的公共成员方法包含从父类继承过来的
        for (Method method : methods) {
            System.out.println(method);
        }
        Method[] declaredMethods = aClass.getDeclaredMethods();//获取所有的成员方法
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        Worker worker = (Worker) aClass.getConstructor().newInstance();
        //获取单个方法不包含私有
        Method method = aClass.getMethod("test1", String.class);
        Object w = method.invoke(worker, "张三");
        Method test3 = aClass.getDeclaredMethod("test3", String.class, int.class);
        test3.setAccessible(true);
        Object invoke = test3.invoke(worker, "张三", 13);
    }
}
//Worker类
package com.westmo3.demo4;
public class Worker {
    public Worker() {
    }
    public void test1(String name){
        System.out.println(name);
    }
    public void test2(String name,int age){
        System.out.println("公有"+name+"="+age);
    }
    private void test3(String name,int age){
        System.out.println("私有"+name+age);
    }
}
6.反射应用

(1)通过反射运行配置文件内容

配置文件 peizhi.txt
className=com.练习.反射应用.运行配置文件内容.Cat
methodName=eat

Cat类
package com.练习.反射应用.运行配置文件内容;
public class Cat {
    public void eat(){
        System.out.println("猫吃鱼");
    }
}
测试类
package com.练习.反射应用.运行配置文件内容;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class MyTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //以前的方法
        //new Cat().eat();
        //new Dog().eat();
        //利用反射来读取配置文件实现
        Properties properties = new Properties();
        properties.load(new FileReader("peizhi.txt"));
        //获取该类的字节码文件对象
        String className1 = properties.getProperty("className");
        Class<?> className = Class.forName(className1);
        //利用反射创建该类的对象
        Object o = className.getConstructor().newInstance();
        //调用猫类中的方法执行
        Method methodName = className.getMethod(properties.getProperty("methodName"));
        Object invoke = methodName.invoke(o);
    }
}

(2)通过反射越过泛型检查

package com.练习.反射应用.越过泛型检查;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class MyDemo {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //给定一个Arraylist<Integer>对象,但要想给集合中添加一个字符串数据该怎么实现?
        ArrayList<Integer> arrayList = new ArrayList<Integer>();
        arrayList.add(100);
        arrayList.add(200);
        arrayList.add(300);
        //泛型只在编译器有效
        Class<? extends ArrayList> aClass = arrayList.getClass();//获取该类的字节码文件对象
        Method add = aClass.getMethod("add", Object.class);
        add.invoke(arrayList,"张三");
        System.out.println(arrayList);
    }
}

四.动态代理

1.为什么会有动态代理?

我们要想对下面这个类中的方法进行增强,但是不想修改下面的代码,我们就可以使用动态代理实现

public class UserDaoImpl implements UserDao {
    @Override
    public void insert() {
        System.out.println("添加一个用户");
    }
    @Override
    public void delete() {
        System.out.println("删除用户");
    }
    @Override
    public void update() {
        System.out.println("修改用户");
    }
    @Override
    public void query() {
        System.out.println("查询用户");
    }
}
2.动态代理概述

定义:为其它对象提供一种代理以控制对这个对象的访问控制;在某些情况下,客户不想或者不能直接引用另一个对象,这时候代理对象可以在客户端和目标对象之间起到中介的作用。

3.动态代理的优势

Java动态代理的优势是实现无侵入式的代码扩展,也就是方法的增强;让你可以在不用修改源码的情况下,增强一些方法;在方法的前后你可以做你任何想做的事情(甚至不去执行这个方法就可以)。

4.使用
在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,
通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。
我们有更强大的代理cglib,Proxy类中的方法创建动态代理类对象
(1)public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
	最终会调用InvocationHandler的方法
(2)InvocationHandler Object invoke(Object proxy,Method method,Object[] args)
newProxyInstance方法的参数:
ClassLoader:类加载器
         它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
Class[]:字节码数组
         它是用于让代理对象和被代理对象有相同方法。固定写法。
InvocationHandler:用于提供增强的代码
         它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。

new InvocationHandler() {
                    /**
                     * 作用:执行被代理对象的任何接口方法都会经过该方法
                     * 方法参数的含义
                     * @param proxy   代理对象的引用
                     * @param method  当前执行的方法
                     * @param args    当前执行方法所需的参数
                     * @return        和被代理对象方法有相同的返回值
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    }
                }         
5.案例演示

User类

扫描二维码关注公众号,回复: 10558224 查看本文章
package com.westmo3.demo4.动态代理;
public interface User {
    void delete();
    void insert();
    void update();
}
class UserDao implements User{
    @Override
    public void delete() {
        System.out.println("删除用户");
    }
    @Override
    public void insert() {
        System.out.println("添加用户");
    }
    @Override
    public void update() {
        System.out.println("修改用户");
    }
}

获取代理对象的类

package com.westmo3.demo4.动态代理;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtils {
    //获取代理对象的方法
    public static User getProxy(User user){
        //newProxyInstance()的三个参数:1.类加载器,负责加载代理对象的字节码
        //2.这个目标对象所实现的所有接口的class类型的数组
        //3.InvocationHandler 接口,每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,
        // 将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。
        User o = (User) Proxy.newProxyInstance(user.getClass().getClassLoader(),
                user.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //执行目标对象的任何方法都会经过该方法
                System.out.println("增强代码1");
                Object invoke = method.invoke(user);//让目标对象中的方法执行
                System.out.println("增强代码2");
                //给其中的某个方法增强
                /*Object invoke=null;
                if (method.getName().equals("delete")) {
                    System.out.println("增强代码1");
                    invoke = method.invoke(user);
                    System.out.println("增强代码2");
                }*/
                return invoke;
            }
        });
        return o;
    }
}

测试类

package com.westmo3.demo4.动态代理;
public class MyTest {
    public static void main(String[] args) {
        User userDao = new UserDao();
        User proxy =  ProxyUtils.getProxy(userDao);
        proxy.delete();
        proxy.insert();
        proxy.update();
    }
}
发布了58 篇原创文章 · 获赞 13 · 访问量 1869

猜你喜欢

转载自blog.csdn.net/weixin_44324174/article/details/104695676