Java反射(Class)(一)

简介: 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。它功能很强大,它能在运行时做很多操作,例如:判断任意一个对象所属的类、运行时构造任意一个类的对象、调用任意一个对象的方法、动态代理生成等等。

前言

image (1).png
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。它功能很强大,它能在运行时做很多操作,例如:判断任意一个对象所属的类、运行时构造任意一个类的对象、调用任意一个对象的方法、动态代理生成等等。

Core

在核心包中,主要由以下类来实现反射机制,且这些类都位于java.lang.reflect中。

  • Class:java.lang.Class,代表一个类。
  • Field:java.lang.reflect.Field,代表类的成员变量。
  • Method:java.lang.reflect.Method,代表类的方法。
  • Constructor:java.lang.reflect.Constructor,代表类的构造方法。

Class

Class类是反射机制中的核心类,其也是方法最多的一个,它的源码如下:

/**
 * Instances of the class {@code Class} represent classes and
 * interfaces in a running Java application.  An enum is a kind of
 * class and an annotation is a kind of interface.  Every array also
 * belongs to a class that is reflected as a {@code Class} object
 * that is shared by all arrays with the same element type and number
 * of dimensions.  The primitive Java types ({@code boolean},
 * {@code byte}, {@code char}, {@code short},
 * {@code int}, {@code long}, {@code float}, and
 * {@code double}), and the keyword {@code void} are also
 * represented as {@code Class} objects.
 *
 * <p> {@code Class} has no public constructor. Instead {@code Class}
 * objects are constructed automatically by the Java Virtual Machine as classes
 * are loaded and by calls to the {@code defineClass} method in the class
 * loader.
 *
 * <p> The following example uses a {@code Class} object to print the
 * class name of an object:
 *
 * <blockquote><pre>
 *     void printClassName(Object obj) {
 *         System.out.println("The class of " + obj +
 *                            " is " + obj.getClass().getName());
 *     }
 * </pre></blockquote>
 *
 * <p> It is also possible to get the {@code Class} object for a named
 * type (or for void) using a class literal.  See Section 15.8.2 of
 * <cite>The Java&trade; Language Specification</cite>.
 * For example:
 *
 * <blockquote>
 *     {@code System.out.println("The name of class Foo is: "+Foo.class.getName());}
 * </blockquote>
 *
 * @param <T> the type of the class modeled by this {@code Class}
 * object.  For example, the type of {@code String.class} is {@code
 * Class<String>}.  Use {@code Class<?>} if the class being modeled is
 * unknown.
 *
 * @author  unascribed
 * @see     java.lang.ClassLoader#defineClass(byte[], int, int)
 * @since   JDK1.0
 */
public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
    //......
}

它的泛型参数是由此Class对象类的类型。例如:String.class的类型是Class,如果类型未知,则可以使用未知泛型Class<?>

Get Class

无论你生成某个类的多少个对象,首先这些对象都会属于同一个Class对象。 要想使用反射,则必须要获得待处理类或对象所对应的Class对象,而获取的方式有如下几种:

  • Class.forName("com.org.Example"):使用Class类的静态方法。
  • String.class:使用每个类都有的.class属性。
  • XXX.getClass():使用每个对象都有的getClass()方法。
public class GetGlassTest {
    public static void main(String[] args) throws Exception {
        Class<?> class1 = Class.forName("com.org.Example");
        
        Example example = new Example();
        Class<?> class2 = example.getClass();
        
        Class<?> class3 = Example.class;
        
        System.out.println("ClassName:" + class1.getName());
        System.out.println("ClassName:" + class2.getName());
        System.out.println("ClassName:" + class3.getName());
    }
}

Core API

getName

