目录
Java反射概述
- Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
- 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个CLass对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所有我们形象的称之为:反射。
动态语言和静态语言
1.动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
主要的动态语言:Object-C、C#,JavaScript、PHP、Python等。
2.静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++等。
Java不是动态语言,但是Java可以被称之为“准动态语言”。既Java有一定的动态性,我们可以利用反射机制,字节码操作获得类似动态语言的特性。
Java的动态性让编程的时候更加灵活!
Java反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
反射相关的主要API
类Java.lang.class
此类加载的过程
程序 经过javac.exe命令后,会生成一个或多个字节码文件(.class结尾的文件)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中,此过程就称之为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类就作为Class的一个实例。与就是说:Class的实例就对应着一个运行时类。
获取Class类实例的四种方式
@Test
public void test1() throws ClassNotFoundException {
//方式一:通过类.class
Class<TestClass> clazz = TestClass.class;
System.out.println(clazz);
//方式二:通过对象去调用.getClass()
TestClass ts = new TestClass();
Class clazz1 = ts.getClass();
System.out.println(clazz1);
//方式三:调用Class的静态方法forName()
//参数包括包名。
Class clazz2 = Class.forName("bk1.TestClass");
System.out.println(clazz2);
//方式四:使用类加载器
ClassLoader classLoader = ClassCreate.class.getClassLoader();
Class aClass = classLoader.loadClass("bk1.TestClass");
System.out.println(aClass);
}
控制台输出结果:
哪些类型可以有Class对象
- class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
- interface:接口
- 【】:数组
- enum:枚举
- annotation:注解@interface
- primitive type:基本数据类型
- void
数组Class对象说明:
数组中只要对象的维度一致,则对应的Class则一致
类的加载过程
具体描述
- 加载:将class文件字节码内容加载到内存中,并将这些静态 数据转换成方法区的运行时数据结构,然后生成以一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(既引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要类加载器参与。
- 链接:将Java类的二进制代码合并到JVM的运行状态中的过程。
验证:确保加载的类信息符合JVM规范,
准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将 在方法区进行分配。
解析:虚拟机常量池内的符号引用(常量池)替换为直接引用(地址)的过程。 - 初始化:
执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化,虚拟机会保证一个类的<clinit>方法在多线程环境中被正确加锁和同步。
类的加载器
类加载器的作用:
1.类加载器的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后再堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
2.类缓存:标准的javaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
ClassLoad的了解
类加载器作用是用来把类加载到内存。JVM规范定义了如下类型的类的加载器。
对于自定义类,使用系统类加载器进行加载。对于调用系统类加载器,使用扩展类加载器,引导类加载器主要负责加载java的核心类库,无法加载自定义类。
运行时类的使用
创建运行时类的对象
newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参构造器。
要想此方法正常的创建运行时类的对象,要求:
1.运行时类必须提供空参构造器
2.空参构造器的访问权限是足够的。通常设置为public。
创建空参构造器
在javabean中要求提供一个public的空参构造器。原因:
1.便于通过反射,创建运行时类的对象。
2.便于子类继承此运行时类时,默认调用super()时,保证父类有次构造器。
获取运行时类的属性内部结构
获取属性结构
测试类中声明为:
getFields():获取运行时类和父类种声明为public的属性
Class<TestClass> clazz = TestClass.class;//获取运行时类
//获取属性结构
Field[] fields = clazz.getFields();
//遍历fields
for(Field f :fields){
System.out.println(f);
}
//输出结果:public java.lang.String bk1.TestClass.name
getDeclaredFields():获取当前运行时类中声明的所有属性(不包括父类中的属性)
Class<TestClass> clazz = TestClass.class;//获取运行时类
//获取属性结构
Field[] Dfields = clazz.getDeclaredFields();
//遍历fields
for(Field f :Dfields){
System.out.println(f);
}
//输出结果: public java.lang.String bk1.TestClass.name
// private int bk1.TestClass.age
获取属性的权限修饰符、数据类型,变量名
Class<TestClass> clazz = TestClass.class;//获取运行时类
//获取属性结构
Field[] Dfields = clazz.getDeclaredFields();
//遍历fields
for(Field f :Dfields){
//获取权限修饰符
int modifiers = f.getModifiers();
System.out.print(Modifier.toString(modifiers) + '\t');
//获取数据类型
Class type = f.getType();
System.out.print(type.getName() + '\t');
//获取变量名
String name = f.getName();
System.out.print(name + '\t');
}
输出结果为:public java.lang.String name private int age
获取方法结构
getMethods():获取当前运行时类和父类声明为public权限的方法
Class<TestClass> clazz = TestClass.class;
Method[] methods = clazz.getMethods();
for(Method m : methods){
System.out.println(m);
}
getDeclaredMethods():获取当前运行时类中声明的所有方法(不包括父类中的方法)
Class<TestClass> clazz = TestClass.class;
Method[] dmethods = clazz.getDeclaredMethods();
for(Method m : dmethods){
System.out.println(m);
}
获取方法的注解、权限修饰符、返回值类型、方法名,形参列表和抛出的异常
Class<TestClass> clazz = TestClass.class;
Method[] dmethods = clazz.getDeclaredMethods();
for(Method m : dmethods){
//获取注解
//说明:要想注解能够获取到,需要注解的元注解声明类型为RunTime
Annotation[] annotations = m.getAnnotations();
for (Annotation a : annotations){
System.out.println(a);
}
//获取权限修饰符
int modifiers = m.getModifiers();
System.out.print(Modifier.toString(modifiers) + '\t');
//获取返回值类型
Class<?> returnType = m.getReturnType();
System.out.print(returnType.getName() + '\t');
//获取方法名
System.out.print(m.getName() + '\t');
//获取形参列表
Class<?>[] parameterTypes = m.getParameterTypes();
if (!(parameterTypes ==null && parameterTypes.length ==0)){
for (int i = 0 ; i < parameterTypes.length; i++){
if (i == parameterTypes.length - 1){
System.out.println(parameterTypes[i].getName() + "args_" + i);
break;
}
System.out.println(parameterTypes[i].getName() + "args_" + i + ",");
}
}
System.out.println(")");
//获取抛出的异常
Class<?>[] exceptionTypes = m.getExceptionTypes();
if(!(exceptionTypes ==null && exceptionTypes.length == 0)){
System.out.println("throws");
for (int i = 0 ; i < exceptionTypes.length; i++){
if (i == exceptionTypes.length - 1){
System.out.println(exceptionTypes[i].getName());
break;
}
System.out.println(exceptionTypes[i].getName() + ",");
}
}
System.out.println("*********");
获取构造器结构
获取构造器内部结构
获取当前运行时类中声明为public的构造器
Class<TestClass> clazz = TestClass.class;
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor c : constructors){
System.out.println(c);
}
获取当前运行时类声明的所有构造器
Class<TestClass> clazz = TestClass.class;
Constructor<?>[] dconstructors = clazz.getDeclaredConstructors();
for (Constructor c : dconstructors){
System.out.println(c);
}
获取运行时类的父类及获取父类泛型
Class<TestClass> clazz = TestClass.class;
Class superclass = clazz.getSuperclass();
System.out.println(superclass);
//获取运行时类带泛型的父类
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
//获取运行时类父类的泛型类型
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = paramType.getActualTypeArguments();
System.out.println(actualTypeArguments);
获取运行时类实现的接口
Class<TestClass> clazz = TestClass.class;
Class[] interfaces = clazz.getInterfaces();
for (Class c : interfaces){
System.out.println(c);
}
获取运行时类所在的包
Class<TestClass> clazz = TestClass.class;
Package aPackage = clazz.getPackage();
System.out.println(aPackage);
获取运行时类的注解
Class<TestClass> clazz = TestClass.class;
Annotation[] annotations = clazz.getAnnotations();
for (Annotation a : annotations){
System.out.println(a);
}
获取运行时类的属性,并赋值
方式一
Class<TestClass> clazz = TestClass.class;
TestClass instance = (TestClass) clazz.newInstance();
Field name = clazz.getField("name");
age.set(instance,"Tom");
//此方式要求调用的属性权限为public ,所以一般不使用此方式。
方式二:
Class<TestClass> clazz = TestClass.class;
TestClass instance = (TestClass) clazz.newInstance();
Field age = clazz.getDeclaredField("age");
//设置为可访问
age.setAccessible(true);
age.set(instance,18);
//调用
System.out.println(age.get(instance));
操作运行时类的方法
非静态方法:
静态方法:
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/154584.html