java反射框架总结

概述

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。下图是java反射框架简述

在这里插入图片描述

1、接口 AnnotatedElement

表示目前正在此 VM 中运行的程序的一个已注释元素。该接口允许反射性地读取注释。由此接口中的方法返回的所有注释都是不可变并且可序列化的。调用者可以修改已赋值数组枚举成员的访问器返回的数组;这不会对其他调用者返回的数组产生任何影响。
如果此接口中的方法返回的注释(直接或间接地)包含一个已赋值的 Class 成员,该成员引用了一个在此 VM 中不可访问的类,则试图通过在返回的注释上调用相关的类返回的方法来读取该类,将导致一个 TypeNotPresentException。
类似地,如果注释中的枚举常量不再以枚举类型存在,那么试图读取一个已赋值的枚举成员将导致EnumConstantNotPresentException。

2、接口 Type
Type 是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。

3、接口 GenericDeclaration

声明类型变量的所有实体的公共接口。

4、类 AccessibleObject

AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。
在反射对象中设置 accessible 标志允许具有足够特权的复杂应用程序(比如 Java Object Serialization 或其他持久性机制)以某种通常禁止使用的方式来操作对象。

boolean isAccessible()//获取此对象的 accessible 标志的值。

static void setAccessible(AccessibleObject[] array, boolean flag)//使用单一安全性检查(为了提高效率)为一组对象设置 accessible 标志的便捷方法。

//值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查
void  setAccessible(boolean flag)

5、接口 Member

成员是一种接口,反映有关单个成员(字段或方法)或构造方法的标识信息。

下面开始介绍反射框架基础类

一、Class

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。

1、获取某一个类所对应的Class 对象

Object 的getClass 方法

所有Java 对象都具备这个方法。该方法用于返回与调用该方法对象所属类关联的Class 对象

Date date1 = new Date();
Date date2 = new Date();
Class c1 = date1.getClass();
Class c2 = date2.getClass();
System.out.println(c1.getName()); // java.util.Date
System.out.println(c1 == c2); // true

上面的代码中,调用Date 对象date1 的getClass 方法将返回用于封装Date 类信息的Class
对象。这里调用了Class 类的getName 方法:public String getName()

这个方法的含义很直观,即返回所封装的类的名称。
需要注意的是,代码中的date1 和date2 的getClass 方法返回了相同的Class 对象(c1==c2的值为true)。这是因为,对于相同的类,JVM 只会载入一次,而与该类对应的Class 对象也只会存在一个,无论该类实例化了多少对象。

另外,需要强调的是,当一个对象被其父类的引用或其实现的接口类型的引用所指向的时,getClass 方法返回的是与对象实际所属类关联的Class 对象。例如:

List c1 = new ArrayList();
List c2 = new ArrayList();
Class w1 = c1.getClass();
Class w2 = c2.getClass();
System.out.println(w2.getName()); //java.util.ArrayList
System.out.println(w1 == w2); // true

上面的代码中,语句getClass()方法返回的是list 所指向对象实际所属类java.util.ArrayList对应的Class 对象而并未java.util.List 所对应的Class 对象。有些时候可以通过这个方法了解一个对象的运行时类型,例如:

HashSet set = new HashSet();
Iterator it = set.iterator();
System.out.println(it.getClass().getName()); //java.util.HashMap$KeyIterator

从代码可以看出,HashSet 的iterator 方法返回的是实现了Iterator 接口的HashMap 内部类(KeyIterator)对象。
因为抽象类和接口不可能实例化对象,因此不能通过Object 的getClass 方法获得与抽象类和接口关联的Class 对象。

使用.class 的方式

使用类名加“.class”的方式即会返回与该类对应的Class 对象

Class clazz = String.class;
System.out.println(clazz.getName()); // java.lang.String

这个方法可以直接获得与指定类关联的Class 对象,而并不需要有该类的对象存在。

下面是基本类型class对象的获取

boolean.class 等价与 Boolean.TYPE    返回的是基本类型boolean
char.class   等价与 Character.TYPE   返回的是基本类型char
byte.class   等价与 Byte.TYPE        返回的是基本类型byte
short.class  等价与Short.TYPE        返回的是基本类型short
int.class    等价与 Integer.TYPE     返回的是基本类型int
long.class   等价与Long.TYPE         返回的是基本类型long
float.class  等价与 Float.TYPE       返回的是基本类型float
double.class 等价与Double.TYPE       返回的是基本类型double
void.class   等价与 Void.TYPE        返回的是关键字void

