反射
JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方 法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。
Java 反射机制,可以实现以下功能:
①在运行时判断任意一个对象所属的类;
②在运行时构造任意一个类的对象;
③在运行时判断任意一个类所具有的成员变量和方法;
④在运行时调用任意一个对象的方法; ⑤生成动态代理。
一、 获取源头 Class(重点)
①Class.forName(”包名.类名”)//一般尽量采用该形式
②类.class
③对象.getClass()
public class Source {
public static void main(String[] args) {
//第一种方式:对象.class
Source s=new Source();
Class<?>c1=s.getClass();
//第二种方式:类.class
Class<?>c2=Source.class;
//第三种方式(推荐方式): Class.forName()
Class<?>c3=null;
try {
c3=Class.forName("com.shsxt.ref.simple.Source");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(c1.getName());
System.out.println(c2.getName());
System.out.println(c3.getName());
}
}
1.创建对象的时候,拿到的都是当天前类型Class对象的一个镜像|赋值体
2.在类加载的时候,会在内存中存在当天前类型的一个Class对象,一个类的Class对象中存储这个类的所有信息(属性,方法,构造器...)
3.只要我们能够获取这个类型的Class对象,就可以对这个类做一切操作
Class 类的实例表示正在运行的 Java 应用程序中的类和接口
public class ReflectDemo02 {
public static void main(String[] args) throws ClassNotFoundException{
//1.对象.getClass()
Class cls1="哈哈".getClass();
Class cls2="呵呵".getClass();
System.out.println(cls1==cls2);
//2.类名.class
Class cls3=String.class;
System.out.println(cls1==cls3);
System.out.println(cls3);
//3.Class.forName("类的权限命名:包名+类名") 推荐
Class cls4=Class.forName("java.lang.String");
System.out.println(cls3==cls4);
//4.根据子类的Class对象获取父类的Class对象
Class cls5=cls4.getSuperclass();
System.out.println(cls5);
//5.获取基本数据类型的Class对象
Class base1=int.class;
System.out.println(base1);
Class cls6=Integer.class;
System.out.println(base1==cls6);
Class base2=Integer.TYPE; //得到对应的基本数据类型的class对象
System.out.println(base1==base2);
//常用的方法
//1.getName() 包名+类名
System.out.println(cls5.getName());
//2. Class<?>[] getInterfaces()
Class[] arr=cls4.getInterfaces();
System.out.println(Arrays.toString(arr));
//3.String getSimpleName()
System.out.println(cls4.getSimpleName());
//4.boolean isInterface() isPrimitive()
System.out.println(Integer.class.isPrimitive());
System.out.println(Integer.TYPE.isPrimitive());
}
}
二、实例化对象****(****重点****)**
1)通过反射获取到类中的构造器
2)根据构造器创建对象
l 构造器Constructor对象.newInstance(实参)方法
l 直接通过class类的newIntance()方法创建对象,方法没有参数 调用空构造
构造器方法
注意****:newInstance()****是调用空构造,如果空构造不存在,会出现异常。由此可知,使用其他构造器创建对象比较麻烦,使用空构造非常简单。 确保空构造存在.
public class ReflectDemo03 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
//1.先获取类的Class
Class cls=Person.class;
//2.通过Class类中的方法,获取到Person类中的构造器
/*
* Constructor<T> getConstructor(Class<?>... parameterTypes)
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
Constructor<?>[] getConstructors()
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
*/
Constructor con1=cls.getConstructor(int.class,String.class,int.class);
Person p=(Person) con1.newInstance(01,"卢妹妹",18);
System.out.println(p);
Person p1=(Person)cls.newInstance();
System.out.println(p1);
/*
* Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。
Constructor<?>[] getDeclaredConstructors()
返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。
*/
Constructor<Person>[] cons=cls.getDeclaredConstructors();
System.out.println(Arrays.toString(cons));
//私有的构造器
//放开权限
cons[1].setAccessible(true); //权限方法
Person p3=cons[1].newInstance(02,"卢双双");
cons[1].setAccessible(false);
System.out.println(p3);
}
}
|
三:属性和方法
获取所有属性(包括父类或接口) ,使用 Field 即可操作
获取所有方法(包括父类或接口),使用 Method 即可。
package com.shsxt.ref01;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
public class ReflectDemo04 {
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
// testField(Person.class);
testMethod(Person.class);
}
/*
* 操作方法 调用方法,能给方法传递实参
*
* Method getDeclaredMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
Method[] getDeclaredMethods()
Method getMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
Method[] getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
*/
public static void testMethod(Class cls) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException{
Method[] arr=cls.getMethods();
Person p=(Person) cls.newInstance();
// Method me=cls.getDeclaredMethod("setAge", int.class);
Method me=cls.getDeclaredMethod("toString");
System.out.println(me.invoke(p));;
System.out.println(p.getAge());
// System.out.println(Arrays.toString(arr));
//静态方法执行的时候,invoke第一个参数可以为null
cls.getMethod("haha").invoke(null);
}
/*
* 反射操作类中的字段 能设置值 能获取值
* Field getDeclaredField(String name)
返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
Field[] getDeclaredFields()
返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
Field getField(String name)
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
Field[] getFields()
*/
public static void testField(Class cls) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
Field name=cls.getDeclaredField("name");
//Object get(Object obj) 返回指定对象上此 Field 表示的字段的值。
name.setAccessible(true);
//void set(Object obj, Object value)
Person p=new Person(05,"大力",18);
name.set(p, "大力水手");
System.out.println(name.get(p));
System.out.println(name.getName());
System.out.println(Modifier.toString(name.getModifiers()));
}
}
四:修饰符
获取修饰符,使用 Modifier 即可
五:类加载器
在 java 中有三种类类加载器: ⑴Bootstrap ClassLoader 此加载器采用 c++编写,一般开发中很少见。 ⑵Extension ClassLoader 用来进行扩展类的加载,一般对应的是 jre\lib\ext 目录中的类 ⑶AppClassLoader 加载 classpath 指定的类,是最常用的加载器。同时也是 java 中默认的加载器。 了解即可。
了解一下类的生命周期 : 在一个类编译完成之后,下一步就需要开始使用类,如果要使用一个类,肯定离不开 JVM。 在程序执行中 JVM 通过装载,链接,初始化这 3 个步骤完成。 类的装载是通过类加载器完成的,加载器将.class 文件的二进制文件装入 JVM 的方法区,并且在堆区创建描述这个类的 java.lang.Class 对象。用来封装数据。 但是同一个类只会被类装载器装载一次。 链接就是把二进制数据组装为可以运行的状态。链接分为校验,准备,解析这 3 个阶段 1、校验一般用来确认此二进制文件是否适合当前的 JVM(版本), 2、准备就是为静态成员分配内存空间。并设置默认值 3、解析指的是转换常量池中的代码作为直接引用的过程,直到所有的符号引用都可以 被运行程序使用(建立完整的对应关系) 完成之后,类型也就完成了初始化,初始化之后类的对象就可以正常使用了,直到一个 对象不再使用之后,将被垃圾回收。释放空间。当没有任何引用指向 Class 对象时就会被卸载,结束类的生命周期。