.class与.getClass()

所谓反射,可以理解为在运行时期获取对象类型信息的操作。传统的编程方法要求程序员在编译阶段决定使用的类型,但是在反射的帮助下,编程人员可以动态获取这些信息,从而编写更加具有可移植性的代码。严格地说,反射并非编程语言的特性,因为在任何一种语言都可以实现反射机制,但是如果编程语言本身支持反射,那么反射的实现就会方便很多。

1,获得类型类

我们知道在Java中一切都是对象,我们一般所使用的对象都直接或间接继承自Object类。Object类中包含一个方法名叫getClass,利用这个方法就可以获得一个实例的类型类。类型类指的是代表一个类型的类,因为一切皆是对象,类型也不例外,在Java使用类型类来表示一个类型。所有的类型类都是Class类的实例。例如,有如下一段代码

A a = new A();  
  
if(a.getClass()==A.class) {  
  
      System.out.println("equal");  
  
} else {  
  
      System.out.println("unequal");  
  
}  
输出equal; 

 

可以看到,对象a是A的一个实例,A是某一个类,在if语句中使用a.getClass()返回的结果正是类A的类型类,在Java中表示一个特定类型的类型类可以用“类型.class”的方式获得,因为a.getClass()获得是A的类型类,也就是A.class,因此上面的代码执行的结果就是打印出“equal”。特别注意的是,类型类是一一对应的,父类的类型类和子类的类型类是不同的,因此,假设A是B的子类,那么如下的代码将得到“unequal”的输出:  

A a = new A();  
  
if(a.getClass()==B.class) {  
  
        System.out.println("equal");  
  
}  else {  
          
          System.out.println("unequal");  
}  
输出unequal; 

因此,如果你知道一个实例,那么你可以通过实例的“getClass()”方法获得该对象的类型类,如果你知道一个类型,那么你可以使用“.class”的方法获得该类型的类型类。

2,获得类型的信息

在获得类型类之后,你就可以调用其中的一些方法获得类型的信息了,主要的方法有:

getName():String:获得该类型的全称名称。

getSuperClass():Class:获得该类型的直接父类,如果该类型没有直接父类,那么返回null。

getInterfaces():Class[]:获得该类型实现的所有接口。

isArray():boolean:判断该类型是否是数组。

isEnum():boolean:判断该类型是否是枚举类型。

isInterface():boolean:判断该类型是否是接口。

isPrimitive():boolean:判断该类型是否是基本类型,即是否是int,boolean,double等等。

isAssignableFrom(Classcls):boolean:判断这个类型是否是类型cls的父(祖先)类或父(祖先)接口。

getComponentType():Class:如果该类型是一个数组,那么返回该数组的组件类型。

此外还可以进行类型转换这类的操作,主要方法有:

asSubclass(Class clazz):Class:将这个类型

-----------------------------------------------------------------------------------

在学习反射时想到了这个问题,.getClass()和.class有没有什么区别?
       当然,最明显的区别就是.getClass()是一个对象实例的方法,只有对象实例才有这个方法,具体的类是没有的。类的Class类实例是通过.class获得的,显然,类没有.getClass()方法。
       从网上还找到一些其他资料:
       1、出现的时期不同:Class.forName()在运行时加载;Class.class和getClass()是在编译时加载.

这里有些个疑问?Class.forName("XXX")这方法是动态加载class,先把类文件加载进来,再使用.newInstance()时创建了一个对象。

 

new ClassName(),就是所谓的静态加载,
Class.forName("ClassName"),就是所谓的动态加载。
区别在于“静态加载”的类在编译的时候就要提供,而动态加载的类在源程序编译时可以缺席,在运行时按需提供。