返回此Class对象所表示的实体(类、接口、数组类、基本类型)名称,如果此类对象表示的是非数组类型的引用类型,则返回该类的二进制名称,如果此类对象表示一个基本类型或void,则返回的名字是一个与该基本类型或void所对应的Java语言关键字相同的名称,如果此类对象表示一个数组类,则名字的内部形式为:表示该数组嵌套深度的一个或多个“[”字符加元素类型名。

元素类型名的编码如下:
Element Type Encoding
boolean Z
byte B
char C
class or interface Lclassname;
double D
float F
int I
long J
short S
代码如下:
public static void main(String[] args) throws Exception {
    // java.lang.String
    System.out.println(String.class.getName());
    // byte
    System.out.println(byte.class.getName());
    // [Ljava.lang.Object;
    System.out.println((new Object[3]).getClass().getName());
    // [[[I
    System.out.println((new int[3][4][5]).getClass().getName());
    // class sun.misc.Launcher$AppClassLoader
    System.out.println(URLClassLoader.getSystemClassLoader().getClass());
}

forName

返回与带有给定字符串名的类或接口相关联的Class对象。调用此方法等效于:Class.forName(className, true, currentLoader),className是所需类的完全限定名;true表示该类将被初始化;currentLoader表示当前类的定义类加载器。指定的类加载器用于加载该类或接口。如果参数loader为 null,则该类通过引导类加载器加载。只有initialize参数为true且以前未被初始化时,才初始化该类。

    public static void main(String[] args) throws Exception {

        Class<?> aClass = Class.forName("java.lang.String");

        System.out.println(aClass.getName());
    }
用法详解

Class.forName返回的是一个类,它的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段。

new和Class.forName有什么区别?

newInstance是一个方法,而new是一个关键字,Class下的newInstance的使用有局限,因为它生成对象只能调用无参的构造函数,而使用new关键字生成对象没有这个限制。

Class.forName做了什么工作?

假设一个类以前从来没有被装进内存过,Class.forName会做以下几件事情:

  • 装载,将字节码读入内存,并产生一个与之对应的java.lang.Class类对象。
  • 链接,这一步会验证字节码,为static变量分配内存,并赋默认值,并可选的解析符号引用。
  • 初始化,为类的static变量赋初始值,假如有static int age = 1,这个赋值为1的操作就是这个时候做的。除此之外,还要调用类的static静态代码块
@ToString
    class User {

        private static int age = getAge();

        static {
            System.out.println("static block!!!!");
        }

        private static int getAge() {
            System.out.println("static method!!!!");
            return 0;
        }
    }

public static void main(String[] args) throws Exception {

    final Class<?> aClass = Class.forName("cn.gov.zcy.User");

    // static method!!!!
    // static block!!!!
}

isPrimitive

  • 判定指定的Class对象是否表示一个基本类型。有九种预定义的Class对象,表示八个基本类型和void。这些类对象由Java虚拟机创建,与其表示的基本类型同名,即boolean、byte、char、short、int、long、float和double。
  • 这些对象仅能通过下列声明为public static final的变量访问,也是这几个Class对象才能使此方法返回 true。
// 判断Class是否Java基本类型
public static void main(String[] args) throws Exception {
    System.out.println(boolean.class.isPrimitive()); // true
    System.out.println(byte.class.isPrimitive()); // true
    System.out.println(char.class.isPrimitive()); // true
    System.out.println(short.class.isPrimitive()); // true
    System.out.println(int.class.isPrimitive()); // true
    System.out.println(long.class.isPrimitive()); // true
    System.out.println(float.class.isPrimitive()); // true
    System.out.println(double.class.isPrimitive()); // true
    System.out.println(void.class.isPrimitive()); // true

    System.out.println(Boolean.class.isPrimitive()); // false
    System.out.println(Byte.class.isPrimitive()); // false
    System.out.println(Character.class.isPrimitive()); // false
    System.out.println(Short.class.isPrimitive()); // false
    System.out.println(Integer.class.isPrimitive()); // false
    System.out.println(Long.class.isPrimitive()); // false
    System.out.println(Float.class.isPrimitive()); // false
    System.out.println(Double.class.isPrimitive()); // false
}

getDeclaredFields

返回Field对象的一个数组,这些对象反映此Class对象所表示的类或接口所声明的所有字段。包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段。返回数组中的元素没有排序,也没有任何特定的顺序。如果该类或接口不声明任何字段,或者此Class对象表示一个基本类型、一个数组类或void,则此方法返回一个长度为0的数组。

获取属性值
    public static void main(String[] args) throws Exception {
        User user = new User("晓断", 22);
        Field[] fields = user.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            Object o = field.get(user);
            // 晓断
            // 22
            System.out.println(o);
        }
    }
