51 反射

反射

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存的方法区中就产生了一个class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射

 

补充:动态语言vs静态语言 1、动态语言 是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。 主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。2、静态语言 与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。 Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。 Java的动态性让编程的时候更加灵活!

 

Java反射机制提供的功能

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时判断任意一个类所具有的成员变量和方法

在运行时获取泛型信息 在运行时调用任意一个对象的成员变量和方法

在运行时处理注解 生成动态代理

 

@Test
   public void test2() throws Exception{

       Class clazz = Person.class;
       //1.通过反射,创建Person类的对象
       Constructor cons = clazz.getConstructor(String.class, int.class);

       Object obj = cons.newInstance("Tom", 12);
       Person p = (Person) obj;
       System.out.println(p.toString());
       //2.通过反射,调用对象指定的属性、方法
       //调用属性
       Field age = clazz.getDeclaredField("age");
       age.set(p,10);
       //调用方法
       Method show = clazz.getDeclaredMethod("show");
       show.invoke(p);

       //通过反射,可以调用Person类的私有结构。比如:私有的构造器、方法、属性
       //调用私有的构造器
       Constructor cons1 = clazz.getDeclaredConstructor(String.class);
       cons1.setAccessible(true);
       Object p1 = cons1.newInstance("Jerry");
       System.out.println(p1);

       //调用私有的属性
       Field name = clazz.getDeclaredField("name");
       name.setAccessible(true);
       name.set(p1,"Hanmeimei");
       System.out.println(p1);

       //调用私有的方法
       Method showNation = clazz.getDeclaredMethod("showNation", String.class);
       showNation.setAccessible(true);
       String nation = (String)showNation.invoke(p1, "中国");//相当于p1.showNation("中国") nation = p1.showNation("中国:)
       
  }

 

疑问:

  1. 通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用哪个?

    建议:直接new的方式。

  2. 什么时候会使用:反射的方式。反射的特征:动态性

  3. 反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?

    不矛盾。

 

Class类

关于Java.lang.Class类的理解

  1. 类的加载过程

    程序经过java.exe命令以后,会生成一个或多个字节码文件(.class结尾),接着我们使用java.exe命令对某个字节码文件进行解释运行。

    加载到内存中,此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。

  2. 换句话说:Class的实例就对应着一个运行时类

  3. 加载到内存中的运行时类,会缓存一定的时间,在此时间之内,我们可以通过不同的方式来获取此运行时类

     

获取Class的实例的方式(前三种方式需要掌握)第三种用的频率高些

   //方式一:调用运行时类的属性:.class
       Class clazz1 = Person.class;
       System.out.println(clazz1);//class com.atguigu.java.Person

       //方式二:通过运行时类的对象,调用getClass()
       Person p1 = new Person();
       Class clazz2 = p1.getClass();

       //方式三:调用Class的静态方法:forName(String classPath)
       Class clazz3 = Class.forName("com.atguigu.java.Person");
       System.out.println(clazz3);

       //方式四:使用类的加载器:ClassLoader(了解)
       ClassLoader classLoader = ReflectionTest.class.getClassLoader();
       Class clazz4 = classLoader.loadClass("com.atguigu.java.Person");

哪些类型可以有class对象? (1) class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类

(2) interface:接口 (3)[]:数组 (4) enum:枚举 (5) annotation:注解@interface

(6) primitive type:基本数据类型

( 7) void

 

 

通过反射创建对应的运行时类的对象

newInstacne():调用此方法,创建对应的运行时类的对象,内部调用了运行时类的空参构造器。

要想方法正常的创建运行时类的对象,要求:

  1. 运行时类必须提供空参的构造器。

  2. 空参的构造器的访问权限得够。通常设置为public。

 

在javabean中要求提供一个public的空参构造器,原因:

  1. 便于通过反射,创建运行时类的对象

  2. 便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器

       Class clazz = Person.class;
      Person obj = (Person) clazz.newInstance();
      System.out.println(obj);

 

 

获取运行时类的属性结构

getFields():获取当前运行时类及其父类中声明为public访问权限的属性

 

getDeclaredFields();:获取当前运行时类中声明 的所有属性。(不包含父类中的属性)

        Class clazz = Person.class;

       //获取属性结构
       Field[] fields = clazz.getFields();
       for(Field f : fields){
           System.out.println(f);
      }
       
       Field[] declaredFields = clazz.getDeclaredFields();
       for(Field f : declaredFields){
           System.out.println(f);
      }

 

权限修饰符 数据类型 变量名

