Java中Class类的详解及用法

1,521 阅读13分钟

Java中Class类及用法

Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识,即所谓的RTTI。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。

说白了就是:

  • Class类也是类的一种,只是名字和class关键字高度相似。Java是大小写敏感的语言。
  • Class类的对象内容是你创建的类的类型信息,比如你创建一个shapes类,那么,Java会生成一个内容是shapes的Class类的对象
  • Class类的对象不能像普通类一样,以 **new shapes() **的方式创建,它的对象只能由JVM创建,因为这个类没有public构造函数
  • Class类的作用是运行时提供或获得某个对象的类型信息,和C++中的typeid()函数类似。这些信息也可用于反射。

一、Class类原理

我们都知道所有的java类都是继承了object这个类,在object这个类中有一个方法:getclass().这个方法是用来取得该类已经被实例化了的对象的该类的引用,这个引用指向的是Class类的对象。我们自己无法生成一个Class对象(构造函数为private),而 这个Class类的对象是在当各类被调入时,由 Java 虚拟机自动创建 Class 对象,或通过类装载器中的 defineClass 方法生成。我们生成的对象都会有个字段记录该对象所属类在CLass类的对象的所在位置。如下图所示:

3whz4vdiad.jpeg

二、获得一个Class类对象

第一种办法,Class类的forName函数

public class shapes{}  
Class obj= Class.forName("shapes");

第二种办法,使用对象的getClass() 函数

public class shapes{}
shapes s1=new shapes();
Class obj=s1.getClass();
Class obj1=s1.getSuperclass();//这个函数作用是获取shapes类的父类的类型

第三种办法,使用类字面常量

Class obj=String.class;
Class obj1=int.class;

注意,使用这种办法生成Class类对象时,不会使JVM自动加载该类(如String类)。而其他办法会使得JVM初始化该类。

三、使用Class类的对象来生成目标类的实例

1. 生成不精确的object实例

获取一个Class类的对象后,可以用 newInstance() 函数来生成目标类的一个实例。然而,该函数并不能直接生成目标类的实例,只能生成object类的实例

Class obj=Class.forName("shapes");
Object ShapesInstance=obj.newInstance();

2. 使用泛化Class引用生成带类型的目标实例

Class<shapes> obj=shapes.class;
shapes newShape=obj.newInstance();

因为有了类型限制,所以使用泛化Class语法的对象引用不能指向别的类。

Class obj1=int.class;
Class<Integer> obj2=int.class;
obj1=double.class;
//obj2=double.class; 这一行代码是非法的,obj2不能改指向别的类了

然而,有个灵活的用法,使得你可以用Class的对象指向基类的任何子类。

Class<? extends Number> obj=int.class;
obj=Number.class;
obj=double.class;

因此,以下语法生成的Class对象可以指向任何类。

Class<?> obj=int.class;
obj=double.class;
obj=shapes.class;

最后一个奇怪的用法是,当你使用这种泛型语法来构建你手头有的一个Class类的对象的基类对象时,必须采用以下的特殊语法

public class shapes{}
class round extends shapes{}
Class<round> rclass=round.class;
Class<? super round> sclass= rclass.getSuperClass();
//Class<shapes> sclass=rclass.getSuperClass();

我们明知道,round的基类就是shapes,但是却不能直接声明 Class < shapes > ,必须使用特殊语法 Class < ? super round >,这个记住就可以啦。

四、Class类中常用方法详解

1、获取一个类对应的Class类的方法

(1). 使用Object.getClass ()方法----引用类型的对象的获取方式

如果我们已经拿到了一个对象,可以使用这个对象的 getClass 方法获得一个 Class 对象(不过这仅限于引用类型的对象):

String string=new String();
//使用已经存在的对象的getClass()方法获取Class对象
Class  class1=string.getClass();
(2). 使用类的class成员属性

如果我们当前没有某个类的对象,无法使用 getClass() 方法来获取Class对象,那还可以使用 类名.class 来获取 Class对象:

//使用 类名.class来获取Class对象
Class class2=String.class;

