Java是“准动态语言”
- 动态语言:在运行时可以改变其结构的语言,例如新的函数、对象、甚至代码可以被引进等。比如C#,JavaScript,Python等等。
- 静态语言:运行时结构不可变的语言。比如,C、C++、Java
- java虽然是静态语言,但是我们可以称其为“准动态语言”,java具有一定的动态性,我们可以通过反射机制获得类似于动态语言的特性。
- Class对象,即Class类的一个实例对象,是编译得到的Class文件经过类加载器加载进内存(堆)之后的存在形式。
什么是反射
反射就是把Java类中的各个成分映射成一个个的Java对象(Fields,Methods,Constructor,Array等类型的对象)。即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。
package com.jing.annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test08 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
// 1. 获取class对象
Class userClass = Class.forName("com.jing.reflection.User");
// 获取类的信息
// 2.1 获取类的字段 Declared可以获取全部属性,包括私有的
Field[] fields = userClass.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("-------------------------");
// 根据字段名获取特定字段
Field age = userClass.getDeclaredField("age");
System.out.println(age);
System.out.println("======================================");
// 2.2 获取类的方法 Declared可以获取本类的全部方法,包括私有的
// getMethods():获取本类及父类的public方法
Method[] methods = userClass.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("-------------------------");
// 根据方法名和参数类型获取特定方法
Method setAge = userClass.getMethod("setAge", int.class);
System.out.println(setAge);
Method A = userClass.getMethod("A", null);
System.out.println(A);
System.out.println("======================================");
// 3.1 获取类的构造器
Constructor[] constructors = userClass.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
/*
程序执行结果:
private java.lang.String com.jing.reflection.User.name
private int com.jing.reflection.User.age
-------------------------
private int com.jing.reflection.User.age
======================================
public java.lang.String com.jing.reflection.User.toString()
public void com.jing.reflection.User.B()
public java.lang.String com.jing.reflection.User.getName()
public void com.jing.reflection.User.setName(java.lang.String)
public void com.jing.reflection.User.A()
public void com.jing.reflection.User.setAge(int)
public int com.jing.reflection.User.getAge()
-------------------------
public void com.jing.reflection.User.setAge(int)
public void com.jing.reflection.User.A()
======================================
public com.jing.reflection.User()
public com.jing.reflection.User(java.lang.String,int)
*/
}
}
反射的使用
1. 通过反射动态创建对象
package com.jing.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
// 利用反射动态创建对象
public class Test09 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
// 1. 获得Class对象
Class c1 = Class.forName("com.jing.reflection.User");
// 2.1 通过newInstance创建对象,前提:必须有无参构造
User u1 = (User) c1.newInstance();
System.out.println(u1);
System.out.println("=======================================================");
// 2.2 通过Constructor构造器创建对象
// 2.2.1 通过有参构造器创建
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class);
User u2 = (User) constructor.newInstance(null, 0); // 必须带参数
System.out.println(u2);
System.out.println("-------------------------------------------------------");
// 2.2.2 通过无参构造器创建
Constructor constructor1 = c1.getDeclaredConstructor();
User u3 = (User) constructor1.newInstance();
System.out.println(u3);
}
}
/*
User{name='null', age=0}
=======================================================
User{name='null', age=0}
-------------------------------------------------------
User{name='null', age=0}
*/
2. 通过反射动态执行方法(在程序运行过程中,在需要的地方,动态获取方法Method,并执行(为什么不直接调用,对象名.方法,因为在写代码时,还不确定何时何地调用哪个方法);重复利用现有代码,而无需显式定义相同功能的方法)
package com.jing.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// 动态执行方法
public class Test10 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
// 1. 获取Class对象
Class c1 = Class.forName("com.jing.reflection.User");
// 2. 利用反射创建User对象
User u1 = (User) c1.newInstance();
// 3. 利用反射获取方法并执行
Method A = c1.getDeclaredMethod("A", String.class);
// 执行私有方法,关闭安全监测
A.setAccessible(true);
Object resultA = A.invoke(u1, "YuJing");
System.out.println("=======================================");
Method B = c1.getDeclaredMethod("B");
Object resultB = B.invoke(u1);
/*
有参数方法A被执行YuJing
=======================================
无参数方法B被执行
*/
/*
动态代理:
· 调用处理器InvocationHandler中的invoke方法,参数:代理对象,Method方法,方法参数;invoke方法被自动调用,来处理代理对象对Method方法的调用
· 在invoke方法中,method.invoke(目标对象, 参数args),这是一种典型的在程序运行过程中方法被动态获取并执行。
· 利用这种灵活性,可以实现对基础方法的前后增强。即将方法摘取出来,然后在前后进行扩展,相当于横向切入一些功能,不影响原有功能。
· 基础方法仍由真实对象负责执行,扩展功能由代理对象完成
· 这种非调用执行方法的方式,可以实现传递哪个Method对象,就执行哪个Method对象对应的方法。
· 在程序运行过程中,可以利用反射获取并执行已存在的方法,而无需在自己的代码中再写一份,代码重用性可可扩展性都得到了提高
*/
}
}
动态代理只调用处理器:
/**
* Proxy类:动态获取代理实例
* InvocationHandler接口:执行代理实例的方法,并返回结果
*
* 一个动态代理类代理一类业务(一类业务一般封装到一个接口中),
* 所以一个动态代理类可以代理多个类,即实现了同一接口的类(因为Proxy是根据接口创建代理实例)
*/
// 通用的调用处理类
public class GeneralInvocationHandler implements InvocationHandler {
// 被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
// 调用处理方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target, args);
}
}
动态代理之测试类:
public class Client {
public static void main(String[] args) {
// 1. 创建真实对象
UserServiceImpl userService = new UserServiceImpl();
// 2.1 获取调用处理器
GeneralInvocationHandler invocationHandler = new GeneralInvocationHandler();
// 2.2 设置具体的被代理的真实对象
invocationHandler.setTarget(userService);
// 3. 获取代理对象
// 所谓代理对象并不是什么复杂的东西,简单理解就是一个类实例
// 扩展了真实对象的功能,这交给invoke方法实现
UserService proxy = (UserService) Proxy.newProxyInstance(
GeneralInvocationHandler.class.getClassLoader(),
userService.getClass().getInterfaces(), // 代理的接口
invocationHandler);
// 4. 执行方法
proxy.add();
}
}
3. 通过反射动态操作属性:get/set
package com.jing.reflection;
import java.lang.reflect.Field;
public class Test11 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
// 1. 获取Class对象
Class c1 = Class.forName("com.jing.reflection.User");
// 2. 利用反射创建User对象
User u1 = (User) c1.newInstance();
// 3. 利用反射获取并操作属性,get/set
Field name = c1.getDeclaredField("name");
// 关闭安全监测,开启操作私有属性的权限
name.setAccessible(true);
name.set(u1, "YuJing");
String nameValue = (String) name.get(u1);
System.out.println(nameValue);
System.out.println(u1.getName());
}
}
/*
YuJing
YuJing
*/
备注:Constructor、Method、Field都有一个setAccessible()方法,来设置是否关闭安全监测,即是否开启操作私有构造器/方法/属性的权限。在使用反射时,关闭检测可以提高运行效率(如果不关闭,每次操作都会检测,降低程序运行效率)
4. 反射获取泛型
5. 反射操作注解
演示程序:通过反射获得注解中的属性值,然后根据属性值生成相应的SQL语句,去操作数据库
package com.jing.reflection;
import java.lang.annotation.*;
import java.lang.reflect.Field;
/**
* 演示:利用反射获取注解
*
* 实际应用:
* ORM(对象关系映射),将java实体类的类名,属性 映射成 数据库的表名和字段(字段名、字段类型、字段长度)
* 映射:获取注解,然后获取注解的属性
* 表名和字段可以用来生成SQL语句
*/
public class Test12 {
public static void main(String[] args) throws NoSuchFieldException {
// 1. 获取Class对象
Class studentClass = Student.class;
// 2.1 获取类上注解
Table table = (Table) studentClass.getAnnotation(Table.class);
// 2.2 获取注解属性——表名
String value = table.value();
System.out.println("表名:" + value);
// 3.1 获取属性上的注解
Field[] declaredFields = studentClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
String fieldName = declaredField.getName();
Column column = declaredField.getAnnotation(Column.class);
System.out.println("属性" + fieldName + ":" );
System.out.print(column.colName() + " ");
System.out.print(column.colType() + " ");
System.out.print(column.length() + " ");
System.out.println();
}
}
}
/*
表名:db_student
属性id:
db_id int 10
属性name:
db_name varchar 20
属性age:
db_age int 10
*/
@Table("db_student")
class Student{
@Column(colName = "db_id", colType = "int", length = 10)
private int id;
@Column(colName = "db_name", colType = "varchar", length = 20)
private String name;
@Column(colName = "db_age", colType = "int", length = 10)
private int age;
public Student() {
}
public Student(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 "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Column{
String colName();
String colType();
int length();
}