一、简介
1.1 反射概述
在程序运行状态中:
- 对于任何一个类,都能够获取这个类的所有属性和方法。
- 对于任意一个对象,都能够调用这个对象的任意一个属性和方法。
这种在运行时动态的获取信息以及动态调用对象的方法的功能称之为反射 reflection。
1.2 类加载器
类加载:把class文件加载到内存(JVM内存)的过程称之为类加载。
类加载器:class文件的加载是与ClassLoader完成的,ClassLoader称之为类加载器。
Java默认有三种类加载器:
**BootstrapClassLoader:**引导启动类加载器,是用c++语言编写,由JVM在启动时加载初始化的,主要负载加载JAVA_HOME/jre/lib下的类库。
**ExtensionClassLoader:**扩展类加载器,它是用java语言编写,且它的父类加载器是Bootstrap。 是由sun.misc.Launcher$ExtClassLoader实现的,主要加JAVA_HOME/jre/lib/ext目录中的类库。它的父加载器是BootstrapClassLoader。
**AppClassLoader:**应用程序类加载器,负责加载应用程序classpath目录下的所有jar和class文 件。它的父加载器为ExtensionClassLoader
可以使用 类名.class.getClassLoader() 获取当前类的类加载器。
class文件读取到内存中会被封装成 java.lang.Class 类的对象。
1.3 双亲委派模型:
如果一个类加载器收到了一个类加载请求。它不会自己去尝试加载这个类,而是把这个请求转交给父类加载器完成。每一层次的类加载器都是如此。因此所有的类加载请求都应该传递到最顶层的的类加载器中,也就是启动类加载器中BootstrapClassLoader。只有到父类加载器反馈自己无法完成这个加载请求(在它的搜索范围内找不到这个类)时,子类加载类才会尝试自己去加载。委派的好处就是避免有些类重复加载。
1.4 反射相关的API
反射相关的api都是在java.lang包中
类、接口名 | 描述 |
---|---|
Class | 表示运行中的类和接口 |
Constructor | 表示类中的构造函数 |
Method | 表示类中的方法 |
Filed | 表示类中的属性 |
Package | 表示类所属的包 |
Modifier | 表示修饰符 |
Parameter | 表示方法的参数 |
二、获取Class对象
2.1 简介
运行中的class文件是通过Class对象来表示
Class对象是在类加载时由JVM自动创建的,一个类在JVM中只会有一个Class对象。
Class类没有公共的构造方法,不能自己创建Class对象,但是可以获取其实例进行操作。
Class是反射的核心类,要想操作类中的属性和方法,都必须从获取Class对象开始。
2.2 获取Class对象的方式
2.2.1 调用类的class属性
public class Person {
private String name;
private String gender;
public int age;
public Person() {
}
public Person(String name) {
this.name = name;
}
private Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setNameAge(String name,int age){
this.name= name;
this.age= age;
}
public int getAge() {
return age;
}
private void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
'}';
}
}
public static void main(String[] args) {
//已知道有Person这个类,调用该类的class属性
Class<Person> personClass = Person.class;
System.out.println("类全名:" + personClass.getName());//类全名:包名+类名
System.out.println("类名:" + personClass.getSimpleName());
System.out.println("类所在的包:" + personClass.getPackage());
}
2.2.2 调用对象的getClass()方法
public static void main(String[] args) {
//创建一个person对象
Person person = new Person();
//获取Class对象,调用getClass()方法
Class<? extends Person> personClass = person.getClass();
System.out.println("类全名:" + personClass.getName());//类全名:包名+类名
System.out.println("类名:" + personClass.getSimpleName());
System.out.println("类所在的包:" + personClass.getPackage());
}
2.2.3 调用Class类的forName()静态方法(推荐使用)
public static void main(String[] args) throws ClassNotFoundException {
//forName方法中需要填写类的全名(包名+类名)
Class personClass = Class.forName("com.ly.zreflection.Person");
System.out.println("类名:" +personClass.getSimpleName());
System.out.println("类加载器:" + personClass.getClassLoader());
}
2.3 特殊类的类对象
基本数据类型的类对象:
基本数据类型.class
包装类.type
基本数据类型包装类对象:
包装类.class
三、操作Constructor
3.1 获取公有的无参构造方法,再通过无参构造创建对象
public static void main(String[] args) throws Exception {
//获取类对象
Class clzss = Class.forName("com.ly.zreflection.Person");
//获取一个无参构造方法
Constructor constructor = clzss.getConstructor();//获取公有
//获取对象
Object o = constructor.newInstance();
System.out.println("无参构造获取对象:" + o);
}
3.2 获取公有的全参构造方法,创建对象
public static void main(String[] args) throws Exception {
//获取类对象
Class clzss = Class.forName("com.ly.zreflection.Person");
//获取全参的构造方法(公有)
Constructor allParameter = clzss.getConstructor(String.class, String.class, int.class);
//创建对象并给对象赋值
Object o1 = all.newInstance("lisi", "男", 18);
System.out.println("全参构造创建对象:" + o1);
}
3.3 获取私有的有参构造,创建对象
public static void main(String[] args) throws Exception {
//获取类对象
Class clzss = Class.forName("com.ly.zreflection.Person");
//获取指定参数的私有的构造方法
Constructor pri = clzss.getDeclaredConstructor(String.class, int.class);
//true则表示忽略访问权限检查 (可以访问任何权限的方法)
pri.setAccessible(true);
Object o = pri.newInstance("zhangsan", 20);
System.out.println("私有的有参构造创建对象:" + o);
}
3.4 获取所有的构造方法
public static void main(String[] args) throws Exception {
//获取类对象
Class clzss = Class.forName("com.ly.zreflection.Person");
//获取所有的构造方法(包含公有和私有的)
Constructor[] declaredConstructors = clzss.getDeclaredConstructors();
for (Constructor constructor :declaredConstructors){
System.out.println(constructor);
}
}
四、操作Method
4.1 获取全部的方法
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
//获取类对象
Class clzss = Class.forName("com.ly.zreflection.Person");
//通过类对象获取所有方法(包含公有、私有)
Method[] methods = clzss.getDeclaredMethods();
for (Method method:methods){
System.out.println(method);
}
}
4.2 获取指定参数类型及个数的方法,并通过方法给对象赋值
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
//获取类对象
Class clzss = Class.forName("com.ly.zreflection.Person");
//获取一个无参构造
Constructor declaredConstructor = clzss.getConstructor();
//通过无参构造创建一个对象
Object o1 = declaredConstructor.newInstance();
//获取类中指定方法 方法名 参数类型 参数类型
Method setNameAndAge = clzss.getMethod("setNameAge", String.class, int.class);
//参数1. 哪个对象要执行的setNameAndAge方法
//参数2. 调用方法传递的参数 0-n
setNameAndAge.invoke(o1,"wangwu",20);
System.out.println(o1);
}
4.3 获取私有方法,并给对象赋值
public static void main(String[] args) throws Exception{
//获取类对象
Class clazz = Class.forName("com.ly.zreflection.Person");
//获取一个无参构造
Constructor constructor = clazz.getConstructor();
//通过无参构造创建一个对象
Object o = constructor.newInstance();
//通过类对象获取一个私有的方法
Method setAge = clazz.getDeclaredMethod("setAge", int.class);
setAge.setAccessible(true);//打开权限,为了后面可以通过方法设置值
//调用invoke方法,给对象赋值
setAge.invoke(o,18);
System.out.println("通过setAge方法给对象赋值:" + o);
}
五、操作Filed
5.1 获取一个公有的类属性,并给对象赋值
public static void main(String[] args) throws Exception{
//获取类对象
Class clazz = Class.forName("com.ly.zreflection.Person");
//获取一个公共的类属性
Field age = clazz.getField("age");//传入一个属性名称
System.out.println("这个属性的名称是:" + age.getName());
//通过类对象获取一个无参构造
Constructor constructor = clazz.getConstructor();
//通过无参构造创建一个对象
Object o = constructor.newInstance();
//给对象赋值
age.set(o,20);//第一个参数是要给赋值的对象,第二个为要赋的值
System.out.println("通过属性为对象赋值:" + o);
}
5.2 获取一个私有的类属性,并给对象赋值
public static void main(String[] args) throws Exception{
//获取类对象
Class clazz = Class.forName("com.ly.zreflection.Person");
//通过类对象获取一个无参构造
Constructor constructor = clazz.getConstructor();
//通过无参构造创建一个对象
Object o = constructor.newInstance();
//获取一个私有的类属性
Field name = clazz.getDeclaredField("name");//传入一个属性名称
//打开权限
name.setAccessible(true);
//给对象赋值
name.set(o,"jack");//第一个参数是要给赋值的对象,第二个为要赋的值
System.out.println("这个属性的名称是:" + name.getName());
System.out.println("通过属性为对象赋值:" + o);
}
5.3 获取类中所有的属性
public static void main(String[] args) throws Exception{
//获取类对象
Class clazz = Class.forName("com.ly.zreflection.Person");
//获取一个所有的类属性(私有、公有)
Field[] fields = clazz.getDeclaredFields();
for (Field filed:fields) {
System.out.println("属性名称为:" + filed.getName());
System.out.println("属性的类型为:" + filed.getGenericType());
System.out.println();
}
}