java反射超详解✌(代码片段)

LL.LEBRON LL.LEBRON     2022-12-23     159

关键词:

Java反射超详解✌

1.反射基础

Java反射机制是在程序的运行过程中,对于任何一个类,都能够知道它的所有属性和方法;对于任意一个对象,都能够知道它的任意属性和方法,这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

Java反射机制主要提供以下这几个功能:

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所有的成员变量和方法
  • 在运行时调用任意一个对象的方法

1.1Class类

Class类,Class类也是一个实实在在的类,存在于JDK的java.lang包中。Class类的实例表示java应用运行时的类(class ans enum)或接口(interface and annotation)(每个java类运行时都在JVM里表现为一个class对象,可通过类名.class类型.getClass()Class.forName("类名")等方法获取class对象)。数组同样也被映射为为class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本类型boolean,byte,char,short,int,long,float,double和关键字void同样表现为 class 对象。

到这我们也就可以得出以下几点信息:

  • Class类也是类的一种,与class关键字是不一样的。
  • 手动编写的类被编译后会产生一个Class对象,其表示的是创建的类的类型信息,而且这个Class对象保存在同名.class的文件中(字节码文件)。
  • 每个通过关键字class标识的类,在内存中有且只有一个与之对应的Class对象来描述其类型信息,无论创建多少个实例对象,其依据的都是用一个Class对象。
  • Class类只存私有构造函数,因此对应Class对象只能有JVM创建和加载。
  • Class类的对象作用是运行时提供或获得某个对象的类型信息,这点对于反射技术很重要(关于反射稍后分析)。

1.2类加载

  1. 类加载机制流程

  2. 类的加载

注:详细的类加载内容,看JVM板块。

2.反射的使用

2.1Class对象的获取

在类加载的时候,jvm会创建一个class对象。class对象可以说是反射中最常见的。

获取class对象的方式的主要三种:

  • 根据类名:类名.class
  • 根据对象:对象.getClass()
  • 根据全限定类名:Class.forName(全限定类名)

public class demo1Main1 
    public static void main(String[] args) throws Exception 
        //获取Class对象的三种对象
        System.out.println("根据类名:\\t" + User.class);
        System.out.println("根据对象:\\t" + new User().getClass());
        System.out.println("根据全限定类名:\\t" + Class.forName("demo1.User"));
        //常用的方法
        Class<User> userClass = User.class;
        System.out.println("获取全限定类名:\\t" + userClass.getName());
        System.out.println("获取类名:\\t" + userClass.getSimpleName());
        System.out.println("实例化:\\t" + userClass.newInstance());

    

输出结果:

根据类名:	class demo1.User
根据对象:	class demo1.User
根据全限定类名:	class demo1.User
获取全限定类名:	demo1.User
获取类名:	User
实例化:	Username='init', age=0

