类加载和Java反射以及动态代理的简介及应用~

文章思路:

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

 

-- 类加载

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

*初始化: 类栈开辟空间,默认初始化,构造初始化

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

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

-- 类加载器的组成
  Boostrap ClassLoader:根类加载器
  Extension ClassLoader:扩展类加载器
  System ClassLoader:系统类加载器

详解:
    1.Boostrap ClassLoader:根类加载器
        也被称为引导类加载器,负责java核心类的加载
          比如System String等。在JDK中JRE的lib目录下rt.jar文件中
    2.Extension ClassLoader:扩展类加载器  (扩展时使用)
        负责JRE的扩展目录中jar包的加载。
            在JDK中JRE的lib目录下ext目录
    3.System ClassLoader:系统类加载器 (一般自己使用时用)
        负责在JVM启动时加载来自java命令的class文件以及classpath环境变量所指定的jar包和类路径

-- 反射

    *含义:
        Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意个对象,都能够调用他的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

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


 * 反射:就是通过Class文件对象去使用该文件中的成员变量,构造方法,成员方法。
 * 要想这样使用,首先必须得到Class文件对象,其实也就是的到Class类的对象。  Class类:代表字节码对象
 *Class类:
     成员变量      Field
     构造方法      Constructor
     成员方法      Method
 
 *反射思路:
     通过class类得到字节码对象,然后通过字节码对象得到Field,Constructor,Method三个对象,调用方法,就称为反射。
 
 *获取class文件对象的方式:
     A:Object类的getClass()方法
     B:数据类型的静态属性class
     C:Class类中的静态方法
         public static Class forName(String Name)
 
 *↑↑↑↑一般使用情况:
     A:自己玩    任选一种,第二种比较方便
     B:开发     第三种
 
 *为什么? 
         因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中(方便更改)。


 *举例:
     一、获取字节码对象
     二、通过反射获取无参构造方并使用
    三、通过反射获取带参构造方法并使用
    四、通过反射获取私有构造方法并使用
    五、通过反射获取成员变量并使用
    六、通过反射获得带参带返回成员方法并使用
    七、通过配置文件运行类中的方法,(通过反射运行配置文件内容)
    八、我给你ArrayList<Integer> 的一个对象 ,我想在这个集合中添加一个字符串数据,如何实现呢?
    九、通过反射写一个通用的设置某个对象的某个属性为指定的值
     

