• 第27天反射(解剖)技术


    反射(解剖)技术

    1. 其他技术

      1. MD5加密技术

    MD5它是一种算法,它可以对普通字符串、数字、文件等进行加密。得到的加密后的数据(密文)是无法被破解的。

    MD5也被称为单向不可逆的算法。

     

    明文            算法                密文

    "abc"    -----à MD5算法    ------à "123384734584823445899"

     

    使用类中静态的方法获取类的对象:

     

    /*

    * 演示 使用 MessageDigest 对数据进行加密

    */

    public class MD5Utils {

     

        /*

         * getMD5 方法是对传递进来的字符串使用MD5算法加密

         */

        public static String getMD5(String value) throws NoSuchAlgorithmException {

     

            // 得到加密的对象

            MessageDigest digest = MessageDigest.getInstance("MD5");

            /*

             * 使用对象对字符串进行加密 byte[] digest(byte[] input)

             * 方法接收的一个字节数组,被加密的明文数据,返回的字节数组就是加密后的密文数据

             */

            byte[] bs = digest.digest(value.getBytes());

            // 定一个字符串缓冲区,用于存储转后的数据

            StringBuilder sb = new StringBuilder();

            // 需要对加密后的数组中的密文数据进行处理,将数据转成字符串,返回给调用者

            for (byte b : bs) {

                /*

                 * 需要对加密后的byte类型的数据转成十六进制,加密的byte数据的范围-128~127之间的数字,

                 * 而这些数字恰好占用8个二进制数位,如果我们取出8个二进制数位之后,将其转成int值,那么就会在高位

                 * 补足24个零。当高位补零之后,所有的数字都会转成正数。

                 */

                int x = b & 255;

                // 将int值转成十六进制数据

                String s = Integer.toHexString(x);

                // 需要将转后的数据保存到字符串缓冲区中

                if( x >=0 && x <= 15 ){

                    sb.append("0");

                    sb.append(s);

                }else{

                    sb.append(s);

                }

            }

            return sb.toString();

        }

     

        public static void main(String[] args) throws NoSuchAlgorithmException {

            System.out.println(getMD5("a你"));

        }

    }

    1. Junit测试(练习)

    IDE:集成开发环境。我们开发软件的时候使用的编辑代码的编辑器(开发工具)。

     

    测试:它是软件开发中的一个环节。开发的软件在上线之前必须经过测试,找到软件开发中的一些问题。

     

    测试分成两种:

        白盒测试:在测试软件的时候,关注的是软件中的代码的流程,代码之间的调用关系等,不关注代码所实现的功能是否符合实际需求。

        黑盒测试:它是指软件开发环节结束,进行功能测试环节,主要是面对软件实现的功能测试,不关注代码。只要功能符合需求即可。

        

     

    任何高级的开发工具,它们都自带了测试代码的工具:eclipse自带的测试工具Junit。

     

    eclipse自带的junit测试使用步骤:

        1、在需要被测试的方法添加@Test 注解

            

        2、在报错的地方使用ctrl + 1 提示,根据提示导入junit的jar包

            

            添加完成之后,在项目目录下会多出一个junit的jar包

            

        3、选中添加@Test的方法,右击选择 junit 运行

            

            在运行之后,会出现junit的视图:

            

            junit的视图如果显示的绿条,表示被测试的方法没有问题。如果是红色条,测试失败。

            

        junit测试的细节:

            1、@Test它只能添加在public修饰的、没有返回值的、不需要参数的方法上。

            2、其他的注解:

                @Before 它是在添加@Test方法运行之前运行

                @After 它是在添加@Test方法运行之后运行

                @BeforeClass 它是在类加载的时候运行,类似于静态代码块的作用

                @AfterClass 它是在程序结束的时候运行        

    1. debug调试(练习)

    debug它是开发工具自带的调试程序的功能。

     

    需求:计算1到10和值,要求打印出sum在计算累加和时的变化情况。

     

     

    debug的调试使用步骤:

        1、在需要查询变量、调试的表达式、语句最左侧双击打上断点

            

        2、运行程序,选择debug as 方式运行        

            

        3、在确认框中选择yes,引入到debug的调试视图中

            

        4、选择对应的符号,进行程序的调试动作

            

             F8 跳转到程序中的下一个断点位置。如果程序中只有一个断点,这时程序会运行结束。

            

             停止JVM的运行

             F6 它是跳转到程序停留所在行的下一行。

             F5 跳转到当前程序停留所在行调用的方法中。

             F7 跳出进入的方法

        5、在调试的过程中,我们可以使用watch 功能,查询某个表达式、变量、语句的结果

        6、调试结束之后,需要操作两个步骤:

            1)清空所有的表达式视图中监控的变量、表达式信息。

            2)清空程序中所有的断点。

            

        7、调试结束之后,将视图切换到当初开发时候使用的视图。

            

    1. 枚举介绍(了解)

      1. 枚举的介绍

    枚举:它是本身是Java在JDK5的时候给出的一个新的数据类型。但这个类型有自己特定的应用场景。

     

    单例类:它是保证类的对象唯一的。

    多例类:一个类的对象是有限个。这时这个类就不能提供公开的构造方法,必须像单例类的书写一样,在类中将所有可以存在的对象创建出来,然后对外提供对应的方法获取对象。

     

    /*

    * 演示多例类

    */

    public class Sex {

        

        // 私有构造方法

        private Sex(){}

        

        // 创建对象

        private static Sex male = new Sex();

        private static Sex female = new Sex();

     

        // 提供方法

        public static Sex getMale(){

            return male;

        }

        public static Sex getFemale(){

            return female;

        }

    }

    上面的代码演示一个简单的多例类,而类中的对象个数是固定的,因此如果我们程序中需要多例类的话,可以使用JDK5中枚举技术代替多例类。

    枚举类的定义:

        修饰符 enum 类名{

        

    }

    /*

    * 定义枚举类

    */

    public enum Gender {

        

        // 必须是定义这个类可以出现的多个对象的引用变量名

        male , female;

    }

    1. 带有参数和成员变量的枚举类

    /*

    * 定义枚举类

    */

    public enum Gender {

        

        // 必须是定义这个类可以出现的多个对象的引用变量名

        male("男") , female("女");

        // 定义成员变量

        private String name;

        // 提供有参数的构造方法

        private Gender( String name ){

            this.name = name;

        }

    }

    1. 拥有抽象方法的枚举类

    /*

    * 定义枚举类

    */

    public enum Gender {

        

        // 必须是定义这个类可以出现的多个对象的引用变量名

        male("男"){

            public void print(){

                System.out.println("男");

            }

        } ,

        female("女"){

            public void print(){

                System.out.println("女");

            }

        };

        // 定义成员变量

        private String name;

        // 提供有参数的构造方法

        private Gender( String name ){

            this.name = name;

        }

        // 书写抽象方法

        public abstract void print();

    }

    1. 枚举类的细节

      1. 只要定义好枚举类之后,这个类中就默认有个私有无参数的构造方法
      2. 枚举类中的第一行,必须是定义这个枚举类可以出现的那些对应的引用变量名称。
      3. 所有枚举类默认的父类都是Enum类,但它可以可以使用implements 实现接口
      4. 自己定义的枚举类,可以直接使用Enum类中的方法

          

        

    面试题:

        switch语句都支持哪些数据类型?

        在JDK5之前支持4种基本类型:byte、short、int、char

        在JDK5的时候支持:enum(枚举)类型

        在JDK7的时候支持:String(字符串)类型

    1. 反射(解剖)的应用场景

    1. Class介绍

      1. Class对象介绍 理解

    我们在程序中书写的接口、抽象类、类、内部类、枚举等它们在使用javac编译之后生成都是class文件(字节码文件)。而所有的class文件对JVM而言就是一类可以被执行运行的文件。class文件可以看成一类特殊的文件数据。

     

    在Java中使用Class类专门负责描述任何的class文件。

    1. 获取Class对象方式 必须掌握

    Class类,它描述的任何的class文件(字节码文件)。任何的class文件它们都是Class类的一个实例。

     

     

    Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

     

    1. 第一种方式(重点)

    我们通过new关键字创建出的任何类的对象它们都是基于某个class文件在对象中的Class对象创建出来的。我们就可以通过new出来的这个类的就一个具体的对象找到它所依赖的那个类对应的class文件对象。

     

        /*

         * 演示获取Class对象(某个类对应的class文件)的第一种方式:

         *     使用Object类中的getClass方法,由于任何使用new关键字创建出来的对象

         *     它们都有能力找到创建自己时依赖的class文件

         */

        @Test

        public void demo1(){

            

            // 创建Person的对象

            Person p = new Person();

            

            // 使用Object类中的getClass方法

            Class clazz = p.getClass();

            System.out.println(clazz);

            

            // 获取数组对应的Class的对象(数组对应的class文件)

            int[] arr = new int[4];

            System.out.println(arr);

            Class clazz2 = arr.getClass();

            System.out.println(clazz2);

            

        }

    1. 第二种方式(重点)

    第一种获取Class对象的方法是必须得到某个类型对应的具体的对象,然后通过对象调用getClass方法得到class文件。平时我们开发中,如果得到某个类的对象,这时就可以通过这个对象调用类中的方法完成需求,没有必要反推得到class文件。

     

    第二种方案:

        任何类型都可以直接调用class属性,得到其对应的class文件(Class对象)。

        格式:类型.class

     

        /*

         * 任何类型其中都一个class属性

         */

        @Test

        public void demo2(){

            

            // 获取自定义类型对应的clas文件

            Class clazz = Person.class;

            System.out.println(clazz);

            

            // 获取数组对应的class文件

            Class clazz2 = int[].class;

            System.out.println(clazz2);

            

            // 基本类型

            Class clazz3 = int.class;

            System.out.println(clazz3);

            

            Class clazz4 = java.util.List.class;

            System.out.println(clazz4);

        }

    1. 第三种方式(重点)

    第二种方式要求必须知道具体的类型,才能获取到class文件。平时我们在获取Class对象(class文件)的时候,并不一定会知道具体的数据类型。

    真正在写程序的时候,我们需要通过文件读取别人配置的一些相关类的信息,这时别人配置什么,我们的程序就会读取到什么。读取到具体的信息后,才能通过读取的数据加载和获取对应的class文件。

     

        /*

         * 介绍第三种方式获取Class对象

         * 在Class类中有个forName方法,可以将需要加载的类或接口的信息以字符串的形式传递给forName方法

         * 这个方法就能够将硬盘的class文件找到并加载到内存中

         *

         * 使用Class类中的forName方法加载某个class文件,这时要求书写的字符串必须的包名和类名

         */

        @Test

        public void demo3() throws ClassNotFoundException{

            

            // 字符串表示的需要被加载的类的信息 ,这里的字符串信息未来是从文件中读取的

            String classname = "cn.itcast.sh.b_class.Person";

            

            /*

             * 使用Class类中的静态方法加载

             * 在我们使用Class类中的forName方法的时候,它的底层其实是将读取到的字符串表示的类或接口的信息对应的class文件

             * 从硬件上加载的方法区中,并在堆中创建出Class的对象,最后将Class对象的内存地址赋值clazz变量

             */

            Class clazz = Class.forName(classname);

            System.out.println(clazz);

        }

    1. 创建class文件对应的类的对象

        /*

         * 使用Class类中的newInstance方法创建某个类的对象

         */

        @Test

        public void demo4() throws Exception{

            

            // 获取Class的对象

            Class clazz = Class.forName("cn.itcast.sh.b_class.Person");

            

            // 调用newInstance方法得到某个类的对象

            Object obj = clazz.newInstance();

            

            // 上面两行代码等价于 Person p = new Person();

            System.out.println(obj);

        }

    注意:

        Class类中的newInstance方法要求被反射的类中必须有公开的空参数的构造方法。否则使用newInstance方法就发生异常。

    1. 反射(解剖、解析)类的成员

      1. 反射成员介绍 (重点)☆☆☆☆☆

    在我们定义类的时候,类中可以书写成员变量、成员方法、构造方法。编译源代码之后生成的class文件中肯定有这些成员内容。

     

    1. 反射构造方法 (重点)☆☆☆☆☆

    一个类中可以有多个构造方法,它们是以重载的形式存在。

    类中多个构造方法怎么区分:

        通过参数列表区分。其实是根据参数列表中变量的具体的数据类型区分。

    1. 反射非私有构造方法(重点)

        /*

         * 反射类中非私有的构造方法

         * public Person(String name, int age)

         */

        @Test

        public void demo1() throws Exception{

            

            // 获取Class对象

            Class clazz = Class.forName("cn.itcast.sh.b_class.Person");

            

            /*

             * 反射构造方法

             * getConstructor(Class<?>... parameterTypes)

             * 参数解释:

             *     Class<?>... parameterTypes : 被反射的构造方法上参数类型对应的Class

             */

            Constructor cons = clazz.getConstructor( String.class , int.class );

            /*

             * 反射到类中的构造方法之后,需要使用Constructor类中的newInstance方法创建这个类的对象

             * newInstance(Object... initargs)

             * 参数解释:

             *     Object... initargs : 被反射的构造方法在运行的时候需要的实际参数

             */

            Object obj = cons.newInstance("班长",18);

            // 上面三行代码等价于 Person p = new Person("班长",18);

            System.out.println(obj);

        }

    1. 反射私有构造方法(重点)

        /*

         * 反射类中的私有的构造方法

         * private Person(String name)

         */

        @Test

        public void demo2() throws Exception{

            // 获取Class对象

            Class clazz = Class.forName("cn.itcast.sh.b_class.Person");

            

            // 反射私有构造方法

            // getDeclaredConstructor(Class<?>... parameterTypes)

            Constructor cons = clazz.getDeclaredConstructor(String.class);

            

            // 如果要访问类中私有的成员,这时必须强制取消Java的权限检查(暴力访问)

            cons.setAccessible(true);

            // 调用newInstance方法得到对象

            Object obj = cons.newInstance("经纪人");

            System.out.println(obj);

        }

    1. 获取所有构造方法

        /*

         * 获取类中所有构造方法

         */

        @Test

        public void demo3() throws Exception{

            // 获取Class对象

            Class clazz = Class.forName("cn.itcast.sh.b_class.Person");

            /*

             * getConstructors 获取到的是类所有的公开的构造方法

             */

            Constructor[] cons = clazz.getConstructors();

            

            for (Constructor con : cons) {

                System.out.println(con);

            }

            System.out.println("------------------------");

            

            /*

             * getDeclaredConstructors 获取到的是类中所有的构造方法(公开和私有)

             */

            Constructor[] cons2 = clazz.getDeclaredConstructors();

            

            for (Constructor con : cons2) {

                System.out.println(con);

            }

            

        }

     

    面试题:private的所用:

        private是修饰类中的成员。被private修饰的成员,只能在本类中访问。但是如果我们使用反射技术,通过取消权限检查,私用的成员,在本类以外的其他地方也可以被使用。

    1. 反射成员变量 ☆☆☆☆☆

    成员变量如何区分:

        类中的成员变量只能通过变量名区分。

    1. 反射非私有非静态成员变量(重点)

        /*

         * 反射类中公开非静态的成员变量

         */

        @Test

        public void demo1() throws Exception{

            

            // 获取Class对象

            Class clazz = Class.forName("cn.itcast.sh.b_class.Person");

            

            /*

             * getField(String name)

             * String name : 被反射的变量名

             *

             * public String addr

             */

            Field field = clazz.getField( "addr" );

            

            /*

             * 类中的非静态的成员变量,它如果要能够存在,必须依赖在当前类的某个对象上。

             * 我们在使用反射的成员变量之前,需要得到当前被反射的这个类的一个对象

             */

            Object obj = clazz.newInstance();

            /*

             * 反射到成员变量,仅仅只能做两件事:

             *     1、给成员变量赋值

             * 2、获取成员变量的值

             *

             * set(Object obj, Object value)

             * Object obj : 成员变量锁依赖的对象,如果成员变量是静态的,这个参数可以书写null

             * Object value : 它是给成员变量设置的值

             */

            field.set(obj, "北京");

            System.out.println(obj);

        }

    1. 反射私有静态成员变量(重点)

        /*

         * 反射私有静态成员变量

         *

         * 静态成员变量,它不需要依赖类的对象

         *    private static String sex;

         */

        @Test

        public void demo2() throws Exception{

            // 获取Class对象

            Class clazz = Class.forName("cn.itcast.sh.b_class.Person");

            

            Field field = clazz.getDeclaredField("sex");

            

            // 由于是静态的,不需要对象,由于是私有的,需要取消权限

            field.setAccessible(true);

            

            field.set(null, "男");

            

            // 在设置值之后,创建对象,通过对象的打印,看反射的静态的值是否可以给当前这个对象使用

            Object obj = clazz.newInstance();

            System.out.println(obj);

        }

    1. 获取类中的所有成员变量

        /*

         * 获取类中的所有成员变量

         */

        @Test

        public void demo3() throws Exception{

            

            Class clazz = Class.forName("cn.itcast.sh.b_class.Student");

            /*

             * getFields 获取到的包含父类中的所有公开的成员变量

             */

            Field[] fields = clazz.getFields();

            for (Field field : fields) {

                System.out.println(field);

            }

            System.out.println("-----------------------------");

            /*

             * getDeclaredFields 获取到的是本类中的所有成员变量

             */

            Field[] fields2 = clazz.getDeclaredFields();

            for (Field field : fields2) {

                System.out.println(field);

            }    

        }

    1. 反射成员方法 ☆☆☆☆☆

    成员方法怎么区分:

        需要根据方法的名称和参数列表的参数类型一起区分。

    1. 反射非私有非静态的有参数没有返回值成员方法(重点)

        /*

         * 反射非私有非静态的有参数没有返回值成员方法

         * public void setName(String name)

         */

        @Test

        public void demo1() throws Exception{

            

            // 获取Class对象

            Class clazz = Class.forName("cn.itcast.sh.b_class.Person");

            

            /*

             * 反射成员方法

             * getMethod(String name, Class<?>... parameterTypes)

             * String name : 被反射的方法名

             * Class<?>... parameterTypes : 方法的参数列表对应的类型的class文件

             */

            Method method = clazz.getMethod("setName", String.class);

            

            /*

             * 由于反射的方法是非静态,因此让被反射的方法运行,必须有对象

             */

            Object obj = clazz.newInstance();

            /*

             * 反射到类中的成员方法之后,会得到Method对象,如果让被反射的方法运行,必须调用Method中的invoke方法

             * Object invoke(Object obj, Object... args)

             * 参数解释:

             *     Object obj :被反射的方法运行的时候依赖的那个对象

             *     Object... args : 被反射的方法运行的时候需要的实际参数

             * invoke方法的返回值Object,其实是被反射的方法的返回值

             */

            Object value = method.invoke(obj, "秋香");

            System.out.println(value);

            System.out.println(obj);

            

        }

    1. 反射类中私有静态的无参数没有返回值成员方法(重点)

        /*

         *反射类中私有静态的无参数没有返回值成员方法

         *private static void demo()

         */

        @Test

        public void demo2() throws Exception{

            // 获取Class对象

            Class clazz = Class.forName("cn.itcast.sh.b_class.Person");

            

            // 由于被反射的方法没有参数,所有反射的时候参数书写null

            Method method = clazz.getDeclaredMethod("demo", null);

            

            // 由于方法是静态的,不需要对象,私有的需要取消权限

            method.setAccessible(true);

            

            method.invoke(null, null);

        }

    1. 反射私有非静态有参数有返回的方法

        /*

         * 反射类中私有,非静态,有参数,有返回值的方法

         */

        @Test

        public void demo3() throws Exception{

            // 获取Class对象

            Class clazz = Class.forName("cn.itcast.sh.b_class.Person");

            

            // 反射 private int add(int a , int b)

            Method method = clazz.getDeclaredMethod("add", int.class , int.class);

            

            // 私有需要取消权限

            method.setAccessible(true);

            // 由于非静态,需要对象

            Object obj = clazz.newInstance();

            // 调用invoke 让方法运行

            Object value = method.invoke(obj, 1,3);

            System.out.println(value);

        }

    1. 反射中需要掌握

    1、理解Class到底是干什么的?

        2、必须掌握获取Class对象三种方式

        3、反射类中的成员变量、成员方法、构造方法,注意私有的反射需要取消权限

    反射后期会结合 动态代理 技术一起使用。

    1. 动态代理介绍    

    1. 代理类

    /*

    * 代理类 (经纪人)

    * 在Java中,如果要对外提供代理类的对象,必须使用Java中的Proxy类来产生代理对象。

    *

    * 使用Proxy类中的newProxyInstance方法 获取代理类对象

    * static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

    *     ClassLoader loader:类加载器。

    *     Class<?>[] interfaces : 被代理类实现的所有接口

    * InvocationHandler h : InvocationHandler 它主要的作用是在拦截外界调用的任何方法

    *     当外界调用了代理对象的任何方法时,都被拦截之后,会直接去执行接口中的invoke方法

    */

    public class ProxyClass {

        //实例化,被代理类对象

        private static final SongXiaoBao sxb = new SongXiaoBao();

        /*

         * 在代理类中书写一个静态方法,对外提供代理类对象

         */

        public static Inter getProxy(){

            //使用Proxy类获取到一个代理对象

            Inter obj = (Inter)Proxy.newProxyInstance(

                    //指定类加载器

                    ProxyClass.class.getClassLoader(),

                    //指定被代理类实现的所有接口

                    sxb.getClass().getInterfaces(),

                    //指定专门用于拦截的接口

                    new InvocationHandler(){

                        /*

                         * 当InvocationHandler 拦截到任何的方法被执行时,自动的运行接口中的invoke方法

                         * 解释invoke方法上的三个参数

                         *     Object proxy:代理类对象

                         *     Method method:被外界调用的方法,已经封装成Method对象

                         *     Object[] args:被调用方法接收的实际参数

                         */

                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                            /*

                             * 拦截到之后,在invoke方法中可以判断当前调用的具体是什么方法

                             * 然后在用反射的方式调用被代理的方法

                             */

                            if( "sing".equals(method.getName()) ){

                                System.out.println("先交钱,再服务.....");

                                //反射被代理类中的sing方法

                                return method.invoke(sxb, args);

                                

                            }else if("action".equals(method.getName())){

                                System.out.println("先准备一个搭档,必须女滴.....");

                                //反射被代理类中的action方法

                                return method.invoke(sxb, args);

                            }else{

                                return "暂时本经纪人不代理明星此项业务";

                            }

                        }

                    });

            //返回代理对象

            return obj;

        }

    }

     

    1. 被代理类

    /*

    * 被代理类 ,代理类实现的接口中的所有方法全部可以被代理

    */

    public class SongXiaoBao implements Inter {

        

        @Override

        public String sing(String name){

            System.out.println("宋小宝唱《"+name+"》歌");

            return "损色";

        }

        @Override

        public void action(){

            System.out.println("宋小宝表演二人转");

        }

        

        public void sleep(){

            System.out.println("宋小宝休息");

        }

    }

    1. 被代理实现的接口

    /*

    * 在Java中规定,只有保存在接口中的方法才能被代理。

    */

    public interface Inter {

     

        public String sing(String name);

     

        void action();

     

    }

    1. 测试类

    /*

    * 测试类

    */

    public class TestProxy {

        public static void main(String[] args) {

            

            //获取到代理类对象(经纪人对象)

            Inter obj = ProxyClass.getProxy();

            //通过代理对象,实现调用被代理类中的方法

            String value = obj.sing("海燕");

            System.out.println(value);

            obj.action();

            Object v = obj.toString();

            System.out.println(v);

        }

    }

  • 相关阅读:
    1.文件I/O
    sqlite-按日期分组,根据日期查询详细内容
    sqlite-在数据库中创建默认时间
    Git-git 忽略 IntelliJ .idea文件
    重启猫(modem)的方法
    从TP、FP、TN、FN到ROC曲线、miss rate、行人检测评估
    畅所欲言第1期
    使用属性表:VS2013上配置OpenCV
    关于OOM那些事儿
    深度学习之江湖~那些大神们
  • 原文地址:https://www.cnblogs.com/beyondcj/p/6270841.html
Copyright © 2020-2023  润新知