文章目录
返回 总目录
参考资料
https://www.bilibili.com/video/BV1Kb411W75N?p=636
1. Java反射机制概述
- Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
- 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
1.1 补充:动态语言 vs 静态语言
-
动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在**运行时代码可以根据某些条件改变自身结构。**主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。 -
静态语言
动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。 -
Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。Java的动态性让编程的时候更加灵活
1.2 Java反射机制研究及应用
-
Java反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
-
反射相关的主要API
java.lang.Class
:代表一个类java.lang.reflect.Method
:代表类的方法java.lang.reflect.Field
:代表类的成员变量java.lang.reflect.Constructor
:代表类的构造器
-
反射初体验
通过反射,调用对象指定的属性和方法
通过反射,是可以调用类的私有结构的。比如:私有的构造器、方法、属性- Person类
package pers.chh3213.reflectionDemo; import org.junit.Test; import java.time.Period; public class Person { private String name; public 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; } public Person(String name, int age) { this.name = name; this.age = age; } private Person(String name) { this.name = name; } public Person(){ } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } public void show(){ System.out.println("hello"); } private String showNation(String nation){ System.out.println("中国"); return nation; } }
- 测试类,test01为不使用反射的处理;tets02为使用反射的处理。
package pers.chh3213.reflectionDemo; import org.junit.Test; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * Created with IntelliJ IDEA. * * @author : chh3213 * @version : 1.0 * @Project : reflection * @Package : pers.chh3213.reflectionDemo * @ClassName : ReflectTest.java * @createTime : 2022/1/22 20:02 * @Email : * @Description : */ public class ReflectTest { /** * 不使用反射的情况 */ @Test public void test01(){ Person person = new Person("jack",20); person.age=15; System.out.println(person.toString()); person.show(); //在Person类的外部,不可以通过Person类的对象调用其内部私有的结构。 //比如:name、showNation以及私有的构造器。 } /** * 使用反射的情况 */ @Test public void test02() throws Exception { //1.通过反射,创建Person类的对象 Class clazz = Person.class; Constructor cons = clazz.getConstructor(String.class,int.class); Object obj = cons.newInstance("john",21); Person person = (Person) obj; System.out.println(person.toString()); //2.通过反射,调用对象指定的属性和方法 //调用属性 Field age = clazz.getDeclaredField("age"); age.set(person,11); System.out.println(person.toString()); //调用方法 Method show = clazz.getDeclaredMethod("show"); show.invoke(person); System.out.println("============"); //通过反射,是可以调用Person类的私有结构的。比如:私有的构造器、方法、属性 //调用私有的构造器 Constructor constructor = clazz.getDeclaredConstructor(String.class); constructor.setAccessible(true); Person p = (Person) constructor.newInstance("kk"); System.out.println(p); //调用私有的属性 Field name = clazz.getDeclaredField("name"); name.setAccessible(true); name.set(p,"zz"); System.out.println(p); //调用私有的方法 Method showNation = clazz.getDeclaredMethod("showNation",String.class); showNation.setAccessible(true); String nation = (String) showNation.invoke(p,"china"); // 相当于String nation = p1.showNation("FaceBook") System.out.println(nation); } }
- 疑问1:通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用那个?
建议:直接new的方式。 - 什么时候会使用:反射的方式。 反射的特征:动态性
- 疑问2:反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?
不矛盾。
- Person类
2. 理解Class类并获取Class实例
-
关于java.lang.Class类的理解
- 类的加载过程:
程序经过Javac.exe命令后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。 - 换句话说,Class的实例就对应着一个运行时类。
- 类的加载过程:
-
在Object类中定义了以下的方法,此方法将被所有子类继承:
public final Class getClass()
以上的方法返回值的类型是一个Class
类,此类是Java反射
的源头,实际上所谓反射
从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。 -
对于每个类而言, JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个结构(
class/interface/enum/annotation/primitive type/void/[]
)的有关信息。- Class本身也是一个类
- Class 对象只能由系统建立对象
- 一个加载的类在 JVM 中只会有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由哪个 Class 实例所生成
- 通过Class可以完整地得到一个类中的所有被加载的结构
- Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
2.1 Class类的常用方法
2.2 获取Class类的实例(四种方法)
-
前提: 若已知具体的类,通过类的class属性获取, 该方法最为安全可靠,程序性能最高
实例:Class clazz = String.class;
-
前提: 已知某个类的实例,调用该实例的
getClass()
方法获取Class对象
实例:Class clazz = “www.atguigu.com”.getClass();
-
前提: 已知一个类的全类名,且该类在类路径下, 可通过Class类的静态方法
forName()
获取,可能抛出ClassNotFoundException
实例:Class clazz = Class.forName(“java.lang.String”);
-
其他方式(不做要求)
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“类的全类名”);
import org.junit.Test; import java.lang.annotation.ElementType; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ReflectionTest { /** * 2.换句话说,Class的实例就对应着一个运行时类。 * 3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式 * 来获取此运行时类。 */ @Test public void test3() throws ClassNotFoundException { //方式一: Class c1 = Person.class; System.out.println(c1); //方式二:通过运行时类的对象,调用getClass() Person p1 = new Person(); Class c2 = p1.getClass(); System.out.println(c2); //方式三:调用Class的静态方法:forName(String classPath) Class c3 = Class.forName("java.lang.String"); System.out.println(c3); System.out.println(c1 == c2); System.out.println(c1 == c3); //方式四:使用类的加载器:ClassLoader (了解) ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class c4 = classLoader.loadClass("java.lang.String"); System.out.println(c4); System.out.println(c1 == c4); } }
2.3 哪些类型可以有Class对象
class
:外部类, 成员(成员内部类, 静态内部类), 局部内部类, 匿名内部类interface
: 接口[]
:数组enum
:枚举annotation
:注解@interface
primitive type
:基本数据类型void
-
示例
package pers.chh3213.reflectionDemo; import org.junit.Test; import java.lang.annotation.ElementType; public class ObtainClass { @Test public void test(){ 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); } }
3. 类的加载与ClassLoader的理解
3.1 了解:类的加载过程
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
-
加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的
java.lang.Class
对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要类加载器参与。 -
链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
- 验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题
- 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
-
初始化:
- 执行
类构造器<clinit>()
方法的过程。类构造器<clinit>()
方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。 - 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 虚拟机会保证一个
类的<clinit>()
方法在多线程环境中被正确加锁和同步。
- 执行
public class ClassLoadingTest {
public static void main(String[] args) {
System.out.println(A.m);
}
}
class A {
static {
m = 300;
}
static int m = 100;
}
//第二步:链接结束后m=0
//第三步:初始化后, m的值由<clinit>()方法执行决定
// 这个A的类构造器<clinit>()方法由类变量的赋值和静态代码块中的语句按照顺序合并产生,类似于
// <clinit>(){
// m = 300;
// m = 100;
// }
3.2 了解: 什么时候会发生类初始化?
-
类的主动引用(一定会发生类的初始化)
- 当虚拟机启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用
java.lang.reflect
包的方法对类进行反射调用 - 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
-
类的被动引用(不会发生类的初始化)
-
当访问一个静态域时,只有真正声明这个域的类才会被初始化
当通过子类引用父类的静态变量,不会导致子类初始化 -
通过数组定义类引用,不会触发此类的初始化
-
引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
package pers.chh3213.classLoader; public class ClassLoadingTest { public static void main(String[] args) { // 主动引用:一定会导致A和Father的初始化 // A a = new A(); // System.out.println(A.m); // Class.forName("com.atguigu.java2.A"); // 被动引用 A[] array = new A[5];//不会导致A和Father的初始化 // System.out.println(A.b);//只会初始化Father // System.out.println(A.M);//不会导致A和Father的初始化 } static { System.out.println("main所在的类"); } } class Father { static int b = 2; static { System.out.println("父类被加载"); } } class A extends Father { static { System.out.println("子类被加载"); m = 300; } static int m = 100; static final int M = 1; }
-
3.3 了解: ClassLoader
-
类加载器的作用:
- 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的
java.lang.Class
对象,作为方法区中类数据的访问入口。 - 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
- 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的
-
类加载器作用是用来把类(class)装载进内存的。JVM 规范定义了如下类型的类的加载器。
- 获取一个系统类加载器
ClassLoader classloader = ClassLoader.getSystemClassLoader();
System.out.println(classloader);
- 获取系统类加载器的父类加载器,即扩展类加载器
classloader = classloader.getParent();
System.out.println(classloader);
- 获取扩展类加载器的父类加载器,即引导类加载器(引导类加载器主要负责加载java的核心类库,无法加载自定义类的。)
classloader = classloader.getParent();
System.out.println(classloader);
- 测试当前类由哪个类加载器进行加载
classloader = Class.forName("exer2.ClassloaderDemo").getClassLoader();
System.out.println(classloader);
- 测试JDK提供的Object类由哪个类加载器加载
classloader =Class.forName("java.lang.Object").getClassLoader();
System.out.println(classloader);
- 关于类加载器的一个主要方法:
getResourceAsStream(String str)
:获取类路径下的指定文件的输入流
InputStream in = null;
in=this.getClass().getClassLoader().getResourceAsStream("exer2\\test.properties");
System.out.println(in);
-
示例:使用ClassLoader加载配置文件
package pers.chh3213.classLoader; import org.junit.Test; import java.io.FileInputStream; import java.io.InputStream; import java.util.Properties; /** * Created with IntelliJ IDEA. * * @author : chh3213 * @version : 1.0 * @Project : reflection * @Package : pers.chh3213.classLoader * @ClassName : PropertiesTest.java * @createTime : 2022/1/23 9:31 * @Email : * @Description : */ public class PropertiesTest { @Test public void test01() throws Exception{ Properties properties = new Properties(); //读取配置文件的方式一,此时文件路径默认在当前的module下 //FileInputStream fileInputStream = new FileInputStream("jdbc.properties"); //FileInputStream fileInputStream = new FileInputStream("src/jdbc1.properties"); //properties.load(fileInputStream); //读取配置文件的方式2,此时文件路径默认识别为当前module下的src ClassLoader classLoader = ClassLoadingTest.class.getClassLoader(); InputStream resourceAsStream = classLoader.getResourceAsStream("jdbc1.properties"); properties.load(resourceAsStream); String user = properties.getProperty("user"); String password = properties.getProperty("password"); System.out.println("user=:"+user+";passward:"+password); } }
4. 创建运行时类的对象
4.1 创建运行时类对象
package pers.chh3213.ClassObj;
import org.junit.Test;
import pers.chh3213.reflectionDemo.Person;
/**
* Created with IntelliJ IDEA.
*
* @author : chh3213
* @version : 1.0
* @Project : reflection
* @Package : pers.chh3213.ClassObj
* @ClassName : NewInstanceTest.java
* @createTime : 2022/1/23 9:54
* @Email :
* @Description :通过反射创建对应的运行时类的对象
*/
public class NewInstanceTest {
@Test
public void test() throws Exception{
Class<Person> clazz = Person.class;
/**
* newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。
*
* 要想此方法正常的创建运行时类的对象,要求:
* 1.运行时类必须提供空参的构造器
* 2.空参的构造器的访问权限得够。通常,设置为public。
*
* 在javabean中要求提供一个public的空参构造器。原因:
* 1.便于通过反射,创建运行时类的对象
* 2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
*/
Person obj = clazz.newInstance();
System.out.println(obj);
}
}
4.2 举例体会反射的动态性
//1.根据全类名获取对应的Class对象
String name = “atguigu.java.Person";
Class clazz = null;
clazz = Class.forName(name);
//2.调用指定参数结构的构造器,生成Constructor的实例
Constructor con = clazz.getConstructor(String.class,Integer.class);
//3.通过Constructor的实例创建对应类的对象,并初始化类属性
Person p2 = (Person) con.newInstance("Peter",20);
System.out.println(p2);
-
示例
package pers.chh3213.ClassObj; import org.junit.Test; import java.util.Random; public class NewInstanceTest2 { @Test public void test2(){ for (int i = 0; i < 100; i++) { int num = new Random().nextInt(3); String classpath = ""; switch (num){ case 0: classpath = "java.util.Date"; break; case 1: classpath = "java.lang.Object"; break; case 2: classpath = "pers.chh3213.reflectionDemo.Person"; break; } try { Object obj = getInstance(classpath); System.out.println(obj); }catch (Exception e){ e.printStackTrace(); } } } /** * 创建一个指定类的对象。 * classPath:指定类的全类名 * * @param classPath * @return * @throws Exception */ public Object getInstance(String classPath) throws Exception { Class clazz = Class.forName(classPath); return clazz.newInstance(); } }
5. 获取运行时类的完整结构
5.1 通过反射获取运行时类的完整结构:
Field
、 Method
、 Constructor
、 Superclass
、 Interface
、Annotation
- 实现的全部接口
- 所继承的父类
- 全部的构造器
- 全部的方法
- 全部的Field
-
实现的全部接口
public Class<?>[] getInterfaces()
确定此对象所表示的类或接口实现的接口。 -
所继承的父类
public Class<? Super T> getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的
Class。 -
全部的构造器
public Constructor<T>[] getConstructors()
返回此 Class 对象所表示的类的所有public构造方法。
public Constructor<T>[] getDeclaredConstructors()
返回此 Class 对象表示的类声明的所有构造方法。- Constructor类中:
- 取得修饰符:
public int getModifiers();
- 取得方法名称:
public String getName();
- 取得参数的类型:
public Class<?>[] getParameterTypes();
- 取得修饰符:
- Constructor类中:
-
全部的方法
public Method[] getDeclaredMethods()
返回此Class对象所表示的类或接口的全部方法
public Method[] getMethods()
返回此Class对象所表示的类或接口的public的方法- Method类中:
public Class<?> getReturnType()
取得全部的返回值public Class<?>[] getParameterTypes()
取得全部的参数public int getModifiers()
取得修饰符public Class<?>[] getExceptionTypes()
取得异常信息
- Method类中:
-
全部的Field
public Field[] getFields()
返回此Class对象所表示的类或接口的public的Field。
public Field[] getDeclaredFields()
返回此Class对象所表示的类或接口的全部Field。- Field方法中:
public int getModifiers()
以整数形式返回此Field的修饰符
public Class<?> getType()
得到Field的属性类型
public String getName()
返回Field的名称。
- Field方法中:
-
Annotation相关
get Annotation(Class<T> annotationClass)
getDeclaredAnnotations()
-
泛型相关
获取父类泛型类型:Type getGenericSuperclass()
泛型类型:ParameterizedType
获取实际的泛型类型参数数组:getActualTypeArguments()
-
类所在的包
Package getPackage()
5.2 小结
- 在实际的操作中,取得类的信息的操作代码,并不会经常开发。
- 一定要熟悉java.lang.reflect包的作用,反射机制。
- 如何取得属性、方法、构造器的名称,修饰符等。
5.3 示例
-
创建示例的相关类和注解
等-
MyInterface接口
package pers.chh3213.full_constructure; public interface MyInterface { void info(); }
-
MyAnnotation注解类
package pers.chh3213.full_constructure; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default "hello"; }
-
Creature类
package pers.chh3213.full_constructure; import java.io.Serializable; public abstract class Creature<T> implements Serializable { private char gender; public double weight; private void breath(){ System.out.println("呼吸"); } public void eat(){ System.out.println("吃东西"); } }
-
Person类
package pers.chh3213.full_constructure; @MyAnnotation(value = "person") public class Person extends Creature<String> implements Comparable<String>,MyInterface{ private String name; int age; public int id; public Person() { } @MyAnnotation(value="name") public Person(String name) { this.name = name; } private Person(String name, int age) { this.name = name; this.age = age; } @MyAnnotation private String show(String nation){ System.out.println("国籍:"+nation); return nation; } @Override public void info() { System.out.println("I love China"); } public String display(String play){ return play; } private static void showDesc(){ System.out.println("hahahhadsa"); } @Override public int compareTo(String o) { return 0; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", id=" + id + '}'; } }
-
-
获取运行时类的属性结构及其内部结构
package pers.chh3213.full_constructure; import org.junit.Test; import java.lang.reflect.Field; import java.lang.reflect.Modifier; /** * Created with IntelliJ IDEA. * * @author : chh3213 * @version : 1.0 * @Project : reflection * @Package : pers.chh3213.full_constructure * @ClassName : FiledTest.java * @createTime : 2022/1/23 11:32 * @Email : * @Description : */ public class FiledTest { /** * //获取属性结构 */ @Test public void test01(){ Class clazz = Person.class; //1.getFields():获取当前运行时类及其父类中声明为public访问权限的属性 for (Field field : clazz.getFields()) { System.out.println(field); } System.out.println("***************"); //2.getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性) for (Field declaredField : clazz.getDeclaredFields()) { System.out.println(declaredField); } } @Test public void test02(){ Class clazz = Person.class; for (Field declaredField : clazz.getDeclaredFields()) { //1.获得属性的权限修饰符 int modifiers = declaredField.getModifiers(); System.out.println(Modifier.toString(modifiers)); System.out.println("============="); //2.获得属性的数据类型 Class type = declaredField.getType(); System.out.print(type.getName() + "\t"); System.out.println(); System.out.println("----------------"); //3.获得属性的变量名 String name = declaredField.getName(); System.out.print(name); } } }
-
获取运行时类的方法结构
package pers.chh3213.full_constructure; import org.junit.Test; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * Created with IntelliJ IDEA. * * @author : chh3213 * @version : 1.0 * @Project : reflection * @Package : pers.chh3213.full_constructure * @ClassName : MethodTest.java * @createTime : 2022/1/23 11:42 * @Email : * @Description : */ public class MethodTest { @Test public void test01(){ Class<Person> personClass = Person.class; //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法 for (Method method : personClass.getMethods()) { System.out.println(method+"\t"); } System.out.println(); System.out.println("=============="); //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法) for (Field declaredField : personClass.getDeclaredFields()) { System.out.println(declaredField+"\t"); } System.out.println(); } }
-
获取运行时类的方法的内部结构
package pers.chh3213.full_constructure; import org.junit.Test; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; /** * Created with IntelliJ IDEA. * * @author : chh3213 * @version : 1.0 * @Project : reflection * @Package : pers.chh3213.full_constructure * @ClassName : MethodInnerTest.java * @createTime : 2022/1/23 11:48 * @Email : * @Description : */ public class MethodInnerTest { @Test public void test01(){ Class<Person> clazz = Person.class; for (Method declaredMethod : clazz.getDeclaredMethods()) { //1.获取方法声明的注解 System.out.println("========注解========"); for (Annotation annotation : declaredMethod.getAnnotations()) { System.out.println(annotation); } //2.权限修饰符 System.out.println("=======权限修饰符==========="); System.out.println(Modifier.toString(declaredMethod.getModifiers()) + "\t"); //3.返回值类型 System.out.println("=========返回值类型======="); System.out.println(declaredMethod.getReturnType().getName()); //4.方法名 System.out.println("==========方法名======"); System.out.println(declaredMethod.getName()); //5.形参列表 System.out.println("======形参列表=============="); Class<?>[] parameterTypes = declaredMethod.getParameterTypes(); if(!(parameterTypes.length==0&¶meterTypes==null)){ for (int i =0;i<parameterTypes.length;i++) { System.out.println(parameterTypes[i].getName()+"args_"+i); } } //6.抛出的异常 System.out.println("抛出的异常"); Class[] exceptionTypes = declaredMethod.getExceptionTypes(); if(exceptionTypes.length>0){ for(int i=0;i<exceptionTypes.length;i++){ System.out.println(exceptionTypes[i].getName()); } } } } }
-
获取运行时类的构造器结构
package pers.chh3213.full_constructure; import org.junit.Test; import java.lang.reflect.Constructor; /** * Created with IntelliJ IDEA. * * @author : chh3213 * @version : 1.0 * @Project : reflection * @Package : pers.chh3213.full_constructure * @ClassName : ConstructorTest.java * @createTime : 2022/1/23 12:03 * @Email : * @Description : */ public class ConstructorTest { @Test public void test01(){ Class<Person> clazz = Person.class; //getConstructors():获取当前运行时类中声明为public的构造器 for (Constructor constructor : clazz.getConstructors()) { System.out.println(constructor); } System.out.println("================="); //getDeclaredConstructors():获取当前运行时类中声明的所有的构造器 for (Constructor declaredConstructor : clazz.getDeclaredConstructors()) { System.out.println(declaredConstructor); } } }
-
获取运行时类的父类及父类的泛型
package pers.chh3213.full_constructure; import org.junit.Test; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; /** * Created with IntelliJ IDEA. * * @author : chh3213 * @version : 1.0 * @Project : reflection * @Package : pers.chh3213.full_constructure * @ClassName : SuperClassTest.java * @createTime : 2022/1/23 12:06 * @Email : * @Description : */ public class SuperClassTest { @Test public void test01(){ Class<Person> clazz = Person.class; //获取运行时类的父类 Class<? super Person> superclass = clazz.getSuperclass(); System.out.println(superclass); //获取运行时类的带泛型的父类 Type genericSuperclass = clazz.getGenericSuperclass(); System.out.println(genericSuperclass); //获取运行时类的带泛型的父类的泛型 Type genericSuperclass1 = clazz.getGenericSuperclass(); ParameterizedType paramType = (ParameterizedType) genericSuperclass1; for (Type actualTypeArgument : paramType.getActualTypeArguments()) { System.out.println(((Class)actualTypeArgument).getName()); } } }
-
获取运行时类的接口、所在包、注解等
package pers.chh3213.full_constructure; import org.junit.Test; import java.lang.annotation.Annotation; /** * Created with IntelliJ IDEA. * * @author : chh3213 * @version : 1.0 * @Project : reflection * @Package : pers.chh3213.full_constructure * @ClassName : OtherTest.java * @createTime : 2022/1/23 12:12 * @Email : * @Description : */ public class OtherTest { /** * 获取运行时类实现的接口 */ @Test public void test01(){ Class<Person> clazz = Person.class; //获取运行时类实现的接口 for (Class<?> anInterface : clazz.getInterfaces()) { System.out.println(anInterface); } System.out.println("============="); //获取运行时类的父类实现的接口 for (Class<?> anInterface : clazz.getSuperclass().getInterfaces()) { System.out.println(anInterface); } } /** * 获取运行时类所在的包 */ @Test public void test02(){ Class<Person> clazz = Person.class; Package clazzPackage = clazz.getPackage(); System.out.println(clazzPackage); } /** * 获取运行时类声明的注解 */ @Test public void test03(){ Class<Person> clazz = Person.class; for (Annotation annotation : clazz.getAnnotations()) { System.out.println(annotation); } } }
6. 调用运行时类的指定结构
6.1 调用指定结构
-
调用指定方法
-
调用指定属性
-
关于setAccessible方法的使用
6.2 示例
-
创建相关类和注解、接口等,与上文一致
-
调用运行时类中的指定属性
package pers.chh3213.specfied_structure; import org.junit.Test; import pers.chh3213.full_constructure.MyAnnotation; import pers.chh3213.full_constructure.Person; import java.lang.reflect.Field; public class FieldTest { @Test public void test01() throws Exception { Class<Person> clazz = Person.class; //创建运行时类的对象 Person person = clazz.newInstance(); //获取指定的属性:要求运行时类中属性声明为public //通常不采用此方法 Field id = clazz.getField("id"); //设置当前属性的值 //set():参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少 id.set(person,1000); //获取当前属性的值 //get():参数1:获取哪个对象的当前属性值 int pId = (int)id.get(person); System.out.println(pId); } /** * 如何操作运行时类中的指定的属性 -- 需要掌握 */ @Test public void test02() throws Exception{ Class<Person> clazz = Person.class; Person person = clazz.newInstance(); //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性 Field name = clazz.getDeclaredField("name"); //2.保证当前属性是可访问的 name.setAccessible(true); //3.获取、设置指定对象的此属性值 name.set(person,"Fire"); System.out.println(name.get(person)); } }
-
调用运行时类中的指定方法
package pers.chh3213.specfied_structure; import org.junit.Test; import pers.chh3213.full_constructure.Person; import java.lang.reflect.Method; public class MethodTest { @Test public void test01() throws Exception{ Class<Person> clazz = Person.class; //创建运行时类的对象 Person person = clazz.newInstance(); //1.获取指定的某个方法 //getDeclaredMethod():参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表 Method show = clazz.getDeclaredMethod("show", String.class); //2.保证当前方法是可访问的 show.setAccessible(true); //3.调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参 //invoke()的返回值即为对应类中调用的方法的返回值。 Object returnValue = show.invoke(person, "hahha"); System.out.println(returnValue); System.out.println("==============="); //调用静态方法 Method showDesc = clazz.getDeclaredMethod("showDesc"); showDesc.setAccessible(true); //如果调用的运行时类中的方法没有返回值,则此invoke()返回null // Object returnVal = showDesc.invoke(null); Object returnVal = showDesc.invoke(Person.class); System.out.println(returnVal); } }
-
调用运行时类中的指定构造器
package pers.chh3213.specfied_structure; import org.junit.Test; import pers.chh3213.full_constructure.Person; import java.lang.reflect.Constructor; public class ConstructorTest { @Test public void test01() throws Exception{ Class<Person> clazz = Person.class; //1.获取指定的构造器 //getDeclaredConstructor():参数:指明构造器的参数列表 Constructor<Person> declaredConstructor = clazz.getDeclaredConstructor(String.class); //2.保证此构造器是可访问的 declaredConstructor.setAccessible(true); //3.调用此构造器创建运行时类的对象 Person jack = declaredConstructor.newInstance("jack"); System.out.println(jack); } }
-
调用运行时类中的指定Annotation
package pers.chh3213.exercise; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; /** * Created with IntelliJ IDEA. * * @author : chh3213 * @version : 1.0 * @Project : reflection * @Package : pers.chh3213.exercise * @ClassName : TestGetAnno.java * @createTime : 2022/1/23 20:53 * @Email : * @Description : */ public class TestGetAnno { public static void main(String[] args) throws Exception{ Class<Student> clazz = Student.class; //for (Annotation annotation : clazz.getAnnotations()) { // System.out.println(annotation); //} System.out.println(clazz.toString()); MyAnnotation declaredAnnotation = clazz.getDeclaredAnnotation(MyAnnotation.class); System.out.println(declaredAnnotation); } } //@MyAnnotation(myName = "hh") class Student { private String name; private int id; public Student(String name, int id) { this.name = name; this.id = id; } @MyAnnotation(myName = "jack") @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", id=" + id + '}'; } } @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation { String myName() default "cao"; }
7. 反射的应用:动态代理
7.1 代理设计模式
-
代理设计模式的原理:
使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。之前为大家讲解过代理机制的操作,属于静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。
动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
-
动态代理使用场合:
调试
远程方法调用 -
动态代理相比于静态代理的优点:
抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。
7.2 静态代理举例
package pers.chh3213.proxy;
public class StaticProxyTest {
public static void main(String[] args) {
//创建被代理类的对象
ClothFactory erkeClothFactory = new ErkeClothFactory();
//创建代理类的对象
ClothFactory proxyClothFactory = new ProxyClothFactory(erkeClothFactory);
proxyClothFactory.produceCloth();
}
}
interface ClothFactory{
void produceCloth();
}
class ProxyClothFactory implements ClothFactory{
private ClothFactory factory;//用被代理类对象进行实例化
public ProxyClothFactory(ClothFactory factory) {
this.factory = factory;
}
@Override
public void produceCloth() {
System.out.println("准备");
factory.produceCloth();
System.out.println("生产服装");
}
}
class ErkeClothFactory implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("鸿星尔克");
}
}
7.3 动态代理步骤
- Java动态代理相关API
- 步骤
7.4 动态代理举例
package pers.chh3213.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
public static void main(String[] args) {
SpyderMan spyderMan = new SpyderMan();
//proxyInstance为代理类的对象
Human proxyInstance = (Human)ProxyFactory.getProxyInstance(spyderMan);
//当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("meat");
System.out.println("======");
ErkeClothFactory erkeClothFactory = new ErkeClothFactory();
ClothFactory proxyFactory = (ClothFactory)ProxyFactory.getProxyInstance(erkeClothFactory);
proxyFactory.produceCloth();
}
}
interface Human{
String getBelief();
void eat(String food);
}
//被代理类
class SpyderMan implements Human{
@Override
public String getBelief() {
return "I can climb the wall";
}
@Override
public void eat(String food) {
System.out.println("我喜欢吃"+food);
}
}
/**
* 要想实现动态代理,需要解决的问题?
* 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
* 问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
*/
class ProxyFactory{
//调用此方法,返回一个代理类的对象。解决问题一
public static Object getProxyInstance(Object obj){//obj:被代理类的对象
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
myInvocationHandler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),myInvocationHandler);
}
}
class ErkeClothFactory implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("鸿星尔克");
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;//需要使用被代理类的对象进行赋值
//当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
//将被代理类要执行的方法a的功能就声明在invoke()中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
//obj:被代理类的对象
Object invoke = method.invoke(obj, args);
return invoke;
}
public void bind(Object obj){
this.obj = obj;
}
}
7.5 动态代理与AOP(Aspect Orient Programming)
-
前面介绍的Proxy和InvocationHandler,很难看出这种动态代理的优势,下面介绍一种更实用的动态代理机制
-
改进后的说明:代码段1、代码段2、代码段3和深色代码段分离开了,但代码段1、2、3又和一个特定的方法A耦合了!最理想的效果是:代码块1、2、3既可以执行方法A,又无须在程序中以硬编码的方式直接调用深色代码的方法。
-
使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理
-
这种动态代理在AOP中被称为AOP代理,AOP代理可代替目标对象,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异:AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理
-
示例
package pers.chh3213.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.lang.reflect.Method; interface Dog{ void info(); void run(); } class HuntingDog implements Dog{ @Override public void info(){ System.out.println("我是一只猎狗"); } @Override public void run(){ System.out.println("我奔跑迅速"); } } class DogUtil{ public void method1(){ System.out.println("=====模拟通用方法一====="); } public void method2(){ System.out.println("=====模拟通用方法二====="); } } class MyInvocationHandler1 implements InvocationHandler { // 需要被代理的对象 private Object target; public void setTarget(Object target){ this.target = target; } // 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Exception{ DogUtil du = new DogUtil(); // 执行DogUtil对象中的method1。 du.method1(); // 以target作为主调来执行method方法 Object result = method.invoke(target , args); // 执行DogUtil对象中的method2。 du.method2(); return result; } } class MyProxyFactory{ // 为指定target生成动态代理对象 public static Object getProxy(Object target) throws Exception{ // 创建一个MyInvokationHandler对象 MyInvocationHandler1 handler = new MyInvocationHandler1(); // 为MyInvokationHandler设置target对象 handler.setTarget(target); // 创建、并返回一个动态代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader() , target.getClass().getInterfaces() , handler); } } public class AOPTest { public static void main(String[] args) throws Exception{ // 创建一个原始的HuntingDog对象,作为target Dog target = new HuntingDog(); // 以指定的target来创建动态代理 Dog dog = (Dog)MyProxyFactory.getProxy(target); dog.info(); dog.run(); } }
练习
-
描述一下JVM加载class文件的原理机制?
答:JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader
是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。 -
利用反射和重载完成以下功能
- 创建Student类,类中有属性name和age并封装属性
- 重载Student的构造函数,一个是无参构造并,另一个是带两个参数的有参构造,要求在构造函数打印提示信息
- 创建带main函数的NewInstanceTest类,利用Class类得到Student对象
- 通过上述获取的Class对象分别调用Student有参函数和无参函数
package pers.chh3213.exercise; import java.lang.reflect.Constructor; /** * Created with IntelliJ IDEA. * * @author : chh3213 * @version : 1.0 * @Project : reflection * @Package : pers.chh3213.exercise * @ClassName : NewInstanceTest.java * @createTime : 2022/1/23 20:17 * @Email : * @Description : */ class Student { private String name; private int age; public Student() { System.out.println("无参构造"); } public Student(String name, int age) { this.name = name; this.age = age; System.out.println("有参构造"); } } public class NewInstanceTest{ public static void main(String[] args) throws Exception{ Class<Student> clazz = Student.class; Constructor<Student> declaredConstructor1 = clazz.getDeclaredConstructor(); //2.无参构造 declaredConstructor1.setAccessible(true); Student jack = declaredConstructor1.newInstance(); //有参构造 Constructor<Student> declaredConstructor2 = clazz.getDeclaredConstructor(String.class,int.class); declaredConstructor2.setAccessible(true); Student tom = declaredConstructor2.newInstance("tom",21); } }
-
利用反射的知识完成下面的操作
创建Mytxt类,创建myCreate()方法完成创建文件D:\myhello.txt文件的功能。创建带main方法的NewInstanceTest类,通过Class类获取Mytxt对象,调用myCreat()package pers.chh3213.exercise; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Method; /** * Created with IntelliJ IDEA. * * @author : chh3213 * @version : 1.0 * @Project : reflection * @Package : pers.chh3213.exercise * @ClassName : NewInstanceTest2.java * @createTime : 2022/1/23 20:42 * @Email : * @Description : */ class Mytxt{ public void myCreate() throws IOException { File file = new File("hello.txt"); if(!file.exists()){ file.createNewFile(); } FileWriter fileWriter = new FileWriter(file); fileWriter.write("hello!!!!!!!"); fileWriter.close(); } } public class NewInstanceTest2 { public static void main(String[] args) throws Exception{ Class<Mytxt> clazz = Mytxt.class; Mytxt mytxt = clazz.newInstance(); Method myCreate = clazz.getDeclaredMethod("myCreate"); myCreate.setAccessible(true); Object invoke = myCreate.invoke(mytxt); } }
-
利用Annotation和反射知识完成操作
-
自定义一个有效的Annotation注释名为MyAnnotation,其中有属性myname创建Student类并重写toString(),toString()要求使用三个基本的Annotation和自定义的MyAnnotation注释
-
创建TestGetAnno类,打印出Student类的toString方法的所有注释
package pers.chh3213.exercise; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; /** * Created with IntelliJ IDEA. * * @author : chh3213 * @version : 1.0 * @Project : reflection * @Package : pers.chh3213.exercise * @ClassName : TestGetAnno.java * @createTime : 2022/1/23 20:53 * @Email : * @Description : */ public class TestGetAnno { public static void main(String[] args) throws Exception{ Class<Student> clazz = Student.class; //for (Annotation annotation : clazz.getAnnotations()) { // System.out.println(annotation); //} System.out.println(clazz.toString()); MyAnnotation declaredAnnotation = clazz.getDeclaredAnnotation(MyAnnotation.class); System.out.println(declaredAnnotation); } } //@MyAnnotation(myName = "hh") class Student { private String name; private int id; public Student(String name, int id) { this.name = name; this.id = id; } @MyAnnotation(myName = "jack") @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", id=" + id + '}'; } } @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation { String myName() default "cao"; }
-
-
利用通过反射修改私有成员变量
- 定义PrivateTest类,有私有name属性,并且属性值为hellokitty,只提供name的getName的公有方法
- 创建带有main方法ReflectTest的类,利用Class类得到私有的name属性
- 修改私有的name属性值,并调用getName()的方法打印name属性值
package pers.chh3213.exercise; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * Created with IntelliJ IDEA. * * @author : chh3213 * @version : 1.0 * @Project : reflection * @Package : pers.chh3213.exercise * @ClassName : ReflectTest.java * @createTime : 2022/1/23 21:18 * @Email : * @Description : */ public class ReflectTest { public static void main(String[] args) throws Exception{ Class<PrivateTest> clazz = PrivateTest.class; PrivateTest privateTest = clazz.newInstance(); Field name = clazz.getDeclaredField("name"); System.out.println(name); name.setAccessible(true); name.set(privateTest,"kitty"); String name2 =(String) name.get(privateTest); System.out.println(name2); Method getName = clazz.getDeclaredMethod("getName"); getName.setAccessible(true); Object invoke = getName.invoke(privateTest); System.out.println(invoke); } } class PrivateTest{ private final String name="hellokitty"; public String getName() { return name; } }
-
利用反射和File完成以下功能
- 利用Class类的forName方法得到File类
- 在控制台打印File类的所有构造器
- 通过newInstance的方法创建File对象,并创建D:\mynew.txt文件
package pers.chh3213.exercise; import org.junit.Test; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Method; /** * Created with IntelliJ IDEA. * * @author : chh3213 * @version : 1.0 * @Project : reflection * @Package : pers.chh3213.exercise * @ClassName : FileTest.java * @createTime : 2022/1/23 21:33 * @Email : * @Description : */ public class FileTest { @Test public void test() throws Exception{ //利用Class类的forName方法得到File类 Class FileAll = Class.forName("java.io.File"); for (Constructor declaredConstructor : FileAll.getDeclaredConstructors()) { //在控制台打印File类的所有构造器 System.out.println(declaredConstructor); } //通过newInstance的方法创建File对象,并创建D:\mynew.txt文件 //1.获取File的一个构造器 Constructor declaredConstructor = FileAll.getDeclaredConstructor(String.class); //2.用上面得到构造器创建File对象 Object file = declaredConstructor.newInstance("mynew.txt"); //3.获取File的createNewFile方法 Method createNewFile = FileAll.getDeclaredMethod("createNewFile"); createNewFile.setAccessible(true); Object invoke = createNewFile.invoke(file); } }