您的位置:首页 > 编程语言 > Java开发

java反射(Reflection)学习一——java.lang.Class类

2016-06-11 12:08 555 查看
反射(Reflection)是Java提供一种在运行时,动态操作类的方法。

在java中,无论是类(Class),枚举(enum),接口(interface),数组(array),注解(annotation),内置数据类型(Primitive type),void类型都包含一个特殊的实例对象,就是java.lang.Class类实例。

java.lang.Class类没有public的构造函数,java.lang.Class类实例只能有JVM自动产生,每次JVM加载Class的时候,JVM就会为其自动生成一个java.lang.Class类实例。

获取java.lang.Class类实例

在java中,可以有两个方法获取类的Class实例。

1. Object类有个getClass()方法,可以获取类的java.lang.Class实例。

2. 每个类有个public的静态变量class。即使是int,long,void这些内置类型也是有class静态变量的,但数组是个比较特殊的,只能通过getClass()方法获取Class变量。如果对数组变量进行.class操作,则会有Unknown Class编译错误。

public class ClassTest {
public static void main(String[] args) {
Class<ClassTest> testClass1 = ClassTest.class;
System.out.println("Class name:"+testClass1.getName());
//Class name:com.shuanghu.learn.reflection.ClassTest

Class<ClassTest> testClass2 = ClassTest.class;
System.out.println("Class name:"+testClass2.getName());
//Class name:com.shuanghu.learn.reflection.ClassTest

int [] arr = new int[2];
System.out.println("Class name:"+arr.getClass());
//Class name:class [I
//arr.class;
//Unknown Class
}
}


java.lang.Class的使用

判断类和对象的类型

通过java.lang.Class类可以判断一个类的类型。主要可以判断一个类是否是内部类(member class),局部类(local class),枚举(enum),接口(interface),数组(array),注解(annotation),内置数据类型(Primitive type)等类型。java.lang.Class提供了很多成员函数来支持这些判断。

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

enum TestEnum{
}

interface TestInterface{

}

@Target(ElementType.METHOD)
@interface TestAnnotation {
String value() default "";
}

public class ClassTest {
class InnerClass{
}

public static void main(String[] args) {
class LocalClass{
}

System.out.println("是否枚举(enum):" + TestEnum.class.isEnum());
System.out.println("是否接口(interface):" + TestInterface.class.isInterface());
System.out.println("是否内置类型(Primitive type):" + int.class.isPrimitive());
System.out.println("是否是内部类:" + InnerClass.class.isMemberClass());
System.out.println("是否local类:" + LocalClass.class.isLocalClass());
System.out.println("是否是注解:" + TestAnnotation.class.isAnnotation());

int [] intArr = new int[2];
//数组特殊,只支持getClass()获取Class,不支持.class这种静态变量的方式
System.out.println("是否数组:" + intArr.getClass().isAnnotation());
}
}


加载类

通过java.lang.Class类可以加载类。在Java中,只有在需要使用一个类的时候,这个类才会被加载。所以,不是所以定义了的类,都会被JVM加载。

java.lang.Class类提供了两个静态方法

public static Class<?> forName(String name) throws ClassNotFoundException;
public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException;


java.lang.Class的forName通过传入一个类的全名,将类加载到虚拟机。这两个方法的区别就是,单参数forName方法默认是进行初始化的,会执行类的static区域,其实就是执行了forName(className, true,ClassLoader.getClassLoader(Reflection.getCallerClass()))。三参数的forName方法,可以指定类的加载器,以及是否初始化。

package com.shuanghu.learn.reflection;

class DemoLoad1{
static {
System.out.println("This is DemoLoad1 class.");
}
}

class DemoLoad2{
static {
System.out.println("This is DemoLoad2 class.");
}
}

class DemoLoad3{
static {
System.out.println("This is DemoLoad3 class.");
}
}

public class ClassTest {
public static void test() throws ClassNotFoundException {
DemoLoad1 c1 = null;
DemoLoad2 c2 = null;
DemoLoad3 c3 = null;

System.out.println("Begin load class DemoLoad1...");
Class.forName("com.shuanghu.learn.reflection.DemoLoad1");
Class.forName("com.shuanghu.learn.reflection.DemoLoad2", false, Thread.currentThread().getContextClassLoader());
System.out.println("Begin init class DemoLoad2...");
new DemoLoad2();
}
public static void main(String[] args) throws ClassNotFoundException {
test();
}
}


执行上面的代码,会有如下结果:

Begin load class DemoLoad1...
This is DemoLoad1 class.
Begin init class DemoLoad2...
This is DemoLoad2 class.


从上面结果可以看出,虽然我们定义了三个类DemoLoad1,DemoLoad2和DemoLoad3,但实际上,只有两个类被JVM加载了,即DemoLoad1和DemoLoad2。从执行结果里看,如果仅仅声明类的变量,不进行实际对象的赋值,JVM是不会真实加载类的,如DemoLoad3。单参数版本的forName函数,在类被加载后,即会立马执行类static区域;三参数版本的forName函数,则可以控制类static区域不执行。

Class.forName最常见的一个应用就是jdbc里,通过Class.forName(driverClass)加载不同的java数据库驱动。

通过java.lang.Class实例,可以创建对象

java中创建对象,最常见的方式就是使用new关键字创建。但new并不是唯一的方式。

java.lang.Class有个newInstance方法,可以生成新的对象。但使用newInstance方法生成对象,必须有默认构造函数,或者public,protected的无参构造函数。如果类只有private构造函数,则用newInstance时,会抛出IllegalAccessException异常;如果类只有有参构造函数,则会抛出InstantiationException异常。

class DemoClass1{
protected DemoClass1(){
System.out.println("Demo class1 is constructing");
}
}

class DemoClass2{
private DemoClass2(){
System.out.println("Demo class2 is constructing");
}
}

class DemoClass3{
private DemoClass3(String param){
System.out.println("Demo class3 is constructing.Param:"+param);
}
}

public class ClassTest {

public static void main(String[] args) throws IllegalAccessException, InstantiationException {
DemoClass1.class.newInstance();

DemoClass2.class.newInstance();
DemoClass3.class.newInstance();
}
}


虽然newInstance方法会抛出IllegalAccessException和InstantiationException异常,但不代表,这两种情况下就无法通过class创建对象了,后面会有其他方法解决这个问题。

通过java.lang.Class实例,获取类的更详细信息。

仅仅通过java.lang.Class类是无法描述java类的全部信息的。所以,java的java.lang.reflect包里,提供了更多的类。

类名称作用
java.lang.reflect.Array描述java的array数组,可以动态访问和新建数据
java.lang.reflect.Constructor描述java中类的构造函数
java.lang.reflect.Field描述java类的成员变量
java.lang.reflect.Method描述java类的方法
上面这些描述类信息的类,java.lang.Class都提供了相应的获取成员函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息