使用Class.forName 方法

Class 有一个著名的static 方法forName

static Class<?> forName(String className)
static Class<?> forName(String name, boolean initialize, ClassLoader loader)

该方法可以根据字符串参数所指定的类名获取与该类关联的Class 对象。注意 forName 方法的参数是类的完整限定名(即包含包名)。且对空格敏感。如果该类还没有被装入,该方法会将该类装入JVM。该方法声明抛出ClassNotFoundException 异常。顾名思义,当该方法无法获取需要装入的类时(例如,在当前类路径中不存在这个类),就会抛出这个异常。

Class.forName()首先会将 类装入JVM,并返回与之关联的Class 对象。JVM 装入Foo 类后对其进行初始化,调用了其static 块中的代码。区别于前面两种获取Class 对象的方法。使用Class.forName 方法所要获取的与之对应的Class对象的类可以通过字符串的方式给定。该方法通常用于在程序运行时根据类名动态的载入该类并获得与之对应的Class 对象。

Class.forName()与.class的区别

答:Class.forName()会初始化类。.class不会,它在编译时就会受到检查(所以可以用范型)(因此不需要置于try语句块中),并且它根除了对class.forName方法的调用,所以也更高效(class.forName是动态初始话,范型对他来说没有用处)

ArrayList<String> tt = ArrayList.class.newInstance();
ArrayList<String> ttg = (ArrayList<String>) Class.forName("java.util.ArrayList").newInstance()

Class<ArrayList> tt = ArrayList.class;
Class<ArrayList> ttg =  (Class<ArrayList>) Class.forName("java.util.ArrayList");
2、判断此class对象的类型
boolean  isAnnotation() //如果此 Class 对象表示一个注释类型则返回 true。
boolean  isAnonymousClass()  //当且仅当底层类是匿名类时返回 true。
boolean isArray()  //判定此 Class 对象是否表示一个数组类。
boolean isAssignableFrom(Class<?> cls)  //判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口。
boolean isEnum()   //当且仅当该类声明为源代码中的枚举时返回 true。
boolean isInstance(Object obj) //判定指定的 Object 是否与此 Class 所表示的对象赋值兼容。
boolean isInterface() //判定指定的 Class 对象是否表示一个接口类型。
boolean isLocalClass() //当且仅当底层类是本地类时返回 true。
boolean isMemberClass()  //当且仅当底层类是成员类时返回 true。
boolean isPrimitive()  //判定指定的 Class 对象是否表示一个基本类型。
boolean isSynthetic()  //如果此类是复合类,则返回 true,否则 false。
3、判断一个对象与另一对象的关系

Java中的instanceofisInstance() 和 isAssignableFrom()

Java instanceof 关键字是如何实现的?

在Java中,这三个方式都是作为类型判断用的,但是这三个东西各有千秋,非常容易引起混淆,下面总结一下这三个方式的异同点,并通过具体示例说明。
在下表中,obj为一个对象,Type为类型(基本类型或类,直接用类名即可),class为Java中Class类的对象,通过类名.class或对象的getClass()方法。

instanceof:他是关键字,使用方法是: obj instanceof Type(同JS) ,用于判断一个对象(obj)是否为一个类(Type)或者Type类的子类的实例

isInstance():他是class的方法,class.isInstance(obj) 用于判断一个对象是否是该Class对象所表示的类或其子类的实例

isAssignableFrom():他是class的方法,class1.isAssignableFrom(class2) 用于判断 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口 可以近似理解(class1 大于等于class2)

4、类的实例化
T newInstance() //创建此 Class 对象所表示的类的一个新实例。

问题:Java 中 newInstance 方法和 new 的区别是什么?

答:在初始化一个类,生成一个实例的时候,newInstance()方法和new关键字除了一个是方法,一个是关键字外,最主要有什么区别?它们的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类。那么为什么会有两种创建对象方式?这主要考虑到软件的可伸缩、可扩展和可重用等软件设计思想。
  
Java中工厂模式经常使用newInstance()方法来创建对象,因此从为什么要使用工厂模式上可以找到具体答案。 例如:

class c = Class.forName(“Example”);
factory = (ExampleInterface)c.newInstance();

//其中ExampleInterface是Example的接口,可以写成如下形式:

String className = "Example";
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();

进一步可以写成如下形式:

String className = readfromXMlConfig;//从xml 配置文件中获得字符串
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();

上面代码已经不存在Example的类名称,它的优点是,无论Example类怎么变化,上述代码不变,甚至可以更换Example的兄弟类Example2 , Example3 , Example4……,只要他们继承ExampleInterface就可以。

从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载。但是使用newInstance()方法的时候,就必须保证:

1、这个类已经加载;

2、这个类已经连接了。而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载java API的那个加载器。

现在可以看出,newInstance()实际上是把new这个方式分解为两步,即首先调用Class加载方法加载某个类,然后实例化。
这样分步的好处是显而易见的。我们可以在调用class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。

最后用最简单的描述来区分new关键字和newInstance()方法的区别:
newInstance: 弱类型。低效率
new: 强类型。相对高效。能调用任何public构造

不带参数的构造方法来生成对象,我们有两种方式:

a) 先获得 Class 对象,然后通过该 Class 对象的 newInstance()方法直接生成即可:

Class<> classType = String.class;
Object obj = classType.newInstance();

b) 先获得 Class 对象,然后通过该对象获得对应的 Constructor 对象,再通过该 Constructor 对象的 newInstance()方法生成:

Class<> classType = Customer.class;
Constructor cons = classType.getConstructor(new Class[]{});
Object obj = cons.newInstance(new Object[]{});

若想通过类的带参数的构造方法生成对象,只能使用下面这一种方式:

Class<> classType = Customer.class;
Constructor cons = classType.getConstructor(new Class[]{String.class, int.class});
Object obj = cons.newInstance(new Object[]{"hello", 3});

其他方法

boolean desiredAssertionStatus()//如果要在调用此方法时将要初始化该类,则返回将分配给该类的断言状态。
Class<?>[] getClasses()//返回一个包含某些 Class 对象的数组,这些对象表示属于此 Class 对象所表示的类的成员的所有公共类和接口。
ClassLoader getClassLoader() //返回该类的类加载器。
Class<?> getComponentType() //返回表示数组组件类型的 Class。
Class<?>[] getDeclaredClasses()//返回 Class 对象的一个数组,这些对象反映声明为此 Class 对象所表示的类的成员的所有类和接口。
Class<?> getDeclaringClass()//如果此 Class 对象所表示的类或接口是另一个类的成员,则返回的 Class 对象表示该对象的声明类。
Class<?> getEnclosingClass() //返回底层类的立即封闭类。
T[] getEnumConstants() //如果此 Class 对象不表示枚举类型,则返回枚举类的元素或 null。
Class<?>[] getInterfaces() //确定此对象所表示的类或接口实现的接口。
String getName() //以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
Package getPackage() //获取此类的包。
ProtectionDomain getProtectionDomain() //返回该类的 ProtectionDomain。
URL getResource(String name) //查找带有给定名称的资源。
InputStream getResourceAsStream(String name) //查找具有给定名称的资源。
Class<? super T> getSuperclass() //返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。

二、Field

Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
Array 允许在执行 get 或 set 访问操作期间进行扩展转换,但如果将发生收缩转换,则抛出一个 IllegalArgumentException

1、调用Class对象返回Field 的方法
//所有,但不包括继承的属性,name - 指的是成员属性的名称
public Field[] getDeclaredFields ()  throws SecurityException
public Field getDeclaredField(String name)  throws NoSuchFieldException,SecurityException

//表示此类 public 字段的 Field 对象的数组,注意 包括继承的属性
public Field[] getFields() throws SecurityException
public Field getField(String name) throws NoSuchFieldException, SecurityException
2、Field的设定新值的方法

第一个参数 obj是这成员属性的对像的引用
第二个参数是要设定的新值

void set(Object obj, Object value) //将指定对象变量上此 Field 对象表示的字段设置为指定的新值。

void setBoolean(Object obj, boolean z)//将字段的值设置为指定对象上的一个 boolean 值。

void setByte(Object obj, byte b)//将字段的值设置为指定对象上的一个 byte 值。

void setChar(Object obj, char c) //将字段的值设置为指定对象上的一个 char 值。

void setDouble(Object obj, double d)//将字段的值设置为指定对象上的一个 double 值。

void setFloat(Object obj, float f)//将字段的值设置为指定对象上的一个 float 值。

