美文网首页程序员
深度分析:那些阿里,腾讯字节跳动面试官都喜欢问到java反射,看

深度分析:那些阿里,腾讯字节跳动面试官都喜欢问到java反射,看

作者: 程序员匡胤 | 来源:发表于2020-07-20 13:50 被阅读0次

反射的定义

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

  java.lang.Class:反射的源头
  java.lang.reflect.Method
  java.lang.reflect.Field
  java.lang.reflect.Constructor
  ....

Class类的理解

1.类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。
2.换句话说,Class的实例就对应着一个运行时类。
3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。

获取Class实例的几种方式:
        //方式一:调用运行时类的属性:.class
       Class clazz1 = Person.class;
       System.out.println(clazz1);
       //方式二:通过运行时类的对象,调用getClass()
       Person p1 = new Person();
       Class clazz2 = p1.getClass();
       System.out.println(clazz2);

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

       System.out.println(clazz1 == clazz2);
       System.out.println(clazz1 == clazz3);

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

       System.out.println(clazz1 == clazz4);

类的加载过程

当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过三个步骤对该类进行初始化


类的加载器的分类

//系统类加载器
java.lang.ClassLoader classLoader = ClassLoader.class.getClassLoader();
System.out.println(classLoader);
//扩展类加载器
java.lang.ClassLoader classLoader1 = classLoader.getParent();
System.out.println(classLoader1);
//引导类加载器---主要加载核心类库(无法直接获取)
java.lang.ClassLoader classLoader2 = classLoader1.getParent();
System.out.println(classLoader2);

 运行结果:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@a09ee92
null

创建运行时类的对象

    Class<Person> clazz = Person.class
    Person obj = clazz.newInstance();
    System.out.println(obj);
newInstance():调用此方法,创建对应的运行时类的对象。
    内部调用了运行时类的空参的构造器。

要想此方法正常的创建运行时类的对象,要求:
1.运行时类必须提供空参的构造器
2.空参的构造器的访问权限得够。通常,设置为public。

获取运行时类的结构

获取运行时类的属性结构

 1.getFields():获取当前运行时类及其父类中声明为public访问权限的属性
 2.getDeclaredFields():获取当前运行时类中声明的所有属性。   
 (不包含父类中声明的属性)

获取属性的权限修饰符 数据类型 变量名

Class clazz = Person.class;
       Field[] fields = clazz.getDeclaredFields();
       for (Field f : fields){
           //1.权限修饰符
           int modifiers = f.getModifiers();
           System.out.println(Modifier.toString(modifiers));
           //2.数据类型
           Class type = f.getType();
           System.out.println(type);
           //3.变量名
           String name = f.getName();
           System.out.println(name);
           System.out.println("--------");
       }
       
   运行结果:
           private
           class java.lang.String
           name
           --------
           
           int
           age
           --------
           public
           int
           id
           --------

获取运行时类的指定属性结构

1.getField(String fieldName):获取指定属性,要求声明为public

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

    a).getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
        
    b).Field name = clazz.getDeclaredField("name");
        需要加上name.setAccessible(true);来保证当前属性是可访问的
            
    c).获取、设置指定对象的此属性值
        name.set(p,"Tom");

获取运行时类的方法结构

  1.getMethods():获取当前运行时类及其父类中声明为public访问权限的方法
  2.getDeclaredMethods():获取当前运行时类中声明的所有方法。  
  (不包含父类中声明的方法)

获取方法的1.@Xxx注解 2.权限修饰符 3.返回值类型 4.方法名 5.(参数列表1 形参1, …) 6.异常

Class clazz = Person.class;
Method[] methods = clazz.getDeclaredMethods();
for (Method m : methods){
     //1.获取方法声明的注解
     Annotation[] annotations = m.getAnnotations();
     for (Annotation a : annotations){
          System.out.println(a);
     }
     //2.获取权限修饰符
     System.out.print(Modifier.toString(m.getModifiers()) + "\t");
     //3.返回值类型
     System.out.print(m.getReturnType().getName() + "\t");
     //4.方法名
     System.out.print(m.getName());
     System.out.print("(");
     //5.形参列表
     Class[] parameterTypes = m.getParameterTypes();
     if (!(parameterTypes == null && parameterTypes.length == 0)){
         for (int i = 0; i < parameterTypes.length; i++){
             if (i == parameterTypes.length - 1){
                 System.out.print(parameterTypes[i].getName() + " args_" + i);
                 break;
             }
             System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
         }
     }
     System.out.print(")");
     //6.抛出的异常
     Class[] exceptionTypes = m.getExceptionTypes();
     if (exceptionTypes.length > 0) {
         System.out.print("throws ");
         for (int i = 0; i < exceptionTypes.length; i++) {
             if (i == exceptionTypes.length - 1){
                 System.out.println(exceptionTypes[i].getName());
                 break;
             }
             System.out.print(exceptionTypes[i].getName() + ",");
         }
     }
}

运行结果:
public  int compareTo(java.lang.String args_0)

public volatile int compareTo(java.lang.Object args_0)

@com.zsxfa.java2.MyAnnotation(value=[只在这个方法上加了注解])
public  void    info()

public  java.lang.String    display(java.lang.String args_0,int args_1)throws java.lang.NullPointerException,java.lang.ClassCastException

private java.lang.String    show(java.lang.String args_0)


获取运行时类中的指定方法结构

1.非静态方法
    获取Class实例Class clazz = Person.class;
    创建运行时类的对象Person p = (Person) clazz.newInstance();
        
    a).获取指定的某个方法
        getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表
        Method show = clazz.getDeclaredMethod("show", String.class);
        
    b).保证当前方法是可访问的
        show.setAccessible(true);

    c).调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
        invoke()的返回值即为对应类中调用的方法的返回值。
        Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
2.静态方法
    获取Class实例Class clazz = Person.class;
    被调用的静态方法private static void showDesc()
    Method showDesc = clazz.getDeclaredMethod("showDesc");
    showDesc.setAccessible(true);
    //如果调用的运行时类中的方法没返回值,则此invoke()返回null
    //Object returnVal = showDesc.invoke(null);
    Object returnVal = showDesc.invoke(Person.class);
    System.out.println(returnVal);//null

获取运行时类的构造器结构

  1.getConstructors():获取当前运行时类声明为public的构造器
  2.getDeclaredConstructors():获取当前运行时类中声明的所有构造器。

获取运行时类的指定构造器结构 (不够通用)

  Class clazz = Person.class;
    //private Person(String name)
  1.获取指定的构造器
    //getDeclaredConstructor():参数:指明构造器的参数列表
    Constructor constructor = clazz.getDeclaredConstructor(String.class);
  2.保证此构造器是可访问的
    constructor.setAccessible(true);
  3.调用此构造器创建运行时类的对象
    Person per = (Person) constructor.newInstance("Tom");
    System.out.println(per);

获取运行时类的父类

Class superclass = getSuperclass();

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

Class clazz = Person.class;
//Type是一个接口,被Class实现
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);

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

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

获取运行时类的父类实现的接口

Class[] interfaces = getSuperclass().getInterfaces();

获取运行时类所在的包

Package pack = clazz.getPackage();

获取运行时类声明的注解

Annotation[] annotations = clazz.getAnnotations();

最后

感谢你看到这里,看完有什么的不懂的可以在评论区问我,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

相关文章

网友评论

    本文标题:深度分析:那些阿里,腾讯字节跳动面试官都喜欢问到java反射,看

    本文链接:https://www.haomeiwen.com/subject/rlnekktx.html