Java Reflection
- 反射是被视为动态语言的关键,反射机制允许程序执行期借助于Reflection。API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
- 加载完类之后,在堆内存的方法区中产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看类的结构,所以,我们的形象的称之为:反射;
反射之前我们可以创建一个对象,调用它的共有的属性及方法但是不能调用它的私有的属性和方法,但是反射可以我们看下面的代码!!
//通过反射,创建Person类的对象
Class clazz = Person.class;
Constructor cons1 = clazz.getConstructor(String.class,int.class);
Object obj = cons.newInstance("suoge",21);
Person p = (Person)obj;
//字段
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p,"king");
//方法
Method method = clazz.getDeclaredMethod("methodName",String.class);
method.setAccessible(true);
method.invoke(p,"hi");
如何看待反射和封装的两个技术
反射的机制与面向对象的封装性是否矛盾?
不矛盾, 封装性是建议我们怎么去调用,私有不调用,
反射:你想调用可以调用
通过直接new的方式和反射的方式都可以调用公共的结构,开发中用哪一个?
建议:用new的方式。
什么时候会使用?
编译时不确定,运行时才能确定的(动态)
关于Java.lang.Class
- 类的加载过程:
程序经过javac.exe的命令以后,会生成一个或多个字节码文件(.class结尾),接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中此过程就称为类的加载。加载到内存中的类我们就称为运行时类,此运行时类就作为Class的一个实例。类也是Class 的一个对象,万事万物皆对象 - Class 的实例对应着一个运行时类
- 加载到内存中的运行时类,会缓存一定的时间,在此时间之内可以通过不同的方式来获取此运行时类。
Class 的实例的获取
- 方式一: 调用运行时类的属性
- 方式二:通过运行时类的对象
- 方式三:调用Class的静态方法forName(String classPath)
示例代码:
public class GetClass {
public static void main(String[] args) throws Exception {
//方式一: 调用运行时类的属性;
Class<Person> clazz1 = Person.class;
System.out.println(clazz1);
//方式二:通过运行时类的对象
Person p = new Person();
Class<? extends Person> clazz2 = p.getClass();
System.out.println(clazz2);
//方式三:调用Class的静态方法forName(String classPath)
Class clazz3 = Class.forName("ReflectionTest.Person");
System.out.println(clazz3);
//方式四:通过类的加载器
ClassLoader classLoader = GetClass.class.getClassLoader();
Class<?> clazz4 = classLoader.loadClass("ReflectionTest.Person");
}
}
- 方式四:使用类的加载器,classloder (了解)
哪些类型可以有Class对象
类的加载过程 了解
Properties
通常用于jdbc的属性的编写时,该文件的读取有一定不同的方式可以看下面的代码来理解
package ReflectionTest;
/**
* @auther kobedu
* @date 2022/3/4
* @time 16:14
*/
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;
/**
* 用于读取配置文件,
*/
public class PropertiesTest {
public static void main(String[] args) throws Exception {
Properties props = new Properties();
// 方式一:以流的方式默认为项目路径下:
FileInputStream fis = new FileInputStream("jdbc.properties");
props.load(fis);
String user = props.getProperty("USER");
System.out.println(user);
//方式二:用类加载器:
ClassLoader classLoader = PropertiesTest.class.getClassLoader();
//默认为当前model下的src目录下:
InputStream is = ClassLoader.getSystemResourceAsStream("jdbc2.properties");
props.load(is);
user = props.getProperty("USER");
System.out.println(user);
}
}
通过反射创建运行时类的对象
package ReflectionTest;
import java.util.jar.Pack200;
/**
* @auther kobedu
* @date 2022/3/4
* @time 16:48
*
*
* 通过反射创建运行时类的对象
*
* javabean中要求提供一个public 的空参构造器。原因:
* 1.便于通过反射创建运行时类的对象
* 2.便于子类继承此运行类时,默认调用 super()时,保证父类有此构造器。
*/
public class NewInstanceTest {
public static void main(String[] args) throws Exception {
Class<Person> clazz = Person.class;
/*
newInstance();调用此方法,创建对应运行时类的对象
newInstance();内部调用了运行时类的空参构造器
要想此方法正常的创建运行时类的对象,要求:
1.运行时类必须提供空参构造器
2.空参的构造器的访问权限得够。通常设置为public
*/
Person person = clazz.newInstance();
System.out.println(person);
//另外还有:调用运行时类的构造器,上面的方式更加的常用。
}
}
反射体现动态性的测试
package ReflectionTest;
import java.util.Random;
/**
* @auther kobedu
* @date 2022/3/4
* @time 17:51
*/
public class DynamicTest {
public static void main(String[] args) throws ClassNotFoundException {
for (int i = 0; i < 22;i++ ) {
Random r = new Random();
int num = r.nextInt(3);
switch (num){
case 0 :
System.out.println(getIntance("ReflectionTest.GetClass"));
break;
case 1 :
System.out.println(getIntance("ReflectionTest.Demo02"));
break;
case 2 :
System.out.println(getIntance("java.util.Random"));
break;
}
}
}
public static Class getIntance(String path) throws ClassNotFoundException {
return Class.forName(path);
}
}
获取运行时类的完整结构
获取属性
public void test01(){
Class clazz = Person.class;
//获取属性结构,
Field[] fields = clazz.getFields(); //getFields();可以获取当前的类及其父类中声明为public的属性
//增强for循环
for (Field f:
fields) {
System.out.println(f);
}
System.out.println("-----------getDeclaredFields--------------------");
//getDeclaredFields();获取当前运行时类的所有属性
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields) {
System.out.println(f);
}
}
获取字段的详细信息:
/**
*权限修饰符 数据类型 变量名
*/
@Test
public void test02() {
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields) {
//1.权限修饰符
int modifiers = f.getModifiers();
System.out.print(Modifier.toString(modifiers)+"\t");
//2.数据类型
Class<?> type = f.getType();
System.out.print(type.getName()+"\t");
//3.变量名
System.out.print(f.getName());
System.out.println();
}
}
获取方法的信息
获取方法上的所有信息,
注解
权限修饰符 返回值类型 方法名(形参1,形参2....) 异常的抛出
Class clazz = test.class;
Method[] declaredMethods = clazz.getDeclaredMethods(); //获取当前运行时类中的所有方法
@Test
public void test2(){
Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {
//1.获取方法声明的注解(在运行时需要执行一些功能的注解才需要去获取)
Annotation[] annotations = method.getAnnotations();
for(Annotation annotation : annotations){
System.out.println(annotation);
}
//2.获取权限修饰符
int modifiers = method.getModifiers();
System.out.print(Modifier.toString(modifiers)+"\t");
//3.返回值类型
System.out.print(method.getReturnType().getName()+"\t");
//4.方法名:
System.out.print(method.getName());
System.out.print("(");
//5.形参列表
Class<?>[] parameterTypes = method.getParameterTypes();
if (!(parameterTypes==null || parameterTypes.length==0)) {
for (int i = 0; i < parameterTypes.length; i++) {
if(i==parameterTypes.length-1){
System.out.print(parameterTypes[i].getName()+"args_"+i);
break;
}
System.out.print(parameterTypes[i].getName()+"args_"+i+",");
}
}
System.out.print(")");
//6.抛出的异常
Class<?>[] exceptionTypes = method.getExceptionTypes();
if (!(exceptionTypes==null||exceptionTypes.length==0)) {
System.out.print("throws ");
for(int i=0;i<exceptionTypes.length; i++) {
if(i==exceptionTypes.length - 1){
System.out.print(exceptionTypes[i]);
break;
}
System.out.print(exceptionTypes[i]+" , ");
}
}
System.out.println();
}
}
获取类的完整信息
- 获取构造器
@Test
public void test01(){
Class clazz = Person.class;
Constructor[] constructors = clazz.getConstructors();//获取当前运行时类的中声明为public类的构造器(不会获取父类中的构造器)
for (Constructor constructor:
constructors) {
System.out.println(constructor);
}
System.out.println("---------------getDeclaredConstructor-----------------");
//获取当前的运行时类中声明的所有的构造器(没有权限的要求)
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (int i = 0;i<declaredConstructors.length; i++){
System.out.println(declaredConstructors[i]);
}
}
- 获取运行时类的父类
// 获取运行时类的父类:
@Test
public void test02(){
Class clazz = Person.class;
Class superClazz = clazz.getSuperclass();
System.out.println(superClazz);
}
- 获取运行时类的带泛型的父类
// 获取运行时类的带泛型的父类:
@Test
public void test03(){
Class clazz = Person.class;
Type superClazz = clazz.getGenericSuperclass();
System.out.println(superClazz);
// 获取运行时类的带泛型的父类的泛型:
//参数化类型:可以获取泛型父类的参数
ParameterizedType paramType = (ParameterizedType) superClazz;
Type[] actualTypeArguments = paramType.getActualTypeArguments();//获取实际的类型 参数
System.out.println(actualTypeArguments[0].getTypeName());
System.out.println(((Class)actualTypeArguments[0]).getName());
}
注意:获取泛型父类的参数的过程:
- 先获取泛型父类 superClazz
- 用(ParameterizedType) 参数化类型
- 获取实际的类型参数 Type[] actualTypeArguments = paramType.getActualTypeArguments();
- 用actualTypeArguments[0].getTypeName() 得到名字
- 获取运行时类的接口
// 获取接口:获取运行时类的接口
@Test
public void test04(){
Class clazz = Person.class;
// 获取运行时类的接口
Class[] interfaces = clazz.getInterfaces();
for (Class clazz1 : interfaces) {
System.out.println(clazz1);
}
// 获取运行时类的父类的接口
System.out.println("获取运行时类的父类的接口");
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
for(Class clazz2 : interfaces1){
System.out.println(clazz2);
}
}
获取类的指定信息(重点)
获取指定的属性并设值:
Class clazz = test.class;
Test t = (Test)clazz.newInstance();
Field field = clazz.getDeclaredField("name");//以字段名获取字段
field.setAccessible(true); //改成可以接收修改的模式,因为如果字段是私有的话没有这个设定是会设值失败!
field.set(t,value);//传入当前的对象和需要设定的值
操作运行时类的指定方法
Class clazz = Test.class;
Test t = clazz.newInsetace(); //实例化运行时类
Method method = clazz.getDeclaredMethod("name",String.class);//参数一:方法的名 参数二:方法的形参类型
method.setAccessible(true);// 私有的方法也可以进行相关的操作
method.invoke(t,'args');// 参数一:方法的调用者 参数二:方法的形参值,如果有多个形参之后也可以有参数 ,invoke()的返回值为方法的返回值
运行时类的静态方法的相关操作
只是在invoke()时的参数调用的对象不同,可以为空因为是属于类的不用对象去调用,可以是类,可以是类的实例化
获取运行时类的构造方法
@Test
public void test9() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class clazz = Person.class;
// private Person (String name )
Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
declaredConstructor.setAccessible(true);
Person p = (Person) declaredConstructor.newInstance("king");
System.out.println(p);
}