*举例说明:
    一、获取字节码对象:
        // 方式1
        Person p1 = new Person();
        Class c1 = p1.getClass();
        
        Person p2 = new Person();
        Class c2 = p2.getClass();
        
        System.out.println("方式一:");
        System.out.println(p1 == p2);
        System.out.println(c1 == c2);
        
        // 方式2
        Class c3 = Person.class;
        //举例:int.class   String.class
        
        System.out.println("方式二:");
        System.out.println(c1 == c3);
        
        // 方式3
        try {
            Class c4 = Class.forName("org.bx.Person");
            
            System.out.println("方式三:");
            System.out.println(c1 == c4);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        
    二、通过反射获取构造方并使用
        //获取字节码文件对象
        Class c = Class.forName("org.bx.demo01.Person");
        
        //获取构造方法
        //public  Constructor[] getConstructor() :获取所有公共的构造方法
        //public  Constructor[] getDeclaredConstructor() :获取所有公共的构造方法
        /*//Constructor[] cons = c.getConstructors();
        Constructor[] cons = c.getDeclaredConstructors();
        
        for(Constructor cs : cons){
            System.out.println(cs);
        }*/
        
        //获取无参构造方法
        //public Constructor<T> getConstructor(Class<?>... param)
        //参数表示的是:你要获取构造方法的构造参数个数及数据类型的class字节码文件对象
        Constructor con = c.getConstructor(); //返回的是构造方法对象
        
        
        //Person p = new Person();
        //System.out.println(p);
        //public T newInstance(Object... init)
        //使用此Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例
        Object obj = con.newInstance();
        
        System.out.println(obj);
        
        Person p = (Person)obj;
        p.show();

    三、通过反射去获取带参构造方法并使用:
        //获取字节码文件
        Class c = Class.forName("org.bx.demo01.Person");
        
        //获取带参构造方法  (参数类型必须一致)
        //public Constructor<T> getConstructor(Class<?>... params))
        Constructor con = c.getConstructor(String.class,int.class,String.class);
        
        //通过带参构造方法对象创建对象
        //public T newInstance(Object... instance)
        Object obj = con.newInstance("白鑫",19,"北京");
        
        System.out.println(obj);

    四、通过反射获取私有构造方法并使用
        //获取字节码对象
        Class c = Class.forName("org.bx.demo01.Person");
        
        /* 获取私有构造方法
         * NoSuchMethodException:不能匹配该方法
         * 原因是一开始使用的方法只能是公共的,下面这中方式就可以了。
         */
        Constructor con = c.getDeclaredConstructor(String.class);
        
        /* 用该私有方法创建实例
         * IllegalAccessException:非法的访问异常
         * setAccessible:设置访问
         * 值为true时指明反射的对象在使用时取消了Java语言访问检查
         */
        con.setAccessible(true);
        Object obj = con.newInstance("白鑫");
        
        System.out.println(obj);

    五、通过反射获取成员变量并使用
        //获取字节码对象
        Class c = Class.forName("org.bx.demo01.Person");
        
        //获取所有的成员属性
        /*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();
        
        //获取单个的成员变量
        //获取address并对其赋值
        Field addressField = c.getField("address");
        //public void set(Object obj,Object value)
        //将指定对象变量上此Field 对象表示的字段设置为指定的新值
        addressField.set(obj,"北京");//给obj对象的addressField字段设置值为“北京”
    
        System.out.println(obj);
        
        //获取name并其赋值
        Field nameField = c.getDeclaredField("name");
        //关闭java语法检查
        nameField.setAccessible(true);
        nameField.set(obj, "白鑫");
        System.out.println(obj);
        
        //age属性一致

    六、通过反射获得带参带返回成员方法并使用
        //获取字节码文件对象
        Class c = Class.forName("org.bx.demo01.Person");    
        
        //获取所有的方法
//        Method[] methods = c.getMethods(); //获取自己的包括父级的公共方法
//        Method[] methods = c.getDeclaredMethods(); //获取自己的所有方法
//        for(Method m : methods){
//            System.out.println(m);
//        }
        
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();
        
        /*
         * Person p = new Person();
         * p.show();
         */
        
        //public Method getMethod(String name,Class<?>... params)
        //第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型
        
        //获取单个方法并使用
        System.out.println("----无参方法----");
        Method m1 = c.getMethod("show");
        //obj.m1();//错误
        //public Object invoke(Object obj,Object... args)
        //返回值是Object接收,第一个参数表示对象是谁,第二个参数表示调用该方法的实际参数
        m1.invoke(obj); //本质上 调用obj对象的m1方法
        
        System.out.println("----无参方法----");
        Method m2 = c.getMethod("method",String.class);
        m2.invoke(obj, "你好");
        
        System.out.println("----有参方法----");
        Method m3 = c.getMethod("method1",String.class,Integer.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("----私有方法----");
        Method m4 = c.getDeclaredMethod("function");
        m4.setAccessible(true);
        m4.invoke(obj);

    七、通过配置文件运行类中的方法,(通过反射运行配置文件内容)

         * 使用反射:
              需要有配置文件配合使用
                用class.txt代替
                  并且知道有两个键:className 、methodName
        -- Test类:
            //反射前的做法
    //        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 o = con.newInstance();
            
            //调用方法
            Method m = c.getMethod(methodName);
            m.invoke(o);
            
        -- class.txt文件,(模拟配置文件)
            className=org.bx.testproject.Student
            methodName=love
            
        -- Student类
            public void love(){
                System.out.println("爱编程!");
            }

    八、我给你ArrayList<Integer> 的一个对象 ,我想在这个集合中添加一个字符串数据,如何实现呢?
        ArrayList<Integer> al = new ArrayList<Integer>();
        
        //操作前
        //al.add("hello");  //错误
        //al.add(10);  //正确
        
        //利用反射
        Class c = al.getClass();//集合ArrayList的class文件对象
        Method m = c.getMethod("add", Object.class);// 源文件的泛型返回的是Object类型
        
        m.invoke(al,"hello");//调用array的add方法,传入的值是hello
        m.invoke(al,"java");

    九、通过反射写一个通用的设置某个对象的某个属性为指定的值

        -- Tool类(公共执行类):
            public void setProperty(Object obj,String propName,Object value) throws Exception{
        //根据对象获取字节码文件对象
        Class c = obj.getClass();
        //获取该对象的peopName成员变量
        Field f = c.getDeclaredField(propName);
        //取消java语法检查
        f.setAccessible(true);
        f.set(obj, value);
    }
    
        -- 测试类
             public class ToolTest {
    
                public static void main(String[] args) throws Exception {
                    Person p = new Person();
                    
                    Tool t = new Tool();
                    t.setProperty(p, "name", "白鑫");
                    t.setProperty(p, "age", 19);
                    System.out.println(p);
                    
                    System.out.println("\n--------------");
                    Dog d = new Dog();
                    t.setProperty(d, "sex", '男');
                    t.setProperty(d, "price", 666.6f);
                    System.out.println(d);
                }
            }
            //内部类Dog
            class Dog{
                char sex;
                float price;
                
                @Override
                public String toString() {
                    return "Dog [sex=" + sex + ", price=" + price + "]";
                }
            }
            //内部类Person
            class Person{
                private String name;
                public int age;
                
                @Override
                public String toString() {
                    return "Person [name=" + name + ", age=" + age + "]";
                }
            }                                                    


-- 动态代理:

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

    举例:春节回家买票让人代买

*动态代理:在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理

*在java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理、我们有更强大的代理cglib
*Proxy类中的方法创建动态代理类对象
    public static Object newProxyInstance(Classloader loader,Class<?>[] interface,InvocationHandler h)
    最终会调用InvocationHandler的方法
*InvocationHandler
    Object invoke(Object proxy,Method method,Object[] args)

*动态代理:
    就是在原有的功能上添加新的功能,  跟Spring框架的aop机制一样

-- 动态代理举例说明:
    -- 实现代理类:
        public class MyInvocationHandler implements InvocationHandler{

            private Object target;//目标对象
            
            public MyInvocationHandler(Object target){
                this.target = target;
            }
            
            @Override
            public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
                 System.out.println("权限校验!");
                 Object result = method.invoke(target, args);
                 System.out.println("日志记录!");
                return result;
            }

        }

    -- 测试类
        System.out.println("----------使用对象----------");
        
        UserDao ud = new UserDaoImpl();
        ud.add();
        ud.delete();
        ud.update();
        ud.find();
        
        System.out.println("\n----------使用代理对象----------");
        
        //创建动态对象
        //Proxy类中有一个方法可以创建动态代理对象
        //public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
        //准备对ud对象做一个代理对象     ClassLoader:类加载器
        MyInvocationHandler handler = new MyInvocationHandler(ud);
        Object proxy = Proxy.newProxyInstance(ud.getClass().getClassLoader(), ud.getClass().getInterfaces(), handler);
        //最终返回的对象
        UserDao ud1 = (UserDao)proxy;
        ud1.add();
        ud1.delete();
        ud1.update();
        ud1.find();    
 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

筱白爱学习

你的鼓励将是我写作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值