javaSE基础学习笔记 day21
反射
-
在反射前,如果想要操作类的属性需要:
① new 一个该类的对象
②通过对象调用属性或方法,在类的外部无法通过对象调用其内部的私有结构。 -
简介:Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
关于 java.lang.Class 类的理解
- 类的加载过程:
程序经过 javac.exe 编译后,会生成一个或多个字节码文件(.class),接着用 java.exe 对某个字节码文件进行解释运行。相当于把某个字节码文件加载到内存中,这个过程称为类的加载。
加载到内存中的类,就是运行时类,此类就会作为 Class 类的一个实例。
换句话说, Class 类的一个实例,就对应着一个运行时的类。 - 加载到内存中的运行时类,就会缓存一段时间,在缓冲区时间内,可通过不同方式获取此运行时类。
获取 Class 类的实例的方式
- 通过运行时的类:类名.class
- 通过类的对象:对象名.getClass()
- 调用 Class 类的 static 方法:Class.forName(String classPath)(参数为类的全类名)
Class类的对象可以是那些结构?
- class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
- interface:接口
- []:数组
- enum:枚举
- annotation:注解@interface
- primitive type:基本数据类型
- void
注意:只要数组的元素类型和维度一样,就是一个 Class 类的实例。
了解 ClassLoader
简介:类加载器作用是用来把类(class)装载进内存的。JVM 规范定义了如下类型的类的加载器。
- 引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。
- 扩展类加载器:负责jre/lib/ext目录下的jar包或 –D java.ext.dirs 指定目录下的jar包装入工作库
- 系统类加载器:负责java –classpath 或 –D java.class.path所指的目录下的类与jar包装入工作库,是最常用的加载器
举例:通过 ClassLoader 加载配置文件
注意:默认路径在当前 Module 的 src 下
public void test() {
InputStream is = null;
try {
//获取系统类加载器
ClassLoader scl = ClassLoader.getSystemClassLoader();
//文件内容为:
//name="Tom"
//age="18"
is = scl.getResourceAsStream("info.properties");
Properties pro = new Properties();
//加载输入流
pro.load(is);
System.out.println(pro.getProperty("name"));//Tom
System.out.println(pro.getProperty("age"));//18
} catch (IOException e) {
e.printStackTrace();
} finally {
if(is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
通过反射创建对应的运行时类的对象
public void test1() throws Exception {
//1.获取运行时类
Class cls = Person.class;
/*
2.调用newInstance()方法
① 相当于调用了类的空参构造器
② 要想要正常创建,必须要有空参的构造器,访问权限要足够,通常为 public 的
*/
Person p = (Person) cls.newInstance();
}
获取运行时类的完整结构
- 获取属性
① getFields():获取当前运行时类及其父类中声明为 public 的属性。
② getDeclaredFields():获取当前运行时类中所有的属性,不包含父类中的。 - 获取方法
① getMethods():获取当前运行时类及其父类中声明为 public 的方法。
② getDeclaredMethods():获取当前运行时类中所有的方法,不包含父类中的。 - 获取构造器
① getConstructors():获取当前运行时类中声明为 public 的构造器。
② getDeclaredConstructors():获取当前运行时类中所有的构造器。 - 获取运行时类的父类
① getSuperclass()
② getGenericSuperclass():获取带泛型的父类
③ 如何获取带泛型的父类的泛型:
public void test1() throws Exception {
Class cls = Person.class;
Type gs = cls.getGenericSuperclass();
ParameterizedType pt = (ParameterizedType) gs;
Type[] ata = pt.getActualTypeArguments();
System.out.println(ata[0].getTypeName());
}
调用运行时类的指定结构
- 获取指定的属性
① public Field getField(String name) 返回此Class对象表示的类或接口的指定的 public 的属性。()由于类内的属性多为 private 的,故基本不使用此方法。
② public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的属性。 - 获取指定的方法
① getDeclaredMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
如何调用指定的结构?
先提供一个测试用的 Person 类
public class Person extends Creature<String> {
private String name;
public int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
private int sleep(int hour){
System.out.println("睡了" + hour + "小时");
return hour;
}
}
public void test1() throws Exception {
//1.获取运行时类
Class cls = Person.class;
/*
2.调用newInstance()方法
① 相当于调用了类的空参构造器
② 要想要正常创建,必须要有空参的构造器,访问权限要足够,通常为 public 的
*/
Person p = (Person) cls.newInstance();
p.setName("Tom");
p.setAge(18);
//修改指定属性,该属性为私有
//先获取指定属性的对象
Field name = cls.getDeclaredField("name");
//设置该属性为可访问
name.setAccessible(true);
//调用set方法修改属性,参数1为要修改的对象,参数2为要修改为的值
name.set(p, "Alice");
System.out.println(p);//Person{name='Alice', age=18}
//调用指定方法,该方法为私有的
//先获取指定方法的对象
Method sleep = cls.getDeclaredMethod("sleep", int.class);
//设置方法为可访问
sleep.setAccessible(true);
//通过调用该方法对象的 invoke() 方法来调用该方法
//如有该方法有返回值,则 invoke 的返回值与该方法返回值一样
//如无返回值,则返回 null
int a = (int) sleep.invoke(p, 10);//睡了10小时
System.out.println(a);//10
}