Reflection反射

前言

在Java语言中,Java的反射机制是它的一个重要特性。

Java的反射机制是指在运行状态中,对于任意一个类都能够调用方法知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意方法和属性。这种在运行状态中获取类或对象的动态信息的功能被称为反射。

反射的功能

  • 获取类的各种信息:反射可以获取一个类中的各种信息,包括类名、父类、实现的接口、类中定义的属性、方法、构造方法等。
  • 创建对象:通过Class对象的newInstance()方法动态创建对象,而不需要在编写代码时事先知道创建的具体对象类型。
  • 调用方法:在运行状态时动态调用一个对象的方法,包括公有、私有、静态方法等。
  • 生成动态代理:在运行时创建代理类实例,由代理类来代理另外一个类的方法调用。

 反射机制获取对象信息

1、获取类型信息

在编译期间确定对象类型的代码如下

String str = new String();

在运行期间确定对象类型,反射机制中有三种方法

  • 通过Class类的静态方法forName得到

String str = Class.forName("String").newInstance();
  •  通过实例化访问

String str = "";
Class stringCls = str.getClass();
  •  通过类名访问

Class stringCls1 = String.class;

注:以上这三种方法在一个main中输出的哈希值相同,因为哈希值只会重复输出首次获取。

2、获取接口信息

//实现的接口
Class[] interfacecls = cls.getInterfaces();
System.out.println("当前类实现的接口:");
for (Class iclass : interfacecls) {
	System.out.println(iclass);
}

 3、获取构造方法

//此处Example为一个自己创建的类
Class cls = Example.class;
//只能访问公有的构造方法
System.out.println("所有的构造方法");
//Constructor[] constructors = cls.getConstructors();//不包括私有
Constructor[] constructors = cls.getDeclaredConstructors();
for (Constructor constructor : constructors) {
	System.out.println(constructor);
 }
		
//获取一个私有的构造方法
Constructor privateConstruct = cls.getDeclaredConstructor(String.class);
		
//调用私有的构造方法
privateConstruct.setAccessible(true);
Example ex = (Example) privateConstruct.newInstance("just");
System.out.println(ex);
System.out.println("---------");


//调用无参构造方法(根据数据类型、数据个数进行调用)
Constructor construct1 = cls.getConstructor();
Constructor construct2 = cls.getConstructor(int.class);
Constructor construct3 = cls.getConstructor(int.class,double.class);

 4、Instanceof运算符和isAssignableFrom运算符

Instanceof运算符:判断引用和类型之间的关系

isAssignableFrom运算符:判断两个类型之间的关系

实现代码如下:

//Instanceof运算符:判断引用和类型之间的关系
Object obj = Double.valueOf(324);
System.out.println("是否为Double类型?"+(obj instanceof Double));
System.out.println("是否为Integer类型?"+(obj instanceof Integer));
System.out.println("是否为Character类型?"+(obj instanceof Character));
System.out.println("是否为RandomAccess接口?"+(obj instanceof RandomAccess));
System.out.println("是否为Comparable接口?"+(obj instanceof Comparable));
		
//isAssignableFrom运算符:判断两个类型之间的关系
System.out.println("Intager <= Intager ?"+Integer.class.isAssignableFrom(Integer.class));
System.out.println("Intager <= Double ?"+Integer.class.isAssignableFrom(Double.class));
System.out.println("Intager <= RandomAccess ?"+Integer.class.isAssignableFrom(RandomAccess.class));
System.out.println("Serializable <= Intager ?"+Serializable.class.isAssignableFrom(Integer.class));
System.out.println("Intager <= Number ?"+Integer.class.isAssignableFrom(Number.class));
System.out.println("Number <= Intager ?"+Number.class.isAssignableFrom(Integer.class));

 输出结果如下: 78c5393d2f3146219f9c634f37f1a8cc.png

 5、访问成员变量

//Book为自己创建的类
Class c = Book.class;
//Field[] fields = c.getFields();//父类子类的公用成员变量都能拿到
Field[] fields = c.getDeclaredFields();//获取本类的所有成员变量
for (Field field : fields) {
   System.out.println("成员访问修饰符int:"+field.getModifiers());
   System.out.println("成员访问修饰符:"+Modifier.toString(field.getModifiers()));
   System.out.println("成员变量类型:"+field.getType());
   System.out.println("成员变量名称:"+field.getName());
   System.out.println("-------");
  }

 6、添加成员变量的值