Class.forName(xxx.xx.xx) 返回的是一个类, .newInstance() 后才创建一个对象 Class.forName(xxx.xx.xx);的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段
      2、 举个例子,Iterator it = s.iterator();得到的it的真正类型是KeyIterator,是Iterator的子类,按常理来说应该可以执行next()方法,但是值 得注意的是,KeyIterator是hashmap的内部类,JAVA给的提示是cannot access a member of class java.util.HashMap$KeyIterator withmodifiers "public"
       从上面的那些例子上也能看出,除内部类外的其他类的应用上.class功能完全等于.getClass()!只是一个是用类直接获得的,一个是用实例获得的。

-------------------------------------------------------------------------------------------------------------------------------------------

Class对象的生成方式如下:

 

1.类名.class           说明: JVM将使用类装载器, 将类装入内存(前提是:类还没有装入内存),不做类的初始化工作.返回Class的对象

 

2.Class.forName("类名字符串")  (注:类名字符串是包名+类名)  说明:装入类,并做类的静态初始化,返回Class的对象

 

3.实例对象.getClass()  说明:对类进行静态初始化、非静态初始化;返回引用运行时真正所指的对象(因为:子对象的引用可能会赋给父对象的引用变量中)所属的类的Class的对象

 

通过下面的程序,来观察一下Class对象的生成的原理。

package ClassTest;  
  
public class TestClass {  
    public static void main(String[] args) {  
  
        try {  
            // 测试.class  
            @SuppressWarnings("rawtypes")  
            Class testTypeClass = TestClassType.class;  
            System.out.println("testTypeClass---" + testTypeClass);  
  
            // 测试Class.forName()  
            @SuppressWarnings("rawtypes")  
            Class testTypeForName = Class.forName("ClassTest.TestClassType");  
            System.out.println("testTypeForName---" + testTypeForName);  
  
            // 测试Object.getClass()  
            TestClassType testTypeGetClass = new TestClassType();  
            System.out.println("testTypeGetClass---"  
                    + testTypeGetClass.getClass());  
  
        } catch (ClassNotFoundException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
  
}  
package ClassTest;  
  
public class  TestClassType {  
  
    // 构造函数  
    public TestClassType() {  
        System.out.println("----构造函数---");  
    }  
  
    // 静态的参数初始化  
    static {  
        System.out.println("---静态的参数初始化---");  
    }  
  
    // 非静态的参数初始化  
    {  
        System.out.println("----非静态的参数初始化---");  
    }  
  
}  


 

 运行结果如下

testTypeClass---class ClassTest.TestClassType  
---静态的参数初始化---  
testTypeForName---class ClassTest.TestClassType  
----非静态的参数初始化---  
----构造函数---  
testTypeGetClass---class ClassTest.TestClassType

 然而单独执行  测试Class.forName() 时结果为

---静态的参数初始化e---
testTypeForName---测试Class.forName()/class ClassTest.TestClassType

 

单独执行测试Object.getClass() 结果为 

---静态的参数初始化e---
----非静态的参数初始化e---
----构造函数e---
testTypeGetClass---测试Object.getClass()/class ClassTest.TestClassType

 

所以根据结果可以发现,三种生成的Class对象一样的。并且程序只打印一次“静态的参数初始化”,但时拆分单独执行时class.forName(),Object.getClass()时

都执行了“静态的参数初始化”。

我们知道,静态的方法属性初始化,是在加载类的时候初始化。而非静态方法属性初始化,是new 类实例对象的时候加载。

因此,这段程序说明,三种方式生成Class对象,其实只有一个Class对象。在生成Class对象的时候,首先判断内存中是否已经加载。

所以,生成Class对象的过程其实是如此的:

当我们编写一个新的java类时,JVM就会帮我们编译成class对象,存放在同名的.class文件中。在运行时,当需要生成这个类的对象,JVM就会检查此类是否已经装载内存中。若是没有装载,则把.class文件装入到内存中。若是装载,则根据class文件生成实例对象。

 

原文地址:

https://www.cnblogs.com/Seachal/p/5371733.html

侵删。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值