void setInt(Object obj, int i)//将字段的值设置为指定对象上的一个 int 值。

void setLong(Object obj, long l)//将字段的值设置为指定对象上的一个 long 值。

void setShort(Object obj, short s)//将字段的值设置为指定对象上的一个 short 值。

实例

Field f = Class.forName("student.A").getDeclaredField("b");
A a= new A();
f.setAccessible(true);
f.setInt(a,22 );
3、获取属性的值
int getInt(Object obj)//获取 int 类型或另一个通过扩展转换可以转换为 int 类型的基本类型的静态或实例字段的值。

long getLong(Object obj)//获取 long 类型或另一个通过扩展转换可以转换为 long 类型的基本类型的静态或实例字段的值。

boolean getBoolean(Object obj)//获取一个静态或实例 boolean 字段的值。

byte getByte(Object obj)//获取一个静态或实例 byte 字段的值。

char getChar(Object obj)//获取 char 类型或另一个通过扩展转换可以转换为 char 类型的基本类型的静态或实例字段的值。

double getDouble(Object obj)//获取 double 类型或另一个通过扩展转换可以转换为 double 类型的基本类型的静态或实例字段的值。

float getFloat(Object obj)//获取 float 类型或另一个通过扩展转换可以转换为 float 类型的基本类型的静态或实例字段的值。

short getShort(Object obj)//获取 short 类型或另一个通过扩展转换可以转换为 short 类型的基本类型的静态或实例字段的值。

Object get(Object obj)//返回指定对象上此 Field 表示的字段的值。
4、Field 对象的信息
Class<?> getType() //返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。如 int 类型啦等等

Type getGenericType()//返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型。

int getModifiers()//以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符。

String getName() //返回此 Field 对象表示的字段的名称。
三、Method

Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。
Method 允许在匹配要调用的实参与底层方法的形参时进行扩展转换;但如果要进行收缩转换,则会抛出 IllegalArgumentException。

1、调用Class对象返回Method 的方法
//无权限限制,但不包括继承的方法
//name 参数是一个 String,它指定所需方法的简称,
//parameterTypes 参数是 Class 对象的一个数组,它按声明顺序标识该方法的形参类型
public Method[] getDeclaredMethods() throws SecurityException
public Method  getDeclaredMethod  (String name,Class<?>... parameterTypes)  throws NoSuchMethodException,SecurityException

//如果该类是本地或匿名类,则返回底层类的立即封闭方法;否则返回 null。
public Method getEnclosingMethod()


//返回此 Class 对象所表示的类或接口的public 方法(只包括public)。注意 包括继承的属性
public Method[] getMethods() throws SecurityException
public Method getMethod (String name, Class<?>... parameterTypes) throws NoSuchMethodException,SecurityException

实例

Method f = Class.forName("student.A").getDeclaredMethod("up",new Class[] {int.class});
2、Method方法的调用
Object invoke(Object obj, Object... args)

实例

private int up(int i,int a){ return i+a;}
A a = new A();
Method f = Class.forName("student.A").getDeclaredMethod ("up", new Class[]{int.class,int.class});
Object result = f.invoke(a, new Object[] {1,2});
System.out.println((Integer)result);
3、方法中形参的信息
Annotation[][] getParameterAnnotations()//返回表示按照声明顺序对此 Method 对象所表示方法的形参进行注释的那个数组的数组。

Class<?>[] getParameterTypes() //按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。

案例

public class Test {
     
     public String get(@Param("contact")String contact ,@Param("userId")Integer userId){
           return contact;    
     }
     
     public static void main(String[] args) {
           Method[] method = Test.class.getDeclaredMethods();
           System.out.println(Arrays.toString(method[1].getParameterTypes()));
           //[class java.lang.String, class java.lang.Integer]
           
           System.out.println(Arrays.toString(method[1].getParameterAnnotations()));
           //[[Ljava.lang.annotation.Annotation;@29453f44, [Ljava.lang.annotation.Annotation;@5cad8086]
     }
}

四、Constructor

Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。
Constructor 允许在将实参与带有底层构造方法的形参的 newInstance() 匹配时进行扩展转换,但是如果发生收缩转换,则抛出 IllegalArgumentException。

1、调用Class对象返回Constructor的方法
//返回此 Class 对象所表示的类或接口的public构造方法,包括继承
public Constructor<?>[] getConstructors()throws SecurityException
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

