Java教程

Java反射机制

本文主要是介绍Java反射机制,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

反射

第一部分 Junit单元测试

1.1、测试分类

  • 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。
  • 白盒测试:需要写代码。关注程序具体的执行流程。

1.2、Junit使用:白盒测试

  • 步骤:

    1. 定义一个测试类(测试用例)

      • 建议:

        a. 测试类名:被测试的类名Test CalculatorTest

        b. 包名:xxx.xxx.xx.test cn.itcast.test

    2. 定义测试方法:可独立运行

      • 建议:

        a. 方法名:test测试的方法名 testAdd()

        b. 返回值:void

        c. 参数列表:空参

    3. 给方法加 @Test

    4. 导入 junit 依赖环境

  • 判定结果:

    • 红色:失败

    • 绿色:成功

    • 一般我们会使用断言操作来处理结果:

      ​ Assert.assertEquals(期望的结果,运算的结果)

第二部分 反射(重点!)

2.1、Java代码在计算机中经历的三个阶段:

image

2.2、反射与框架

  • 框架:半成品软件;可在框架的基础上进行软件开发,极大地简化代码。
  • 反射:将类的各个组成部分封装为其它对象;是框架设计的灵魂。
  • 反射的好处:
    • 可以在程序运行过程中,操作这些对象。
    • 解耦合,提高程序的可扩展性!

2.3、获取Class类对象的方式

  • java.lang.Class extends java.lang.Object

  • Class Class:Class类的实例表示正在运行的Java应用程序中的类和接口。

    1. 枚举是一种类,注解是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(booleanbytecharshortintlongfloatdouble)和关键字 void 也表示为 Class 对象。

    2. Class 没有公共构造方法Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

    3. 类型参数:

      T - 由此 Class 对象建模的类的类型。

      例如,String.class 的类型是 Class<String>。如果将被建模的类未知,则使用 Class<?>

1. Class.forName("全类名"): 将字节码文件加载进内存,返回Class对象 <"全类名"即是:包名.类名>
     多用于配置文件,将类名定义在配置文件中,读取文件,加载类。
2. 类名.class:通过类名的属性class获取
     多用于参数的传递
3. 对象.getClass():getClass()方法在Object类中定义着,返回此Object运行时类。
     多用于对象获取字节码的方式
    
注意:
    同一个字节码文件(*.class)在一次程序运行过程中只会被加载一次,不论通过哪种方式获取的Class对象都是同一个。
public class ReflectDemo1{
    public static void main(String[] args) throws ClassNotFoundException {
        //1. Class.forName("全类名") 全类名:包名.类名。
        Class cls1 = Class.forName("day13.reflection.demo1.Person");
        System.out.println(cls1);

        //2. 类名.class
        Class<Person> cls2 = Person.class;
        System.out.println(cls2);

        //3. 对象.getClass()
        Person p = new Person();
        Class<? extends Person> cls3 = p.getClass();
        System.out.println(cls3);

 //相同字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪种方式获取的Class对象都是同一个!
        System.out.println(cls1 == cls2); //true
        System.out.println(cls1 == cls3); //true
    }
}

