java基础加强(反射、内省、BeanUtils工具类、类加载器、动态代理)



反射:就是把java类中的各种成分映射成相应的java类。
1.获取类的字节码:
    String str = "abc";
    Class cls1 = str.getClass();
    Class cls2 = String.class;
    Class cls3 = Class.forName("java.lang.String");
    得到的三分类的字节码都是一样的。比较结果都为true,就是说内存中只创建了一份字节码
    int.class!=Integer.class;  int.class==Integer.TYPE;
2.获取类的构造函数:
    Constructor<T> getConstructor(Class<?>... parameterTypes) 
        返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。 
    //利用反射实现 new String(new StringBuffer("abc"));
    Constructor constructor = String.class.getConstructor(StringBuffer.class);
    //反射回来的结果需要进行强转
    String str = (String)constructor.newInstance(new StringBuffer("abc"));
3.获取字段
    Field fieldY = obj.getClass().getField("y");//再利用Field类型对象中的方法得到相应的字段值。
    暴力访问:如果该字段是private修饰的。
    Field fieldX = obj.getClass().getDeclaredField("x");
    fielsX.setAccessible(true);//此次访问为暴力访问。再利用相应的方法获取字段值。
    获取所有公共字段 getFields();
4.获取方法
    Method getMethod(String name, Class<?>... parameterTypes) 
          返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 
    //实现 str.charAt(1);
    Mehtod methodCharAt = String.class.getMethod("charAt",int.class);
    methodCharAt.invoke(str,1);//Object invoke(Object obj, Object... args);
    若为methodCharAt.invoke(null,1);则表示此方法为静态方法。因为不需要对象去调用它,可以直接用类名调用。
    *方法参数为数组时:
    按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每一个元素对应一个参数,
    当把一个数组作为参数传递给invoke(obj,new Object[]{...}),由于jdk1.5肯定要兼容jdk1.4,所以传递的数组对象
    会按照jdk1.4的语法当做多个参数来解析,,因此出现参数类型不对的问题。
    *解决办法:
    1.invoke(obj,new Object[]{new Object[]{...}});如此,将数组对象封装到一个数组中,
        这个数组中只有一个对象,就是此数组。
    2.invoke(obj,(Object)new Object[]{...});如此,将数组对象进行强转动作,
        让虚拟机认为他就是一个Object对象而不是Object数组。
--------------------------------------------------------
内省:IntroSpector
    1.利用内省调用ReflectPoint类中的getX()方法,取出x值。
    ReflectPoint pt1 = new ReflectPoint(3,5);
    String propertyName = "x";
    PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt1.getClass());
    Mehtod methodGetX = pd.getReadMethod();
    Object retVal = methodGetX.invoke(pt1);//retVal就是利用内省取出的x值。
    2.利用内省调用ReflectPoint类中的setX()方法,修改x值。
    Method methodSetX = pd.getWriteMethod();;
    methodSetX.invoke(pt1,7);//利用内省将pt1对象中的x值改为7.
---------------------------------------------------------
BeanUtils工具类:
    利用 BeanUtils获取和修改ReflectPoint类中的x值,
    BeanUtils.getProperty(pt1,"x");//pt1ReflectPoint类的一个对象。
    BeanUtils.setProperty(pt1,"x","7")//7为修改后的值。"x"为要修改的属性。
    设birthdayReflectPoint中的一个成员,Birthday类中有time属性。想用BeanUtils修改time的值,如下:
    BeanUtils.setProperty(pt1,"birthday.time","111");//修改后的值为111,也就是说setProperty支持级联属性。
    同样也可以用getProperty(pt1,"birthday.time")获取time值。
  BeanUtilsPropertyUtils区别:
    BeanUtils以字符串的形式对javaBean进行操作。
    PropertyUtils是以属性本身的类型对javaBean进行操作。
---------------------------------------------------------
类加载器:
    1.java虚拟机中可以安装多个类加载器,系统默认3个主要类加载器,每个类负责加载特定位置的类:
    BootStrap,ExtClassLoader,AppClassLoader
    2.类加载器也是java类,因为其他java类的类加载器本身也要被类加载器加载,因此必须有一个类加载器不是
    java类,这正是BootStrap
    3.java虚拟机中的所有类加载器采用父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指
    定一个父级类加载器对象或默认采用系统类加载器为其父级类加载器。
    BootStrap--->JRE/lib/rt.jar
    ExtClassLoader--->JRE/lib/ext/*.jar 。 */
    AppClassLoader--->CLASSPATH指定的所有jar或目录

    *类加载器的委托机制
    java虚拟机加载类的3种方式:
    1.首先当前线程的类加载器去加载线程中的第一个类。
    2.如果类A中引用了类Bjava虚拟机将使用加载类A的类加载器来加载类B
    3.还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。//ClassLoader位于java.lang包下。
    每个类加载器加载类时,先委托给其上级类加载器,其上级类加载器再委托给上级类加载器。让最顶层的加载器去加载类,
    当父类加载器没有加载到类,回到下一级加载器中找,若找不到,再向下一级,直到回到发起者类加载器,还加载不了,
    则抛ClassNotFoundException.
    自定义的类加载器必须继承抽象类ClassLoader