Java反射获取属性上的注解内容
@ToString
    class User {

        @JSONField(name = "name")
        private String name;

        @JSONField(name = "age")
        private int age;

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

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

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

    public static void main(String[] args) throws Exception {
        User user = new User("晓断", 22);
        Field[] fields = user.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            JSONField jsonField = field.getAnnotation(JSONField.class);
            // name
            // age
            System.out.println(jsonField.name());
        }
    }

getFields

  • 返回一个包含某些Field对象的数组,这些对象反映此Class对象所表示的类或接口的所有可访问公共public字段。返回数组中的元素没有排序,也没有任何特定的顺序。如果类或接口没有可访问的公共字段,或者表示一个数组类、一个基本类型或void,则此方法返回长度为0的数组。如果该Class对象表示一个类,则此方法返回该类及其所有超类的公共字段。如果该Class对象表示一个接口,则此方法返回该接口及其所有超接口的公共字段。
@ToString
class User {
    
        @JSONField(name = "name")
        private String name;

        @JSONField(name = "age")
        private int age;

        public String sex;

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

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

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

public static void main(String[] args) throws Exception {
        User user = new User("晓断", 22);
        Field[] fields = user.getClass().getFields();
        for (Field field : fields) {
            // sex
            System.out.println(field.getName());
        }
}

getDeclaredMethods

  • 返回所有本类声明的方法,它返回Method对象的一个数组,这些对象反映此Class对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。返回数组中的元素没有排序,也没有任何特定的顺序。如果该类或接口不声明任何方法,或者此Class对象表示一个基本类型、一个数组类或void,则此方法返回一个长度为0的数组。类初始化方法 不包含在返回数组中。如果该类声明带有相同参数类型的多个公共成员方法,则它们都包含在返回的数组中。
getDeclaredMethods、getMethods的区别?

getDeclaredMethods获取当前类的所有声明的方法,包括public、protected和private修饰的方法。这些方法一定是在当前类中声明的,从父类中继承的不算,实现接口的方法由于有声明所以包括在内。而getMethods是获取当前类和父类的所有public的方法。这里的父类,指的是继承层次中的所有父类。

@ToString
    class User {

        @JSONField(name = "name")
        private String name;

        @JSONField(name = "age")
        private int age;

        public String sex;

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

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

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

public static void main(String[] args) throws Exception {
    User user = new User("晓断", 22);
    Method[] methods = user.getClass().getDeclaredMethods();
    for (Method method : methods) {
        // toString
        // setName
        // setAge
        System.out.println(method.getName());
    }
}

getMethods

  • 返回一个包含某些Method对象的数组,这些对象反映此Class对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共public成员方法。数组类返回从 Object类继承的所有公共的方法。返回数组中的元素没有排序,也没有任何特定的顺序。如果此 Class 对象表示没有公共成员方法的类或接口,或者表示一个基本类型或void,则此方法返回长度为0的数组。
public static void main(String[] args) throws Exception {
        User user = new User("晓断", 22);
        Method[] methods = user.getClass().getMethods();
        for (Method method : methods) {
            // toString
            // setName
            // setAge
            // wait
            // wait
            // wait
            // equals
            // hashCode
            // getClass
            // notify
            // notifyAll
            System.out.println(method.getName());
        }
}


@ToString
class User {

    @JSONField(name = "name")
    private String name;

    @JSONField(name = "age")
    private int age;

    public String sex;

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

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

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

newInstance

  • 创建此Class对象所表示的类的实例。如同用一个带有一个空参数列表的new关键字实例化该类。如果该类尚未初始化,则初始化这个类。
@ToString
class User {

    private String name;

    private int age;

