康师傅说:"获取Class类的实例、获取运行时类对象、调用运行时类的指定结构这三个是比较重要的,需要掌握,其他的理解为主“。
概念描述
① Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
②加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
Java反射机制提供的功能
①在运行时判断任意一个对象所属的类
②在运行时构造任意一个类的对象
③在运行时判断任意一个类所具有的成员变量和方法
④在运行时获取泛型信息
⑤在运行时调用任意一个对象的成员变量和方法
⑥在运行时处理注解
⑦生成动态代理
反射相关的主要API
①java.lang.Class:代表一个类
②java.lang.reflect.Method:代表类的方法
③java.lang.reflect.Field:代表类的成员变量
④java.lang.reflect.Constructor:代表类的构造器
Class类的概述
Object类中定义了以下的方法,此方法将被所有子类继承:public final Class getClass()
以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。
通过Class类对象的调用可以可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些口。
获取Class类的实例
四种实现方法(第四类不做说明,基本不用):
①调用运行时类的属性.class
②通过运行时类的对象,调用getClass()
③调用Class的静态方法:forName(String classPath)classPath为全路径
实现代码展示
//获取运行时类
@Test
public void test3() throws ClassNotFoundException {
//方式一:调用运行时类的属性.class
Class<Person> clazz1 = Person.class;
System.out.println(clazz1);
//方式二:通过运行时类的对象,调用getClass()
Person p1 = new Person();
Class<? extends Person> clazz2 = p1.getClass();
System.out.println(clazz2);
//方式三:调用Class的静态方法:forName(String classPath)classPath为全路径
Class<?> clazz3 = Class.forName("day18.com.it.java0.Person");
System.out.println(clazz3);
System.out.println(clazz1 == clazz2);
System.out.println(clazz1 == clazz3);
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class<?> clazz4 = classLoader.loadClass("day18.com.it.java0.Person");
System.out.println(clazz4);
System.out.println(clazz1 == clazz4);
}
实现结果
类的加载过程
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
ClassLoader的理解
类加载器作用是用来把类(class)装载进内存的。JVM 规范定义了如下类型的类的加载器。
三种加载器
①引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取扩展
②类加载器:负责jre/lib/ext目录下的jar包或 –D java.ext.dirs 指定目录下的jar包装入工作库
③系统类加载器:负责java –classpath 或 –D java.class.path所指的目录下的类与jar包装入工作 ,是最常用的加载
利用Load实现配置文件的获取
public class LoadTest {
/*
测试:使用Properties来读取配置文件
*/
@Test
public void test1() throws Exception {
Properties pros = new Properties();
//此时的默认文件在当前的module下
//读取配置文件的方式一:
FileInputStream fis = new FileInputStream("jdbc.properties");
pros.load(fis);
//此时的默认文件在当前module的src下
//读取配置文件的方式二:
// ClassLoader clazz = LoadTest.class.getClassLoader();
// InputStream is = clazz.getResourceAsStream("jdbc1.properties");
// pros.load(is);
String name = pros.getProperty("name");
String password = pros.getProperty("password");
System.out.println("name = " + name + ", password = " + password);
}
}
①根据全类名获取对应的Class对象
②调用指定参数结构的构造器,生成Constructor的实例
③通过Constructor的实例创建对应类的对象,并初始化类属性
通过newInstance()获取运行时类的对象
@Test
public void test1() throws InstantiationException, IllegalAccessException {
Class<Person> clazz = Person.class;
/*
new Instance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参构造器
要想此方法正常的创建运行时类的对象,要求:
1.运行时类必须提供空参构造器
2.空参构造器的访问权限得够。通常,设置为public.
在javabean中提供一个public的空参构造器。原因:
1.便于通过反射,创建运行时类的对象
2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
*/
Person person = clazz.newInstance();
System.out.println(person);
}
动态获取运行时类的对象
@Test
public void test2(){
int num = new Random().nextInt(3);
String classPath = "";
switch (num){
case 0:
classPath = "java.util.Date";
break;
case 1:
classPath = "java.sql.Date";
break;
case 2:
classPath = "day18.com.it.java0.Person";
break;
}
try {
Object obj = getInstance(classPath);
System.out.println(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
public Object getInstance(String classPath) throws Exception {
Class clazz = Class.forName(classPath);
return clazz.newInstance();
}
通过反射获取运行时类的完整结构:Field(属性)、Method(方法)、Constructor(构造器)、Superclass(父类)、Interface(接口)、Annotation(注解)
【理解即可,不用掌握】
对于这些直接看代码理解更好一点
获取属性
@Test
public void test1(){
Class<Person> clazz = Person.class;
//获取属性结构
//getFields():获取当前运行类及其父类中声明为public访问权限的属性
Field[] fields = clazz.getFields();
for (Field f : fields){
System.out.println(f);
}
System.out.println("********************");
//getDeclaredFields()获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields){
System.out.println(f);
}
}
通过反射访问属性的权限修饰符 数据类型 变量名
//通过反射访问权限修饰符 数据类型 变量名
@Test
public void test2(){
Class<Person> clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields){
//权限修饰符
int modifier = f.getModifiers();
System.out.print(Modifier.toString(modifier) + "\t");
//数据类型
Class<?> type = f.getType();
System.out.print(type + "\t");
//变量名
String name = f.getName();
System.out.println(name);
}
}
获取方法
@Test
public void test1() {
//getMethods()获取当前运行时类及其父类中声明为public权限的方法
Class<Person> clazz = Person.class;
Method[] methods = clazz.getMethods();
for (Method m : methods) {
System.out.println(m);
}
//getDeclaredMethods()获取当前运行时类中声明的所有方法
//获取当前运行时类中所声明的所有方法
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m : declaredMethods) {
System.out.println(m);
}
}
获取方法的权限修饰符 返回值类型 方法名(形参列表)throws 异常,以及注解
@Test
public void test2() {
Class<Person> clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m : declaredMethods) {
//获取注解
Annotation[] annotations = m.getAnnotations();
for (Annotation a : annotations) {
System.out.println(a);
}
//获取权限修饰符
System.out.print(Modifier.toString(m.getModifiers()) + "\t");
//返回值类型
Class<?> returnType = m.getReturnType();
System.out.print(returnType + "\t");
//方法名
System.out.print(m.getName());
//形参列表
System.out.print("(");
Class<?>[] parameterTypes = m.getParameterTypes();//获取当前方法的形参列表结合
if (!(parameterTypes == null && parameterTypes.length == 0)) {
for (int i = 0; i < parameterTypes.length; i++) {
if (i == parameterTypes.length - 1) {
System.out.print(parameterTypes[i].getName() + "args_" + i);
}
System.out.print(parameterTypes[i].getName() + "args_" + i + ",");
}
System.out.print(")");
}
//获取异常信息
Class<?>[] exceptionTypes = m.getExceptionTypes();
if (exceptionTypes.length > 0) {
System.out.print("throws");
for (int i = 0; i < exceptionTypes.length; i++) {
if (i == exceptionTypes.length - 1) {
System.out.print(exceptionTypes[i].getName());
}
System.out.print(exceptionTypes[i].getName() + ",");
}
}
System.out.println();
}
}
获取构造器结构
//获取构造器结构
@Test
public void test1() throws NoSuchMethodException {
//getConstructors():获取当前运行时类中声明为public的构造器
Class<Person> clazz = Person.class;
Constructor<?>[] constructor = clazz.getConstructors();
for (Constructor c : constructor){
System.out.println(c);
}
System.out.println();
//getDeclaredConstructors():获取当前运行时类中声明的所有构造器
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor c : declaredConstructors){
System.out.println(c);
}
}
对于其他的使用相同的方法即可获取,这里就不展示了
获取指定的属性、方法、构造器
【这里比较重要需要掌握】
调用运行时类中指定的属性
@Test
public void testField1() throws Exception{
//获取Class类的实例
Class<Person> clazz = Person.class;
//2.获取一个运行时类的对象
Person p1 = clazz.newInstance();
//3.getDeclaredField():获取指定的属性
Field name = clazz.getDeclaredField("name");
//4.将属性设置为可用
name.setAccessible(true);
//5.设置属性数值
name.set(p1,"Jack");
System.out.println(name.get(p1));
}
获取指定的方法
@Test
public void testMethod() throws Exception {
//创建Class类的实例
Class<Person> clazz = Person.class;
//创建运行时类的对象
Person p1 = clazz.newInstance();
//获取指定的某个方法
//getDeclaredMethod():参数1:知名获取的方法名称 参数2:指明获取方法的参数列表
Method show = clazz.getDeclaredMethod("show", String.class);
//保证此方法是可访问的
show.setAccessible(true);
//调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
//invoke()的返回值即为对应类中调用方法的返回值
Object obj = show.invoke(p1, "CHN");
System.out.println(obj);
System.out.println("*************如何调用静态方法***************");
//没有返回值是调用方法返回的是null
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
Object o = showDesc.invoke(p1);
System.out.println(o);
}
获取指定的构造器
@Test
public void testConstructor() throws Exception{
//创建Class类的实例
Class<Person> clazz = Person.class;
//获取指定的构造器
Constructor<Person> constructor = clazz.getDeclaredConstructor(String.class);
//保证获取的构造器是可用的
constructor.setAccessible(true);
//创建一个运行时类的对象
Person person = constructor.newInstance("Tom");
System.out.println(person);
}
代理设计模式的原理
①使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
②静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。
③动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
④动态代理使用场合:
(1)调试
(2)远程方法调用
动态代理步骤
①创建一个实现接口InvocationHandler的类,它必须实现invoke方法,以完成代理的具体操作。
②创建被代理的类以及接口
③通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个Subject接口代理
④通过 Subject代理调用RealSubject实现类的方法