程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2023-06(3)

Java核心编程总结(十、反射)

发布于2021-06-12 13:57     阅读(304)     评论(0)     点赞(14)     收藏(2)


1.基础回顾+面试

1.1单元测试

  1. 什么是单元测试?

    答:单元测试是指程序员写的测试代码给自己类中的方法进行预期正确性的验证,单元测试一旦写好了这些测试代码,就可以一直使用,可以实现一定程度上的自动化测试。单元测试一般要使用框架进行。

  2. 什么是框架?

    答:框架是前人或者一些牛逼的技术公司在实战或者研发中设计的一些优良的设计方案或者成型的代码功能,作为一个完整的技术体系发行出来称为框架

单元测试的经典框架: Junit

  1. 什么是Junit?

    答:

    • Junit是Java语言编写的第三方单元测试框架
    • Junit框架的方案可以帮我们方便且快速的测试我们的代码的正确性
  2. 什么是单元测试?

    答:

    • 单元:在Java中,一个类就是一个单元
    • 单元测试:程序猿用Junit编写的一小段代码,用来对某个类中的某个方法进行功能测试或者业务逻辑测试

1.2Junit框架使用步骤

  1. 下载这个框架
    • 框架一般都是jar包的形式,jar包里面都是class文件,class文件就是我们调用的核心代码
  2. 直接用Junit测试代码即可
    • 写测试类:以Test开头,以业务类类名结尾
      • 业务名称:UserService
      • 测试这个业务类的测试类:TestUserService
    • 在测试类中写测试方法
      • 测试方法的命名规范:以Test开头,以业务方法名结尾
      • 被测试业务方法:login
      • 测试方法名:testLogin
    • 测试方法注意事项
      • 必须用public修饰,没有返回值,没有参数
      • 必须使用注解@Test修饰

1.3Junit常用注解(4.xx版本)

  • @Test 测试方法!
  • @Before: 用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次
  • @After: 用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次
  • @BeforeClass: 用来静态修饰方法,该方法会在所有测试方法之前只执行一次
  • @AfterClass: 用来静态修饰方法,该方法会在所有测试方法之后只执行一次

1.4Junit常用注解(5.xx版本)

  • Test 测试方法
  • @BeforeEach: 用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次
  • AfterEach: 用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次
  • BeforeAll: 用来静态修饰方法,该方法会在所有测试方法之前只执行一次
  • AfterAll: 用来静态修饰方法,该方法会在所有测试方法之后只执行一次

1.5反射

反射,注解,代理,泛型是Java的高级技术,是以后框架的底层院里必须使用到的技术

  1. 什么是反射?

    答:反射是指对于任何一个类,在“运行的时候”都可以直接得到这个类的全部成分

    • 在运行时,可以直接得到这个类的构造器对象(Constructor)
    • 在运行时,可以直接得到这个类的成员变量对象(Field)
    • 在运行时,可以直接得到这个类的成员方法对象(Method)
  2. 反射的核心思想和关键就是得到:编译后的class文件对象

  3. 反射是在运行时获取类的字节码文件对象:然后可以解析类中的全部成分

1.6反射获取Class类对象

引入:

  • 反射是通过先得到编译以后的Class类对象:字节码文件,然后才可以得到类中的全部成分,进行一些功能设计
  • 反射为一个类的全部成分都设计了一个类型来代表这个对象
    • Class:字节码文件的类型
    • Constructor:构造器的类型
    • Field:成员变量的类型
    • Method:方法的类型

反射获取Class类对象:

1.反射技术的第一步永远是先得到Class类对象:有三种方式获取

  • 类名.class
  • 通过类的对象.getClass()方法
  • Class.forName(“类的全限名”)
    • public static Class<?> forName(String className)

2.Class类下的方法

  • String getSimpleName(): 获得类名字符串:类名

  • String getName(): 获得类全名:包名 + 类名

public class ReflectDemo01 {
    public static void main(String[] args) throws Exception {
        // 反射的第一步永远是先得到类的Class文件对象: 字节码文件。
        // 1.类名.class
        Class c1 = Student.class;
        System.out.println(c1);
		// class com.itheima._03反射_获取Class类对象.Student
        
        // 2.对象.getClass()
        Student swk = new Student();
        Class c2 = swk.getClass();
        System.out.println(c2);
        // class com.itheima._03反射_获取Class类对象.Student

        // 3.Class.forName("类的全限名")
        // 直接去加载该类的class文件。
        Class c3 = Class.forName("com.itheima._03反射_获取Class类对象.Student");
        System.out.println(c3);
        // class com.itheima._03反射_获取Class类对象.Student

        System.out.println(c1.getSimpleName()); // 获取类名本身(简名)
        // Student
        System.out.println(c1.getName()); // 获取类的全限名
        // class com.itheima._03反射_获取Class类对象.Student
    }
}

