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)); 
    }
目录
相关文章
|
1月前
|
Java 编译器 Maven
Java“class file contains wrong class”解决
当Java程序运行时出现“class file contains wrong class”错误,通常是因为类文件与预期的类名不匹配。解决方法包括:1. 确保类名和文件名一致;2. 清理并重新编译项目;3. 检查包声明是否正确。
57 3
|
2月前
|
存储 Java
[Java]反射
本文详细介绍了Java反射机制的基本概念、使用方法及其注意事项。首先解释了反射的定义和类加载过程,接着通过具体示例展示了如何使用反射获取和操作类的构造方法、方法和变量。文章还讨论了反射在类加载、内部类、父类成员访问等方面的特殊行为,并提供了通过反射跳过泛型检查的示例。最后,简要介绍了字面量和符号引用的概念。全文旨在帮助读者深入理解反射机制及其应用场景。
26 0
[Java]反射
|
2月前
|
Java 程序员 编译器
在Java编程中,保留字(如class、int、for等)是具有特定语法意义的预定义词汇,被语言本身占用,不能用作变量名、方法名或类名。
在Java编程中,保留字(如class、int、for等)是具有特定语法意义的预定义词汇,被语言本身占用,不能用作变量名、方法名或类名。本文通过示例详细解析了保留字的定义、作用及与自定义标识符的区别,帮助开发者避免因误用保留字而导致的编译错误,确保代码的正确性和可读性。
55 3
|
3月前
|
安全 Java 索引
Java——反射&枚举
本文介绍了Java反射机制及其应用,包括获取Class对象、构造方法、成员变量和成员方法。反射允许在运行时动态操作类和对象,例如创建对象、调用方法和访问字段。文章详细解释了不同方法的使用方式及其注意事项,并展示了如何通过反射获取类的各种信息。此外,还介绍了枚举类型的特点和使用方法,包括枚举的构造方法及其在反射中的特殊处理。
67 9
Java——反射&枚举
|
2月前
|
安全 Java 测试技术
🌟Java零基础-反射:从入门到精通
【10月更文挑战第4天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
29 2
|
3月前
|
Java
java基础(4)public class 和class的区别及注意事项
本文讲解了Java中`public class`与`class`的区别和注意事项。一个Java源文件中只能有一个`public class`,并且`public class`的类名必须与文件名相同。此外,可以有多个非`public`类。每个类都可以包含一个`main`方法,作为程序的入口点。文章还强调了编译Java文件生成`.class`文件的过程,以及如何使用`java`命令运行编译后的类。
50 3
java基础(4)public class 和class的区别及注意事项
|
2月前
|
Java
让星星⭐月亮告诉你,Java synchronized(*.class) synchronized 方法 synchronized(this)分析
本文通过Java代码示例,介绍了`synchronized`关键字在类和实例方法上的使用。总结了三种情况:1) 类级别的锁,多个实例对象在同一时刻只能有一个获取锁;2) 实例方法级别的锁,多个实例对象可以同时执行;3) 同一实例对象的多个线程,同一时刻只能有一个线程执行同步方法。
20 1
|
2月前
|
小程序 Oracle Java
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
这篇文章是关于JVM基础知识的介绍,包括JVM的跨平台和跨语言特性、Class文件格式的详细解析,以及如何使用javap和jclasslib工具来分析Class文件。
52 0
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
|
3月前
|
安全 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版)
|
3月前
|
Java
java的class类
java的class类
42 5