JAVA基础篇——JAVA高级(与框架有关的知识)

十九、JAVA高级

1. 单元测试

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

main方法进行单元测试的问题:

  • 只能在main里面调用别的方法进行测试
  • 无法自动化测试,一个方法失败会影响其他方法的测试
  • 无法获得测试报告,需要程序员自己去观察测试是否成功

Junit单元测试框架

  • 第三方公司开源,开发工具已集成
  • 一键自动化测试
  • 生成测试报告

Junit使用步骤

  • 导入Junit的jar包到项目(IDEA已集成)
  • 为业务类中的业务方法定义对应的测试方法(公有、无参、无返回值
  • 必须加**@Test**,加了在之后才可测试运行
  • 开始测试,绿色通过,红色有错

自动化测试

在项目上右键Run All Test

案例

工具类

public class StringUtil {
    public static void printNumber(String name) {
        System.out.println("名字的长度是:" + name.length());
    }

    // 求最大索引
    public static int getMaxIndex(String data) {
        if (data == null) {
            return -1;
        }
        return data.length();
    }
}

测试类

public class StringUtilTest {
    @Test
    public void testPrintNumber() {
        StringUtil.printNumber("admin");
//        StringUtil.printNumber(null);
    }

    @Test
    public void testGetMaxIndex() {
        int r1 = StringUtil.getMaxIndex("admin");

        // 断言,看实际期望的和程序输出的是否有出入
        Assert.assertEquals("有问题", 4, r1);

    }
}

Junit框架的常见注解

在测试方法之前执行的方法——初始化资源

在测试方法之后执行的方法——释放资源

测试类

public class StringUtilTest {
    @Before  // 每个测试方法执行之前执行一次
    public void test1() {
        System.out.println("----");
    }

    @BeforeClass  // 所有测试方法执行之前执行一次
    public static void test11() {
        System.out.println("String测试类开始执行了");
    }

    @After  // 每个测试方法执行之后执行一次
    public void test2() {
        System.out.println("====");
    }

    @AfterClass  // 所有测试方法执行之前执行一次
    public static void test22() {
        System.out.println("String测试类执行结束了");
    }

    @Test
    public void testPrintNumber() {
        StringUtil.printNumber("admin");
//        StringUtil.printNumber(null);
    }

    @Test
    public void testGetMaxIndex() {
        int r1 = StringUtil.getMaxIndex("admin");
        System.out.println(r1);

        // 断言,看实际期望的和程序输出的是否有出入
        Assert.assertEquals("有问题", 4, r1);

    }
}

2. 反射

学习框架、理解源码/开发框架

java.lang.reflect

概念: 加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等)——反序列化/向外界展示已经封装好的内部东西

学什么: 如何获取类的信息并操作他们

步骤:

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

1)获取Class对象的三种方式

  • Class c1 = 类名.class

  • 调用Class提供的方法:public static Class forName(String package);

  • Object提供的方法:public Class getClass(); Class c3 = 对象.getClass();

    public class Test1Class {
        public static void main(String[] args) throws Exception {
            // 方式一
            Class c1 = Student.class;
            System.out.println(c1.getName());  // 全类名 com.itheima.d2_reflect.Student
            System.out.println(c1.getSimpleName());  // 简名 Student
    
            // 方式二
            Class c2 = Class.forName("com.itheima.d2_reflect.Student");
            System.out.println(c2.getName());  // com.itheima.d2_reflect.Student
    
            // 方式三,通过类的实例对象
            Student s = new Student();
            Class c3 = s.getClass();
            System.out.println(c3.getName());  // com.itheima.d2_reflect.Student
    
            System.out.println(c1 == c2);  // true
            System.out.println(c2 == c3);  // true
    
        }
    }
    

2)获取Constructor对象

public class Cat {
    private String name;
    private int age;