1.7反射获取Constructor构造器

反射中Class类型获取构造器提供了很多的API:

  • Constructor getDeclaredConstructor(Class...parameterTypes)
    • 根据参数匹配获取某个构造器,不关心权限修饰符
  • Constructor[] getDeclaredConstructors()
    • 获取所有申明的构造器
public class TestStudent {
    // 1.getDeclaredConstructors():
    // 获取全部的构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。
    @Test
    public void getDeclaredConstructors(){
        // a.反射第一步先得到Class类对象
        Class c = Student.class ;
        // b.getDeclaredConstructors(): 定位全部构造器,只要申明了就可以拿到
        Constructor[] cons = c.getDeclaredConstructors();
        // c.遍历这些构造器
        for (Constructor con : cons) {
            System.out.println(con.getName()+"===>"+con.getParameterCount());
        }
    }
    // 2.getDeclaredConstructor
    // 获取某个构造器: 只要你敢写,这里就能拿到,无所谓权限是否可及。
    @Test
    public void getDeclaredConstructor() throws Exception {
        // a.反射第一步先得到Class类对象
        Class c = Student.class ;
        // b.getDeclaredConstructor():定位某个构造器,根据参数匹配,只要申明了就可以获取
        Constructor con = c.getDeclaredConstructor(); // 可以拿到!定位无参数构造器!
        //Constructor con = c.getDeclaredConstructor(String.class  , int.class); // 有参数的!!
        // c.构造器名称和参数
        System.out.println(con.getName()+"===>"+con.getParameterCount());
    }
}

1.8反射获取构造器并初始化对象

反射获取Constructor构造器然后通过这个构造器初始化对象

Constructor的API:

  • T newInstance(Object... initargs): 创建对象,注入构造器需要的数据
  • void setAccessible(true): 修改访问权限,true代表暴力攻破权限,false表示保留
public class Student {
    private String name ;
    private int age ;

    private Student(){
        System.out.println("无参数构造器被执行~~~~");
    }

    public Student(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 "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class TestStudent02 {
    // 1.调用无参数构造器得到一个类的对象返回。
    @Test
    public void createObj01() throws Exception {
        // a.反射第一步是先得到Class类对象
        Class c = Student.class ;
        // b.定位无参数构造器对象
        Constructor constructor = c.getDeclaredConstructor();
        // c.暴力打开私有构造器的访问权限
        constructor.setAccessible(true);
        // d.通过无参数构造器初始化对象返回
        Student swk = (Student) constructor.newInstance(); // 最终还是调用无参数构造器的!
        System.out.println(swk);
    }

    // 2.调用有参数构造器得到一个类的对象返回。
    @Test
    public void createObj02() throws Exception {
        // a.反射第一步是先得到Class类对象
        Class c = Student.class ;
        // b.定位有参数构造器对象
        Constructor constructor = c.getDeclaredConstructor(String.class , int.class);
        // c.通过无参数构造器初始化对象返回
        Student swk = (Student) constructor.newInstance("孙悟空",10000); // 最终还是调用有参数构造器的!
        System.out.println(swk);
    }
}
  • 可以通过定位类的构造器对象
  • 如果构造器对象没有访问权限可以通过:void setAccessible(true)打开权限
  • 构造器可以通过T newInstance(Object... initargs) 调用自己,传入参数

1.9反射获取Field成员变量对象

  • Field getDeclaredField(String name): 根据成员变量名获取对应Field对象
  • Field[] getDeclaredFields(): 获得所有的成员变量对应的Field对象
public class FieldDemo {
    /**
     * 1.获取全部的成员变量。
     */
    @Test
    public void getDeclaredFields(){
        // a.先获取class类对象
        Class c = Dog.class;
        // b.获取全部申明的成员变量对象
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName()+"===>"+field.getType());
        }
    }

    /**
        2.获取某个成员变量对象
     */
    @Test
    public void getDeclaredField() throws Exception {
        // a.先获取class类对象
        Class c = Dog.class;
        // b.定位某个成员变量对象 :根据名称定位!!
        Field ageF = c.getDeclaredField("age");
        System.out.println(ageF.getName()+"--->"+ageF.getType());
    }
}

1.10成员变量赋值和取值

Field的方法:给成员变量赋值和取值

  • void set(Object obj,Object value): 给对象注入某个成员变量数据
  • Object get(Object obj): 获取对象的成员变量的值
  • void setAccessible(true): 暴力反射,设置为可以直接访问私有类型的属性
  • Class getType(): 获取属性的类型,返回Class对象
  • String getName(): 获取属性的名称