//返回此类所有已声明的构造方法包括私有构造方法,但不包括继承的方法
public Constructor<?>[] getDeclaredConstructors()throws SecurityException
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)throws NoSuchMethodException,SecurityException

//如果该类是本地或匿名类,则返回底层类的立即封闭构造方法;否则返回 null。
public Constructor<?> getEnclosingConstructor()
2、使用Constructor 对象创建实例
//使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
T newInstance(Object... initargs)

实例

class A{  
  int a= 1;   
  private A(int a){ }  
}
Constructor f = Class.forName("student.A").getDeclaredConstructor( new Class[] {int.class});
Object result = f.newInstance(new Object[]{1});
System.out.println(((A)result).a);

五、Array

Array类提供静态方法来动态创建和访问Java数组。
Array允许在获取或设置操作期间扩大转换,但如果发生缩小转换,则抛出IllegalArgumentException 。

1、调用Class方法返回数组组件类型的方法
public Class<?> getComponentType()

返回表示数组组件类型的 Class。如果此类不表示数组类,则此方法返回 null。
如果此类是数组,则返回表示此类组件类型的 Class

2、动态创建多维数组
//componentType - 表示数组类型的 Class 对象
//dimensions - 表示新数组维度的 int 数组,注意它是一个数组
public static Object newInstance(Class<?> componentType,int... dimensions)throws IllegalArgumentException, NegativeArraySizeException
3、动态创建一维数组
//componentType - 表示新数组的组件类型的 Class 对象
//length - 新数组的长度,注意它是一个int值
public static Object newInstance(Class<?> componentType,int length)throws NegativeArraySizeException

案例

Class str = String.class;
Object arr= Array.newInstance(str, 3);
Array.set(arr, 0,1);//错误,只能加字符串
System.out.println(Array.get(arr, 0));
4、设定新值
// 参数array表示被操作的数组的引用
static void set(Object array, int index, Object value)//将指定数组对象中索引组件的值设置为指定的新值。

static void setBoolean(Object array, int index, boolean z)//将指定数组对象中索引组件的值设置为指定的 boolean 值。

static void setByte(Object array, int index, byte b)//将指定数组对象中索引组件的值设置为指定的 byte 值。

static void setChar(Object array, int index, char c)//将指定数组对象中索引组件的值设置为指定的 char 值。

static void setDouble(Object array, int index, double d)//将指定数组对象中索引组件的值设置为指定的 double 值。

static void setFloat(Object array, int index, float f)//将指定数组对象中索引组件的值设置为指定的 float 值。

static void setInt(Object array, int index, int i)//将指定数组对象中索引组件的值设置为指定的 int 值。

static void setLong(Object array, int index, long l)//将指定数组对象中索引组件的值设置为指定的 long 值。

static void setShort(Object array, int index, short s)//将指定数组对象中索引组件的值设置为指定的 short 值。
5、获取数组中的元素
static Object get(Object array, int index) //返回指定数组对象中索引组件的值。

static boolean getBoolean(Object array, int index)//以 boolean 形式返回指定数组对象中索引组件的值。

static byte getByte(Object array, int index) //以 byte 形式返回指定数组对象中索引组件的值。

static char getChar(Object array, int index)//以 char 形式返回指定数组对象中索引组件的值。

static double getDouble(Object array, int index)//以 double 形式返回指定数组对象中索引组件的值。

static float getFloat(Object array, int index)//以 float 形式返回指定数组对象中索引组件的值。

static int getInt(Object array, int index)//以 int 形式返回指定数组对象中索引组件的值。

static int getLength(Object array)//以 int 形式返回指定数组对象的长度。

static long getLong(Object array, int index) //以 long 形式返回指定数组对象中索引组件的值。

static short getShort(Object array, int index)//以 short 形式返回指定数组对象中索引组件的值。

案例

int[] dims = new int[] { 5, 10, 15 };
Object array = Array.newInstance(Integer.TYPE, dims);
System.out.println(array instanceof int[][][]);//true
Object arrayObj = Array.get(array, 3);
       
System.out.println(arrayObj instanceof int[][]);//true
       
arrayObj = Array.get(arrayObj, 5);
       
System.out.println(arrayObj instanceof int[]);//true
       
Array.setInt(arrayObj, 10, 37);
int[][][] arrayCast = (int[][][]) array;
System.out.println(arrayCast[3][5][10]);//37      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值