    private Cat() {
    }

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

获取Constructor对象

public class TestConstructor {
    @Test
    public void testGetConstructor() {
        // 1. 首先要获取类的class对象
        Class c = Cat.class;
        // Class.forName("com.itheima.d2_reflect.Cat");
        // 对象.getClass()
        // 2. 获取全部构造器
        Constructor[] constructors = c.getConstructors();  // 只能获取public修饰的构造器
        for (Constructor constructor : constructors) {
            System.out.println(constructor.getName() + "--->" + constructor.getParameterCount());
        }

        Constructor[] constructors1 = c.getDeclaredConstructors();  // 获取任何修饰符修饰的构造器
        for (Constructor constructor : constructors1) {
            System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
        }
    }

    @Test
    public void testGetOneConstructor() throws Exception {
        // 1. 首先要获取类的class对象
        Class c = Cat.class;
        // Class.forName("com.itheima.d2_reflect.Cat");
        // 对象.getClass()
        // 2. 获取1个构造器
        Constructor constructor1 = c.getDeclaredConstructor();  // 获取无参构造器
        System.out.println(constructor1.getName() + "--->" + constructor1.getParameterCount());

        Constructor constructor2 = c.getDeclaredConstructor(String.class, int.class);  // 获取任何修饰符修饰的构造器
        System.out.println(constructor2.getName() + "===>" + constructor2.getParameterCount());
    }
}

作用:

初始化对象返回

@Test
public void testGetOneConstructor() throws Exception {
    // 1. 首先要获取类的class对象
    Class c = Cat.class;
    // Class.forName("com.itheima.d2_reflect.Cat");
    // 对象.getClass()
    // 2. 获取1个构造器
    Constructor constructor1 = c.getDeclaredConstructor();  // 获取无参构造器
    System.out.println(constructor1.getName() + "--->" + constructor1.getParameterCount());
    // 重点!!!
    constructor1.setAccessible(true);  // 禁止检查访问权限,也就是不管private还是public,都能创建对象,会破坏封装性
    Cat cat = (Cat) constructor1.newInstance();  // 调用此构造器,并传入参数,完成对象的初始化

    Constructor constructor2 = c.getDeclaredConstructor(String.class, int.class);  // 获取任何修饰符修饰的构造器
    System.out.println(constructor2.getName() + "===>" + constructor2.getParameterCount());
}

3)获取成员变量Field

public Field[] getFields()——获取全部public修饰的成员变量

public Field[] getDeclaredFields()——获取全部成员变量

public Field getField(String name)——根据成员变量的名字,获取某个成员变量,只能获取public修饰的

public Field getDeclaredField(String name)——根据成员变量的名字,获取某个成员变量

作用: 赋值、取值

void set(Object obj, Object value);

Object get(Object obj);

public void setAccessible(boolean flag); // 禁止访问权限

4)获取成员方法Method

同上

作用:执行

public Object invoke(Object obj, Object……args); // 出发某个对象的该方法执行

public void setAccessible(boolean flag); /// 设置true,表示禁止检查访问控制

反射的作用

  • 基本作用:可以得到一个类的全部成分然后操作
  • 可以破坏封装性
  • 适合做Java框架,基本上,主流的框架都会基于反射设计出一些通用功能

3. 注解

JAVA代码里的特殊标记

作用: 让其他程序根据在注解信息来决定怎么执行该程序

自定义注解: 自己定义注解

格式:

public @interface 注解名称{

​ public 属性类型 属性名() default 默认值;

}

/*
* 自定义注解
*/
public @interface MyTest1 {
//    String value();  // 特殊属性,只有这一个属性时,或者后面是有默认值的,用的时候可以不写名字
    String aaa();
    boolean bbb() default true;
    String[] ccc();
}
@MyTest1(aaa = "小鱼0135", ccc = {"嘻嘻", "嘎嘎"})
public class AnnotationTest1 {
    @MyTest1(aaa="小君", bbb=false, ccc={"123"})
    public void test1(){
    }
}

原理:

  • 注解本质是一个接口,Java中所有注解都继承了Annotation接口
  • 一个实现类对象,实现了该注解以及Annotation接口

元注解:修饰注解的注解