其实这种方式不仅能用于引用类型,基本类型也可以。数组也是可以的:

Class class3=int.class;
Class class4=int[][].class
(3). 使用Class类的forName("类完整路径")方法获取

如果我们有一个类的完整路径,就可以使用 Class.forName(“类完整的路径”) 来得到相应的 Class,这个方法只能用于引用类型,所谓类的完整路径是:包名.类名 例如:java.lang.String。

Class<?> strClass=Class.forName("java.lang.String");
(4). 使用包装类的TYPE属性来获取包装类对应的Class类
public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");

2. 返回Class类对应的实体类的相关的Class类的方法:

如果我们已经有了一个类的 Class类,可以使用下面的一些方法来获得它相关的类:

(1). 返回当前Class类对应的实体类的父类的Class类
public Class<? super T> getSuperclass()

例如:

public static void main(String[] args) throws ClassNotFoundException{
    Class<String> strClass = (Class<String>) Class.forName("java.lang.String");
    // 返回调用类的父类
    Class<?> superClass = strClass.getSuperclass();
    System.out.println("当前实体类的Class类    :"+strClass);
    System.out.println("实体类的父类的Class类:"+superClass);
}

结果:

当前实体类的Class类    :class java.lang.String
实体类的父类的Class类:class java.lang.Object
(2). 返回类定义的公共的内部类,以及从父类、父接口那里继承来的内部类
public Class<?>[] getClasses()
(3). 返回类中定义的公共、私有、保护的内部类
public Class<?>[] getDeclaredClasses()

实例:

Person类:

public class Person{
	public class PA{};
	private class PB{};
	public interface PIA{};
	private interface PIB{};
}

Student类:

public class Student extends Person{
	//公共的内部类
	public class A{}
	//保护内部类
	protected class B{}
	//默认内部类
	class C{}
	//私有内部类:
	private class D{}
	//共有静态内部类
	public static class E{}
	//共有内部接口
	public interface IA{}
	//保护内部接口
	protected interface IB{}
	//默认内部接口
	interface IC{}
	//私有内部接口
	private interface ID{}
}

main方法:

public static void main(String[] args) throws ClassNotFoundException{
	Class<String> stuClass = (Class<String>) Class.forName("reflect.Student");
	//获取调用类的所有公共的内部类和接口,包括继承的共有的内部类和接口的Class
	System.out.println(stuClass.getName()+"的公有内部类和接口(包括继承):");
	Class[] strClasses = stuClass.getClasses();
	for (Class class1 : strClasses){
		System.out.println(class1.getName());
	}
	System.out.println("-------------------------");
	System.out.println(stuClass.getName()+"的所有内部类和接口(包括继承):");
	Class[] stuAllClasses=stuClass.getDeclaredClasses();
	for (Class class1 : stuAllClasses){
		System.out.println(class1.getName());
	}
}

运行结果:

reflect.Student的公有内部类和接口(包括继承):
reflect.Student$A
reflect.Student$E
reflect.Student$IA
reflect.Person$PA
reflect.Person$PIA
-------------------------
reflect.Student的所有内部类和接口(包括继承):
reflect.Student$A
reflect.Student$B
reflect.Student$C
reflect.Student$D
reflect.Student$E
reflect.Student$IA
reflect.Student$IB
reflect.Student$IC
reflect.Student$ID
(4). 返回一个成员内部类/属性/方法/构造器所在的类的Class,这些方法是上面那两个方法的逆操作
java.lang.reflect.Class.getDeclaringClass()    ;//返回一个成员内部类所在的类的Class
java.lang.reflect.Field.getDeclaringClass()      ;//返回一个字段所在的类的Class
java.lang.reflect.Method.getDeclaringClass()    ;//返回一个方法所在的类的Class
java.lang.reflect.Constructor.getDeclaringClass() ;//返回一个构造器所在的类的Class

Class.getDeclaringClass()实例:--获取该成员内部类所在声明类的Class,所谓声明类,就是声明该内部类的外部类

