反射笔记

1.Junit单元测试

  • 测试分类
    • 测试就是在我们编写完代码之后对代码的功能进行测试,测试一般分为黑盒测试和白盒测试.
      • 黑盒测试:黑盒测试也称为功能测试,用于检测每个功能是否可以正常使用,在测试中把程序看为一个不能打开的黑盒子,在完全不考虑程序的代码内部结构和内部特性的情况下,传入数据查看结果.
      • 白盒测试:白盒测试也称为结构测试,主要用于检测软件编码过程中的错误.
    • 黑盒测试人员可以不用编写代码,只需要测试代码是否可以运行,检测不同情况下代码的运行结果即可.(市面上大多数的测试人员)
    • 白盒测试人员需要知道如何编写代码,检测代码的健壮性,拓展性,耦合程度等.
  • Junit的使用步骤
    • 1.定义测试类(测试用例)
      • 建议:
        • 测试类的类名应该为:被测试的类名+Test
          • 例:测试Calc类,测试类名应该叫做CalcTest
        • 测试类所在的包应该为:xxx.xxx.test
          • 例:测试类CalcTest应该在com.itheima.test包下.
    • 2.定义测试方法
      • 建议:
        • 测试方法的方法名称应该为:test+方法名
          • 例:测试add方法,测试方法名应该叫做testAdd(小驼峰命名法)
        • 测试方法的返回值类型应该为:void
          • 运行测试方法查看结果即可,因为测试方法是单独运行的,返回结果无意义,因为没有调用者进行接收。
        • 测试方法的参数列表应该为:空
          • 运行测试方法为单独运行,没必要传入参数.
    • 3.在测试方法上添加注解@Test
    • 4.导入Junit依赖,在@Test注解处使用万能键Alt+Enter选择Add’JUnit4’ to classpath,等待下载之后会自动导入到项目中.
  • 测试方法的结果分类
    • package com.itheima.junit.use;
      
      import com.itheima.junit.function.Calc;
      import org.junit.Assert;
      import org.junit.Test;
      
      public class CalcTest {
      
          /**
           * 测试Calc类的divisionFunction方法.
           */
          @Test
          public void testDivision1(){
              //创建Calc类对象.
              Calc c = new Calc();
              //调用Calc类对象的divisionFunction方法进行测试(代码运行成功为绿色)
              int result = c.divisionFunction(9, 3);
              System.out.println(result);
          }
      
          /**
           * 测试Calc类的divisionFunction方法.
           */
          @Test
          public void testDivision2(){
              //创建Calc类对象.
              Calc c = new Calc();
              //调用Calc类对象的divisionFunction方法进行测试(代码运行出错为为红色)
              int result = c.divisionFunction(9, 0);
              System.out.println(result);
          }
      
          /**
           * 测试Calc类的divisionFunction方法.
           */
          @Test
          public void testDivision3(){
              //创建Calc类对象.
              Calc c = new Calc();
              //调用Calc类对象的divisionFunction方法进行测试(代码运行结果与预期不符为黄色)
              int result = c.divisionFunction(9, 3);
              //使用Assert类的静态方法对结果与预期结果进行判断.
              Assert.assertEquals(4,result);
          }
      }
      
      

      代码运行成功没有异常控制台显示绿色

      代码运行失败出现错误控制台显示红色:

      代码运行没问题但是与预期结果不同控制台显示黄色:

  • @Before注解
    • @Before用于修饰测试类中的初始化方法,一般用于资源的申请.
    • 初始化方法:所有的测试方法执行之前都会先执行该方法.
  • @After注解
    • @After用于修饰测试类中的结束方法,一般用于资源的释放.
    • 结束方法:所有的测试方法执行完之后,都会自动执行该方法.
