Java基础 - 14 - Java高级技术

一.单元测试

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

1.1 Junit单元测试框架

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

优点

        可以灵活的编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部方法的自动化测试,且各自独立

        不需要程序员去分析测试的结果,会自动生成测试报告出来

//StringUtil
public class StringUtil {
    public static void printNumber(String name){
        if(name == null){
            System.out.println("名字长度:" + 0);
            return;
        }
        System.out.println("名字长度:" + name.length());
    }

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

//StringUtilTest 
//测试类
public class StringUtilTest {
    @Test //测试方法的必须拥有@Test注解
    public void testPrintNumber(){
        StringUtil.printNumber("admin");
        StringUtil.printNumber(null);
    }

    @Test //测试方法的必须拥有@Test注解
    public void testGetMaxIndex(){
        int index1 = StringUtil.getMaxIndex(null);
        System.out.println(index1);

        int index2 = StringUtil.getMaxIndex("admin");
        System.out.println(index2);

        //断言机制:程序员可以通过预测业务方法的结果
        Assert.assertEquals("方法内有bug",4,index2);
    }
}

1.2 Junit框架的常用注解

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

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

//测试类
public class StringUtilTest {

    @Before
    public void test1(){
        System.out.println("----> test1 Before 执行了 -----------");
    }

    @After
    public void test2(){
        System.out.println("----> test2 After 执行了 -----------");
    }

    @BeforeClass
    public static void test3(){
        System.out.println("----> test3 BeforeClass 执行了 -----------");
    }

    @AfterClass
    public static void test4(){
        System.out.println("----> test4 AfterClass 执行了 -----------");
    }

    @Test //测试方法的必须拥有@Test注解
    public void testPrintNumber(){
        StringUtil.printNumber("admin");
        StringUtil.printNumber(null);
    }

    @Test //测试方法的必须拥有@Test注解
    public void testGetMaxIndex(){
        int index1 = StringUtil.getMaxIndex(null);
        System.out.println(index1);

        int index2 = StringUtil.getMaxIndex("admin");
        System.out.println(index2);

        //断言机制:程序员可以通过预测业务方法的结果
        Assert.assertEquals("方法内有bug",4,index2);
    }
}

二.反射

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

反射学什么?

        学习获取类的信息、操作它们

            1.反射第一步:加载类,获取类的字节码:Class对象

            2.获取类的构造器:Constructor对象

            3.获取类的成员变量:Field对象

            4.获取类的成员方法:Method对象

2.1 反射第一步:加载类,获取类的字节码:Class对象

获取Class对象的三种方式

· Class c1 = 类名.class

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

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

//demo
public class demo {
    public static void main(String[] args) throws Exception {
        Class c1 = Student.class;
        System.out.println(c1.getName()); //全类名
        System.out.println(c1.getSimpleName()); //简名

        Class c2 = Class.forName("com.wosun.jinjie.Student");
        System.out.println(c1 == c2);

        Student student = new Student();
        Class c3 = student.getClass();
        System.out.println(c3 == c1);
    }
}

//Student
public class Student {
}

2.2 获取类的构造器:Constructor对象

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

//TestConstructor
public class TestConstructor {
    //拿全部的构造器
    @Test
    public void testGetConstructors() {
        //1.反射第一步:必须先得到这个类的Class对象
        Class c = Cat.class;
        //2.获取类的全部构造器
        //Constructor[] constructors = c.getConstructors(); //只能获取public修饰的构造器
        Constructor[] constructors = c.getDeclaredConstructors();  //只要存在的构造器都能拿到
        //遍历数组中的每个构造器对象
        for (Constructor constructor : constructors) {
            System.out.println(constructor.getName() + "---->" + constructor.getParameterCount());
        }
    }

    //拿某个构造器
    @Test
    public void testGetConstructor() throws Exception {
        //1.反射第一步:必须先得到这个类的Class对象
        Class c = Cat.class;
        //2.获取类的某个构造器:无参数构造器
        //Constructor constructor1 = c.getConstructor();  //只能获取public修饰的构造器
        Constructor constructor1 = c.getDeclaredConstructor();  //只要存在的构造器都能拿到

        System.out.println(constructor1.getName() + "---->" + constructor1.getParameterCount());

        constructor1.setAccessible(true); //禁止检查访问权限
        Cat cat1 = (Cat) constructor1.newInstance();
        System.out.println(cat1);

        //3.获取类的某个构造器:有参数构造器
        //Constructor constructor2 = c.getConstructor(String.class , int.class);  //只能获取public修饰的构造器
        Constructor constructor2 = c.getDeclaredConstructor(String.class , int.class);

        System.out.println(constructor2.getName() + "---->" + constructor2.getParameterCount());

        constructor1.setAccessible(true); //禁止检查访问权限
        Cat cat2 = (Cat) constructor2.newInstance("叮当猫", 3);
        System.out.println(cat2);
    }
}


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

