Java反射机制
一、 概述
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
-
创建对象的正常方式:
引入需要的“包类”名称 ==> 通过new关键字实例化 ==> 取得实例化对象
-
反射方式:
实例化对象==> getClass()方法 ==>得到完整的“包类”名称
-
简而言之 : 通常的方式是通过类来得到对象 , 而反射是通过对象得到类的过程
动态语言&静态语言:
-
动态语言:是一类在运行时可以改变其结构的语言。通俗讲就是:在运行时代码可以根据某些条件改变自身结构。
主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。
-
静态语言:运行时结构不可变的语言就是静态语言。如:Java、C、C++。
Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。
二、Class类
2.1 概述
在Object类中定义了以下的方法,此方法将被所有子类继承:
public final Class getClass()
:该方法的返回值类型是一个Class类,此类是Java反射的源头,即:可以通过对象反射求出类的名称。
Class类的几点注意事项:
- Class本身也是一个类。
- Class对象只能由系统建立对象。
- 一个加载的类在 JVM 中只会有一个Class实例。
- 一个Class对象对应的是一个加载到 JVM 中的一个.class文件。
- 通过Class可以完整地得到一个类中的所有被加载的结构。
- Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。
2.2 获取Class类对象的三种方式
-
已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法
forName()
获取,可能抛出ClassNotFoundException
如:
Class aClass1 = Class.forName(“java.lang.String”);
-
已知某个类的实例,调用该实例的
getClass()
方法获取Class对象。如:
Class aClass2="str".getClass();
-
若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高。
如:
Class aClass3=String.class;
第4种了解即可:
ClassLoader cl = this.getClass().getClassLoader();
Class aClass4 = cl.loadClass(“类的全类名”);
2.3 常用方法
获取成员变量的方法:
public Field[] getField(String name)
:获取指定的公共成员变量。public Field[] getFields()
:获取所有的成员变量(包含从父类继承过来的)。public Field[] getDeclaredField(String name)
:获取指定的成员变量(包括私有成员变量)。public Field[] getDeclaredFields()
:获取所有的成员变量(包含私有的,也包含从父类继承过来的)。
获取构造方法的方法:
public Constructor<T> getConstructor(Class<?>... parameterTypes)
:获取单个的构造方法不包含私有构造方法。public Constructor<?>[] getConstructors()
:获取所有的构造方法(不包含私有构造方法)。public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
:获取单个的构造方法(包含私有构造方法)。public Constructor<?>[] getDeclaredConstructors()
:获取所有的构造方法(包括私有构造方法)。
获取成员方法的方法:
-
public Method getMethod(String name,Class<?>... parameterTypes)
:获取单个的方法 不包含私有的方法。 -
public Method[] getMethods()
:获取所有的公共的成员方法不包含私有的,包含从父类继承过来的过来的公共方法。 -
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
:获取单个方法包括私有的方法。 -
public Method[] getDeclaredMethods()
:获取自己的所有成员方法(包含私有的方法)。
2.4 Class对象
哪些类有Class对象?
- class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
- interface:接口
- []:数组
- enum:枚举
- annotation:注解@interface
- primitive type:基本数据类型
- void
Class c1 = Object.class;
Class c2 = Comparable.class;
Class c3 = String[].class;
Class c4 = int[][].class;
Class c5 = ElementType.class;
Class c6 = Override.class;
Class c7 = int.class;
Class c8 = void.class;
Class c9 = Class.class;
int[] a = new int[10];
int[] b = new int[100];
Class c10 = a.getClass();
Class c11 = b.getClass();
// 只要元素类型与维度一样,就是同一个Class
System.out.println(c10 == c11);//true
2.5 动态创建对象执行方法
实体类:
public class User{
private int id;
private String name;
private int age;
public User() {
}
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类
//动态的创建对象,通过反射
public class Demo09 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获得Class对象
Class c1 = Class.forName("org.westos.reflection.User");
//构造一个对象
/*User user = (User) c1.newInstance(); //本质上是调用了类的无参构造器
System.out.println(user);*/
//通过构造器创建对象
/*Constructor constructor = c1.getDeclaredConstructor(int.class, String.class, int.class);
User user2 = (User) constructor.newInstance(001, "贾伦", 24);
System.out.println(user2);*/
//通过反射调用普通方法
User user3 = (User) c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke:激活的意思
//参数(对象,"方法的值")
setName.invoke(user3, "张三");
System.out.println(user3.getName());
//通过反射操作属性
User user4 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,我们需要关闭权限检测
name.setAccessible(true);
name.set(user4,"李四");
System.out.println(user4.getName());
}
}
三、类的加载和类加载器(ClassLoader)
3.1 类的加载过程
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。如下图。
3.2 类加 载器
负责将.class文件加载到内在中,并为之生成对应的Class对象。
类加载器的分类:
Bootstrap ClassLoader
引导类加载器:
是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。
Extension ClassLoader
扩展类加载器:
负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录。
Sysetm ClassLoader
系统类加载器:
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
代码演示:
public class Demo07 {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//获取系统类加载器的父类加载器-->扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
//获取扩展类加载器的父类加载器-->根加载器(C/C++),又叫引导类加载器
ClassLoader parentParent = parent.getParent();
System.out.println(parentParent);
//测试当前类是哪个加载器加载的
ClassLoader classLoader = Class.forName("org.westos.reflection.Demo07").getClassLoader();
System.out.println(classLoader);
//测试JDK内置的类是谁加载的
classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);
}
}
总结:
- 用户自定义的类由系统类加载器加载
- JDK 内置的类由根加载器加载(又叫引导类加载器
3.3 双亲委派机制
-
简述 : 当系统的某个加载器接收到加载请求时,他会首先将加载认为委托给父类加载器,如果父类加载器能够加载当前类,则任务完成。父类不能完成加载任务,自己才回去完成。
-
双亲委派机制存在的意义: 防止内存中出现多份同样的字节码。
-
说明:
- 若要运行运行自己写的类,如java.lang.String 类(与系统的String类重名),这时如果使用系统的类加载器加载,默认加载的是Java自己的String类,自己写的类不能得到加载。这时想加载自己的String类,需要自定义类加载器完成。