测试类:

public class A {
    String name;
    class C{}
}

在这里,内部类C的声明类就是A,在类A里面声明了内部类C和成员属性name.对于成员属性name和成员内部类C而言,它们的声明类都是A.这样说可以理解了吧。

main方法:

public static void main(String[] args) throws ClassNotFoundException{
	Class stuClass =  Class.forName("reflect.A$C");//获取一个成员内部类的Class对象
	//获取成员内部类的声明类,也就是他的外部类
	Class stuclass2=stuClass.getDeclaringClass();
	System.out.println(stuClass);
	System.out.println(stuclass2);
}

运行结果:

class reflect.A$C
class reflect.A

获取声明该字段,方法,构造器的类的Class后面再补上。

(5). 获取Class对应类(或者接口)的修饰符:Class.getModifiers()
//返回此类或接口以整数编码的 Java 语言修饰符
public int getModifiers();

修饰符由 Java 虚拟机的 public、protected、private、final、static、abstract 和 interface 对应的常量组成; 它们应当使用 Modifier 类的方法来解码。

一个类(或接口)可以被以下修饰符的一种或者多种修饰:

  • 访问权限控制符:public, protected, private
  • 抽象的、需要实现的:abstract
  • 限制只能有一个实例的:static
  • 不允许修改的:final
  • 线程同步锁:synchronized
  • 原生函数:native
  • 采用严格的浮点精度:strictfp
  • 接口
  • 注解

当然上面的修饰符不是所有类(或接口)都可以修饰,比如:

  • Interface 不能是 final 的
  • enum 不能是 abstract 的

java.lang.reflect.Modifier 可以用来解码 Class 修饰符。我们可以使用 Class.getModifiers() 获得调用类的修饰符的二进制值,然后使用Modifier.toString(int modifiers)将获取到的二进制值转换为字符串。

实例:

测试类:

public class A{
	String name;
	public class B{};
	protected class C{};
	private class D{};
	public static class E{};
	abstract class F{};
	interface G{};
}

测试类:

public class ModifierTest {
	public static void printModifier(String ClassPath) throws ClassNotFoundException{
		Class aClass=Class.forName(ClassPath);
		int modifier=aClass.getModifiers();
		System.out.println(ClassPath+"的修饰符为:"+Modifier.toString(modifier));
	}
	public static void main(String[] args) throws ClassNotFoundException{
		printModifier("reflect.A");
		printModifier("reflect.A$B");
		printModifier("reflect.A$C");
		printModifier("reflect.A$D");
		printModifier("reflect.A$E");
		printModifier("reflect.A$F");
		printModifier("reflect.A$G");
	}
}

运行结果:

reflect.A的修饰符为:public
reflect.A$B的修饰符为:public
reflect.A$C的修饰符为:protected
reflect.A$D的修饰符为:private
reflect.A$E的修饰符为:public static
reflect.A$F的修饰符为:abstract
reflect.A$G的修饰符为:abstract static interface

下面给上Modifier.toString(int modifiers)JDK的源码:

public static String toString(int mod) {
        StringBuilder sb = new StringBuilder();
        int len;
        if ((mod & PUBLIC) != 0)        sb.append("public ");
        if ((mod & PROTECTED) != 0)     sb.append("protected ");
        if ((mod & PRIVATE) != 0)       sb.append("private ");
        
        /* Canonical order */
        if ((mod & ABSTRACT) != 0)      sb.append("abstract ");
        if ((mod & STATIC) != 0)        sb.append("static ");
        if ((mod & FINAL) != 0)         sb.append("final ");
        if ((mod & TRANSIENT) != 0)     sb.append("transient ");
        if ((mod & VOLATILE) != 0)      sb.append("volatile ");
        if ((mod & SYNCHRONIZED) != 0)  sb.append("synchronized ");
        if ((mod & NATIVE) != 0)        sb.append("native ");
        if ((mod & STRICT) != 0)        sb.append("strictfp ");
        if ((mod & INTERFACE) != 0)     sb.append("interface ");

        if ((len = sb.length()) > 0)    /* trim trailing space */
            return sb.toString().substring(0, len-1);
        return "";
}