JAVA基础篇——JAVA高级(与框架有关的知识)_第1张图片

/*
* 自定义注解
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest1 {
//    String value();  // 特殊属性,只有这一个属性时,或者后面是有默认值的,用的时候可以不写名字
    String aaa();
    boolean bbb() default true;
    String[] ccc();
}

注解的解析

判断类上、方法上、成员变量上是否存在注解,并把注解里面的内容给解析出来

如何解析注解:

要解析谁上面的注解,就要先拿到谁(要解析类上面的注解,应该先获取该类的Class对象,再通过Class对象解析其上面的注解;Class、Method、Field、Constructor都实现了AnnotatedElement接口,他们都有解析注解的能力)

/*
* 注解的解析
* */
public class AnnotationTest3 {
    @Test
    public void parseClass() {
        // 1. 先得到Class对象
        Class c =  Demo.class;
        // 2. 解析类上的注解
        // 判断类上是否包含了注解
        if(c.isAnnotationPresent(MyTest4.class)) {
            MyTest4 myTest4 = (MyTest4) c.getDeclaredAnnotation(MyTest4.class);
            System.out.println(myTest4.value());
            System.out.println(myTest4.aaa());
            System.out.println(Arrays.toString(myTest4.bbb()));
        }
    }

}

应用场景:结合反射等做框架

整理:就是做框架的时候会自定义很多注解,说明怎么执行这段程序(功能),那别人使用这个功能就要完成一些操作,这个时候就要用到反射,拿出类信息、构造器信息、成员变量和方法等信息,执行一些初始化、取值赋值、执行等一些操作。

4. 动态代理

程序为什么需要代理

对象如果干的事太多,可以通过代理来转移部分职责

代理的样子

对象有什么方法想被代理,代理一定要有对应的方法

代理如何知道有什么方法需要代理

接口:想代理的方法

案例:

BigStar

public class BigStar implements Star{
    private String name;

    public BigStar(String name) {
        this.name = name;
    }

    // 唱歌行为
    public String sing(String name) {
        System.out.println(this.name + "正在唱" + name);
        return "唱完了!谢谢大噶!";
    }

    // 跳舞行为
    public void dance() {
        System.out.println(this.name + "正在跳舞!");
    }
}

想要代理的接口类Star

// 指明有哪些方法需要被代理
public interface Star {
    String sing(String name);
    void dance();
}

代理对象的类

public class ProxyUtil {
    // 生成代理对象,并返回
    public static Star creatrProxy(BigStar bigStar) {
        // Java为产生代理,设计了Proxy类。java.lang.reflect
/*        newProxyInstance(ClassLoader loader,
                Class[] interfaces,
                InvocationHandler h)*/
        // 参数1:用于指定一个类的加载器
        // 参数2:指定生成的代理的样子,也就是方法,接口数组
        // 参数3:指定生成的代理对象要干什么事情,InvocationHandler是一个接口,也就是要实现一个匿名内部类,指定对象要要干的事情
        Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                new Class[]{Star.class}, new InvocationHandler() {
                    @Override  // 回调方法
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 代理对象,要调用的方法,调用的方法需要传递的参数
                        // 代理对象要做的事情
                        if (method.getName().equals("sing")) {
                            System.out.println("准备话筒,收钱20万");  // 代理要干的事情
                            // return method.invoke(bigStar, args);
                        } else if(method.getName().equals("dance")) {
                            System.out.println("准备场地,收钱1000万");
                            // return method.invoke(bigStar, args);
                        }
                        return method.invoke(bigStar, args);
                    }
                });  // 产生一个代理对象
        return starProxy;

    }
}

主程序

public class Test {
    public static void main(String[] args) {
        BigStar s = new BigStar("杨");
        Star starProxy = ProxyUtil.creatrProxy(s);

        String rs = starProxy.sing("好日子");
        System.out.println(rs);

        starProxy.dance();
    }
}

一些通用的功能可以通过代理在不修改原代码的情况下,为不同对象添加通用功能

你可能感兴趣的:(JAVA基础篇,java,开发语言)