Java基础15-Java高级

十五、Java高级

单元测试、反射、注解、动态代理。

1、单元测试

定义:就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试。

1.1 Junit单元测试框架

可以用来对方 法进行测试,它是第三方公司开源出来的(很多开发工具已经集成了Junit框架,比如IDEA)

优点:可以灵活的编写测试代码,可以针对某个方法执行测试,也支持一键 完成对全部方法的自动化测试,且各自独立。不需要程序员去分析测试的结果,会自动生成测试报告出来。

1.2 测试案例

某个系统,有多个业务方法,请使用Junit单元测试框架, 编写测试代码,完成对这些方法的正确性测试。

具体测试步骤:

  1. 将Junit框架的jar包导 入到项目中(注意: IDEA集成了Junit框架,不需要我们自己手工导入了)
  2. 为需要测试的业务类,定义对应的测试类,并为每个业务方法,编写对应的测试方法(必须:公共、无参、无返回值)
  3. 测试方法 上必须声明@Test注解,然后在测试方法中,编写代码调用被测试的业务方法进行测试。
  4. 开始测试: 选中测试方法,右键选择"JUnit运行”,如果测试通过则是绿色;如果测试失败,则是红色。

1、只测试某个方法:直接点击该方法的左侧运行按钮运行即可

2、测试该测试类的所以方法:点击该参数类的左侧运行按钮运行即可

3、要测试该项目的所以测试类:直接右击该项目名——>点击 run All Tests

//工具类
public class Function {
    //获取字符串长度
    public static int getStringLenght(String str){
         if(str==null){
             return 0;
         }
       return str.length();
    }

    //获取字符串最后一个字母的索引
    public static int  getStringMaxIndex(String  str){
        if(str==null){
            return -1;
        }
        return str.length();
    }
}
//工具类的测试类
//点击在测试类左侧的运行按钮就测试所以的方法
public class FunctionTest {

    @Test  //测试类(点击左侧的运行按钮即可运行所对应的方法)
    //保证方法是(public、无返回值、无形参)
    public void getStringLenghtTest(){
        //直接调用要测试的方法(这里写的是静态方法,直接通过类名调取)
        int len= Function.getStringLenght("hello");
        int len2=Function.getStringLenght(null);
        System.out.println(len);
        System.out.println(len2);
    }

    @Test
    public void getStringMaxIndexTest(){
        int index1=Function.getStringMaxIndex("hello");
        int index2=Function.getStringMaxIndex(null);
        System.out.println(index1);
        System.out.println(index2);  //会发现测试没有报错,但是结果可能有问题。

        //可以使用结果断言,就是可以用自己的预测结果和方法得出的结果进行比较,看看是不是一样的。
        //参数(有误时的提示信息,自己预测结果,方法得出的结果)
        Assert.assertEquals("测试结果有误:",4,index1);
    }
}
Junit单元测试常见注解 说明
@Test 测试类中的方法必须用它修饰才能成为测试方法,才能启动执行
@Before Junit5改名为 ( @BeforeEach) 用来修饰一个实例方法,该方法会在每一个测试方法执行之前执行一次。
@After Junit5改名为 (@BeforeEach) 用来修饰一个实例方法,该方法会在每一个测试方法执行之后执行一次。
@BeforeClass Junit5改名为(@BeforeAll ) 用来修饰一个静态方法,该方法会在所有测试方法之前只执行一次。
@AfterClass Junit5改名为(@AfterAll) 用来修饰一个静态方法,该方法会在所有测试方法之后只执行一次。

在测试方法执行前执行的方法,常用于:初始化资源。

在测试方法执行完后再执行的方法,常用于:释放资源。

2、反射

反射就是:加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等)。

作用:反射主要是用来做框架的

学习获取类的信息、操作它们,实现步骤:

  1. 反射第一步:加载类,获取类的字节码: Class对象
  2. 获取类的构造器:Constructor对象
  3. 获取类的成员变量:Field对象
  4. 获取类的成员方法::Method对象
2.1 反射第一步:加载类,获取类的字节码: Class对象

获取Class对象的三种方式:

  1. Class c1 =类名.class
  2. 调用Class提供方 法: public static Class forName(String package);
  3. Object提供的方法: public Class getClass(); Class c3 =对象.getClass();
public class Student {
    private String name;
    private String sex;
    private  int  age;
}
//测试类
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        //Class  c1 =类名.class
        Class c1=Student.class;
        System.out.println(c1.getName());  //获取全类类名(包名加类名)
        System.out.println(c1.getSimpleName()); //获取简单类名(类名)

        //调用Class提供方 法: public static Class forName(String package);
        Class c2=Class.forName("akc4.reflect.Student");  //参数为全类类名
        System.out.println(c1==c2);  //等到的都是同一个类(true)

        //Object提供的方法: public Class getClass(); Class c3 =对象.getClass();
        Student  student=new Student();
        Class c3=student.getClass();
        System.out.println(c2==c3);  //等到的都是同一个类(true)

    }
}
2.2 获取类的构造器:Constructor对象
Class提供了从类中获取构造器的方法 说明
Constructor<?> [ ] getConstructors() 获取全部构造器(只能获取public修饰的)
Constructor<?> [ ] getDeclaredConstructors() 获取全部构造器(只能存在就能拿到)
Constructor getConstructor(Class<?> … parmeterTypes ) 获取某个构造器(只能获取public修饰的)
Constructor getDeclaredConstructor( Class<?> … paramerTyoes ) 获取某个构造器(只要存在就能拿到)
//学生实体类
public class Student {
    private String name;
    private String sex;
    private  int  age;