2.反射Reflex
  • 反射是框架设计的灵魂
    • 框架:半成品软件,我们在框架的基础上进行软件开发,可以简化编码.
    • 反射:将类的各个组成部分封装为其他对象.
  • Java代码在计算机中的三个阶段

    • Source源代码阶段
    • Class类对象阶段
    • Runtime运行时阶段
  • 反射的好处
    • 可以在程序的运行过程中,操作这些对象.
    • 可以降低程序的耦合度,提高程序的可拓展性.
  • 获取Class对象的三种方式
    • Class.forName(“全类名”) 源代码阶段

      • 将字节码文件加载进内存,返回Class对象.
        • 多用于配置文件,将类名可以定义在配置文件中,读取文件加载类.
    • 类名.class Class类对象阶段

      • 通过类名的属性class来获取.
        • 多用于参数的传递.
    • 对象.getClass() Runtime运行时阶段

      • getClass()方法在Object类中.
        • 多用于对象获取字节码的方式.
    • package com.itheima.reflect;
      
      import com.itheima.domain.Person;
      
      public class ReflectDemo01 {
          public static void main(String[] args) throws Exception {
              //1.通过Class.forName("全类名")源代码阶段获取Person的Class
              Class cls1 = Class.forName("com.itheima.domain.Person");
              System.out.println(cls1);
      
              //2.通过类名.class获取Person的Class(类加载阶段)
              Class cls2 = Person.class;
              System.out.println(cls2);
      
              //3.对象.getClass()Runtime运行时阶段获取Person的class.
              Class cls3 = new Person().getClass();
              System.out.println(cls3);
      
              //使用==比较三个class对象.
              System.out.println(cls1 == cls2); //true
              System.out.println(cls2 == cls3); //true
              System.out.println(cls1 == cls3); //true
          }
      }
      
      
    • 同一个字节码文件(*.class)在一次程序的运行过程中只会被加载一次,无论通过哪一种方式获取的class文件都是同一个.Class对象的获取功能

    • Class对象获取成员变量

      • //获取单个成员变量[public]
        Field getField(String name)
        //获取单个成员变量[任意修饰符]
        Field getDeclaredField(String name)
        //获取所有成员变量[public]
        Field[] getFields()
        //获取所有成员变量[任意修饰符]
        Field[] getDeclaredFields()
        
      • Field类

        • Field提供有关类或接口的单个字段的信息,以及对它的动态访问权限。

        • 常用方法

          • //指定一个对象,获取该对象的Field字段值.
            Object get(Object obj);
            //指定一个对象,设置该对象的Field字段的值.
            set(Object obj, Object value);
            //是否忽略访问修饰符的安全检查.
            setAccessible(boolean b);
            
      • 代码演示

        • package com.itheima.reflect;
          
          import com.itheima.domain.Student;
          import java.lang.reflect.Field;
          
          /**
           * 获取Student的成员变量.
           */
          public class Reflect_GetField {
              public static void main(String[] args) throws Exception {
                  //获取Student的Class字节码对象.
                  Class cls = Student.class;
          
                  /*
                  获取所有的成员变量[public]
                  Field[] getFields()
                   */
                  Field[] clsFields = cls.getFields();
                  for (Field field : clsFields) {
                      System.out.println(field);
                      //public java.lang.String com.itheima.domain.Student.address 只获取到public修饰的String类型的成员变量.
                  }
                  System.out.println("----------------------------");
                  /*
                  获取指定名称的成员变量[public]
                   */
                  //Field field = cls.getField("name"); 如果获取的不是public修饰的或者成员变量不存在,则会抛出异常.
                  Field field = cls.getField("address");
          
                  /*
                  Field是Java封装成员变量的对象,通过Field对象可以实现设置成员变量的值和获取某个对象的成员变量的值.
                  Object get(Object obj):指定一个对象,获取该对象的Field字段值.
                  set(Object obj, Object value):指定一个对象,设置该对象的Field字段的值.
                   */
                  Student student = new Student("001", "路飞", 19, "风车村");
                  Object result = field.get(student);
                  //获取s对象的address成员变量的值.
                  System.out.println(result);
                  //设置s对象的address成员变量的值.
                  field.set(student, "东海");
                  System.out.println(student);
                  System.out.println("----------------------------");
          
                  /*
                  获取所有成员变量[任意修饰符]
                   */
                  Field[] declaredFields = cls.getDeclaredFields();
                  for (Field declaredField : declaredFields) {
                      System.out.println(declaredField);
                      /*
                      private java.lang.String com.itheima.domain.Student.sid
                      protected java.lang.String com.itheima.domain.Student.name
                      java.lang.Integer com.itheima.domain.Student.age
                      public java.lang.String com.itheima.domain.Student.address  获取到的所有的成员变量,所有修饰符.
                       */
                  }
                  System.out.println("----------------------------");
          
                  /*
                  获取单个成员变量[任意修饰符]
                   */
                  Field f = cls.getDeclaredField("name");
                  System.out.println(f); //这次可以获取到name的成员变量字段,因为getDeclaredField方法可以获取到任意修饰符修饰的成员变量.
                  //Object value = f.get(student);//student成员变量是受保护的,所以获取的时候会报异常,失败.
                  //如果想要获取到任意修饰符修饰的成员变量的具体值,需要忽略访问修饰符的安全检查,也就是进行暴力反射.
                  f.setAccessible(true);
                  Object value = f.get(student);
                  System.out.println(value);
          
              }
          }
          
    • Class对象获取构造方法

      • //获取单个构造方法[public]
        Constructor<T> getConstructor(类<?>... parameterTypes)
        //获取单个构造方法[任意修饰符]
        Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
        //获取所有构造方法[public]
        Constructor<?>[] getConstructors()
        //获取所有构造方法[任意修饰符]
        Constructor<?>[] getDeclaredConstructors()
        
        • Constructor类

          • 提供了一个类的单个构造函数.

          • 常用方法

            • //使用由此Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例. 
              T newInstance(Object... initargs);
              
      • 如果想通过Class对象创建一个空参对象可以直接通过newInstance()方法获得一个对象,而不需要去通过获取无参构造器对象来实现.

      • 代码演示

        • package com.itheima.reflect;
          
          import com.itheima.domain.Student;
          
          import java.lang.reflect.Constructor;
          import java.lang.reflect.InvocationTargetException;
          
          /**
           * 获取Student类的构造方法
           */
          public class Reflect_GetConstructor {
              public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
                  //获取Student类的字节码对象.
                  Class cls = Class.forName("com.itheima.domain.Student");
          
                  /*
                  获取所有构造方法[public]
                  Constructor<?>[] getConstructors()
                   */
                  Constructor[] constructors = cls.getConstructors();
                  for (Constructor constructor : constructors) {
                      System.out.println(constructor);
                      /*
                      public com.itheima.domain.Student(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String)
                      public com.itheima.domain.Student(java.lang.String,java.lang.Integer)
                      public com.itheima.domain.Student() 获取到的所有构造方法都是public修饰的.
                       */
                  }
                  System.out.println("----------------------------");
          
                  /*
                  获取所有构造方法[任意修饰符]
                  Constructor<?>[] getDeclaredConstructors()
                   */
                  Constructor[] declaredConstructors = cls.getDeclaredConstructors();
                  for (Constructor declaredConstructor : declaredConstructors) {
                      System.out.println(declaredConstructor);
                      /*
                      protected com.itheima.domain.Student(java.lang.String,java.lang.String,java.lang.Integer)
                      public com.itheima.domain.Student(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String)
                      public com.itheima.domain.Student(java.lang.String,java.lang.Integer)
                      public com.itheima.domain.Student() 获取到任意修饰符修饰的构造方法.
                       */
                  }
                  System.out.println("----------------------------");
          
                  /*
                  获取单个构造方法[public]
                  Constructor<T> getConstructor(类<?>... parameterTypes)
                   */
                  Constructor constructor = cls.getConstructor(String.class, Integer.class);
                  Object o = constructor.newInstance("路飞", 23);
                  System.out.println(o);
                  System.out.println("----------------------------");
                  //如果想要通过Class对象创建一个对象,而无需指定任何参数,可以通过newInstance方法获取一个对象.
                  Object o1 = cls.newInstance();
                  System.out.println(o1);
                  System.out.println("----------------------------");
                  /*
                  获取单个构造方法[任意修饰符]
                  Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
                   */
                  Constructor declaredConstructor = cls.getDeclaredConstructor(String.class, String.class, Integer.class);
                  //忽略访问修饰符的安全检查,进行暴力反射.
                  declaredConstructor.setAccessible(true);
                  Object o2 = declaredConstructor.newInstance("001", "索隆", 29);
                  System.out.println(o2);
              }
          }
          
          
    • Class对象获取成员方法

      • //获取单个成员方法[public]
        Method getMethod(String name, 类<?>... parameterTypes)
        //获取单个成员方法[任意修饰符]
        Method getDeclaredMethod(String name, 类<?>... parameterTypes)
        //获取所有成员方法[public]
        Method[] getMethods()
        //获取所有成员方法[任意修饰符]
        Method[] getDeclaredMethods()
        
      • Method类

        • 方法在类或接口上提供有关单一方法的信息.

        • 常用方法

          • //在具有指定参数的指定对象上调用此 方法对象表示的基础方法.
            Object invoke(Object obj, Object... args);
            //返回由该 方法对象表示的方法的名称,作为String. 
            String getName();
            
      • 代码演示

        • package com.itheima.reflect;
          
          import com.itheima.domain.Student;
          import java.lang.reflect.Method;
          
          /**
           * 获取Student的成员方法
           */
          public class Reflect_GetMethod {
              public static void main(String[] args) throws Exception {
                  //获取Student的Class字节码对象.
                  Class cls = Student.class;
          
                  /*
                  获取所有成员方法[public]
                  Method[] getMethods()
                   */
                  Method[] methods = cls.getMethods();
                  for (Method method : methods) {
                      System.out.println(method);
                      /*
                      Get/Set方法/继承Object类的方法此处忽略.
                      public void com.itheima.domain.Student.show() 获取到的所有方法都是public修饰的.
                      */
                  }
                  System.out.println("----------------------------");
          
                  /*
                  获取所有成员方法[任意修饰符]
                  Method[] getDeclaredMethods()
                   */
                  Method[] declaredMethods = cls.getDeclaredMethods();
                  for (Method declaredMethod : declaredMethods) {
                      System.out.println(declaredMethod);
                      /*
                      Get/Set方法/继承Object类的方法此处忽略.
                      void com.itheima.domain.Student.eat()
                      protected void com.itheima.domain.Student.play()
                      private void com.itheima.domain.Student.study()
                      public void com.itheima.domain.Student.show() 获取到任意修饰符修饰的所有方法.
                       */
                  }
                  System.out.println("----------------------------");
          
                  /*
                  获取指定的成员方法[public]
                  Method getMethod(String name, 类<?>... parameterTypes)
                   */
                  Method method = cls.getMethod("show"); //show方法无参.
                  //想要执行method方法,需要一个对象进行执行,那么在这里我们使用反射获取一个对象.
                  method.invoke(cls.newInstance());
          
                  Method method1 = cls.getMethod("goHome", String.class); //goHome方法需要一个String类型的参数.
                  //想要执行method方法,需要一个对象进行执行,那么在这里我们使用反射获取一个对象.
                  method1.invoke(cls.newInstance(), "西安");
                  System.out.println("----------------------------");
                  /*
                  //获取单个成员方法[任意修饰符]
                  Method getDeclaredMethod(String name, 类<?>... parameterTypes)
                   */
                  Method method2 = cls.getDeclaredMethod("study");
                  //忽略访问修饰符的安全检查,也就是进行暴力反射
                  method2.setAccessible(true);
                  method2.invoke(cls.newInstance());
              }
          }
          
          
    • Class对象获取类名

      • 获取类名
        String getName();
        
    • 反射案例(编写"框架",不改变任何代码的前提下,可以执行任意类的任意方法)

      • package com.itheima.reflect;
        
        import java.io.*;
        import java.lang.reflect.Method;
        import java.util.Properties;
        
        public class Reflect_Framework {
            /**
             * 编写"框架",不改变任何代码的前提下,可以执行任意类的任意方法.
             * @param args
             */
            public static void main(String[] args) throws Exception {
                //创建Properties对象,用于加载配置文件.
                Properties prop = new Properties();
                //获取class目录下的配置文件信息 -> 获取本类的ClassLoader.
                ClassLoader classLoader = Reflect_Framework.class.getClassLoader();
                InputStream stream = classLoader.getResourceAsStream("info.properties");
                //使用prop对象读取stream流中的信息.
                prop.load(stream);
                //获取配置文件的信息.
                String className = prop.getProperty("className");
                String methodName = prop.getProperty("methodName");
                //加载该类进入到内存中,获取到对应的字节码对象.
                Class cls = Class.forName(className);
                //通过字节码对象获取到className的实例.
                Object o = cls.newInstance();
                //通过字节码对象获取指定名称的method方法对象(methodName).
                Method method = cls.getDeclaredMethod(methodName);
                //忽略访问修饰符检查,进行暴力反射.
                method.setAccessible(true);
                method.invoke(o);
            }
        }
        
        