public class Dog {
    private String name;
    private int age ;
    private String color ;
    public static String school;
    public static final String SCHOOL_1 = "宠物学校";

    public Dog() {
    }

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

    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 String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}';
    }
}
public class FieldDemo02 {
    @Test
    public void setField() throws Exception {
        // a.反射的第一步获取Class类对象
        Class c = Dog.class ;
        // b.定位name成员变量
        Field nameF = c.getDeclaredField("name");
        // c.为这个成员变量赋值!
        Dog taiDi = new Dog();
        nameF.setAccessible(true); // 暴力反射!
        /**
         * 参数一:被赋值的对象。
         * 参数二:该成员变量的值。
         */
        nameF.set(taiDi , "勇敢的泰迪");
        System.out.println(taiDi);

        // d.获取成员变量的值
        String value = nameF.get(taiDi)+"";
        System.out.println(value);
    }
}

1.11反射获取Method方法对象

反射获取类的Method方法对象:

  1. Method getDeclaredMethod(String name,Class...args)

    • 根据方法名和参数类型获取对应的方法对象
  2. Method[] getDeclaredMethods()

    • 获得类中所有成员方法对象,返回数组

Method的方法执行:

  • Object invoke(Object obj,Object... args)
  • 参数一:触发的是哪个对象的方法执行
  • 参数二:args:调用方法时传递的实际参数
public class Dog {

    private String name ;

    public Dog(){
    }

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

    public void run(){
        System.out.println("狗跑的贼快~~");
    }

    private void eat(){
        System.out.println("狗吃骨头");
    }

    private void eat(String name){
        System.out.println("狗吃"+name);
    }

    public static void inAddr(){
        System.out.println("在吉山区有一只单身狗!");
    }

    public String getName() {
        return name;
    }

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

}
public class MethodDemo01 {
    /**
     * 1.获得类中的所有成员方法对象
     */
    @Test
    public void getDeclaredMethods(){
        // a.先获取class类对象
        Class c = Dog.class ;
        // b.获取全部申明的方法!
        Method[] methods = c.getDeclaredMethods();
        // c.遍历这些方法
        for (Method method : methods) {
            System.out.println(method.getName()+"====>"
                    + method.getParameterCount()+"===>" + method.getReturnType());
        }

    }
    /**
     * 2. 获取某个方法对象
     */
    @Test
    public void getDeclardMethod() throws Exception {
        // a.先获取class类对象
        Class c = Dog.class;
        // b.定位它的某个方法
        Method run = c.getDeclaredMethod("run");
        // c.触发方法执行!
        Dog jinMao = new Dog();
        Object rs = run.invoke(jinMao); // 触发jinMao对象的run()方法执行!
        System.out.println(rs);// 如果方法没有返回值,结果是null

        /**
         * 参数一:方法名称
         * 参数二:方法的参数个数和类型(可变参数!)
         */
        Method eat = c.getDeclaredMethod("eat",String.class);
        eat.setAccessible(true); // 暴力反射!
        /**
         * 参数一:被触发方法所在的对象
         * 参数二:方法需要的入参值
         */
        Object rs1 = eat.invoke(jinMao,"肉");
        System.out.println(rs1);// 如果方法没有返回值,结果是null
    }
}

1.12暴力攻击集合泛型

– 反射可以破坏面向对象的封装性(暴力反射)

– 同时可以破坏泛型的约束性

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 泛型只能工作在编译阶段,运行阶段泛型就消失了,
        // 反射工作在运行时阶段。
        List<Double> scores = new ArrayList<>();
        scores.add(99.3);
        scores.add(199.3);
        scores.add(89.5);

        // 拓展:通过反射暴力的注入一个其他类型的数据进去。
        // a.先得到集合对象的Class文件对象
        Class c = scores.getClass();
        // b.从ArrayList的Class对象中定位add方法
        Method add = c.getDeclaredMethod("add", Object.class);
        // c.触发scores集合对象中的add执行(运行阶段,泛型不能约束了)
        add.invoke(scores,"波仔");

        System.out.println(scores);


    }
}

1.13反射的作用

  • 可以在运行时得到一个类的全部成分然后操作
  • 可以破坏封装性
  • 可以破坏泛型的约束性
  • 更重要的用途:适合做Java高级框架,基本上主流框架都会基于反射设计一些通用技术功能

原文链接:https://blog.csdn.net/Augenstern_QXL/article/details/117744497



所属网站分类: 技术文章 > 博客

作者:长这么胖

链接:http://www.javaheidong.com/blog/article/222094/916f027c84213b80a62d/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

14 0
收藏该文
已收藏

评论内容:(最多支持255个字符)