    public Student() {
    }

    private Student(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

    public Student(String name, String sex, int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
}
//测试类
public class StudentTest {

    @After  //每个测试方法结束后都会执行
    public void soutAfter(){
        System.out.println("---------------");
    }

    //获取多少构造器
    @Test
    public void testGetConstructors(){
        //1、获取到对应类的类Class对象
        Class  c1=Student.class;
        //2、通过 getConstructors()方法获取到所有用public修饰的构造器
        Constructor[] cons1=c1.getConstructors();
        for (Constructor constructor : cons1) {
            //获取构造器名和形参个数
            System.out.println(constructor.getName()+"--->"+constructor.getParameterCount());
        }

        System.out.println("============");
        //3、通过getDeclaredConstructors() 方法获取所有的构造器(一般用这个获取多个构造器)
        Constructor[] cons2=c1.getDeclaredConstructors();
        for (Constructor constructor : cons2) {
            //获取构造器名和形参个数
            System.out.println(constructor.getName()+"--->"+constructor.getParameterCount());
        }
    }

    @Test
    public void testGetDeclaredConstructor() throws Exception {
        //1、获取对应类的Class
        Class c2=Student.class;
        //2、通过getConstructor(形参类型)方法获取public修饰的某个构造器
        Constructor con1=c2.getConstructor(); //获取无参构造器(public修饰的)
        //获取public修饰的形参为三个,且形参类型对应的构造器
        Constructor  cons2=c2.getConstructor(String.class,String.class,int.class);
        //获取指定的任何一个构造器,不限修饰类型
        Constructor  cons3=c2.getDeclaredConstructor(String.class,String.class);
        System.out.println(con1.getName()+" "+cons2.getName()+" "+cons3.getName());

    }

}

获取类构造器的作用:依然是初始化对象返回

Constructor对象提供的方法 说明
T newIntstance(Object…initargs) 调用此构造器对象表示的构造器,并传入参数,完成对象的初始化并返回
public void setAccessible( boolean flag ) 设置为true,表示禁止检查访问控制,就是即使是私有的构造器也能访问(暴力反射)
//学生实体类
public class Student {
    private String name;
    private String sex;
    private  int  age;


    public Student() {
        System.out.println("无参构造器被调用了");
    }



    private Student(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

    public Student(String name, String sex, int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                '}';
    }
}
//测试类
public class StudentTest {
 @Test
    public void testGetDeclaredConstructor() throws Exception {
        //1、获取对应类的Class
        Class c2=Student.class;
        //2、通过getConstructor(形参类型)方法获取public修饰的某个构造器
        Constructor con1=c2.getConstructor(); //获取无参构造器(public修饰的)
        //获取public修饰的形参为三个,且形参类型对应的构造器
        Student student=(Student) con1.newInstance();  //没有写参数调用的就是无参构造器
        System.out.println(student);


        //获取指定的任何一个构造器,不限修饰类型
        Constructor  cons3=c2.getDeclaredConstructor(String.class,String.class);
        cons3.setAccessible(true); //暴力反射, 即使是private修饰的也能调用了                          
         Student student1=(Student) cons3.newInstance("小明","男");
        System.out.println(student1);

    }

}
2.3 获取类的成员变量:Field对象

Class提供了从类中获取成员变量的方法

方法 说明
public Field[ ] getFields() 获取类id全部成员变量(只能获取public修饰的)
public Field[ ] getDeclaredields() 获取类的全部成员变量(只要存在就能拿到)
public Field getField(Sting name) 获取类的某个成员变量(只能获取public修饰的)
public Field getDeclaredField(String name) 获取类的某个成员变量(只要存在就能拿到)

获取到成员变量的作用:依然是赋值、取值。

方法 说明
void set(Object obj , Object value) 赋值
Object get( Object obj ) 取值
public void setAccessible( boolean flag ) 设置true ,表示禁止检查访问控制(暴力反射)
//实体类
public class Student {
    public  String name;
    public   String  id;
    private String sex;
    private  int  age;
        public Student() {
    }
}
//测试类
public class StudentTest {

    @After  //每个测试方法结束后都会执行
    public void soutAfter(){
        System.out.println("---------------");
    } 
@Test
    public  void  testFields(){
        //1、获取到对应类的Class对象
        Class c=Student.class;
        //2、获取到所有用public修饰的成员变量
        Field[] fields=c.getFields();
        //3、遍历拿到的成员变量
        for (Field field : fields) {
            //输出变量名和变量类型
            System.out.println(field.getName()+"==>"+field.getType());
        }

        System.out.println("==========");
        //2、获取全部成员变量
        Field[] fields1=c.getDeclaredFields();
        //3、遍历拿到的成员变量
        for (Field field : fields1) {
            //输出变量名和变量类型
            System.out.println(field.getName()+"==>"+field.getType());
        }
    }

       @Test
    public  void  testField() throws Exception {
        //1、获取到对应类的Class对象
         Class  c1=Student.class;
         //2、获取public修饰的某个成员变量(成员变量名)
        Field field= c1.getField("name");
        System.out.println(field.getName()+"==>"+field.getType());

        System.out.println("============");
        //2、获取某个成员变量。不限修饰(成员变量名)
        Field field1=c1.getDeclaredField("age");
        System.out.println(field1.getName()+"==>"+field1.getType());

        //3、为成员变量赋值
        //3.1 创建一个对象
           Student student=new Student();
           //由于field1对应的age是私有的成员变量,所以要暴力反向
          field1.setAccessible(true);
          //赋值(要赋值的具体对象,符的值)
          field1.set(student,20);

          //获取值
         int  age=(int)field1.get(student);
        System.out.println(age);
    }
}
3.4 获取类的成员方法::Method对象

Class提供了从类中获取成员方法的API。

方法 说明
Method[ ] getMethod() 获取类的全部成员方法(只能获取public修饰的)
Method[ ] getDeclaredMethods() 获取类的全部成员方法(只要存在就能拿到)
Method getMethod(String name, Class<?> … parameterTypes) 获取类的某个成员方法(只能获取public修饰的)参数:(方法名,参数类型,如:String.class)
Method getDeclaredMethod(String name , Class<?> … parameterType ) 获取类的某个成员方法(只要存在就能拿到)参数:(方法名,参数类型,如:String.class)

成员方法的作用:依然是执行

Method提供的方法 说明
public Object invoke( Object obj , Object … args ) 触发某个对象的该方法执行
public void setAccessible( boolean flag ) 设置true,表示禁止检查访问控制(暴力反射)
//实体类
public class Student {
    private String name;
    private String sex;
    private  int  age;

    public Student() {
    }

   private  String run(int num){
        return "跑"+num+"圈操场!";
   }
}
//测试类 
public class StudentTest {
   @Test
    public void testMethod() throws Exception {
        //1、创建一个类,对应的Class
        Class  c=Student.class;
        //获取某个方法,不限修饰,参数为:要获取方法的方法名,形参列表的各个类型
       Method method= c.getDeclaredMethod("run",int.class);
        //2、由于run方法是私有的所以要暴力反向
        method.setAccessible(true);

        //3、获取某个方法,需要一个对应对象
        Student student=new Student();

        //4、调用该方法,并传入参数(要调用对象的对象名,调用方法的参数)
        String  str=  (String) method.invoke(student,10);
        System.out.println(str);

    }

}
3.5 反射的运用场景

基本作用:可以得到一个类的全部成分然后操作。可以破坏封装性。

最重要的用途是:适合做Java的框架,基本上,主流的框架都会基于反射设计出- -些通用的功能。

使用反射做一个简易版的框架

需求:对于任意一个对象,该框架都可以把对象的字段名和对应的值,保存到文件中去。

实现步骤:

①定义一个方法,可以接收任意对象。

②每收到一个对象后,使用反射获取该对象的Class对象,然后获取全部的成员变量。

③遍历成员变量,然后提取成员变量在该对象中的具体值。

④把成员变量名、和其值,写出到文件中去即可。

3、注解( Annotation )

就是Java代码里的特殊标记,比如: @Override、 @Test等,作用是:让其他程序根据注解信息来决定怎么执行该程序。
注意:注解可以用在类上、构造器上、方法上、成员变量上、参数上、等位置处。

3.1 自定义注解
//自定义注解格式
public   @interface  注解名称{
    public  属性类型  属性名()  default  默认值;
}
//注解
public @interface MyTest {
    //默认public可以不写
      public String  name();
      //可以写默认值(写了,在赋值的时候可以不写)
      int  age() default 10;
      String[] hobby();
}
//测试注解
@MyTest(name="小明",age=19,hobby={"唱","跳","篮球"})
public class AnnotationTest {
    public static void main(String[] args) {

    }
    @MyTest(name="小红",hobby={"唱","跳","篮球"})
    public static void run(){}
}

1、特殊属性名: value

如果注解中只有一个value属性,使用注解时, value名称可以不写!

2、注解的本质

注解本质是一个接口,Java中所有注解都是继承了Annotation接口的。

@注解(…):其实就是一个实现类对象,实现了该注解以及Annotation接口。

<
上一篇:设计一个支持自动化测试执行的测试框架


下一篇:Linux之例行性工作-1.单一执行的例行性工作at