总结:

Class.getModifiers():获取当前Class类的修饰符编码(int 类型),

Modifier.toString(int modifiers):把上面获取到的修饰符编码,转换成字符串形式。

注意:

  • Interface 默认是 abstract 的,虽然我们没有添加,编译器会为每个 Interface 添加这个修饰符。
  • 只有被 @Retention(RetentionPolicy.RUNTIME) 修饰的注解才可以在运行时被发射获取
  • Java 中预定义的注解 @Deprecated,@Override, 和 @SuppressWarnings 中只有 @Deprecated 可以在运行时被访问到
(6). 获取Class对应的类或者接口的成员Member(成员有:属性,方法,构造方法)

java.lang.reflect.Member是一个接口,代表 Class 的成员,每个成员都有类型,分为是否从父类继承,还有是否可以直接访问。

Member 有三个实现类:

  • java.lang.reflect.Constructor:表示该 Class 的构造函数
  • java.lang.reflect.Field:表示该 Class 的成员变量
  • java.lang.reflect.Method:表示该 Class 的成员方法

获取构造函数

java.lang.Class 提供了以下方法用于获取该类的构造函数:

a. 返回构造器数组:

//返回所有的构造方法的Constructor对象的数组
Constructor<?>[] getDeclaredConstructors();
//返回所有共有的构造方法的Constructor对象的数组
Constructor<?>[] getConstructors();

b. 返回指定的一个构造器:

//返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法
Constructor<T> getConstructor(Class<?>... parameterTypes)
//返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

获取成员变量

java.lang.Class 提供了以下方法用于获取该类的成员变量:

a. 返回字段对象数组:

//返回此 Class 对象所表示的类或接口的所有公有字段数组(Field 对象数组)
Field[] getFields()
//返回此 Class 对象所表示的类或接口中所有声明的字段数组(Field对象数组)
Field[] getDeclaredFields() 

b. 返回单个字段对象:

//返回此 Class 对象所表示的类或接口的指定的公有成员字段对象(Field对象)
Field getField(String name)
//返回一个此 Class 对象所表示的类或接口的指定已声明字段(Field 对象)
Field getDeclaredField(String name)

获取成员方法

java.lang.Class 提供了以下方法用于获取该类的成员方法:

a.获取方法对象数组:第一个只能获取public修饰的方法数组,第二个可以获取除继承外的所有方法

//返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口
//(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法 
Method[] getMethods() 
//返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,
//包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。     
Method[] getDeclaredMethods() 

b.获取单个方法:第一个只能获取到public修饰的方法,第二个能获取除继承外的方法所有方法(公有,保护,默认,私有)

//返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定的public成员方法。 
Method getMethod(String name, Class<?>... parameterTypes)
//返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
(7). 创建对象的方法:
//创建此 Class 对象所表示的类的一个新实例
T newInstance()
(8). 返回字符串(String)的方法:
//返回 Java Language Specification 中所定义的底层类的规范化名称。 
String getCanonicalName()
//以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称(全限定名:包名.类名)
String getName()
//返回源代码中给出的底层类的简称。 
String getSimpleName() 
//将对象转换为字符串
String toString()

例如:

public static void main(String[] args) throws ClassNotFoundException{
	Class<String> strClass = (Class<String>) Class.forName("java.lang.String");
	System.out.println("当前Class表示的实体的规范化名字:"+strClass.getCanonicalName());
	System.out.println("当前Class表示的实体的完整名字:"+strClass.getName());
	System.out.println("当前Class表示的实体的简称:"+strClass.getSimpleName());
	System.out.println("当前Class表示的实体的toString():"+strClass.toString());
}

结果:

当前Class表示的实体的规范化名字:java.lang.String
当前Class表示的实体的完整名字:java.lang.String
当前Class表示的实体的简称:String
当前Class表示的实体的toString():class java.lang.String
(9). 返回boolean的方法:

a. 判断当前类是什么类

//判断是不是局部类,也就是方法里面的类 
boolean isLocalClass();
//判断是不是成员内部类,也就是一个类里面定义的类
boolean isMemberClass();
//判断当前类是不是匿名类,匿名类一般用于实例化接口
boolean isAnonymousClass();

实例:

public class ClassTest{  
    public static void main(String[] args) {  
        ClassTest son = new ClassTest();  
        //测试匿名类  
        son.testAnonymous(new AnonymousClass() {  
            @Override  
            public void test() {  
                System.out.println("AnonymousClass是成员类:-> " + this.getClass().isMemberClass());  
                System.out.println("AnonymousClass是匿名类: -> " + this.getClass().isAnonymousClass());  
                System.out.println("AnonymousClass是局部类:-> " + this.getClass().isLocalClass());  
                System.out.println("---------------------------------------");  
            }  
        });  
        //测试成员类  
        son.testMember();  
        //测试静态成员类  
        new StaticMemberClass();  
        //测试局部类  
        class LocalClass{  
            public LocalClass(){  
                System.out.println("LocalClass是成员类:-> " + this.getClass().isMemberClass());  
                System.out.println("LocalClass是匿名类: -> " + this.getClass().isAnonymousClass());  
                System.out.println("LocalClass是局部类:-> " + this.getClass().isLocalClass());  
                System.out.println("---------------------------------------");  
            }  
        }  
        new LocalClass();  
    }  
    //测试匿名内部类
    private void testAnonymous(AnonymousClass inner) {  
        inner.test();  
    }
    //测试成员内部类:
    private void testMember() {  
        new MemberClass();  
    } 
    /** 
     * 接口,用于测试匿名类
     */
    interface AnonymousClass{  
        public void test();  
    }
    /** 
     * 静态成员类 
     */  
    static class StaticMemberClass{  
        public StaticMemberClass() {  
            System.out.println("StaticMemberClass是成员类:-> " + this.getClass().isMemberClass());  
            System.out.println("StaticMemberClass是匿名类: -> " + this.getClass().isAnonymousClass());  
            System.out.println("StaticMemberClass是局部类:-> " + this.getClass().isLocalClass()); 
            System.out.println("---------------------------------------");  
        }  
    }  
    /** 
     * 成员类 
     */  
    class MemberClass{  
        public MemberClass() {  
            System.out.println("MemberClass是成员类:-> " + this.getClass().isMemberClass());  
            System.out.println("MemberClass是匿名类: -> " + this.getClass().isAnonymousClass());  
            System.out.println("MemberClass是局部类:-> " + this.getClass().isLocalClass());  
            System.out.println("---------------------------------------");  
        }  
    }  
}

运行结果:

AnonymousClass是成员类:-> false
AnonymousClass是匿名类: -> true
AnonymousClass是局部类:-> false
---------------------------------------
MemberClass是成员类:-> true
MemberClass是匿名类: -> false
MemberClass是局部类:-> false
---------------------------------------
StaticMemberClass是成员类:-> true
StaticMemberClass是匿名类: -> false
StaticMemberClass是局部类:-> false
---------------------------------------
LocalClass是成员类:-> false
LocalClass是匿名类: -> false
LocalClass是局部类:-> true
---------------------------------------

b. 其他返回boolean的方法:

//判断当前Class对象是否是注释类型
boolean isAnnotation()
//如果指定类型的注释存在于此元素上,则返回 true,否则返回 false
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 
//判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口
boolean isAssignableFrom(Class<?> cls) 
//当且仅当该类声明为源代码中的枚举时返回 true
boolean isEnum() 
//判定指定的 Object 是否与此 Class 所表示的对象赋值兼容
boolean isInstance(Object obj) 
//判定指定的 Class 对象是否表示一个接口类型
boolean isInterface() 
//判定指定的 Class 对象是否表示一个基本类型
boolean isPrimitive() 
//如果此类是复合类,则返回 true,否则 false
boolean isSynthetic()