Class clazz = Person.class;
       Field[] declaredFields = clazz.getDeclaredFields();
       for (Field f:declaredFields){
           //1.权限修饰符
           int modifier = f.getModifiers();
           System.out.println(Modifier.toString(modifier));

           //2.数据类型
           Class type = f.getType();
           System.out.println(type);

           //3.变量名
           String name = f.getName();
           System.out.println(name);

 

 

获取运行时类的方法结构

getMethods():获取当前运行时类及其父类中声明为public访问权限的方法

getDeclaredMethods():获取当前运行时类中声明 的所有方法。(不包含父类中的方法)

        Class clazz = Person.class;
       Method[] methods = clazz.getMethods();
       for (Method m : methods){
           System.out.println(m);
      }

       Method[] declaredMethods = clazz.getDeclaredMethods();
       for (Method m : declaredMethods){
           System.out.println(m);
      }

 

@Xxxx

权限修饰符 返回值类型 方法名(参数类型1,参数类型2......) throws XxxException{

}

   Class clazz = Person.class;
       Method[] declaredMethods = clazz.getDeclaredMethods();
       for (Method m : declaredMethods){
         //1.获取方法声明的注解
           Annotation[] annotations = m.getAnnotations();
           for (Annotation a : annotations){
               System.out.println(a);
          }

           //2.获取权限修饰符
           System.out.println(Modifier.toString(m.getModifiers()));

           //3.返回值类型
           System.out.println(m.getReturnType().getName());

           //4.方法名
           System.out.println(m.getName());

           //5.形参列表
           Class[] parameterTypes = m.getParameterTypes();

           //6.抛出的异常
           Class[] exceptionTypes = m.getExceptionTypes();

           

 

其他的

getConstructor():获取当前运行时类中声明为public的构造器

Class clazz = Person.class;
       Constructor[] constructors = clazz.getConstructors();
       for (Constructor c: constructors){
           System.out.println(c);
      }
  }

 

 

获取运行时类的父类

Class clazz = Person.class;

       Class superclass = clazz.getSuperclass();
       System.out.println(superclass);

 

获取运行时类的带泛型类的父类的泛型

        Type genericSuperclass = clazz.getGenericSuperclass();
       ParameterizedType paramType = (ParameterizedType) genericSuperclass;
       //获取泛型类型
       Type[] actualTypeArguments = paramType.getActualTypeArguments();
       System.out.println(actualTypeArguments[0]);
       
Type genericSuperclass = clazz.getGenericSuperclass();
       ParameterizedType paramType = (ParameterizedType) genericSuperclass;
       //获取泛型类型
       Type[] actualTypeArguments = paramType.getActualTypeArguments();
       System.out.println(actualTypeArguments[0]);
       

 

获取运行时类的接口

        Class clazz = Person.class;
       Class[] interfaces = clazz.getInterfaces();
       for (Class c : interfaces)
           System.out.println(c);

       //获取运行时类的父类实现的接口
       clazz.getSuperclass().getInterfaces();

 

获取运行时类所在的包

        Package Pack = clazz.getPackage();
       System.out.println(Pack);

 

调用运行时类中指定的结构

调用运行时类中指定的结构:属性、方法、构造器

 

如何操作运行时类中的指定的属性

        //不需要掌握
//方式一:
Class clazz = Person.class;

       //创建运行时类的对象
       Person p = (Person) clazz.newInstance();

       //获取指定的属性:要求运行时类中属性声明为public
       //通常不采用此方法
       Field id = clazz.getField("id");

       //设置当前属性的值
       //set(): 参数1:指明设置那个对象的属性 参数2:将此属性值设置为多少
       id.set(p,1001);

       //获取当前属性的值
       //get():参数1:获取哪个对象的当前属性值
       int pId  = (int)id.get(p);

//需要掌握
//方式二:

Class clazz = Person.class;

       //创建运行时类的对象
       Person p = (Person) clazz.newInstance();

       //1.getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
       Field name = clazz.getDeclaredField("name");

       //2.保证当前属性是可访问的
       name.setAccessible(true);
       //3.获取、设置指定此属性的值
       name.set(p,"哈哈");

       System.out.println(name.get(p));

 

 

如何操作运行时类中的指定的方法


       Class clazz = Person.class;

       //创建运行时类的对象
       Person p = (Person) clazz.newInstance();

       //1.获取指定的某个方法
       //getDeclaredMethod():参数1:指明获取的方法的名称 参数2:指明获取的方法的形参列表
       Method show = clazz.getDeclaredMethod("show", String.class);

       //2.保证当前方法是可访问的
       show.setAccessible(true);

       //3.调用方法的 invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
       //invoke()的返回值即为对应类中调用的方法的返回值
       Object returnValue = show.invoke(p,"CHN");

 

如何调用静态的方法

        Method showDesc = clazz.getDeclaredMethod("showDesc");
       showDesc.setAccessible(true);
       //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
       Object returnVal = showDesc.invoke(Person.class);

 

 

如何操作运行时类中的指定的构造器

        Class clazz = Person.class;

       //1.获取指定的构造器
       //getDeclaredConstructor(): 参数:指明构造器的参数列表
       Constructor constructor = clazz.getDeclaredConstructor(String.class);

       //2.保证此构造器是可访问的
       constructor.setAccessible(true);

       //3.调用构造器创建运行时类的对象
       Person P = (Person) constructor.newInstance("Tom");

 

posted @   flypiggg  阅读(59)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示