//运行期
//使用反射的方式完成成员变量变值
Class cls = Book.class;//获取class类型的对象
Object obj = cls.newInstance();//通过反射创建Book类的对象
		
//按照字段名称,获取指定成员变量
Field field = cls.getDeclaredField("BookName");
//参数1:目标Book对象
//参数2:存入变量中的值
field.set(obj, "哈哈哈");
		
Field field2 = cls.getDeclaredField("stock");
field2.set(obj, 90);
		
Field field3 = cls.getDeclaredField("sale");
field3.setAccessible(true);//访问私有成员变量
field3.set(obj, 0.8);
System.out.println(obj);

7、获取类中所有方法

Class cls = Book.class;		
//获取所有Public方法(包括父类)
//Method[] methods = cls.getMethods();
//获取所有定义的方法
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
  System.out.println("方法的访问修饰符"+Modifier.toString(method.getModifiers()));
  System.out.println("方法的返回值类型"+method.getReturnType());
  System.out.println("方法的名称"+method.getName());
  //获取所有的参数类型
  //Class[] paramTypes = method.getParameterTypes();
  //获取所有参数对象
  Parameter[] params = method.getParameters();
  for (Parameter p : params) {
  System.out.println(p.getName());
  System.out.println(p.getType());
  System.out.println("--------------");
    }
  System.out.println();
  }
  •  获取方法对象

//反射获取class对象
Class cls = Base.class;
Object obj = cls.newInstance();
//按照方法名称和参数类型获取method方法对象
//create()
//Method method = cls.getMethod("create",int.class);
			
Method method = cls.getMethod("create", int.class);
//Method对象的invoke()作用
//以反射的方式执行create()方法
int r = (int) method.invoke(obj, 1000);
System.out.println(r);

//Base类
class Base{
	public int  create() {
		return create(100);
	}
	public int create(int x) {
		return (int) (Math.random()*x);
	}
}

 注:如果是调用一个类中的方法其中它的子类也覆写了这个方法,父类.class获取的Method,作用于Student实例时,调用的方法是子类的方法。实现代码如下:

//main
Method method = PersonS.class.getMethod("hello");
method.invoke(new Student());//目标对象调用方法归属

//类
class PersonS{
	public void hello() {
		System.out.println("hello");
	}
}
class Student extends PersonS{
	public void hello() {
		System.out.println("你好");
	}
}


//输出为>>你好

Class类中的主要方法有:

  • getName():获取类的全限定名
  • getSimpleName():获取类的简单类名
  • getFields():获取类中的所有public属性
  • getDeclaredFields():获取类声明中的所有属性,包括private属性、protect属性
  • getMethods():获取类中所有public方法,包括从父类中继承来的方法
  • getDeclaredMethods():获取类中声明的所有方法,包括private方法
  • getConstructors():获取类中的所有public构造方法
  • getDeclaredConstructors():获取类中声明的所有构造方法,包括private方法
  • newInstance():通过Class对象动态创建对象
  • isAssignableFrom(Class<?> cls):判断当前class对象是否可以从指定的Class对象进行赋值操作
  • getInterfaces():获取当前类实现的接口
  • getSuperclass():获取当前类的父类

 除此之外还有很多动态获取信息操作以及判断方法,上文只是列出来一部分内容。

最重要的静态代理:

代理模式有两种:静态代理、动态代理。是根据字节码的创建时机来分类的。

静态是指在程序运行之前我们就已经知道代理类和真实角色的关系

动态是指在运行期间由JVM根据反射等动态机制的生成,在运行之前不知道代理类和真实角色的关系。

代理角色有三种:

  • Subject(抽象主题角色):定义代理类和真实主题的公告对外方法,通常类型是接口
  • RealSubject(真实主题校色):真正实现业务逻辑的类
  • proxy(代理主题角色):用来代理和封装真实主题

静态代理案例实现:

public class client {
	public static void main(String[] args) {
		subject subject = new subjectproxy();
		subject.request();

		UserService service = new UserServiceProxy();
		service.select();
		service.update();
	}
}
public class Realsubject implements subject{
	public void request() {
		System.out.println("逻辑1");
		System.out.println("逻辑2");
		System.out.println("逻辑3");
	}
}
public interface subject {
	void request();
}
public class subjectproxy implements subject{
	private subject target;
	public subjectproxy() {
		target = new Realsubject();
	}
	@Override
	public void request() {
		System.out.println("begin-----------");
		target.request();
		System.out.println("end-----------");
		
	}

}

静态代理案例详细实现2:

public interface UserService {
	void select();
	void update();
}
public class UserServiceImpl implements UserService {

	@Override
	public void select() {
		System.out.println("select * ..................");
		System.out.println("数据库中完成用户信息的查询执行!");
	}

	@Override
	public void update() {
		System.out.println("update ...................");
		System.out.println("数据库中用户状态的更新执行!");
	}

}
public class UserServiceProxy implements UserService{
	private UserService target;
	public UserServiceProxy() {
		target = new UserServiceImpl();
	}

	@Override
	public void select() {
		begin();
		target.select();
		end();
	}

	@Override
	public void update() {
		begin();
		target.update();
		end();
		
	}
	private void begin() {
		System.out.println("开始");
	}
	private void end() {
		System.out.println("结束");
	}
}

public class client {
	public static void main(String[] args) {
		UserService service = new UserServiceProxy();
		service.select();
		service.update();
	}
}

 动态代理案例实现:

public interface UserService {
	void select();
	void update();
}
public class UserServiceImpl implements UserService {

	@Override
	public void select() {
		System.out.println("select * ..................");
		System.out.println("数据库中完成用户信息的查询执行!");
	}

	@Override
	public void update() {
		System.out.println("update ...................");
		System.out.println("数据库中用户状态的更新执行!");
	}

}
//InvocationHandler接口实现类:代理类中扩展逻辑抽取封装
public class LogInvocationHandlerImpl implements InvocationHandler {
	private Object target;
	public LogInvocationHandlerImpl(Object target) {
		this.target = target;
	}
	
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

		System.out.printf("方法%s开始执行\n",method.getName());
		
		//执行目标对象的目标方法
		Object ReturnValue =method.invoke(target, args);
		
		System.out.printf("方法%s结束执行\n",method.getName());
		
		return ReturnValue;
	}

}
public class client {
	public static void main(String[] args) {
		LogInvocationHandlerImpl handler1 = new LogInvocationHandlerImpl(new UserServiceImpl());
		
		UserService proxy1 = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(), 
				new Class[] {UserService.class},
				handler1);
		proxy1.select();
		proxy1.update();
   }
}

以上为部分方法实现案例 

 静态代理的优缺点:

  • 优点:

  1. 可以在不修改目标对象的情况下增强目标对象的功能,实现了业务的分离,降低了系统的耦合度。
  2. 可以让代理对象在不影响目标对象的前提下对目标对象进行扩展。
  3. 可以对目标对象进行访问控制,增加系统的安全性。
  • 缺点 

  1. 每一个代理类只能为一个接口服务,增加了系统中类的数量,复杂度增加。
  2. 代理类和目标对象实现了相同的接口,当接口发生改变时,代理类和目标对象都需要同时进行修改,增加了系统的维护成本。
  3. 如果目标对象的方法很多,那么代理类也需要实现相同数量的方法,代码量相当大,实现起来比较麻烦。

动态代理的优缺点:

  • 优点:

  1. 动态代理可以实现接口中所有方法的代理,不需要像静态代理一样手动为每个方法创建代理类。
  2. 动态代理可以在不修改源码的情况下,对目标对象进行增强,非常灵活。
  3. 动态代理可以通过使用 InvocationHandler 接口,在代理对象方法执行前后添加额外的逻辑。
  • 缺点: 

  1. 动态代理代理的接口必须是已知的,如果被代理的对象实现的接口不确定,则无法使用动态代理。
  2. 动态代理在运行期间生成代理类的过程比较消耗时间,如果实例化频繁,可能会影响系统性能。
  3. 动态代理只能代理接口,无法代理实现类的方法。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿究院懒羊羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值