-------------------------------------------------------
动态代理:
    交叉业务的编程问题即为面向方面的编程(Aspect oriented program,简称AOP),AOP的目标就是要使交叉业
    务模块化,可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的。
    代理是实现AOP功能的核心和关键技术。
    动态代理技术
    1JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
    2JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用做具有相同接口的目标类的代理。
    3CGLIB库(还不是标准,只是一个开源的东西。)可以动态生成一个类的子类,一个类的子类也可以用作该类
    的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
    4。代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的
    如下4个位置加上系统功能代码:
    a.在调用目标方法之前
    b.在调用目标方法之后
    c.在调用目标方法前后
    d.在处理目标方法异常的catch块中。
    用于了解代理的一段代码:
    public static void main(String[] args) throws Exception{
        //获取代理类的字节码
        Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
        //创建动态类实例对象
        Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
        //利用匿名内部类创建InvocationHandler对象,创建一个动态类实例对象,此处创建的是Collection对象
        Collection proxy = (Collection) constructor.newInstance(new InvocationHandler(){
            private ArrayList list = new ArrayList();
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                long startTime = System.currentTimeMillis();
                Object retVal = method.invoke(list, args);
                long endTime = System.currentTimeMillis();
                System.out.println(retVal+"time="+(endTime-startTime));
                //返回值为调用相应方法的返回值,例如调用listadd方法,返回boolean值。
                return retVal;
            }
        });
        //利用代理类创建的对象调用其相应的方法,在调用方法时,如下add方法,则会调用上面的匿名内部类
        //InvocationHandler类内部实现的invoke方法,各参数的意义:
        //proxy:就是动态创建的那个类对象proxy
        //method:就是proxy调用的add方法;
        //args:就是add方法要添加的参数;
        proxy.add("lihuoming");
        proxy.add("bixiangdong");
        //在代理类中的调用模式
        /*Class Proxy${
        add(Object object){
        return invocationhandler.invode((Object proxy,Method method,Object[] args);
        }
        }*/
        System.out.println(proxy);
        //proxy调用size()方法,其实是通过handler对象调用的invoke方法。与上面的add方法基本思想相同
        System.out.println(proxy.size());

        /*另外一种直接创建动态类实例的方法,不需要或去代理类的字节码便可以直接创建对象。
         * 此处用的是代理类Proxy的另外一个静态方法,
         * Proxy.newProxyInstance(loader, interfaces[], invocationhandler)
        Collection proxy2= (Collection)Proxy.newProxyInstance(
                Collection.class.getClassLoader(), 
                new Class[]{Collection.class}, 
                new InvocationHandler(){
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        
                        return null;
                    }
                });
         */
    }


用于对代理的封装,使得其更具扩展性。真正的动态代理是如何实现的
public interface Advice {
    void beforeMethod(Method method);
    void afterMethod(Method method);
}

/*此类实现了Advice接口,用于实现给代理类中的invoke添加功能用的。
 * beforeMethod中的method就是指代理类中InvocationHandlerinvoke方法,afterMethod亦是如此。 
 */
public class MyAdvice implements Advice{
    private long startTime;
    private long endTime;
    //此处接收method参数,主要是可以使用method添加一些自己的操作。
    public void afterMethod(Method method) {
        startTime = System.currentTimeMillis();
    }
    public void beforeMethod(Method method) {
        endTime = System.currentTimeMillis();
        System.out.println("time="+(endTime-startTime));
    }
}

public class RealProxy {
    public static void main(String[] args) throws Exception{
        final ArrayList target = new ArrayList();
        Collection proxy2 = (Collection)getProxy(target,new MyAdvice());
        proxy2.add("lihuoming");
        proxy2.add("bixiangdong");
        System.out.println(proxy2);
        System.out.println(proxy2.size());
    }
    /*为了实现更高的扩展性,将所有的硬编码,都换为可变的,实时性的。根据所传的参数来获取相应的代理类。
     *参数:Object target:将要被代理的类的对象,通过此对象获取将被代理的类的信息。
     *参数:Advice adviceAdvice为一个接口,目的是为了满足所有实现了Advice接口的子类。以便实现自己的添加功能。 
     */
    public static Object getProxy(final Object target,final Advice advice) {
        Object objProxy = Proxy.newProxyInstance(
                target.getClass().getClassLoader(), 
                target.getClass().getInterfaces(), 
                new InvocationHandler(){
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                advice.beforeMethod(method);
                Object retVal = method.invoke(target, args);
                advice.afterMethod(method);
                return retVal;
            }
        });
        return objProxy;
    }
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值