Java Class对象与反射机制
发布日期:2021-07-19 12:30:10 浏览次数:10 分类:技术文章

本文共 21586 字,大约阅读时间需要 71 分钟。

Java Class对象

RTTI

RTTI(Run-Time Type Identification)运行时类型识别,其作用是在运行时识别一个对象的类型和类的信息。这里分两种:

  • 传统的”RRTI” — 它假定我们在编译期已知道了所有类型(在没有反射机制创建和使用类对象时,一般都是编译期已确定其类型,如new对象时该类必须已定义好)
  • 反射机制 — 它允许我们在运行时发现和使用类型的信息

在Java中用来表示运行时类型信息的对应类就是Class类,Class类也是一个实实在在的类,存在于JDK的java.lang包中,其部分源码如下:

 

public final class Class
implements java.io.Serializable,GenericDeclaration,Type, AnnotatedElement { private static final int ANNOTATION= 0x00002000; private static final int ENUM = 0x00004000; private static final int SYNTHETIC = 0x00001000; private static native void registerNatives(); static { registerNatives(); } /* * Private constructor. Only the Java Virtual Machine creates Class objects.(私有构造,只能由JVM创建该类) * This constructor is not used and prevents the default constructor being * generated. */ private Class(ClassLoader loader) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader; }

Class类被创建后的对象就是Class对象,注意,Class对象表示的是自己手动编写类的类型信息,比如创建一个Shapes类,那么,JVM就会创建一个Shapes对应Class类的Class对象,该Class对象保存了Shapes类相关的类型信息。实际上在Java中每个类都有一个Class对象,每当我们编写并且编译一个新创建的类就会产生一个对应Class对象并且这个Class对象会被保存在同名.class文件里(编译后的字节码文件保存的就是Class对象),那为什么需要这样一个Class对象呢?是这样的,当我们new一个新对象或者引用静态成员变量时,JVM中的类加载器子系统会将对应Class对象加载到JVM中,然后JVM再根据这个类型信息相关的Class对象创建我们需要实例对象或者提供静态变量的引用值。需要特别注意的是,手动编写的每个class类,无论创建多少个实例对象,在JVM中都只有一个Class对象,即在内存中每个类有且只有一个相对应的Class对象: 

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

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

Class对象的获取及加载

(1)获取:

获取Class对象引用的3种方式:

  • 继承自Object类的getClass方法
  • Class类的静态方法forName
  • 字面常量的方式
/**通过Class.forName获取Gum类的Class对象*/ try{      Class clazz1=Class.forName("com.hx.Gum");      System.out.println("clazz1:"+clazz1.getName());    } catch (ClassNotFoundException e) {      e.printStackTrace();    }    //通过实例对象获取Gum的Class对象    Gum gum = new Gum();    Class clazz2=gum.getClass();    System.out.println("clazz2:"+clazz2.getName());    //通过字面常量Gum的Class对象    Class clazz3 = Gum.class;    System.out.println("clazz3:"+clazz3.getName());

这里着重看下字面常量的方式,这种方式相对前面两种方法更加简单、安全。因为它在编译时就会受到编译器的检查同时由于无需调用forName方法效率也会更高,因为通过字面量的方法获取Class对象的引用不会自动初始化该类。更加有趣的是字面常量获取Class对象引用的方式不仅可以应用于普通的类,也可以应用于接口,数组以及基本数据类型,这点在反射技术应用传递参数时很有帮助,关于反射技术稍后会分析,由于基本数据类型有对应的基本包装类型,其包装类型有一个标准字段TYPE,而这个TYPE就是一个引用,指向基本数据类型的Class对象,其等价转换如下,一般情况下更倾向使用.class的形式,这样可以保持与普通类的形式统一。

 

boolean.class = Boolean.TYPE;char.class = Character.TYPE;byte.class = Byte.TYPE;short.class = Short.TYPE;int.class = Integer.TYPE;long.class = Long.TYPE;float.class = Float.TYPE;double.class = Double.TYPE;void.class = Void.TYPE;

在Java SE5引入泛型后,我们可以利用泛型来表示Class对象更具体的类型,即使在运行期间会被擦除,但编译期足以确保我们使用正确的对象类型。如下:

//没有泛型Class intClass = int.class;/**带泛型的Class对象*/Class
integerClass = int.class;integerClass = Integer.class;//编译期错误,无法编译通过//integerClass = double.class//编译无法通过,因为Integer的Class对象并非Number的Class对象的子类//Class
numberClass = Integer.class;//编译通过Class
clazz = Integer.class;

(2)加载:

前面我们已提到过,Class对象是由JVM加载的,都是在类被第一次使用时动态加载到JVM中的,当程序创建第一个对类的静态成员引用或new对象时,就会加载这个被使用类的Class对象(实际上加载的就是这个类的字节码文件)。

在《深入理解Java虚拟机》第7章 一文中我们曾讲过类加载的过程: 

初始化是类加载的最后一个阶段,也就是说完成这个阶段后类也就加载到内存中(Class对象在加载阶段已被创建),此时可以对类进行各种必要的操作了(如new对象,调用静态成员等),注意在这个阶段,才真正开始执行类中定义的Java程序代码或者字节码。

虚拟机规范严格规定了有且只有5种情况必须立即对类进行“初始化”,详见上面那篇文章。

上面三种获取Class对象的方法哪些会触发初始化呢?

  • 继承自Object类的getClass方法(会触发初始化)
  • Class类的静态方法forName(会触发初始化)
  • 字面常量的方式(不会触发初始化)

Java 反射机制

反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。一直以来反射技术都是Java中的闪亮点,在Java中,Class类与java.lang.reflect类库一起对反射技术进行了全力的支持。在反射包中,我们常用的类主要有Constructor类表示的是Class 对象所表示的类的构造方法,利用它可以在运行时动态创建对象、Field表示Class对象所表示的类的成员变量,通过它可以在运行时动态修改成员变量的属性值(包含private)、Method表示Class对象所表示的类的成员方法,通过它可以动态调用对象的方法(包含private),下面将对这几个重要类进行分别说明。

Constructor类

Constructor类存在于反射包(java.lang.reflect)中,反映的是Class 对象所表示的类的构造方法。获取Constructor对象是通过Class类中的方法获取的,Class类与Constructor相关的主要方法如下:

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

写一个例子:

private void testReflectConstruct() throws Exception {        Class
clazz = null; /**获取Class对象的引用*/ clazz = Class.forName("com.hx.reflect.User"); /**第一种方法,实例化默认构造方法,User必须无参构造函数,否则将抛异常*/ User user = (User) clazz.newInstance(); user.setAge(29); user.setName("Watson"); System.out.println("user:"+user.toString()); System.out.println("--------------------------------------------"); /**第二种方法,获取带String参数的public构造函数*/ Constructor cs1 = clazz.getConstructor(String.class); /**创建user对象*/ User user1= (User) cs1.newInstance("Jerry"); user1.setAge(28); 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(27, "Tom"); System.out.println("user2:"+user2.toString()); System.out.println("--------------------------------------------"); Constructor cs3 = clazz.getDeclaredConstructor(int.class, String.class); System.out.println("-----toString-----"); //以字符串形式返回此构造方法的名称 System.out.println(cs3.toString()); System.out.println("-----getDeclaringClass-----"); Class uclazz = cs3.getDeclaringClass(); //Constructor对象表示的构造方法的类 System.out.println("构造方法的类:"+uclazz.getName()); System.out.println("-----getName-----"); //以字符串形式返回此构造方法的名称 System.out.println(cs3.getName()); System.out.println("-----getoGenericString-----"); //返回描述此 Constructor 的字符串,其中包括类型参数。 System.out.println(cs3.toGenericString()); 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()); } }
class User {    private int age;    private String name;    public User() {        super();    }    public User(String name) {        super();        this.name = 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;    }    public String toString() {        return "[age="+age+" name="+name+"]";    }}

结果:

09-18 10:20:47.787 10755-10755/com.hx.reflect I/System.out: user:[age=29 name=Watson]09-18 10:20:47.787 10755-10755/com.hx.reflect I/System.out: --------------------------------------------09-18 10:20:47.787 10755-10755/com.hx.reflect I/System.out: user1:[age=28 name=Jerry]09-18 10:20:47.787 10755-10755/com.hx.reflect I/System.out: --------------------------------------------09-18 10:20:47.787 10755-10755/com.hx.reflect I/System.out: user2:[age=27 name=Tom]09-18 10:20:47.787 10755-10755/com.hx.reflect I/System.out: --------------------------------------------09-18 10:20:47.787 10755-10755/com.hx.reflect I/System.out: -----toString-----09-18 10:20:47.787 10755-10755/com.hx.reflect I/System.out: private com.hx.reflect.User(int,java.lang.String)09-18 10:20:47.788 10755-10755/com.hx.reflect I/System.out: -----getDeclaringClass-----09-18 10:20:47.788 10755-10755/com.hx.reflect I/System.out: 构造方法的类:com.hx.reflect.User09-18 10:20:47.788 10755-10755/com.hx.reflect I/System.out: -----getName-----09-18 10:20:47.788 10755-10755/com.hx.reflect I/System.out: com.hx.reflect.User09-18 10:20:47.788 10755-10755/com.hx.reflect I/System.out: -----getoGenericString-----09-18 10:20:47.790 10755-10755/com.hx.reflect I/System.out: private com.hx.reflect.User(int,java.lang.String)09-18 10:20:47.790 10755-10755/com.hx.reflect I/System.out: -----getGenericParameterTypes-----09-18 10:20:47.790 10755-10755/com.hx.reflect I/System.out: 参数名称tp:int09-18 10:20:47.790 10755-10755/com.hx.reflect I/System.out: 参数名称tp:class java.lang.String09-18 10:20:47.790 10755-10755/com.hx.reflect I/System.out: -----getParameterTypes-----09-18 10:20:47.790 10755-10755/com.hx.reflect I/System.out: 参数名称:int09-18 10:20:47.790 10755-10755/com.hx.reflect I/System.out: 参数名称:java.lang.String

Field类

Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。同样的道理,我们可以通过Class类的提供的方法来获取代表字段信息的Field对象,Class类与Field对象相关方法如下:

上述方法需要注意的是,如果我们不期望获取其父类的字段,则需使用Class类的getDeclaredField/getDeclaredFields方法来获取字段即可,倘若需要获取到父类的字段,那么请使用Class类的getField/getFields,但是也只能获取到public修饰的的字段,无法获取父类的私有字段。

Field类常用的方法如下:

上述方法可能是较为常用的,事实上在设置值的方法上,Field类还提供了专门针对基本数据类型的方法,如setInt()/getInt()、setBoolean()/getBoolean、setChar()/getChar()等等方法,这里就不全部列出了,需要时查API文档即可。需要特别注意的是被final关键字修饰的Field字段是安全的,在运行时可以接收任何修改,但最终其实际值是不会发生改变的。

 

private void testReflectField() throws Exception {        Class
clazz = null; /**获取Class对象的引用*/ clazz = Class.forName("com.hx.reflect.Student"); System.out.println("================getFields===================="); /**获取所有修饰符为public的字段,包含父类字段,注意修饰符为public才会获取*/ Field fields[] = clazz.getFields(); for (Field f:fields) { System.out.println("f:"+f.getName()); } /**获取指定字段名称的Field类,注意字段修饰符必须为public而且存在该字段,否则抛NoSuchFieldException*/ Field field = clazz.getField("age"); System.out.println("field:"+field); System.out.println("================getDeclaredFields===================="); //获取当前类所字段(包含private字段),注意不包含父类的字段 Field fields2[] = clazz.getDeclaredFields(); for (Field f:fields2) { System.out.println("f2:"+f.getName()); } //获取指定字段名称的Field类,可以是任意修饰符的自动,注意不包含父类的字段 Field field2 = clazz.getDeclaredField("desc"); System.out.println("field2:"+field2); Student st= (Student) clazz.newInstance(); //获取父类public字段并赋值 Field ageField = clazz.getField("age"); ageField.set(st, 18); Field nameField = clazz.getField("name"); nameField.set(st, "Lily"); //只获取当前类的字段,不获取父类的字段 Field descField = clazz.getDeclaredField("desc"); descField.set(st, "I am student"); Field scoreField = clazz.getDeclaredField("score"); //设置可访问,score是private的 scoreField.setAccessible(true); scoreField.set(st, 88); System.out.println(st.toString()); //获取字段值 System.out.println(scoreField.get(st)); }

结果:

09-18 17:31:49.980 4329-4329/com.hx.reflect I/System.out: ================getFields====================09-18 17:31:49.980 4329-4329/com.hx.reflect I/System.out: f:desc09-18 17:31:49.980 4329-4329/com.hx.reflect I/System.out: f:$change09-18 17:31:49.980 4329-4329/com.hx.reflect I/System.out: f:serialVersionUID09-18 17:31:49.980 4329-4329/com.hx.reflect I/System.out: f:name09-18 17:31:49.980 4329-4329/com.hx.reflect I/System.out: f:age09-18 17:31:49.980 4329-4329/com.hx.reflect I/System.out: f:$change09-18 17:31:49.980 4329-4329/com.hx.reflect I/System.out: f:serialVersionUID09-18 17:31:49.981 4329-4329/com.hx.reflect I/System.out: field:public int com.hx.reflect.User.age09-18 17:31:49.981 4329-4329/com.hx.reflect I/System.out: ================getDeclaredFields====================09-18 17:31:49.981 4329-4329/com.hx.reflect I/System.out: f2:desc09-18 17:31:49.981 4329-4329/com.hx.reflect I/System.out: f2:score09-18 17:31:49.981 4329-4329/com.hx.reflect I/System.out: f2:$change09-18 17:31:49.981 4329-4329/com.hx.reflect I/System.out: f2:serialVersionUID09-18 17:31:49.981 4329-4329/com.hx.reflect I/System.out: field2:public java.lang.String com.hx.reflect.Student.desc09-18 17:31:49.981 4329-4329/com.hx.reflect I/System.out: [age=18 name=Lily desc=I am student score=88]09-18 17:31:49.981 4329-4329/com.hx.reflect I/System.out: 88

Method类

Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息,所反映的方法可能是类方法或实例方法(包括抽象方法)。下面是Class类获取Method对象相关的方法:

Method 类常用的方法如下:

Method类的invoke(Object obj,Object… args)第一个参数代表调用的对象,第二个参数传递的调用方法的参数。这样就完成了类方法的动态调用。

getReturnType方法/getGenericReturnType方法都是获取Method对象表示的方法的返回类型,只不过前者返回的Class类型后者返回的Type(前面已分析过),Type就是一个接口而已,在Java8中新增一个默认的方法实现,返回的就参数类型信息。

public interface Type {    //1.8新增    default String getTypeName() {        return toString();    }}

而getParameterTypes/getGenericParameterTypes也是同样的道理,都是获取Method对象所表示的方法的参数类型,其他方法与前面的Field和Constructor是类似的。

private void testReflectMethod() throws Exception {        Class clazz = Class.forName("com.hx.reflect.Circle");        /**根据参数获取public的Method,包含继承自父类的方法*/        Method method = clazz.getMethod("draw",int.class,String.class);        System.out.println("method:"+method);        /**获取所有public的方法*/        Method[] methods =clazz.getMethods();        for (Method m:methods){            System.out.println("m::"+m);        }        System.out.println("=========================================");        //获取当前类的方法包含private,该方法无法获取继承自父类的method        Method method1 = clazz.getDeclaredMethod("drawCircle");        System.out.println("method1::"+method1);        //获取当前类的所有方法包含private,该方法无法获取继承自父类的method        Method[] methods1=clazz.getDeclaredMethods();        for (Method m:methods1){            System.out.println("m1::"+m);        }        System.out.println("=========================================");        //创建对象        Circle circle = (Circle) clazz.newInstance();        //通过Method对象的invoke(Object obj,Object... args)方法调用        method.invoke(circle, 15, "圆圈");        //修改私有方法的访问标识        method1.setAccessible(true);        method1.invoke(circle);        //对有返回值得方法操作        Method method2 =clazz.getDeclaredMethod("getAllCount");        Integer count = (Integer) method2.invoke(circle);        System.out.println("count:"+count);    }
public class Shape {    public void draw(){        System.out.println("draw");    }    public void draw(int count , String name){        System.out.println("draw "+ name +",count="+count);    }}public class Circle extends Shape {    private void drawCircle(){        System.out.println("drawCircle");    }    public int getAllCount(){        return 100;    }}

结果:

09-19 10:11:39.071 23585-23585/com.hx.reflect I/System.out: method:public void com.hx.reflect.Shape.draw(int,java.lang.String)09-19 10:11:39.072 23585-23585/com.hx.reflect I/System.out: m::public static transient java.lang.Object com.hx.reflect.Circle.access$super(com.hx.reflect.Circle,java.lang.String,java.lang.Object[])09-19 10:11:39.072 23585-23585/com.hx.reflect I/System.out: m::public static transient java.lang.Object com.hx.reflect.Shape.access$super(com.hx.reflect.Shape,java.lang.String,java.lang.Object[])09-19 10:11:39.072 23585-23585/com.hx.reflect I/System.out: m::public void com.hx.reflect.Shape.draw()09-19 10:11:39.072 23585-23585/com.hx.reflect I/System.out: m::public void com.hx.reflect.Shape.draw(int,java.lang.String)09-19 10:11:39.072 23585-23585/com.hx.reflect I/System.out: m::public boolean java.lang.Object.equals(java.lang.Object)09-19 10:11:39.072 23585-23585/com.hx.reflect I/System.out: m::public int com.hx.reflect.Circle.getAllCount()09-19 10:11:39.072 23585-23585/com.hx.reflect I/System.out: m::public final java.lang.Class java.lang.Object.getClass()09-19 10:11:39.072 23585-23585/com.hx.reflect I/System.out: m::public int java.lang.Object.hashCode()09-19 10:11:39.072 23585-23585/com.hx.reflect I/System.out: m::public final native void java.lang.Object.notify()09-19 10:11:39.072 23585-23585/com.hx.reflect I/System.out: m::public final native void java.lang.Object.notifyAll()09-19 10:11:39.072 23585-23585/com.hx.reflect I/System.out: m::public java.lang.String java.lang.Object.toString()09-19 10:11:39.072 23585-23585/com.hx.reflect I/System.out: m::public final native void java.lang.Object.wait() throws java.lang.InterruptedException09-19 10:11:39.073 23585-23585/com.hx.reflect I/System.out: m::public final void java.lang.Object.wait(long) throws java.lang.InterruptedException09-19 10:11:39.073 23585-23585/com.hx.reflect I/System.out: m::public final native void java.lang.Object.wait(long,int) throws java.lang.InterruptedException09-19 10:11:39.073 23585-23585/com.hx.reflect I/System.out: =========================================09-19 10:11:39.073 23585-23585/com.hx.reflect I/System.out: method1::private void com.hx.reflect.Circle.drawCircle()09-19 10:11:39.073 23585-23585/com.hx.reflect I/System.out: m1::public int com.hx.reflect.Circle.getAllCount()09-19 10:11:39.073 23585-23585/com.hx.reflect I/System.out: m1::public static transient java.lang.Object com.hx.reflect.Circle.access$super(com.hx.reflect.Circle,java.lang.String,java.lang.Object[])09-19 10:11:39.073 23585-23585/com.hx.reflect I/System.out: m1::private void com.hx.reflect.Circle.drawCircle()09-19 10:11:39.073 23585-23585/com.hx.reflect I/System.out: =========================================09-19 10:11:39.073 23585-23585/com.hx.reflect I/System.out: draw 圆圈,count=1509-19 10:11:39.073 23585-23585/com.hx.reflect I/System.out: drawCircle09-19 10:11:39.073 23585-23585/com.hx.reflect I/System.out: count:100

Array类

在Java的java.lang.reflect包中存在着一个可以动态操作数组的类,Array,它提供了动态创建和访问 Java 数组的方法。Array 允许在执行 get 或 set 操作进行取值和赋值。在Class类中与数组关联的方法是:

java.lang.reflect.Array中的常用静态方法如下:

private void testReflectArray() throws Exception {        int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };        //获取数组类型的Class 即int.class        Class
clazz = array.getClass().getComponentType(); //创建一个具有指定的组件类型和长度的新数组。 //第一个参数:数组的类型,第二个参数:数组的长度 Object newArr = Array.newInstance(clazz, 15); //获取原数组的长度 int co = Array.getLength(array); //赋值原数组到新数组 System.arraycopy(array, 0, newArr, 0, co); //把数组对象的索引位置为6的元素设置为"666" Array.set(newArr, 6, 666); for (int i:(int[]) newArr) { System.out.println(i+","); } }

结果:

09-19 10:54:48.112 31121-31121/com.hx.reflect I/System.out: 109-19 10:54:48.112 31121-31121/com.hx.reflect I/System.out: 209-19 10:54:48.112 31121-31121/com.hx.reflect I/System.out: 309-19 10:54:48.112 31121-31121/com.hx.reflect I/System.out: 409-19 10:54:48.112 31121-31121/com.hx.reflect I/System.out: 509-19 10:54:48.112 31121-31121/com.hx.reflect I/System.out: 609-19 10:54:48.112 31121-31121/com.hx.reflect I/System.out: 66609-19 10:54:48.112 31121-31121/com.hx.reflect I/System.out: 809-19 10:54:48.112 31121-31121/com.hx.reflect I/System.out: 909-19 10:54:48.112 31121-31121/com.hx.reflect I/System.out: 009-19 10:54:48.112 31121-31121/com.hx.reflect I/System.out: 009-19 10:54:48.112 31121-31121/com.hx.reflect I/System.out: 009-19 10:54:48.112 31121-31121/com.hx.reflect I/System.out: 009-19 10:54:48.112 31121-31121/com.hx.reflect I/System.out: 009-19 10:54:48.112 31121-31121/com.hx.reflect I/System.out: 0

其实除了上的set/get外Array还专门为8种基本数据类型提供特有的方法,如setInt/getInt、setBoolean/getBoolean,其他依次类推,需要使用是可以查看API文档即可。除了上述动态修改数组长度或者动态创建数组或动态获取值或设置值外,可以利用泛型动态创建泛型数组如下:

 

/**  * 接收一个泛型数组,然后创建一个长度与接收的数组长度一样的泛型数组,  * 并把接收的数组的元素复制到新创建的数组中,  * 最后找出新数组中的最小元素,并打印出来  * @param a  * @param 
*/ public
> void min(T[] a) { //通过反射创建相同类型的数组 T[] b = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length); for (int i = 0; i < a.length; i++) { b[i] = a[i]; } T min = null; boolean flag = true; for (int i = 0; i < b.length; i++) { if (flag) { min = b[i]; flag = false; } if (b[i].compareTo(min) < 0) { min = b[i]; } } System.out.println(min); }

毕竟我们无法直接创建泛型数组,有了Array的动态创建数组的方式这个问题也就迎刃而解了。

//无效语句,编译不通T[] a = new T[];

其他类及方法

当通过反射与一个未知类型的对象打交道时,JVM只会简单地检查这个对象,判断该对象属于那种类型,同时也应该知道,在使用反射机制创建对象前,必须确保已加载了这个类的Class对象,当然这点完全不必由我们操作,毕竟只能JVM加载,但必须确保该类的”.class”文件已存在并且JVM能够正确找到。

关于Class类的方法在前面我们只是分析了主要的一些方法,其实Class类的API方法挺多的,建议查看一下API文档,浏览一遍,有个印象也是不错的选择,这里仅列出前面没有介绍过又可能用到的API:

 

/**  *    修饰符、父类、实现的接口、注解相关   *///获取修饰符,返回值可通过Modifier类进行解读public native int getModifiers();//获取父类,如果为Object,父类为nullpublic native Class
getSuperclass();//对于类,为自己声明实现的所有接口,对于接口,为直接扩展的接口,不包括通过父类间接继承来的public native Class
[] getInterfaces();//自己声明的注解public Annotation[] getDeclaredAnnotations();//所有的注解,包括继承得到的public Annotation[] getAnnotations();//获取或检查指定类型的注解,包括继承得到的public A getAnnotation(Class annotationClass);public boolean isAnnotationPresent(Class
annotationClass);/** * 内部类相关 *///获取所有的public的内部类和接口,包括从父类继承得到的public Class
[] getClasses();//获取自己声明的所有的内部类和接口public Class
[] getDeclaredClasses();//如果当前Class为内部类,获取声明该类的最外部的Class对象public Class
getDeclaringClass();//如果当前Class为内部类,获取直接包含该类的类public Class
getEnclosingClass();//如果当前Class为本地类或匿名内部类,返回包含它的方法public Method getEnclosingMethod();/** * Class对象类型判断相关 *///是否是数组public native boolean isArray(); //是否是基本类型public native boolean isPrimitive();//是否是接口public native boolean isInterface();//是否是枚举public boolean isEnum();//是否是注解public boolean isAnnotation();//是否是匿名内部类public boolean isAnonymousClass();//是否是成员类public boolean isMemberClass();//是否是本地类public boolean isLocalClass();

 

 

转载地址:https://blog.csdn.net/jdsjlzx/article/details/98867513 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:ESP32之软件SPI驱动及SPI、HSPI和VSPI的理解
下一篇:Git中tag标签的使用

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年03月29日 09时03分05秒