2.4 Class常用方法:

  • 获取功能:

    ①获取成员变量:

    Field[] getFields(); 获取所有public修饰的成员变量	(所有public)
    Field getField(String name); 获取指定名称的public修饰的成员变量 (单个)
    
    Field[] getDeclaredFields(); 获取所有的成员变量,不考虑修饰符 (所有)
    Field getDeclaredField(String name); 获取指定名称的成员变量 (单个)
    
    import java.lang.reflect.Field;
    
    public class ReflectDemo2 {
        public static void main(String[] args) throws Exception {
            //获取Class对象
            Class cls = Person.class;
            //1.Field[] getFields(); 获取所有public修饰的成员变量
            Field[] fields = cls.getFields();
            for(Field f : fields){
                System.out.println(f);
            }
            
            System.out.println("-----------------------");
            /*
                Field getField(String name); 获取指定名称的public修饰的成员变量
                操作:<Field类中的两个方法:>
                    ①设置值:void set(Object obj,Object value)
                    ②获取值:Object get(Object obj)
                    ③忽略访问权限修饰符的安全检查:
            setAccessible(true); 暴力反射-->解决访问私有属性时抛出的IllegalAccessException异常!
            访问私有属性/方法时,均可使用暴力反射!
             */
            Field a = cls.getField("a");
            Person p = new Person();
            //获取成员变量a的值
            Object value = a.get(p);
            System.out.println(value);
            //设置a的值
            a.set(p,"Basketball");
            System.out.println(p);
    
            System.out.println("=========================");
            //Field[] getDeclaredFields(); 获取所有的成员变量,不考虑修饰符
            Field[] declaredFields = cls.getDeclaredFields();
            for(Field f : declaredFields){
                System.out.println(f);
            }
            //Field getDeclaredField(String name); 获取指定名称的成员变量
            Field d = cls.getDeclaredField("d");
            //忽略访问权限修饰符的安全检查
            d.setAccessible(true); //暴力反射-->解决访问私有属性时出现的IllegalAccessException!
            //获取私有成员变量d的值
            System.out.println(d.get(p)); 
        }
    }
    

    ②获取构造方法:

    Constructor<?>[] getConstructors();
    Constructor<T> getConstructor(Class<?>... parameterTypes);
    
    Constructor<?>[] getDeclaredConstructors();
    Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes);
    
    java.lang.reflect.Constructor类: 
    	public T newInstance(Object... initargs); 用于创建有参对象
    	
    java.lang.Class类: 
    	public T newInstance(); 创建由此类对象表示的类的新实例(用于无参构造的创建)
    	public ClassLoader getClassLoader(); 返回该类的类加载器
    
    import java.lang.reflect.Constructor;
    
    public class ReflectDemo3 {
        public static void main(String[] args) throws Exception {
            //获取Class类对象
            Class cls = Person.class;
    
            //Constructor<T> getConstructor(Class<?>... parameterTypes);
            Constructor constructor = cls.getConstructor(String.class,int.class);
            //创建对象
            Object obj = constructor.newInstance("杜兰特",35);
            //System.out.println(obj.getClass()); //Object类中的getClass():返回此Object的运行时类。
            System.out.println(obj);
    
            System.out.println("-----------------------------");
            //创建对象:无参
            Constructor constructor1 = cls.getConstructor();
            Object obj1 = constructor1.newInstance();
            System.out.println(obj1);
    
            //Class类中的 public T newInstance(); 创建由此类对象表示的类的新实例
            Object o = cls.newInstance();
            System.out.println(o);
        }
    }
    

    ③获取成员方法:

    Method[] getMethods();
    Method getMethod(String name,Class<?>... parameterTypes);
    
    Method[] getDeclaredMethods();
    Method getDeclareMethod(String name,Class<?>... parameterTypes);
    
    java.lang.reflect.Method类:
    	public Object invoke(Object obj,Object... args); 
    	对带有指定参数的指定对象调用由此Method对象表示的底层方法(执行方法)
    	public String getName(); 以 String 形式返回此 Method 对象表示的方法名称。 
    	
    
    import java.lang.reflect.Method;
    
    public class ReflectDemo4 {
        public static void main(String[] args) throws Exception {
            //获取Class类对象
            Class cls = Class.forName("day13.reflection.demo1.Person");
            Method eat_method = cls.getMethod("eat");
            System.out.println(eat_method);
            Person p = new Person();
            //执行方法
            eat_method.invoke(p);
    
            Method eat = cls.getMethod("eat", String.class);
            //执行方法
            eat.invoke(p,"土豆");
    
            System.out.println("===============================");
            //获取所有public修饰的方法
            Method[] methods = cls.getMethods();
            for (Method method : methods) {
                System.out.println(method);
                String name = method.getName(); //获取方法名称 getName();
                System.out.println(name);
                //method.setAccessible(true); //暴力反射
            }
    
            //获取类名:String getName()
            String className = cls.getName();
            System.out.println(className);
    
        }
    }
    

    ④获取类名

    public String getName(); 获取类名
    

    2.5、练习:配置文件+反射

    Class类:
       public ClassLoader getClassLoader();返回该类的类加载器
       
    ClassLoader抽象类:
       public InputStream getResourceAsStream(String name);返回用于读取指定资源的输入流
    
    Properties集合类:
    java.util.Properties extends java.util.Hashtable<K,V> implements Map<K,V>
       public void load(InputStream inStream);从输入字节流读取属性列表(键值对)
       public String getProperty(String key);用指定的键在此属性列表中搜索属性
    
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.lang.reflect.Method;
    import java.util.Properties;
    /*
        需求:
            写一个“框架”,不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法。
        实现:
            1.配置文件
            2.反射
        步骤:
            1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中
            2.在程序中加载读取配置文件
            3.使用反射技术来加载类文件进内存
            4.创建对象
            5.执行方法
     */
    public class ReflectTest {
        /*
          前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法。
        */
        public static void main(String[] args) throws Exception {
    
            //1.创建Properties对象
            Properties pro = new Properties();
            //2.通过类加载器获取配置文件
            ClassLoader cl = ReflectTest.class.getClassLoader();
            //配置文件要放在src路径下面!否则出现NullPointException
            InputStream is = cl.getResourceAsStream("pro.properties");
            //3.加载配置文件
            pro.load(is);
            //4.获取配置文件中定义的数据
            String className = pro.getProperty("className");
            String methodName = pro.getProperty("methodName");
            //5.加载该类进内存
            Class cls = Class.forName(className);
            //6.创建对象
            Object o = cls.newInstance();
            //7.执行方法
            Method method = cls.getMethod(methodName);
            method.invoke(o);
        }
    }
    

第三部分 注解

3.1、注解与注释?

  • 注解:说明程序,给计算机看的。
  • 注释:用文字描述程序,给程序员看的。

3.2、定义与描述

注解(Annotation):也叫元数据。一种代码级别的说明,JDK1.5及以后版本引入的新特性,与类、接口、枚举同属一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明、注释。

概念描述:

  • JDK1.5之后的新特性
  • 说明程序的
  • 使用注解:@注解名称

java.lang.annotation.Interface Annotation: 所有 annotation 类型都要扩展的公共接口。

3.3、作用分类

①编写文档:通过代码里标识的注解生成文档【生成doc文档】

②代码分析:通过代码里标识的注解对代码进行分析【使用反射】

③编译检查:通过代码库标识的注解让编译器能够实现基本的编译检查【Override】

3.4、JDK内置注解

  • @Override:检测被该注解标注的方法是否继承自父类/接口
  • @Deprecated:该注解标注的内容,表示已过时
  • @SuppressWarnings:压制警告!

3.5、自定义注解?

这篇关于Java反射机制的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!