3.注解Annoation
  • 注解的概念
    • 注解(Annotation)也称为元数据,一种代码级别的说明,它是Jdk1.5之后进入的一个新特性,与类/接口/枚举在同一个层次,它可以声明在包/类/字段/方法/局部变量/方法参数等的前面,用来对这些元素进行说明,注释.
  • 注解的作用
    • 编写文档:通过代码里面标识的注解生成文档(javadoc).
    • 代码分析:通过代码里面表示的注解对代码进行分析.
    • 编译检查:通过代码里标识的注解让编译期能够实现基本的编译检查.
  • Jdk中预定义的一些注解
    • @Override

      • //@Override注解可以帮助我们检测被该注解标注的方法是否是继承父类(接口)的.
            @Override
            public String toString() {
                return super.toString();
            }
        
            //如果@Override注解标注的方法不是继承自父类(接口)的,那么@Override注解将会报错.
            //@Override
            public int hhashCode() {
                return super.hashCode();
            }
        

自定义注解

  • 格式:

    • package com.itheima.annotation;
      
      //自定义的注解MyAnno
      public @interface MyAnno {
      }
      

      public @interface 注解名称

  • 注解的本质:

    • 自定义注解的Java文件内容

    • 使用javac命令进行编译之后对生成的class文件进行反编译.

    注解的本质就是一个接口.

    • 一个继承了java.lang.annotation.Annotation接口的接口.
  • 注解的属性

    • 注解的属性其实就是接口中可以定义的内容
      • 常量
      • 抽象方法
        • 抽象方法的要求
          • 方法的返回值类型只能是如下几种,其他的都不可以.
            • (1)基本数据类型
              (2)String
              (3)枚举
              (4)注解
              (5)以上几种类型的数组
          • 定义属性(注解中的方法)之后,在使用注解的时候需要赋值,如果不赋值,要在注解的定义中使用default进行赋值.
          • 如果注解中只有一个属性,并且属性的名称是value,则value可以省略,直接定义值可以.
          • 数组赋值时,如果需要使用大括号进行包裹,如果只有一个值,则大括号可以省略不写.
  • 元注解

    • 概述:用于描述注解的注解.
    • 常用元注解
      • @Target 描述注解可以作用的位置.

        • @Target注解的属性是一个ElementType类型的数组,ElementType是一个枚举类.
          • ElementType.TYPE 可以作用在类上.
          • ElementType.METHOD 可以作用在方法上.
          • ElementType.FIELD 可以作用于成员变量上.
        • 注意此处的构造方法为单独的ElementType.CONSTRUCTOR,表示可以作用于构造方法上.
      • @Retention 描述注解被保留的阶段.

        • RetentionPolicy.SOURCE
          • 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃.
        • RetentionPolicy.CLASS
          • 注解被保留到class文件,但JVM加载class文件时候被遗弃.
        • RetentionPolicy.RUNTIME
          • 注解不仅被保存到class文件中,JVM加载class文件之后,仍然存在.
      • @Documented 描述注解能否被抽取到API文档中.

      • @Inherited 描述注解能否被子类继承.

  • 注解解析的方式
    • 如果我们想要获取一个类上的注解中的信息的话,可以通过以下方式进行获取

      • 注解

        • package com.itheima.annotation;
          
          import java.lang.annotation.ElementType;
          import java.lang.annotation.Retention;
          import java.lang.annotation.RetentionPolicy;
          import java.lang.annotation.Target;
          
          /**
           * 此注解用于描述需要执行的类名,方法名.
           */
          @Target(value = {ElementType.TYPE}) //表示此注解可以标注在类上.
          @Retention(RetentionPolicy.RUNTIME) //表示此注解可以保留到运行期.
          public @interface Pro {
              //描述要获取的类的信息.
              String className();
              //描述要获取的方法的信息.
              String methodName();
          }
          
          
      • 获取方式

        • package com.itheima.annotation;
          
          
          import java.lang.reflect.Method;
          
          @Pro(className = "com.itheima.domain.Student", methodName = "study")
          public class Reflect_InvokeByAnno{
              public static void main(String[] args) throws Exception {
                  //获取本类的字节码文件对象.
                  Class<Reflect_InvokeByAnno> cls = Reflect_InvokeByAnno.class;
                  //通过本类的字节码文件对象获取本类的指定注解.
                  Pro pro = cls.getAnnotation(Pro.class);
                  //根据Pro注解的方法获取对应的信息.
                  String methodName = pro.methodName(); //获取到的值:study
                  String className = pro.className(); //获取到的值:com.itheima.domain.Student
                  
              }
          }
          
  • 以后大多数情况,我们会使用注解,但是我们会使用Java或者第三方类库给我们提供好的注解帮助我们提高开发效率.

  • 注解一般用于用于编译期,或者用于解析程序.

  • 注解不是程序的一部分.

获取本类的字节码文件对象.
Class<Reflect_InvokeByAnno> cls = Reflect_InvokeByAnno.class;
//通过本类的字节码文件对象获取本类的指定注解.
Pro pro = cls.getAnnotation(Pro.class);
//根据Pro注解的方法获取对应的信息.
String methodName = pro.methodName(); //获取到的值:study
String className = pro.className(); //获取到的值:com.itheima.domain.Student

          }
      }
      ```
  • 以后大多数情况,我们会使用注解,但是我们会使用Java或者第三方类库给我们提供好的注解帮助我们提高开发效率.

  • 注解一般用于用于编译期,或者用于解析程序.

  • 注解不是程序的一部分.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值