初识反射
反射(Reflection)被视为动态预言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
反射机制提供的功能
1.在运行时判断任意一个对象所属的类。
2.在运行时构造任意一个类的对象。
3.在运行时判断任意一个类所具有的成员变量和方法。
4.在运行时调用任意一个对象的成员变量和方法。
5.生成动态代理。
反射相关的主要API
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造方法
Class类
Class类相当于一个照妖镜,通过镜子可以得到的信息:某个类的属性、方法和构造器、某个类实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个类的有关信息。
在Object类中定义了以下方法,此方法被所有子类继承:
public final native Class<?> getClass();
以上的方法返回值的类型是一个Class类,此类是java反射的源头。
正常方式:1.引入需要的“类”名称 > 2.通过new实例化 > 3.取得实例化对象
反射方式:1.实例化对象 > 2.getClass()方法 > 3.得到完整的“类”名称
Class类常用的方法
Class clazz = Person.class;
clazz.newInstance();//调用缺省构造函数,返回该class对象的一个实例
clazz.getName(); //返回此class对象所表示的实体名称
clazz.getSuperclass();//返回当前class对象的父类的class对象
clazz.getInterfaces();//返回当前class对象的接口数字
clazz.getClassLoader();//返回该类的类加载器
clazz.getAnnotations();//返回当前class对象的所有注解
反射的应用
测试类Person.class
public class Person {
private String name;
private int 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;
}
}
实例化Class类对象的四种方法
1.前提:已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高。
//Class类:类的类Person.class
Class clazz = Person.class;
2.前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象。
Person person = new Person();
//getClass是继承Object的方法,返回实例所属的类的信息
Class clazz = person.getClass();
3.前提:已知一个类的全类名,可通过Class类的静态方法forName()获取。
//通过类所在路径全名,获取这个类的相关信息
Class clazz = Class.forName("com.ms.reflectdemo.Person");
//调用此类的无参构造方法,得到此类的实例
Object obj = clazz.newInstance();
4.前提:已知一个类的全类名,可通过类加载器loadClass()获取。
//通过类所在路径全名,获取这个类的相关信息
Class clazz = this.getClass().getClassLoader().loadClass("com.ms.reflectdemo.Person");
//调用此类的无参构造方法,得到此类的实例
Object obj = clazz.newInstance();
获取成员变量
//getField只能获取有访问权限的成员变量,获取无访问权限的成员变量会抛异常
//Field field = clazz.getField("name");
//getDeclaredField能获取该类的成员变量(包括私有、共有、保护)
Field field2 = clazz.getDeclaredField("name");
//设置字段可以访问,越过访问权限的限制
field2.setAccessible(true);
//设置成员变量name的值
field2.set(obj,"张三");
//获取成员变量name的值
System.out.println(field2.get(obj));
//获取类中public声明的所有成员变量
Field[] fields = clazz.getFields();
//getDeclaredFields获取所有的成员变量
Field[] fields2 = clazz.getDeclaredFields();
获取方法
//获取类中public声明的无参方法
Method method = clazz.getMethod("getName");
//获取类中的方法(包括私有、共有、保护)
Method method2 = clazz.getDeclaredMethod("getName");
//获取类中public声明的有参方法
Method method3 = clazz.getMethod("setName", String.class);
//设置方法可以访问,越过访问权限的限制
method.setAccessible(true);
//调用setName()方法
method3.invoke(obj,"李四");
//调用getName()方法
System.out.println(method2.invoke(obj));
//获取类中所有public方法
Method[] methods = clazz.getMethods();
//获取类中所有方法
Method[] methods2 = clazz.getDeclaredMethods();
类的加载过程
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
1.类的加载:将类的class文件读入内存中,并为之创建一个java.lang.Class对象。此过程由类加载器完成。
2.类的连接:将类的二进制数据合并到JRE中
3.类的初始化:JVM负责对类进行初始化
类加载器(ClassLoader)
类加载器是用来把类(class)装载进内存的。JVM规范定义了两种类型的类加载器:启动类加载器(bootstrap)和用户自定义加载器(user-defined class loader)。JVM在运行时会产生3个类加载器组成的初始化加载器层次结构。
引导类加载器(Bootstrap ClassLoader):用C++编写的,是JVM自带的类加载器,负责加载java平台核心库。该加载器无法直接获取。
扩展类加载器(Extension ClassLoader):负责加载jre/lib/ext目录下的jar包或-D java.ext.dirs指定目录下的jar包。
系统类加载器(System ClassLoader):负责加载java classpath或-D java.class.path所指目录下的类和jar包,是最常用的加载器。
public class TestClassLoader {
public static void main(String[] args) throws IOException {
//1.获取一个系统类加载器
ClassLoader sysLoader = ClassLoader.getSystemClassLoader();
//2.获取系统类加载器的父类加载器,即扩展类加载器
ClassLoader extLoader = sysLoader.getParent();
//3.获取扩展类加载器的父类加载器,即引导类加载器;获取结果为null
ClassLoader bootLoader = extLoader.getParent();
//4.通过获取Class的实例,然后获取加载这个类的类加载器
Class clazz = TestClassLoader.class;
ClassLoader clazzLoader = clazz.getClassLoader();
//5.所有lang包中的类都属于核心类库,由引导类加载器加载。
}
}
通过类加载器获取properties配置文件
//通过类加载器获取配置文件信息,在类加载的时候执行一次,提高程序执行效率
Properties properties = new Properties();
properties.load(clazzLoader.getResourceAsStream("dbcofig.properties"));
String username = properties.getProperty("username");
还有什么方法可以用请查看源码,哪里不会点哪里。