前言
以前一直觉得反射这一块非常的难,没用在上面花费太多时间,平时工作中也使用的很少,因此,对这一块来说掌握的也不太好,最近有时间整理一下Java基础知识点,把这一块内容进行了复习,发现还是很有趣的,在此整理一些知识点,方便再次复习。
知识点
背景
Java反射是非常重要的一块Java知识点,没用它就没有Java准动态语言的说法,它被大量使用在框架、设计模式中,是框架诞生的基石。
概念
反射:允许程序在执行期间借助Reflection API获取任何类的内部信息(比如:变量、方法、构造方法),并能操作对象的属性及方法。
类加载
此处借用韩老师的一张图,上面详细的描述了类加载的过程。首先,Java源码通过javac编译成为字节码文件,再通过java运行字节码文件加载到内存中。
类加载后内存中的布局是方法区中有类的字节码文件二进制数据,也就是元数据,并指向堆中的类的Class对象。
分别阐明各阶段任务。
验证:
准备:
解析:
相关类
反射所使用到的类基本都在java.lang.reflect包中,主要有以下几个类。
1、java.lang.Class 代表一个类 Class的对象表示某个类加载到堆中的对象
2、java.lang.reflect.Field 代表类的属性
3、java.lang.reflect.Method 代表类的方法
4、java.lang.reflect.Constructor 代表类的构造方法
API
此处列举常用API,其他API可看源码或手册查询。
//、、、、、、、、、、 获取Class对象的四种方式
// 方式一:类名.class
Class c1 = Test2.class;
// 方式二:对象.getClass()
Class c2 = new Test2().getClass();
// 方式三:Class.forName()
Class c3 = Class.forName("Test2");
// 方式四:类加载器
ClassLoader classLoader = Test2.class.getClassLoader();
Class c4 = classLoader.loadClass("Test2");
//、、、、、、、、、、、获取Class对象的其他内容
//获取Class对象的全类名
String name = c1.getName();
//获取简单类名
String simpleName = c1.getSimpleName();
//获取包名
Package package1 = c1.getPackage();
//获取父类
Class<? super Test2> superclass = c1.getSuperclass();
//获取注解
Annotation[] annotations = c1.getAnnotations();
//、、、、、、、、、、、获取构造方法
//获取Class对象及其父类所有的public的构造方法
Constructor[] constructors = c1.getConstructors();
//获取Class对象所有的构造方法
Constructor[] declaredConstructors = c1.getDeclaredConstructors();
//获取Class及其父类对象指定public的构造方法
Constructor<Test2> constructor = c1.getConstructor();
//获取Class对象指定的构造方法
Constructor<Test2> declaredConstructor = c1.getDeclaredConstructor();
//、、、、、、、、、、、获取类的实例对象
//方式一:
Object obj = c1.newInstance();
Test2 t1 = (Test2) obj;
//方式二:
Test2 t2 = (Test2) c1.newInstance();
//方式三:
Test2 t3 = declaredConstructor.newInstance();
//、、、、、、、、、、、获取对象属性(见名知意,如构造方法)
Field[] fields = c1.getFields();
Field field = c1.getField("name");
Field[] declaredFields = c1.getDeclaredFields();
Field declaredField = c1.getDeclaredField("name");
//、、、、、、、、、、、获取对象方法(见名知意,如构造方法)
Method[] methods = c1.getMethods();
Method method = c1.getMethod("getName");
Method[] declaredMethods = c1.getDeclaredMethods();
Method declaredMethod = c1.getDeclaredMethod("getName");
// 方法执行,方法的返回值就是Object的invoke
Object invoke = declaredMethod.invoke(c1);
//获取方法修饰符。返回值是int,需要将int转为对象的类似public等修饰符
int modifiers = declaredMethod.getModifiers();
String string = Modifier.toString(modifiers);
//获取方法返回类型
Class<?> type = declaredMethod.getReturnType();
//返回方法的参数类型,无法获取参数名
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
练习
这里记录两个小练习,非常基础,不过也能用到一些常用API。
练习一
定义一个类,有私有属性name,值为“helloKitty”,提供getName公共方法,使用反射修改name值并打印。
public class PrivateTest {
private String name="hellokitty";
public String getName(){
return name;
}
}
public class Test {
public static void main(String[] args) throws Exception {
Class<PrivateTest> cls = (Class<PrivateTest>) Class.forName("reflectDemo.PrivateTest");
PrivateTest o = cls.newInstance();
Field field = cls.getDeclaredField("name");
field.setAccessible(true);
field.set(o, "你好Kitty");
Method declaredMethod = cls.getDeclaredMethod("getName");
String name = (String) declaredMethod.invoke(o);
System.out.println(name);
}
}
练习二
使用反射打印File所有的构造器,并使用newInstance创建“D://test.txt”文件。
public class Test {
public static void main(String[] args) throws Exception {
Class<File> cls = (Class<File>) Class.forName("java.io.File");
Constructor[] constructors = cls.getDeclaredConstructors();
for(Constructor c:constructors){
c.setAccessible(true);
// 拼接构造方法
// System.out.print(Modifier.toString(c.getModifiers())+"\t");
// System.out.print(c.getName()+"(");
// Parameter[] parameters = c.getParameters();
// for (int i = 0; i < parameters.length; i++) {
// if(i==parameters.length-1){
// System.out.print(parameters[i].getType().getName()+" "+parameters[i].getName());
// break;
// }
// System.out.print(parameters[i].getType().getName()+" "+parameters[i].getName()+",");
// }
// System.out.println(")");
// 直接打印构造方法
System.out.println(c);
}
Constructor<File> declaredConstructor = cls.getDeclaredConstructor(String.class);
File file = declaredConstructor.newInstance("D://mytest.txt");
Method declaredMethod = cls.getDeclaredMethod("createNewFile");
declaredMethod.invoke(file);
System.out.println("创建成功");
}
}
此处使用了两种打印构造方法的方式,第一种是拼接,将构造方法的各部分信息拿到,并手动拼接,第二种是直接打印,但是第二种方式没用参数名,只有参数类型。
创建文件我使用的是public java.io.File(java.lang.String)构造方法,该方法是public的,因此不需要爆破,当然,也可以使用其他构造方法创建实例。
参考
本文参考于B站韩顺平老师的视频内容。地址为https://www.bilibili.com/video/BV1g84y1F7df,有兴趣的可以区看看,讲的挺不错。