再来看看Class类的方法:

  • toString()

    public String toString() 
        return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
            + getName();
    
    

    toString()方法能够将对象转换为字符串,toString()首先判断Class类型是否是接口类型,也就是说普通类和接口都能用Class对象表示,然后在判断是否是基本数据类型,这里判断的都是基本数据类型和包装类,还有void类型。

  • getName()

    获取类的全限定名称。(包括包名)即类的完整名称。

    • 如果是引用类型。比如 String.class.getName()→java.lang.String
    • 如果是基本数据类型。比如 byte.class.getName()→byte
    • 如果是数组类型。比如 new Object[3].getClass().getName()→[Ljava.lang.Object;
  • getSimpleName()

    获取类名(不包括包名)。

  • getCanonicalName()

    获取全限定的类名(包括包名)。

  • toGenericString()

    返回类的全限定名称,而且包括类的修饰符和类型参数信息。

  • forName()

    根据类名获得一个Class对象的引用,这个方法会使类对象进行初始化。

    例如:Class t = Class.forName("java.lang.Thread")就能够初始化一个Thread线程对象。

    在Java中,一共有三种获取类实例的方式:

    • Class.forName(java.lang.Thread)
    • Thread.class
    • thread.getClass()
  • newInstance()
    创建一个类的实例,代表着这个类的对象。上面forName()方法对类进行初始化,newInstance方法对类进行实例化。使用该方法创建的类,必须带有无参的构造器。

  • getClassLoader()

    获取类加载器对象。

  • getInterfaces()

    获取当前类实现的类或是接口,可能是多个,所以返回的是Class数组。

  • isInterface()

    判断Class对象是否是表示一个接口。

  • getFields()

    获得某个类的所有的公共(public)的字段,包括继承自父类的所有公共字段。 类似的还有getMethodsgetConstructors

  • getDeclaredFields

    获得某个类的自己声明的字段,即包括public、private和proteced,默认但是不包括父类声明的任何字段。类似的还有getDeclaredMethodsgetDeclaredConstructors

getName、getCanonicalName与getSimpleName的区别:

  • getSimpleName:只获取类名.
  • getName:类的全限定名,jvm中Class的表示,可以用于动态加载Class对象,例如Class.forName。
  • getCanonicalName:返回更容易理解的表示,主要用于输出(toString)或log打印,大多数情况下和getName一样,但是在内部类、数组等类型的表示形式就不同了。

栗子:

package com.cry;
public class Test 
    private  class inner
    
    public static void main(String[] args) throws ClassNotFoundException 
        //普通类
        System.out.println(Test.class.getSimpleName()); //Test
        System.out.println(Test.class.getName()); //com.cry.Test
        System.out.println(Test.class.getCanonicalName()); //com.cry.Test
        //内部类
        System.out.println(inner.class.getSimpleName()); //inner
        System.out.println(inner.class.getName()); //com.cry.Test$inner
        System.out.println(inner.class.getCanonicalName()); //com.cry.Test.inner
        //数组
        System.out.println(args.getClass().getSimpleName()); //String[]
        System.out.println(args.getClass().getName()); //[Ljava.lang.String;
        System.out.println(args.getClass().getCanonicalName()); //java.lang.String[]
        //我们不能用getCanonicalName去加载类对象,必须用getName
        //Class.forName(inner.class.getCanonicalName()); 报错
        Class.forName(inner.class.getName());
    

2.2Constructor类及其用法

Constructor类存在于反射包(java.lang.reflect)中,反映的是Class 对象所表示的类的构造方法。

获取Constructor对象是通过Class类中的方法获取的,Class类与Constructor相关的主要方法如下:

方法返回值方法名称方法说明
ConstructorgetConstructor(Class<?>… parameterTypes)返回指定参数类型、具有public访问权限的构造函数对象
Constructor<?>[]getConstructors()返回所有具有public访问权限的构造函数的Constructor对象数组
ConstructorgetDeclaredConstructor(Class<?>… parameterTypes)返回指定参数类型、所有声明的(包括private)构造函数对象
Constructor<?>[]getDeclaredConstructors()返回所有声明的(包括private)构造函数对象
TnewInstance()调用无参构造器创建此 Class 对象所表示的类的一个新实例。

栗子:

public class ConstructionTest implements Serializable 
    public static void main(String[] args) throws Exception 
        Class<?> clazz = null;

        //获取Class对象的引用
        clazz = Class.forName("com.example.javabase.User");

        //第一种方法,实例化默认构造方法,User必须无参构造函数,否则将抛异常
        User user = (User) clazz.newInstance();
        user.setAge(20);
        user.setName("Jack");
        System.out.println(user);

        System.out.println("--------------------------------------------");

        //获取带String参数的public构造函数
        Constructor cs1 =clazz.getConstructor(String.class);
        //创建User
        User user1= (User) cs1.newInstance("hiway");
        user1.setAge(22);
        System.out.println("user1:"+user1.toString());

        System.out.println("--------------------------------------------");

        //取得指定带int和String参数构造函数,该方法是私有构造private
        Constructor cs2=clazz.getDeclaredConstructor(int.class,String.class);
        //由于是private必须设置可访问
        cs2.setAccessible(true);
        //创建user对象
        User user2= (User) cs2.newInstance(25,"hiway2");
        System.out.println("user2:"+user2.toString());

        System.out.println("--------------------------------------------");

        //获取所有构造包含private
        Constructor<?> cons[] = clazz.getDeclaredConstructors();
        // 查看每个构造方法需要的参数
        for (int i = 0; i < cons.length; i++) 
            //获取构造函数参数类型
            Class<?> clazzs[] = cons[i].getParameterTypes();
            System.out.println("构造函数["+i+"]:"+cons[i].toString() );
            System.out.print("参数类型["+i+"]:(");
            for (int j = 0; j < clazzs.length; j++) 
                if (j == clazzs.length - 1)
                    System.out.print(clazzs[j].getName());
                else
                    System.out.print(clazzs[j].getName() + ",");
            
            System.out.println(")");
        
    



class User 
    private int age;
    private String name;
    public User() 
        super();
    
    public User(String name) 
        super();
        this.name = name;
    

    /**
     * 私有构造
     * @param age
     * @param name
     */
    private User(int age, String name) 
        super();
        this.age = age;
        this.name = name;
    

    public int getAge() 
        return age;
    

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

    public String getName() 
        return name;
    

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

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

输出结果:


Userage=20, name='Jack'
--------------------------------------------
user1:Userage=22, name='hiway'
--------------------------------------------
user2:Userage=25, name='hiway2'
--------------------------------------------
构造函数[0]:private com.example.javabase.User(int,java.lang.String)
参数类型[0]:(int,java.lang.String)
构造函数[1]:public com.example.javabase.User(java.lang.String)
参数类型[1]:(java.lang.String)
构造函数[2]:public com.example.javabase.User()
参数类型[2]:()

关于Constructor类本身一些常用方法如下(仅部分,其他可查API):

方法返回值方法名称方法说明
ClassgetDeclaringClass()返回 Class 对象,该对象表示声明由此 Constructor 对象表示的构造方法的类,其实就是返回真实类型(不包含参数)
Type[]getGenericParameterTypes()按照声明顺序返回一组 Type 对象,返回的就是 Constructor对象构造函数的形参类型。
StringgetName()以字符串形式返回此构造方法的名称。
Class<?>[]getParameterTypes()按照声明顺序返回一组 Class 对象,即返回Constructor 对象所表示构造方法的形参类型
TnewInstance(Object… initargs)使用此 Constructor对象表示的构造函数来创建新实例
StringtoGenericString()返回描述此 Constructor 的字符串,其中包括类型参数。

栗子:

Constructor cs3 = clazz.getDeclaredConstructor(int.class,String.class);
System.out.println("-----getDeclaringClass-----");
Class uclazz=cs3.getDeclaringClass();
//Constructor对象表示的构造方法的类
System.out.println("构造方法的类:"+uclazz.getName());

System.out.println("-----getGenericParameterTypes-----");
//对象表示此 Constructor 对象所表示的方法的形参类型
Type[] tps=cs3.getGenericParameterTypes();
for (Type tp:tps) 
    System.out.println("参数名称tp:"+tp);

System.out.println("-----getParameterTypes-----");
//获取构造函数参数类型
Class<?> clazzs[] = cs3.getParameterTypes();
for (Class claz:clazzs) 
    System.out.println("参数名称:"+claz.getName());

System.out.println("-----getName-----");
//以字符串形式返回此构造方法的名称
System.out.println("getName:"+cs3.getName());

System.out.println("-----getoGenericString-----");
//返回描述此 Constructor 的字符串,其中包括类型参数。
System.out.println("getoGenericString():"+cs3.toGenericString());

输出结果:

-----getDeclaringClass-----
构造方法的类:com.example.javabase.User
-----getGenericParameterTypes-----
参数名称tp:int
参数名称tp:class java.lang.String
-----getParameterTypes-----
参数名称:int
参数名称:java.lang.String
-----getName-----
getName:com.example.javabase.User
-----getoGenericString-----
getoGenericString():private com.example.javabase.User(int,java.lang.String)

2.3Field类及其用法

Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。

同样的道理,我们可以通过Class类的提供的方法来获取代表字段信息的Field对象,Class类与Field对象相关方法如下:

方法返回值方法名称方法说明
FieldgetDeclaredField(String name)获取指定name名称的(包含private修饰的)字段,不包括继承的字段
Field[]getDeclaredField()获取Class对象所表示的类或接口的所有(包含private修饰的)字段,不包括继承的字段
FieldgetField(String name)获取指定name名称、具有public修饰的字段,包含继承字段
Field[]getField()获取修饰符为public的字段,包含继承字段

栗子:

public class ReflectField 

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException 
        Class<?> clazz = Class.forName("reflect.Student");
        //获取指定字段名称的Field类,注意字段修饰符必须为public而且存在该字段,
        

java学习注解和反射超详细笔记(代码片段)

...注解1、注解入门2、内置注解3、自定义注解,元注解二、反射机制1、Java反射机制概念1.1静态&动态语言1.2反射机制概念1.3反射机制研究与应用1.4反射机制优缺点1.5实现2、理解Class类并获取Class实例2.1class类介绍2.2获取Class类的实... 查看详情

java反射详解(代码片段)

1.什么是反射 反射是一种间接操作目标对象的机制,在程序程序运行时(动态)获取或者设置对象自身的信息。只要给定类的名字,就可以通过反射获取类的所有信息,接着便能调用它的任何一个方法和属性。   J... 查看详情

java基础之详解java反射机制(代码片段)

一、什么是Java的反射机制???反射(Reflection)是Java的高级特性之一,是框架实现的基础,定义:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个... 查看详情

raytracinginoneweekend超详解光线追踪1-6(代码片段)

...的一年,前来打卡 Preface回顾上一篇,我们讲述了漫反射材质,也就是平时的磨砂表面。它一种将入射光经表面随机散射形成的材质,是一种非常普遍的表面形式。这一篇,我们将来学习镜面反射,或者说是金属材质镜面在... 查看详情

java反射枚举lambda表达式详解(代码片段)

目录反射反射相关的类反射机制的起源Class类中的相关方法常用获得类相关的方法获取对象的3种方法常用获得类中属性相关的方法获得类中构造器相关的方法获得类中方法相关的方法反射的优缺点优点缺点枚举Enum类的常用方法La... 查看详情

java反射之method对象详解(代码片段)

使用Java反射,可以在运行时检查一个方法的信息以及在运行期调用这个方法,通过使用java.lang.reflect.Method类就可以实现上述功能。获取Method对象可以通过Class对象获取Method对象,如下例:ClassaClass=...//获取Class... 查看详情

java反射--获取类的内部结构详解(代码片段)

准备工作:提供丰富的Person类结构,继承父类,实现接口,加上注解1.自定义父类CreaturepublicclassCreature<T>implementsSerializableprivatechargender;publicdoubleweight;privatevoidbreath()System.out.prin 查看详情

javase语法基础---反射(基础知识问答+代码详解)(代码片段)

文章目录JavaSE语法基础---反射(基础知识问答+代码详情)反射是什么,简单介绍一下反射,说一说你对反射的理解?Java反射API有几类?java反射创建对象效率高还是通过new创建对象的效率高?实例化... 查看详情

java的反射机制,看完这篇轻松应对高级框架(超详细总结)(代码片段)

导读:很多优秀的高级框架都是通过反射完成的,反射的重要性,由此可见一斑。反射机制可以使得程序更加灵活,只有学习好反射的基础语法,这样才能自己写出优秀的框架。好了一起打卡学习吧,别忘记了素质三连哦!往期... 查看详情

2018.8.1java中的反射和同步详解(代码片段)

为何要使用同步?java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调... 查看详情

java反射机制详解(代码片段)

对于一般的开发者,很少需要直接使用Java反射机制来完成功能开发,但是反射是很多框架譬如Spring,Mybatis实现的核心,反射虽小,能量却很大。本文主要介绍反射相关的概念以及API的使用,关于反射的应用将在下一篇文章中介... 查看详情

java反射机制详解和应用(代码片段)

1反射机制是什么反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的... 查看详情

java数组超详解(代码片段)

目录一、前言二、数组的定义数组定义的形式:格式1:格式2:三、数组的初始化方式:1.动态初始化动态开辟的示意图:2.静态初始化四、索引访问数组五、数组长度表示六、遍历数组方法一:实例演示&#x... 查看详情

一看你就懂,超详细java中的classloader详解(代码片段)

ClassLoader翻译过来就是类加载器,普通的java开发者其实用到的不多,但对于某些框架开发者来说却非常常见。理解ClassLoader的加载机制,也有利于我们编写出更高效的代码。ClassLoader的具体作用就是将class文件加载到jvm虚拟机中去... 查看详情

数据结构java版二叉树的实现(超多图超详解)(代码片段)

文章目录1.树型结构1.1概念1.2要掌握的知识点1.3树的存储形式1.4树的应用2.二叉树2.1概念2.2二叉树的基本形态2.3两种特殊的二叉树2.3.1满二叉树2.3.2完全二叉树2.4二叉树的性质2.5二叉树的存储2.6二叉树的基本操作2.6.1二叉树的前、... 查看详情

java基础语法详解java的反射(代码片段)

文章目录1.定义2.为什么要有反射2.用途3.反射相关的类3.1总览图3.2Class类相关的方法3.3Filed类相关的方法3.4Method类相关的方法3.5Constructor类相关的方法4.获取Class对象4.1Class类(反射机制的起源)4.2获取Class对象三种方式5.反... 查看详情

java基础语法详解java的反射(代码片段)

文章目录1.定义2.为什么要有反射2.用途3.反射相关的类3.1总览图3.2Class类相关的方法3.3Filed类相关的方法3.4Method类相关的方法3.5Constructor类相关的方法4.获取Class对象4.1Class类(反射机制的起源)4.2获取Class对象三种方式5.反... 查看详情

反射3(代码片段)

packagereflect.corecode;importjava.lang.reflect.Field;importjava.lang.reflect.Method;publicclassTestOnepublicstaticvoidmain(String[]args)/****反射*动态操纵Java代码,被大量应用于Javabean中。**getFields当前类及其超类的公有域*get 查看详情