    public User() {

    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
通过Class对象的newInstance方法调用类的默认构造方法实例化
    public static void main(String[] args) throws Exception {
        Class<?> userClass = Class.forName("cn.gov.zcy.item.User");

        User user = (User) userClass.newInstance();

        // User(name=null, age=0)
        System.out.println(user);
    }
通过Class对象获取Constructor,再通过Constructor对象的newInstance()方法实例化
    public static void main(String[] args) throws Exception {
        Class<?> userClass = Class.forName("cn.gov.zcy.item.User");

        Constructor<?> constructor = userClass.getConstructor(String.class, int.class);

        user = (User) constructor.newInstance("晓断", 22);
        // User(name=晓断, age=22)
        System.out.println(user);
    }

getClassLoader

由于BootStrap ClassLoader是用c++写的,所以在返回该ClassLoader时会返回null。
返回该类的类加载器。有些实现可能使用null来表示引导类加载器。如果该类由引导类加载器加载,则此方法在这类实现中将返回null。如果存在安全管理器,并且调用者的类加载器不是null,也不同于或是请求其类加载器的类的类加载器的祖先,则此方法通过 RuntimePermission("getClassLoader") 权限调用此安全管理器的 checkPermission方法,以确保可以访问该类的类加载器。如果此对象表示一个基本类型或void,则返回null。

// 判断一个类是否自定义类
public static void main(String[] args) throws Exception {
    Class<?> clz = Integer.class;
    // true
    System.out.println(clz.getClassLoader() == null);

    Class<?> userclz = User.class;
    // false
    System.out.println(userclz.getClassLoader() == null);
}

isAssignableFrom

  • 判定此Class对象所表示的类或接口与指定的Class参数所表示的类或接口是否相同,或是否是其超类或超接口。如果是则返回true,否则返回 false。如果该Class表示一个基本类型,且指定的Class参数正是该Class对象,则该方法返回true,否则返回 false。
    public static void main(String[] args) throws Exception {
        // false
        System.out.println(String.class.isAssignableFrom(Object.class));
        // true
        System.out.println(Object.class.isAssignableFrom(String.class));
        // true
        System.out.println(Object.class.isAssignableFrom(Object.class)); 
    }
目录
相关文章
|
15天前
|
安全 Java 索引
Java——反射&枚举
本文介绍了Java反射机制及其应用,包括获取Class对象、构造方法、成员变量和成员方法。反射允许在运行时动态操作类和对象,例如创建对象、调用方法和访问字段。文章详细解释了不同方法的使用方式及其注意事项,并展示了如何通过反射获取类的各种信息。此外,还介绍了枚举类型的特点和使用方法,包括枚举的构造方法及其在反射中的特殊处理。
37 9
Java——反射&枚举
|
11天前
|
Java
java基础(4)public class 和class的区别及注意事项
本文讲解了Java中`public class`与`class`的区别和注意事项。一个Java源文件中只能有一个`public class`,并且`public class`的类名必须与文件名相同。此外,可以有多个非`public`类。每个类都可以包含一个`main`方法,作为程序的入口点。文章还强调了编译Java文件生成`.class`文件的过程,以及如何使用`java`命令运行编译后的类。
15 3
java基础(4)public class 和class的区别及注意事项
|
22天前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
11天前
|
Java
java的class类
java的class类
18 5
|
28天前
|
Java 程序员 编译器
Java的反射技术reflect
Java的反射技术允许程序在运行时动态加载和操作类,基于字节码文件构建中间语言代码,进而生成机器码在JVM上执行,实现了“一次编译,到处运行”。此技术虽需更多运行时间,但广泛应用于Spring框架的持续集成、动态配置及三大特性(IOC、DI、AOP)中,支持企业级应用的迭代升级和灵活配置管理,适用于集群部署与数据同步场景。
|
12天前
|
存储 安全 Java
扫盲java基础-反射(一)
扫盲java基础-反射(一)
|
12天前
|
Java
扫盲java基础-反射(二)
扫盲java基础-反射(二)
|
2月前
|
Java 测试技术 Docker
记录一次很坑的报错:java.lang.Exception: The class is not public.
这篇文章记录了作者在Docker中运行服务进行单元测试时遇到的一系列问题,包括Spring Boot与Spring Cloud版本不一致、Bean注入问题、测试单元引入问题以及公共类和方法的可见性问题,并提供了解决问题的方法和成功测试通过的代码示例。
记录一次很坑的报错:java.lang.Exception: The class is not public.
|
2月前
|
Java
JAVA中public class和class的区别
JAVA中public class和class的区别
34 7
|
3月前
|
安全 Java 测试技术
day26:Java零基础 - 反射
【7月更文挑战第26天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
30 5
下一篇
无影云桌面