    public Cat() {
        System.out.println("无参数构造器执行了");
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

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

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

//TestField
public class TestField {
    @Test
    public void TestGetFields() throws Exception {
        //1.反射第一步:必须先得到类的class对象
        Class c = Cat.class;
        //2.获取类的全部成员变量
        Field[] fields = c.getDeclaredFields();
        //3.遍历成员变量数组
        for (Field field : fields) {
            System.out.println(field.getName() + "---->" + field.getType());
        }
        //4.定位某个成员变量
        Field fName = c.getDeclaredField("name");
        System.out.println(fName.getName() + "===>" + fName.getType());

        Field fAge = c.getDeclaredField("age");
        System.out.println(fAge.getName() + "===>" + fAge.getType());

        //赋值
        Cat cat = new Cat();
        fName.setAccessible(true);
        fName.set(cat, "咖啡猫");
        System.out.println(cat);

        //取值
        String name = (String) fName.get(cat);
        System.out.println(name);
    }
}

//Cat
public class Cat {
    private static int a;
    public static final String COUNTRY = "CHN";
    private String name;
    private int age;

    public Cat() {
        System.out.println("无参数构造器执行了");
    }

    public Cat(String name, int age) {
        System.out.println("有参数构造器执行了");
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

2.4 获取类的成员方法:Method对象

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

//TestMethod
public class TestMethod {
    @Test
    public void TestGetMethods() throws Exception {
        //1.反射第一步:先得到Class对象
        Class c = Cat.class;
        //2.获取类的全部成员方法
        Method[] methods = c.getDeclaredMethods();
        //3.遍历数组中的每个方法对象
        for (Method method : methods) {
            System.out.println(method.getName() + "===>" + method.getParameterCount() + "===>" + method.getReturnType());
        }

        Method method_run = c.getDeclaredMethod("run");
        System.out.println(method_run.getName() + "===>" + method_run.getParameterCount() + "===>" + method_run.getReturnType());

        Method method_eat =c.getDeclaredMethod("eat", String.class);
        System.out.println(method_eat.getName() + "===>" + method_eat.getParameterCount() + "===>" + method_eat.getReturnType());

        Cat cat = new Cat();
        method_run.setAccessible(true);
        Object rs1 = method_run.invoke(cat);//调用无参的run方法,用cat对象触发调用的
        System.out.println(rs1);

        Object rs2 = method_eat.invoke(cat,"🐟");
        System.out.println(rs2);

    }
}

//Cat
public class Cat {
    private static int a;
    public static final String COUNTRY = "CHN";
    private String name;
    private int age;

    public Cat() {
        System.out.println("无参数构造器执行了");
    }

    public Cat(String name, int age) {
        System.out.println("有参数构造器执行了");
        this.name = name;
        this.age = age;
    }

    private void run(){
        System.out.println("跑的真快");
    }

    public void eat(){
        System.out.println("爱吃猫粮");
    }

    public String eat(String name){
        return "猫最爱吃" + name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

2.5 反射的作用、应用场景

反射的作用

· 基本作用:可以得到一个类的全部成分然后操作

· 可以破坏封装性

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

//TestFrame
public class TestFrame {
    @Test
    public void save() throws Exception {
        Student s1 = new Student("张三",22,'男',180.5,"编程");
        Teacher t1 = new Teacher("李四",1000000);

        //需求:把任意对象的字段名和其对应的值等信息,保存到文件中
        ObjectFrame.saveObject(s1);
        ObjectFrame.saveObject(t1);
    }
}

//ObjectFrame
public class ObjectFrame {
    //目的:保存任意对象的字段和其数据到文件中去
    public static void saveObject(Object obj) throws Exception {
        //创建打印流
        PrintStream ps = new PrintStream(new FileOutputStream("src\\data.txt",true));
        //obj是任意对象,到底有多少个字段要保存呢?
        Class c = obj.getClass();
        String cName = c.getSimpleName();
        ps.println("---------" + cName + "---------");
        //从这个类中提取它的全部成员变量
        Field[] fields = c.getDeclaredFields();
        //遍历每个成员变量
        for (Field field : fields) {
            //拿到成员变量的名字
            String name = field.getName();
            //拿到这个成员变量在对象中的数据
            field.setAccessible(true); //禁止检查访问控制
            String value = field.get(obj) + "";
            ps.println(name + "=" + value);
        }
        ps.close();
    }
}

//Student
public class Student {
    private String name;
    private int age;
    private char sex;
    private double height;
    private String hobby;

    public Student() {
    }

    public Student(String name, int age, char sex, double height, String hobby) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.height = height;
        this.hobby = hobby;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
}

//Teacher
public class Teacher {
    private String name;
    private double salary;

    public Teacher() {
    }

    public Teacher(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }
}

三.注解

        注解(Annotation)就是Java代码里的特殊标记,比如:@Override、@Test等,作用是让其他程序根据注解信息来决定怎么执行该程序

· 注意:注解可以用在类上、构造器上、方法上、成员变量上、参数上、等位置处

自定义注解

public @interface 注解名称 {

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

}

特殊属性名:value

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

//AnnotationTest 

@MyTest1(aaa = "牛魔王", ccc = {"java","python"})
// @MyTest2(value = "孙悟空")
// @MyTest2("孙悟空") //如果只有一个属性,可以不写value
// @MyTest2(value = "孙悟空", age = 100)
@MyTest2("孙悟空") //如果除了value其他属性都有默认值,也可以不写value
public class AnnotationTest {
    @MyTest1(aaa = "铁扇公主", bbb = false, ccc = {"c","c++"})
    public void test1(){

    }
}

/**
 * 自定义注解
 */
public @interface MyTest1 {
    String aaa();
    boolean bbb() default true;
    String[] ccc();
}

public @interface MyTest2 {
    String value(); //特殊属性
    int age() default 22;
}

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

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

3.1 元注解

指的是:修饰注解的注解

//MyTest3

@Target({ElementType.TYPE, ElementType.METHOD}) //ElementType.TYPE当前被修饰的注解只能用在类上
@Retention(RetentionPolicy.RUNTIME) //控制下面的注解一直保留到运行时
public @interface MyTest3 {
}

3.2 注解的解析

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

如何解析注解?

· 指导思想:要解析谁上面的注解,就应该先拿到谁

· 比如要解析类上面的注解,则应该先获取该类的Class对象,再通过Class对象解析其上面的注解

· 比如要解析成员方法上的注解,则应该获取到该成员方法的method对象,再通过Method对象解析其上面的注解

· Class、Method、Field、Constructor都实现了AnnotatedElement接口,它们都拥有解析注释的能力

//MyTest4

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest4 {
    String value();
    double aaa() default 100;
    String[] bbb();
}


//demo

@MyTest4(value = "张三", aaa = 99, bbb={"java","python"})
public class demo {
    @MyTest4(value = "李四", bbb={"c","c++"})
    public void test1(){

    }
}

//AnnotationTest2 

public class AnnotationTest2 {
    @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()));
        }
    }

    @Test
    public void parseMethod() throws Exception {
        //1.先得到Class对象
        Class c  = demo.class;
        Method m = c.getDeclaredMethod("test1");
        //2.解析类上的注解
        //判断方法上是否包含了某个注解
        if(m.isAnnotationPresent(MyTest4.class)){
            MyTest4 myTest4 = (MyTest4) m.getDeclaredAnnotation(MyTest4.class);
            System.out.println(myTest4.value());
            System.out.println(myTest4.aaa());
            System.out.println(Arrays.toString(myTest4.bbb()));
        }
    }
}

3.3 注解的应用场景

//MyTest

@Target(ElementType.METHOD) //只能注解方法
@Retention(RetentionPolicy.RUNTIME) //让当前注解可以一直存活
public @interface MyTest {

}

//AnnotationTest
public class AnnotationTest {
    // @MyTest
    public void test1(){
        System.out.println("=====test1=====");
    }

    @MyTest
    public void test2(){
        System.out.println("=====test2=====");
    }

    // @MyTest
    public void test3(){
        System.out.println("=====test3=====");
    }

    @MyTest
    public void test4(){
        System.out.println("=====test4=====");
    }

    public static void main(String[] args) throws Exception {
        AnnotationTest t = new AnnotationTest();
        //启动程序
        //1.得到class对象
        Class c = AnnotationTest.class;
        //2.提取类中的全部成员方法
        Method[] methods = c.getDeclaredMethods();
        //3.遍历这个数组中的每个方法,看这个方法是否存在@Mytest注解
        for (Method method : methods) {
            if(method.isAnnotationPresent(MyTest.class)){
                //说明当前方法上是存在@MyTest
                method.invoke(t);
            }
        }
    }
}

四.动态代理

程序为什么需要代理?

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

代理长什么样?

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

中介如何知道要派有什么样方法的代理呢?

        接口!

//Test
public class Test {
    public static void main(String[] args) {
        BigStar bigStar = new BigStar("张三");
        Star starProxy = ProxyUtil.createProxy(bigStar);

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

        starProxy.dance();
    }
}

//ProxyUtil 
public class ProxyUtil {
    public static Star createProxy(BigStar bigStar){

/**
 *         public static Object newProxyInstance(ClassLoader loader,
 *                 Class<?>[] interfaces,
 *                 InvocationHandler h)
 *                 参数1:用于指定一个类加载器
 *                 参数2:指定生成的代理长什么样子,也就是有哪些方法
 *                 参数3:用来指定生成的代理对象想干什么事情
 */

        Star startProxy = (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("收钱20w");
                            //return method.invoke(bigStar,args);
                        }else if(method.getName().equals("dance")){
                            System.out.println("收钱30w");
                            //return method.invoke(bigStar,args);
                        }
                        return method.invoke(bigStar,args);
                    }
                });
        return startProxy;
    }
}


//Star
public interface Star {
    String sing(String name);
    void dance();
}

//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 "thanks";
    }

    public void dance(){
        System.out.println(this.name + "正在跳舞");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值