• 30反射


     ·

    耦合度:多个模块之间的关联或者依赖关系(低耦合)

    解析类:用于找到字节码对象以产生实例对象的过程----反射   字节码文件就是.class

    Class---代表类的类,把类抽取成一个对象,代表的也就是.class的类

    Field --------代表属性的类

    Method-------代表方法的类

    Construct-----代表构造方法的类

    Annotation----代表注解的类

    Package------代表包的类

     

     类加载器

    类加载器负责将.class文件(可能在磁盘上,也可能在网络上)加载到内存种,并为之生成对应的

    java.;ang.Class对象。

    获取字节码对象的方式

        //1.类型.class
        //获取String的字节码对象
        Class<String>clz=String.class;
        System.out.println(clz); //class java.lang.String
        //接口的字节码对象
        Class<List>clc=List.class;
        System.out.println(clc); //interface java.util.List
        //基本类型的字节码对象
        Class cla=int.class;
        System.out.println(cla);//int
        String str="abc";
    //2对象.class 返回的是对象实际创建类的字节码对象
        Class<String>clb=(Class<String>) str.getClass();//class java.lang.String
        System.out.println(clb);
        Integer i=1;
        Class<Integer> cld=(Class<Integer>) i.getClass();//class java.lang.Integer
        System.out.println(cld);
        //3.没有类没有对象获取对应的字节码对象 ----Class.forName(全路径名);
        Class clh=Class.forName("java.utils.Math"); //class java.lang.String
        //class java.lang.Integer
        System.out.println(clh);

     

    通过构造器创建实例对象

    public class ClassDemo2 {
        public static void main(String[] args) throws ReflectiveOperationException, IllegalAccessException {
            //先获取Stirng的字节码对象
            Class<String> clz=String.class;
            //获取实例对象
            //newInstance()---默认调用类的无参构造
            //String str=clz.newInstance();    
            //str="abc";
            //System.out.println(str);
            //通过字节码对象调用到String类的有参构造
            Constructor c=clz.getConstructor(char[].class);//传入构造方法参数类型的字节码对象,获取到一个构造方法
            Constructor c1=clz.getConstructor(String.class);
            //获取到的有参构造调用newInstance()传入实际参数构造实例对象
            String str=(String) c.newInstance(new char[] {'1','4','a'});
            String str1=(String)c1.newInstance("dbsb");
            System.out.println(str);
            System.out.println(str1);
            //根据Integer创建一个对象
            //通过Class.forname获取字节码对象
            Class<Integer> clz3=(Class<Integer>) Class.forName("java.lang.Integer");
            //获取有参构造方法new Integer(int)
            Constructor c2=clz3.getConstructor(int.class);
            Integer str2=(Integer) c2.newInstance(123);
            System.out.println(str2);
        }
    }

    获取私有或者默认的构造方法

    public class ClassDemo3 {
        public static void main(String[] args) throws ReflectiveOperationException, SecurityException {
            //获取字节码对象
            Class<String> clz=String.class;
            //获取指定构造方法,忽略访问权限修饰符
            //getDeclaredConstructor 和getConstructor区别,前者可以忽略掉构造方法的访问修饰符,尽量都用前者
            Constructor c1=clz.getDeclaredConstructor(char[].class,boolean.class);
            c1.setAccessible(true);//为true就是暴力破解---将原来的构造方法破解成我们可以直接赋值的构造方法
            //传参--构建实例对象;
            String str=(String) c1.newInstance(new char[] {'1','2','3'},true);//直接赋值时不行的,因为访问修饰符你
            //不能传值进去,要通过反射的暴力破解特殊的构造方法
        }
    }

    获取全部的构造方法

        public static void main(String[] args) {
            Class<String>clz=String.class;
            //获取字符数组的所有的构造方法
            Constructor[] cs=clz.getDeclaredConstructors(); 
            System.out.println(Arrays.toString(cs));
        }

    获取属性

    public class ClassDemo5 {
        public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
            //String 字节码对象
            Class<String> clz=String.class;
            //获取指定属性
            Field f=clz.getDeclaredField("hash");//hash属性是私有的
            //暴力破解
            f.setAccessible(true);
            //给字符串对象 属性赋值
            String str="abc";
            //给对象属性赋值 ---给Str对象的hash属性赋值123
            f.set(str, 123);
            //取值 ----获取str对象hash属性的值
            System.out.println(f.get(str));//123
            //获取String对象所有的属性
            Field[] fs=clz.getDeclaredFields();
    
        }
    }

    获取普通方法

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class ClassDemo6 {
        public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
            //获取字节码对象
            Class<String>clz=String.class;
            //获取指定方法
            Method m=clz.getDeclaredMethod("charAt",int.class);
            String string="ndsjknajk";
            //调用方法 ---s.charAt(3)
            char c=(char) m.invoke(string, 3);
            System.out.println(c);//j
            //获取全部的方法
            Method[] m2=clz.getDeclaredMethods();
        }
    }

    反射的缺点:

    1.打破了封装原则

    2跳过泛型的类型检测

    反射跳过泛型检查
    
     @Test
    public void method() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            //创建集合
    ArrayList<Integer> list = new ArrayList<>();
            //通过对象获取字节码对象
    Class<? extends ArrayList> aClass = list.getClass();
            //反射获取add ()
    Method method = aClass.getMethod("add", Object.class);
            method.invoke(list,"mlj");
            method.invoke(list, "qq");
            method.invoke(list, 'h');
            System.out.println(list);
            //输出 [mlj, qq, h]
    
    //        分析:反射直接跳过了泛型Integer,list存储String类型值;
    //        结论:泛型只是给编译器看的,实际的单列集合,双列集合可以交叉存储任意引用值
    }
     

    反射案例: 实现克隆案例

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    
    //通过反射实现克隆
    public class ClassClone {
        public static void main(String[] args) throws InstantiationException, IllegalAccessException {
            Person p1=new Person(10,"徐旺骑");
            Person p2=(Person) clone(p1);
            System.out.println(p2.age+p2.name);
        }
        //实现Clone方法
        public static Object clone(Object obj) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
        {
            //1.获取字节码对象
            Class<Object> clz=(Class<Object>) obj.getClass();
            //2.获取实例对象 ---新对象
            //2.1获取构造方法---保证一定能拿到构造方法
            Constructor c=clz.getDeclaredConstructors()[0];
            //返回拿到的这个构造方法上所有的参数类型的字节码对象
            Class[] cs    =c.getParameterTypes();
            //Object的数组,这个是为了给构造方法给值
            Object[]os=new Object[cs.length];
            //遍历参数数组
            for(int i=0;i<cs.length;i++)
            {
                //判断参数是否是基本类型
                if(cs[i].isPrimitive())
                {
                    if(cs[i]==byte.class||cs[i]==short.class||cs[i]==int.class)
                    {
                        os[i]=0;
                    }
                    if(cs[i]==char.class)
                    {
                        os[i]='u0000';
                    }
                    if(cs[i]==float.class)
                    {
                        os[i]=0.0f;
                    }
                    if(cs[i]==double.class)
                    {
                        os[i]=0.0d;
                    }
                    if(cs[i]==boolean.class)
                    {
                        os[i]=false;
                    }
                    
                }
                else {
                    os[i]=null;
                }
            }
            //传值--构造实例对象--新对象
            Object o1=c.newInstance(os);
            //3.获取原对象的所有属性
            Field[] fs=clz.getDeclaredFields();
            //4.把原对象的属性给新对象
            for(Field f:fs)
            {
                //暴力破解
                f.setAccessible(true);
                //获取原对象的属性
                Object o2=f.get(obj); //原对象的每个属性
                //所有属性赋值给新对象
                f.set(o1, o2);
    
            }
            //5.返回新对象
            return o1;
        }
    }
    class Person{
        int age;
        String name;
        public Person(int age,String name) {
            this.age=age;
            this.name=name;
        }
    }
    package cn.tedu.reflect;
    
    public class ClassDemo6 {
        
        public static void main(String[] args) {
            //枚举类的字节码对象
        /*    Class<Demo> clz=Demo.class;
            //返回枚举常量
            Demo[] ds=clz.getEnumConstants();
            for (Demo demo : ds) {
                System.out.println(demo); //ABCD
            }*/
            
            //
            Class<String> clz=String.class;
            //没有枚举常量是返回一个null
            String[] ss=clz.getEnumConstants();
            
            //返回的是实现的接口
            Class[] cs=clz.getInterfaces();
            for (Class class1 : cs) {
                System.out.println(class1);
                //interface java.io.Serializable
                //interface java.lang.Comparable
                //interface java.lang.CharSequence
            }
            
            //字节码对象对应类全路径名
            System.out.println(clz.getName());//MethodDemo.java
            
            //返回字节码对象对应类的包的信息
            System.out.println(clz.getPackage());//package java.lang, Java Platform API Specification, version 1.8
            
            //字节码对象对应类的类名
            System.out.println(clz.getSimpleName());//String
            
            //得到父类的字节码对象
            System.out.println(clz.getSuperclass());//class java.lang.Object
        }
    
    }
    
    
    //
    enum Demo{
        A,B,C,D;//枚举常量---对象
    }
        //获取字节码对象
            Class<String> clz=String.class;
            //可以获取指定属性
            Field f=clz.getDeclaredField("serialVersionUID");
            //返回属性的数据类型
            System.out.println(f.getType()); //long
    public class MethodDemo {
        
        public static void main(String[] args) throws NoSuchMethodException, SecurityException {
            //
            Class<String> clz=String.class;
            //获取指定方法
            /*Method m=clz.getDeclaredMethod
                    ("getBytes", String.class);*/
            Method m=clz.getDeclaredMethod
                    ("getBytes", Charset.class);
            /*//返回的是编译时异常
            Class[] cs=m.getExceptionTypes();
            for (Class class1 : cs) {
                System.out.println(class1);
            }*/
            
            //获取参数类型的字节码放到一个数组
            Class[] cs=m.getParameterTypes();
            for (Class class1 : cs) {
                System.out.println(class1);
            }
        }
    
    }
  • 相关阅读:
    Linux关闭防火墙命令
    js改变数组的两个元素的位子,互换、置顶
    vue nexttick的理解和使用场景
    vue mint-ui 框架下拉刷新上拉加载组件的使用
    vue项目中使用了vw适配方案,引入第三方ui框架mint-ui时,适配问题解决
    小程序开发笔记【二】,抽奖结果json数据拼装bug解决
    gulp插件gulp-nunjucks-render的使用及gulp4的简单了解
    小程序开发笔记【一】,查询用户参与活动列表 left join on的用法
    mysql数据插入前判断是否存在
    微信公众号通过图片选取接口上传到阿里oss
  • 原文地址:https://www.cnblogs.com/xuwangqi/p/11263205.html
Copyright © 2020-2023  润新知