第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);

    }

}

原文地址:https://www.cnblogs.com/beyondcj/p/6270841.html