本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1. Java反射机制概念
1.1 静态语言、动态语言
-
动态语言
- 是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
- 主要动态语言:Object-C、C#、 JavaScript、PHP、 Python等。
-
静态语言
- 与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。
- Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活。
1.2 反射机制概念
-
Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于 Reflection AP取得仼何类的内部信息,并能直接操作任意对象的内部属性及方法。
- Class c= Class.forName(“java. lang String”);
-
加载完类之后,在堆內存的方法区中就产生了一个 Class类型的对象(一个类只有一个Cass对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
1.3 反射机制研究与应用
Java反射机制提供的功能
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时获取泛型信息;
- 在运行时调用任意一个对象的成员变量和方法;
- 在运行时处理注解;
- 生成动态代理;
- ……
1.4 反射机制优缺点
-
优点:
- 可以实现动态创建对象和编译,体现出很大的灵活性。
-
缺点
- 对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。
1.5 反射相关的主要API
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field :代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造器
实现
package com.lpl.reflection;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author lipenglong
* @version 1.0
* @describe
*/
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
// 通过反射获取类的Class对象
Class<?> class1 = Class.forName("com.lpl.reflection.User");
Class<?> class2 = Class.forName("com.lpl.reflection.User");
Class<?> class3 = Class.forName("com.lpl.reflection.User");
Class<?> class4 = Class.forName("com.lpl.reflection.User");
System.out.println(class1);
/**
* 一个类在内存中只有一个Class对象
一个类被加载后,类的整个结构都会被封装在Class对象中
* public native int hashCode();返回该对象的hash码值
* 注:哈希值是根据哈希算法算出来的一个值,这个值跟地址值有关,但不是实际地址值。
*/
System.out.println(class1.hashCode());
System.out.println(class2.hashCode());
System.out.println(class3.hashCode());
System.out.println(class4.hashCode());
}
}
@Data
@Accessors(chain = true)
class User {
private String name;
private int id;
private int age;
}
执行结果
class com.lpl.reflection.User
1956725890
1956725890
1956725890
1956725890
2. 理解Class类并获取Class实例
2.1 class类介绍
-
在 Object类中定义了以下的方法,此方法将被所有子类继承
public final Class getclass()
-
以上的方法返回值的类型是一个 Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。
-
对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口对于每个类而言,JRE都为其保留一个不变的Cass类型的对象。一个Class对象包含了特定某个结构( class/interface/enum/annotation/ primitive type/void/[])的有关信息。
- Class本身也是一个类;
- Class对象只能由系统建立对象;
- 一个加载的类在JVM中只会有一个Class实例;
- 一个Cass对象对应的是一个加载到JM中的一个class文件;
- 每个类的实例都会记得自己是由哪个Class实例所生成;
- 通过class可以完整地得到一个类中的所有被加载的结构;
- class类是 Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。
-
class类的常用方法
| 方法名 | 功能说明 |
|---|---|
| static ClassforName (String name) | 返回指定类名name的class对象 |
| Object newInstance () | 调用缺省构造函数,返回 Class对象的一个实例 |
| getName () | 返回此Class对象所表示的实体(类,接口,数组类或void)的名称。 |
| Class getSuperClass () | 返回当前class对象的父类的class对象 |
| Class[] getinterfaces () | 获取当前 Class对象的接口 |
| ClassLoader getclassLoader () | 返回该类的类加载器 |
| Constructor getConstructors () | 返回一个包含某些 Constructor对象的数组 |
| Method getMothed (String name, Class…T) | 返回一个 Method对象,此对象的形参类型为paramType |
| Field[] getDeclaredFields () | 返回Field对象的一个数组 |
2.2 获取Class类的实例的几种方式
-
若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高。
Class clazz=Person.class;
-
已知某个类的实例,调用该实例的 getclass () 方法获取Class对象。
Class clazz= person. getClass();
-
已知一个类的全类名,且该类在类路径下,可通过class类的静态方法 forName(获取,可能抛出 ClassNotFound Exception。
Class clazz Class forName(”demo01 Student”);
-
内置基本数据类型可以直接用类名.Type。
-
还可以利用 Classloader。
package com.lpl.reflection;
/**
* @author lipenglong
* @version 1.0
* @describe 测试class类的创建方式有哪些
*/
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println("这个人是:" + person);
// 方式一:通过对象获得
Class c1 = person.getClass();
System.out.println(c1.hashCode());
// 方式二:forName获得
Class c2 = Class.forName("com.lpl.reflection.Student");
System.out.println(c2.hashCode());
// 方式三:通过类名.class获得
Class c3 = Student.class;
System.out.println(c3.hashCode());
// 方式四:基本类型的包装类都有一个Type
Class c4 = Integer.TYPE;
System.out.println(c4);
// 获得父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);
}
}
class Person{
String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
'}';
}
}
class Student extends Person{
public Student() {
this.name = "学生";
}
}
执行结果
这个人是:Person{name='学生'}
1956725890
1956725890
1956725890
int
class com.lpl.reflection.Person
2.3 哪些类型可以有Class对象
- class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
- interface:接口
- []:数组
- enum:枚举
- annotation:注解@interface
- primitive type:基本数据类型
- void
public class TestAllTypeClass {
public static void main(String[] args) {
Class<Object> objectClass = Object.class; // 类
Class<Comparable> comparableClass = Comparable.class; // 接口
Class<String[]> aClass = String[].class; // 一维数组
Class<int[][]> aClass1 = int[][].class; // 二维数组
Class<Override> overrideClass = Override.class; // 注解
Class<ElementType> elementTypeClass = ElementType.class; // 枚举类型
Class<Integer> integerClass = Integer.class; // 基本数据类型
Class<Void> voidClass = void.class; // void
Class<Class> classClass = Class.class; // Class
System.out.println(objectClass);
System.out.println(comparableClass);
System.out.println(aClass);
System.out.println(aClass1);
System.out.println(overrideClass);
System.out.println(elementTypeClass);
System.out.println(integerClass);
System.out.println(voidClass);
System.out.println(classClass);
}
}
执行结果
class java.lang.Object
interface java.lang.Comparable
class [Ljava.lang.String;
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class
只要元素类型与维度一样,就是同一个Class
int[] a = new int[10];
int[] b = new int[100];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
// 执行结果
1956725890
1956725890
3. 类的加载与ClassLoader
3.1 Java内存分析
3.2 类的加载
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化
-
加载:将 class文件字节码內容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的 java. lang . Class对象。
-
链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
- 验证:确保加载的类信息符合JVM规范,没有安全方面的问题。
- 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
-
初始化:
- 执行类构造器< clinit>()方法的过程。类构造器< clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。 (类构造器是构造类信息的,不是构造该类对象的构造器)。
- 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 虛拟机会保证一个类的< clinit >()方法在多线程环境中被正确加锁和同步。
例如,如下代码将输出300
public class ClassLoaderTest {
public static void main(String[] args) {
System.out.println(B.m); // 输出300
}
}
class B{ // 第一步:类的加载
// 第二步:链接,静态变量m=0
static int m = 100; // 赋值100
static {
m = 300;
}
// 第三步:初始化后,m的值由<clinit>()方法执行决定,由类变量的赋值和静态代码块中的语句按照顺序合并产生,类似于:
// <clinit>(){
// m=100;
// m=300;
// }
}
而如下代码输出100
public class ClassLoaderTest {
public static void main(String[] args) {
System.out.println(B.m); // 输出100
}
}
class B{
static {
m = 300;
}
static int m = 100;
// <clinit>(){
// m=300;
// m=100;
// }
}
3.3 什么时候会发生类初始化?
-
类的主动引用(一定会发生类的初始化)
- 当虚拟机启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
-
类的被动引用(不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化
- 当通过子类引用父类的静态变量,不会导致子类初始化
- 通过数组定义类引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
3.4 ClassLoader的理解
类加载器的作用:
- 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口
- 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象
类加载器作用是用来把类(class)装载进内存的。JVM 规范定义了如下类型的类的加载器
@Test
public void test1(){
//对于自定义类,使用系统类加载器进行加载
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader);
//调用系统类加载器的getParent():获取扩展类加载器
ClassLoader classLoader1 = classLoader.getParent();
System.out.println(classLoader1);
//调用扩展类加载器的getParent():无法获取引导类加载器,会返回null
//引导类加载器主要负责加载java的核心类库,无法加载自定义类的
ClassLoader classLoader2 = classLoader1.getParent();
System.out.println(classLoader2);
//String属于Java核心库中的类,也无法获取引导类加载器,会返回null
ClassLoader classLoader3 = String.class.getClassLoader();
System.out.println(classLoader3);
}
执行结果
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1f32e575
null
null
3.5 使用ClassLoader加载配置文件
当前moudle下的jdbc.properties
user=root
password=123456
使用FileInputStream读取配置文件
/**
* prooerties:读取配置文件
*/
@Test
public void test2() {
FileInputStream fileInputStream = null;
try {
Properties properties = new Properties();
// 当前moudle下的jdbc.properties
fileInputStream = new FileInputStream("jdbc.properties");
properties.load(fileInputStream);
String user = properties.getProperty("user");
String password = properties.getProperty("password");
System.out.printf("user:%s\npassword:%s", user, password);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
assert fileInputStream != null;
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用ClassLoader加载配置文件
@Test
public void test3() {
InputStream resourceAsStream = null;
try {
Properties properties = new Properties();
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
// 此时配置文件需要当前moudle的src目录下的jdbc.properties
resourceAsStream = classLoader.getResourceAsStream("jdbc.properties");
properties.load(resourceAsStream);
String user = properties.getProperty("user");
String password = properties.getProperty("password");
System.out.printf("user:%s\npassword:%s", user, password);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
assert resourceAsStream != null;
resourceAsStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
执行结果
user:root
password:123456
4. 通过反射,创建运行时类的对象
@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);
}
反射的动态性
@Test
public void test2() {
for (int i = 0; i < 5; i++) {
int num = new Random().nextInt(3);//0,1,2
String classPath = "";
switch (num) {
case 0:
classPath = "java.util.Date";
break;
case 1:
classPath = "java.lang.Object";
break;
case 2:
classPath = "com.lpl.reflection.Person";
break;
}
try {
Object obj = getInstance(classPath);
System.out.println("case:" + num + ",obj:" + 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();
}
执行结果
case:2,obj:Person{name='null'}
case:1,obj:java.lang.Object@66cd51c3
case:1,obj:java.lang.Object@4dcbadb4
case:0,obj:Mon Mar 07 22:38:29 CST 2022
case:0,obj:Mon Mar 07 22:38:29 CST 2022
5. 获取运行时类的完整结构
5.1 提供结构丰富Person类
Person类
@EqualsAndHashCode(callSuper = true)
@MyAnnotation(value = "hi")
@Data
public class Person extends Creature<String> implements Comparable<String>, MyInterface {
/**
* 提供不同权限的属性
*/
private String name;
public int id;
int age;
/**
* 提供不同权限的构造器
*/
@MyAnnotation(value = "空参构造器")
public Person() {
}
private Person(String name) {
this.name = name;
}
Person(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 提供不同权限的方法
*/
@MyAnnotation(value = "private方法")
private String show(String nation){
return "我的国籍-"+nation;
}
private static String showDesc(){
return "Person类的静态方法showDesc()";
}
// 这里抛出的异常只是测试,没有实际意义
public String display(String interest,int age) throws NullPointerException,ClassCastException{
return interest;
}
@Override
public void info() {
System.out.println("我是一个人");
}
@Override
public int compareTo(String o) {
return 0;
}
}
MyAnnotation注解
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "hello";
}
MyInterface接口类
public interface MyInterface {
void info();
}
Person父类Creature
public class Creature<T> implements Serializable {
/**
* 提供不同权限的属性
*/
private char gender;
public double weight;
/**
* 提供不同权限的方法
*/
private void breath(){
System.out.println("生物呼吸");
}
public void eat(){
System.out.println("生物吃东西");
}
}
5.2 获取运行时类的属性结构及其内部结构
getFields()获取当前运行时类机器父类声明为public访问权限的属性
@Test
public void testFiled(){
Class<Person> personClass = Person.class;
// 获取属性结构
Field[] fields = personClass.getFields();
for (Field field : fields) {
System.out.printf("filed:%s\n",field);
}
}
//执行结果
filed:public int com.lpl.test.Person.id
filed:public double com.lpl.test.Creature.weight
getDeclaredFields()获取当前运行时类中声明的属性,不包含父类中声明的属性
@Test
public void testFiled(){
Class<Person> personClass = Person.class;
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.printf("declaredField:%s\n", declaredField);
}
}
//执行结果
declaredField:private java.lang.String com.lpl.test.Person.name
declaredField:public int com.lpl.test.Person.id
declaredField:int com.lpl.test.Person.age
获取属性的权限修饰符、数据类型、变量名
@Test
public void test2(){
Class<Person> personClass = Person.class;
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 获取变量名
System.out.printf("变量名:%s\n", declaredField.getName());
// 获取权限修饰符
System.out.printf("权限修饰符:%s\n", Modifier.toString(declaredField.getModifiers()));
// 获取数据类型
System.out.printf("数据类型:%s\n", declaredField.getType());
System.out.println("..........");
}
}
// 执行结果
变量名:name
权限修饰符:private
数据类型:class java.lang.String
..........
变量名:id
权限修饰符:public
数据类型:int
..........
变量名:age
权限修饰符:
数据类型:int
..........
5.3 获取运行时类的方法结构
getMethods()获取当前运行时类及其所有父类中声明为public权限的方法
@Test
public void testMethod(){
Class<Person> personClass = Person.class;
// getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
Method[] methods = personClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
}
// 执行结果
public boolean com.lpl.test.Person.equals(java.lang.Object)
public java.lang.String com.lpl.test.Person.toString()
public int com.lpl.test.Person.hashCode()
public int com.lpl.test.Person.compareTo(java.lang.Object)
public int com.lpl.test.Person.compareTo(java.lang.String)
public java.lang.String com.lpl.test.Person.getName()
public int com.lpl.test.Person.getId()
public void com.lpl.test.Person.setName(java.lang.String)
public void com.lpl.test.Person.info()
public java.lang.String com.lpl.test.Person.display(java.lang.String,int) throws java.lang.NullPointerException,java.lang.ClassCastException
public void com.lpl.test.Person.setId(int)
public int com.lpl.test.Person.getAge()
public void com.lpl.test.Person.setAge(int)
public void com.lpl.test.Creature.eat()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
getMethods()获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
@Test
public void testMethod2(){
Class<Person> personClass = Person.class;
// getMethods()获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
Method[] declaredMethods = personClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
}
// 执行结果
public boolean com.lpl.test.Person.equals(java.lang.Object)
public java.lang.String com.lpl.test.Person.toString()
public int com.lpl.test.Person.hashCode()
public int com.lpl.test.Person.compareTo(java.lang.Object)
public int com.lpl.test.Person.compareTo(java.lang.String)
public java.lang.String com.lpl.test.Person.getName()
public int com.lpl.test.Person.getId()
public void com.lpl.test.Person.setName(java.lang.String)
public void com.lpl.test.Person.info()
public java.lang.String com.lpl.test.Person.display(java.lang.String,int) throws java.lang.NullPointerException,java.lang.ClassCastException
private java.lang.String com.lpl.test.Person.show(java.lang.String)
protected boolean com.lpl.test.Person.canEqual(java.lang.Object)
public int com.lpl.test.Person.getAge()
public void com.lpl.test.Person.setId(int)
private static java.lang.String com.lpl.test.Person.showDesc()
public void com.lpl.test.Person.setAge(int)
5.4 获取运行时类的方法的内部结构
获取权限修饰符、返回值类型、方法名(参数1,参数2)、注解、异常
// 获取权限修饰符、返回值类型、方法名(参数1,参数2)、注解、异常
@Test
public void testMethod3() {
Class<Person> personClass = Person.class;
// getMethods()获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
Method[] declaredMethods = personClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
// 获取方法名
System.out.printf("方法:%s", declaredMethod.getName());
// 获取形参列表
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
if (parameterTypes.length > 0) {
System.out.print("(");
for (int i = 0; i < parameterTypes.length; i++) {
if (i == parameterTypes.length - 1) {
System.out.printf("%s arg_%d", parameterTypes[i].getName(), i);
break;
}
System.out.printf("%s arg_%d,", parameterTypes[i].getName(), i);
}
System.out.println(")");
} else {
System.out.println("()");
}
// 获取方法声明的注解
Annotation[] annotations = declaredMethod.getAnnotations();
if (annotations.length > 0) {
for (Annotation annotation : annotations) {
System.out.printf("注解:%s\n", annotation);
}
} else {
System.out.printf("注解:{%s}\n", (Object) null);
}
// 获取权限修饰符
System.out.printf("权限修饰符:%s\n", Modifier.toString(declaredMethod.getModifiers()));
// 获取返回值类型
System.out.printf("返回值类型:%s\n", declaredMethod.getReturnType());
// 获取抛出的异常
Class<?>[] exceptionTypes = declaredMethod.getExceptionTypes();
if (exceptionTypes.length > 0) {
System.out.print("异常:{");
for (int i = 0; i < exceptionTypes.length; i++) {
if (i == exceptionTypes.length - 1) {
System.out.printf("异常%d:%s", i, exceptionTypes[i]);
break;
}
System.out.printf("异常%d:%s,", i + 1, exceptionTypes[i]);
}
System.out.println("}");
} else {
System.out.printf("异常:{%s}\n", (Object) null);
}
System.out.println("..............");
}
}
执行结果
方法:info()
注解:{null}
权限修饰符:public
返回值类型:void
异常:{null}
..............
方法:display(java.lang.String arg_0,int arg_1)
注解:{null}
权限修饰符:public
返回值类型:class java.lang.String
异常:{异常1:class java.lang.NullPointerException,异常1:class java.lang.ClassCastException}
..............
方法:canEqual(java.lang.Object arg_0)
注解:{null}
权限修饰符:protected
返回值类型:boolean
异常:{null}
..............
方法:showDesc()
注解:{null}
权限修饰符:private static
返回值类型:class java.lang.String
异常:{null}
..............
方法:show(java.lang.String arg_0)
注解:@com.lpl.test.MyAnnotation(value=private方法)
权限修饰符:private
返回值类型:class java.lang.String
异常:{null}
..............
方法:getAge()
注解:{null}
权限修饰符:public
返回值类型:int
异常:{null}
..............
方法:setAge(int arg_0)
注解:{null}
权限修饰符:public
返回值类型:void
异常:{null}
..............
方法:setId(int arg_0)
注解:{null}
权限修饰符:public
返回值类型:void
异常:{null}
..............
方法:equals(java.lang.Object arg_0)
注解:{null}
权限修饰符:public
返回值类型:boolean
异常:{null}
..............
方法:toString()
注解:{null}
权限修饰符:public
返回值类型:class java.lang.String
异常:{null}
..............
方法:hashCode()
注解:{null}
权限修饰符:public
返回值类型:int
异常:{null}
..............
方法:compareTo(java.lang.Object arg_0)
注解:{null}
权限修饰符:public volatile
返回值类型:int
异常:{null}
..............
方法:compareTo(java.lang.String arg_0)
注解:{null}
权限修饰符:public
返回值类型:int
异常:{null}
..............
方法:getName()
注解:{null}
权限修饰符:public
返回值类型:class java.lang.String
异常:{null}
..............
方法:getId()
注解:{null}
权限修饰符:public
返回值类型:int
异常:{null}
..............
方法:setName(java.lang.String arg_0)
注解:{null}
权限修饰符:public
返回值类型:void
异常:{null}
..............
5.5 获取运行时类的构造器结构
getConstructors()获取当前运行时类中声明为public的构造器
@Test
public void testOther() {
Class<Person> personClass = Person.class;
//getConstructors()获取当前运行时类中声明为public的构造器
Constructor<?>[] constructors = personClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.printf("构造器:%s\n",constructor);
}
}
// 执行结果
构造器:public com.lpl.test.Person()
getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
@Test
public void testOther2() {
Class<Person> personClass = Person.class;
//getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.printf("构造器:%s\n", declaredConstructor);
}
}
// 执行结果
构造器:com.lpl.test.Person(java.lang.String,int)
构造器:private com.lpl.test.Person(java.lang.String)
构造器:public com.lpl.test.Person()
5.6 获取运行时类的父类及父类的泛型
@Test
public void testOther3() {
Class<Person> personClass = Person.class;
// 获取运行时父类
Class<? super Person> superclass = personClass.getSuperclass();
System.out.printf("Person的父类:%s\n",superclass);
// 获取运行时带泛型的父类
Type genericSuperclass = personClass.getGenericSuperclass();
System.out.printf("Person带泛型的父类:%s\n",genericSuperclass);
// 获取运行时父类的泛型类型
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.printf("Person的父类的泛型类型:%s\n", actualTypeArgument.getTypeName());
}
}
执行结果
Person的父类:class com.lpl.test.Creature
Person带泛型的父类:com.lpl.test.Creature<java.lang.String>
Person的父类的泛型类型:java.lang.String
5.7 获取运行时类的接口、所在包、注解等
获取运行时类实现的接口
@Test
public void testOther4() {
Class<Person> personClass = Person.class;
Class<?>[] interfaces = personClass.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.printf("Person类实现的接口:%s\n",anInterface);
}
Class<?>[] superClassInterfaces = personClass.getSuperclass().getInterfaces();
for (Class<?> superClassInterface : superClassInterfaces) {
System.out.printf("Person类父类实现的接口:%s\n",superClassInterface);
}
}
// 执行结果
Person类实现的接口:interface java.lang.Comparable
Person类实现的接口:interface com.lpl.test.MyInterface
Person类父类实现的接口:interface java.io.Serializable
获取运行时类所在的包
@Test
public void testOther5() {
Class<Person> personClass = Person.class;
Package aPackage = personClass.getPackage();
System.out.println(aPackage);
}
// 执行结果
package com.lpl.test
获取运行时类注解
@Test
public void testOther6() {
Class<Person> personClass = Person.class;
Annotation[] annotations = personClass.getAnnotations();
for (Annotation annotation : annotations) {
System.out.printf("注解:%s",annotation);
}
}
6. 调用运行时类的指定结构
6.1 调用运行时类中的指定属性
通过getField()获取指定属性,要求运行时类属性声明为public,而通常不是public,所以不采用此方法
@Test
public void testField() throws NoSuchFieldException, InstantiationException, IllegalAccessException {
Class<Person> personClass = Person.class;
// 创建运行时类对象
Person person = personClass.newInstance();
// 获取指定的属性 要求运行时类属性声明为public,而通常不是public,所以不采用此方法
Field id = personClass.getField("id");
//Field age = personClass.getField("age");
/*
设置当前属性的值
参数1:指明设置哪个对象的属性
参数2:属性值
*/
id.set(person, 1000);
/*
获取属性值
参数:指明设置哪个对象的属性
*/
System.out.printf("id:%s\n",id.get(person));
//System.out.printf("age:%s\n",age.get(person));
}
一般使用getDeclaredField()方法获取指定属性名,并设置属性是可访问的
@Test
public void testField1() throws InstantiationException, IllegalAccessException, NoSuchFieldException {
Class<Person> personClass = Person.class;
// 创建运行时类对象
Person person = personClass.newInstance();
// 获取指定名的属性
Field name = personClass.getDeclaredField("name"); // name属性为private的
// 保证当前属性是可访问的
name.setAccessible(true);
// 设置值
name.set(person,"LPL");
// 获取值
System.out.printf("name:%s",name.get(person));
}
// 执行结果
name:LPL
6.2 调用运行时类中的指定方法
getDeclaredMethod()调用运行时类中指定的方法
@Test
public void testMethod() throws Exception {
Class<Person> personClass = Person.class;
// 创建运行时类对象
Person person = personClass.newInstance();
/*
获取指定的某个方法 getDeclaredMethod()
参数1 :指明获取的方法的名称
参数2:指明获取的方法的形参列表
*/
Method show = personClass.getDeclaredMethod("show", String.class); // show方法为private的非静态方法
// 保证当前方法时可访问的
show.setAccessible(true);
/*
调用方法的invoke()
参数1:方法的调用者
参数2:给方法形参赋值的实参
*/
Object returnValue = show.invoke(person, "China");
System.out.printf("show()方法返回结果:%s\n",returnValue);
}
// 执行结果
show()方法返回结果:我的国籍-China
调用静态方法
// Person类新增静态方法
private static String showDesc(){
return "Person类的静态方法showDesc()";
}
// 测试
Method showDesc = personClass.getDeclaredMethod("showDesc"); // private static String showDesc()
showDesc.setAccessible(true);// 保证当前方法时可访问的
Object invoke = showDesc.invoke(person);
System.out.printf("showDesc()方法返回结果:%s\n",invoke);
// 执行结果
showDesc()方法返回结果:Person类的静态方法showDesc()
6.3 调用运行时类中的指定构造器
一般不这样做,不通用
@Test
public void testConstructor() throws Exception {
Class<Person> personClass = Person.class;
/*
获取指定的构造器getDeclaredConstructor()
参数:指明构造器的参数列表
*/
Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class);// 构造器 private Person(String name)
// 保证此构造器是可访问的
declaredConstructor.setAccessible(true);
// 调用此构造器创建运行时类的对象
Person tom = declaredConstructor.newInstance("Tom");
System.out.printf("Person的name:%s",tom.getName());
}
// 执行结果
Person的name:Tom
7. 反射的应用——动态代理
7.1 代理模式与静态、动态代理
代理设计模式的原理:
使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
静态代理:
静态代理是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。
动态代理:
动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象,使用场合:调试和远程方法调用
动态代理相比于静态代理的优点:
抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。
7.2 静态代理举例
代理类和被代理类在编译期间就确定下来了,例如
public class StaticProxyTest {
public static void main(String[] args) {
// 被代理类对象
ExampleClothFactory exampleClothFactory = new ExampleClothFactory();
// 代理类对象
ProxyClothFactory proxyClothFactory = new ProxyClothFactory(exampleClothFactory);
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 ExampleClothFactory implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("Example工厂开始工作....");
}
}
执行结果
代理工厂做一些准备工作
Example工厂开始工作....
代理工厂做一些后续的收尾工作
7.3 动态代理举例
代理类---invoke----被代理类
interface Human {
String getBelief();
void eat(String food);
}
// 被代理类
class SuperMan implements Human {
@Override
public String getBelief() {
return "I believe I can fly!";
}
@Override
public void eat(String food) {
System.out.println("吃" + food);
}
}
/**
* 要想实现动态代理,需要解决的问题?
* 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象?
* 问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法?
*/
class ProxyFactory {
// 调用此方法返回一个代理类对象,解决问题一
public static Object getProxyInstance(Object object) { // obj :被代理的对象
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(object);
return Proxy.newProxyInstance(object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
handler);
}
}
class MyInvocationHandler implements InvocationHandler {
// 创建被代理对象
private Object object; // 需要使用被代理类的对象进行赋值
public void bind(Object object){
this.object = object;
}
/**
* 当通过代理类的对象,调用方法a时,会自动调用invoke()方法
* 将被代理类要执行的方法a的功能就声明在此方法中
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// method:即为代理类对象调用的方法,此方法也就成为了被代理类对象调用的方法
// object:被代理类对象
return method.invoke(object,args);
}
}
public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
// proxyInstance:代理类对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
System.out.println(proxyInstance.getBelief());
proxyInstance.eat("麻辣烫");
}
}
执行结果
I believe I can fly!
吃麻辣烫
使用ProxyFactory动态创建7.2中的代理类
ExampleClothFactory exampleClothFactory = new ExampleClothFactory();
// proxyClothFactory:代理类对象
ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(exampleClothFactory);
proxyClothFactory.produceCloth();// 代理类执行被代理类的方法
// 执行结果
Example工厂开始工作....
7.4 AOP与动态代理的举例
改进后
改进后的说明:代码段1、代码段2、代码段3和方法A中的代码段分离开了,但代码段1、2、3又和一个特定的方法A耦合了!最理想的效果是:代码块1、2、3既可以执行方法A,又无须在程序中以硬编码的方式直接调用A。
使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理,这种动态代理在AOP中被称为AOP代理,AOP代理可代替目标对象,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异:AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理
interface Human {
String getBelief();
void eat(String food);
}
// 被代理类
class SuperMan implements Human {
@Override
public String getBelief() {
return "I believe I can fly!";
}
@Override
public void eat(String food) {
System.out.println("吃" + food);
}
}
class HumanUtil{ // 本次新增代码
public void method1(){
System.out.println(".....通用方法1.....");
}
public void method2(){
System.out.println(".....通用方法2.....");
}
}
/**
* 要想实现动态代理,需要解决的问题?
* 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象?
* 问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法?
*/
class ProxyFactory {
// 调用此方法返回一个代理类对象,解决问题一
public static Object getProxyInstance(Object object) { // obj :被代理的对象
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(object);
return Proxy.newProxyInstance(object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
handler);
}
}
class MyInvocationHandler implements InvocationHandler {
// 创建被代理对象
private Object object; // 需要使用被代理类的对象进行赋值
public void bind(Object object){
this.object = object;
}
/**
* 当通过代理类的对象,调用方法a时,会自动调用invoke()方法
* 将被代理类要执行的方法a的功能就声明在此方法中
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
HumanUtil humanUtil = new HumanUtil(); // 本次新增代码
humanUtil.method1(); // 本次新增代码
// method:即为代理类对象调用的方法,此方法也就成为了被代理类对象调用的方法
// object:被代理类对象
Object invoke = method.invoke(object, args);
humanUtil.method2();// 本次新增代码
return invoke;
}
}
public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
// proxyInstance:代理类对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
// 通过代理类对象调用方法时,会自动的调用被代理类的同名方法
proxyInstance.eat("麻辣烫");
ExampleClothFactory exampleClothFactory = new ExampleClothFactory();
// 创建代理类对象
ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(exampleClothFactory);
proxyClothFactory.produceCloth();// 代理类执行被代理类的方法
}
}
执行结果
.....通用方法1.....
吃麻辣烫
.....通用方法2.....
.....通用方法1.....
Example工厂开始工作....
.....通用方法2.....
\