目录
方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取:
方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取:
一 、概述
Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。
Java 反射机制在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法或者为属性赋值。例如:在主流的 ORM 框架的实现中,运用 Java 反射机制可以读取任意一个 JavaBean 的所有属性,或者给这些属性赋值。
Java 反射机制主要提供了以下功能,这些功能都位于java.lang.reflect包。
二 、Class类
想要实现反射,Class类是不可或缺的
我们可以通过创建对应的Class实例来获得该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段(成员变量)等。
因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。这种通过Class实例获取class信息的方法称为反射(Reflection)。
获取class对象的三种方法
方法一:直接通过一个class的静态变量class获取:
Class cls = String.class;
方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取:
String s = "Hello";
Class cls = s.getClass();
方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取:
Class cls = Class.forName("java.lang.String");
三 、获取类的构造方法
如果通过反射来创建新的实例,可以调用Class提供的newInstance()方法:
调用Class.newInstance()的局限是,它只能调用该类的public无参数构造方法。如果构造方法带有参数,或者不是public,就无法直接通过Class.newInstance()来调用。
我们先创建一个Exmple类,接下来调用这个类的构造方法
class Example{
private Example(String a){
System.out.println("Example类的有参构造:"+a);
}
protected Example(float a){
System.out.println("Example类的有参构造:"+a);
}
public Example(){
System.out.println("Example类的无参构造!");
}
public Example(int a){
System.out.println("Example类的有参构造:"+a);
}
public Example(int a,double b){
System.out.println("Example类的有参构造:"+a+","+b);
}
当我们想要调用构造方法时
Class cls =Example.class;
Example example = (Example) cls.newInstance();
//查看所有构造方法
System.out.println("所有的构造方法:");
Constructor[] constructors =cls.getDeclaredConstructors();
for (Constructor constructor:constructors){
System.out.println(constructor);
}
System.out.println("------------------------------");
// 获取构造方法
Constructor constructor =cls.getConstructor();
Constructor constructor1 =cls.getConstructor(int.class);
Constructor constructor2 =cls.getConstructor(int.class,double.class);
//调用构造方法
Example example1 =(Example) constructor.newInstance();//无参
Example example2 = (Example) constructor1.newInstance(1024);//有参
Example example3 = (Example) constructor2.newInstance(1024,3.1415926);//有参
//调用私有构造方法
Constructor constructor3 =cls.getDeclaredConstructor(String.class);
constructor3.setAccessible(true);
Example example4= (Example) constructor3.newInstance("just");//私有+有参
四 、获取类的父类与实现接口
//父类
Class superClass = FileInputStream.class.getSuperclass();
System.out.println("父类:"+superClass.getName());
//接口
System.out.println("实现接口列表:");
Class[] in =StringBuilder.class.getInterfaces();
for (Class a :in) {
System.out.println(a.getName());
}
五 、instanceof运算符与判断类型与类型之间的关系
//instanceof运算符
Object obj = Integer.valueOf(153);
System.out.println("是否位Integer类型?:"+(obj instanceof Integer));
System.out.println("是否位Double类型?:"+(obj instanceof Double));
System.out.println("是否位Number类型?:"+(obj instanceof Number));
System.out.println("是否位Comparable接口?:"+(obj instanceof Comparable));
System.out.println("------------------------------------------");
//isAssignableFrom()方法 判断类型与类型之间的关系
System.out.println("Integer <= Integer? "+Integer.class.isAssignableFrom(Integer.class));
System.out.println("Integer <= Number? "+Integer.class.isAssignableFrom(Number.class));
System.out.println("Number <= Integer? "+Number.class.isAssignableFrom(Integer.class));
System.out.println("Double <= Integer? "+Double.class.isAssignableFrom(Integer.class));
System.out.println("Comparable <= Integer? "+Comparable.class.isAssignableFrom(Integer.class));
六 、获取成员变量与调用
我们可以通过Class实例获取所有Field对象
Person person =new Person("葫芦娃",16);
Class cls =person.getClass();
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
System.out.println("成员变量名称:"+field.getName());
//判断是否为私有变量
if (!field.isAccessible()){
//可访问私有变量
field.setAccessible(true);
}
System.out.println("成员变量内容:"+field.get(person));
}
}
七 、获取所有方法与调用
我们已经能通过Class实例获取所有Field对象,同样的,可以通过Class实例获取所有方法(Method类型的对象)
Class cls = String.class;
//所有方法(包括父类)
// 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());
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter.getName());
System.out.println(parameter.getType());
System.out.println("-----------------------");
}
System.out.println();
}
调用静态方法
// 获取Integer.parseInt(String)方法,参数为String:
Method m = Integer.class.getMethod("parseInt", String.class);
// 调用该静态方法并获取结果:
Integer n = (Integer) m.invoke(null, "12345");
// 打印调用结果:
System.out.println(n);
调用非public方法
同样我们需要将getMethod()改为getDeclaredMethod()并将setAccessible()设置为true
public class Main {
public static void main(String[] args) throws Exception {
Person p = new Person();
Method m = p.getClass().getDeclaredMethod("setName", String.class);
m.setAccessible(true);
m.invoke(p, "Bob");
System.out.println(p.name);
}
}
class Person {
String name;
private void setName(String name) {
this.name = name;
}
}
八 、代理模式
代理模式:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。
代理模式角色分为 3 种:
●Subject(抽象主题角色):定义代理类和真实主题的公共对外方法,通常被设计成接口;
●RealSubject(真实主题角色):真正实现业务逻辑的类;
●Proxy(代理主题角色):用来代理和封装真实主题;
代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层。
1.静态代理
subject接口
public interface Subject {
void request();
}
被代理类
public class RealSubject implements Subject {
public void request(){
System.out.println("逻辑业务1");
System.out.println("逻辑业务2");
System.out.println("逻辑业务3");
}
}
代理类
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-------------------");
}
}
测试
Subject proxy =new SubjectProxy();
proxy.request();
输出
通过静态代理,我们达到了功能增强的目的,而且没有侵入原代码,这是静态代理的一个优点。
虽然静态代理实现简单,且不侵入原代码,但是,当场景稍微复杂一些的时候,静态代理的缺点也会暴露出来。
1、 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:
●只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
●新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类
2、 当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护。
2.动态代理
动态代理的处理类
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.println("方法" + method.getName() + "开始执行!!");
Object returnValue = method.invoke(target, args);
System.out.println("方法" + method.getName() + "开始执行!!");
return returnValue;
}
}
接下来我们分别通过两个案例来实现动态代理
OrderService接口
public interface OrderService {
void createOrder();
void closeOrder();
}
OrderServiceImpl类
public class OrderServiceImpl implements OrderService{
@Override
public void createOrder() {
System.out.println("生成新的订单!");
}
@Override
public void closeOrder() {
System.out.println("关闭当前订单!");
}
}
UserService接口
public interface UserService {
void select();
void update();
}
UserServiceImpl类
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("数据库中用户状态的更新执行!");
}
}
Client类
public class Client {
public static void main(String[] args) {
//案例一:UserService
//创建一个将传给代理类的调用请求处理器,处理所有的代理对象上的方法调用
//这里创建的是一个自定义的日志处理器,须传入实际的执行对象 userServiceImpl
LogInvocationHandlerImpl handler =new LogInvocationHandlerImpl(new UserServiceImpl());
//创建UserService接口的动态代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),//获取对应的 ClassLoader
new Class[]{UserService.class},//获取所有接口的Class,这里的UserServiceImpl只实现了一个接口UserService
handler//目标对象
);
proxy.select();//调用select方法
System.out.println();
proxy.update();//调用update方法
System.out.println("-----------------------");
//案例二:OrderService
OrderServiceImpl orderServiceTarget =new OrderServiceImpl();
LogInvocationHandlerImpl handler1 = new LogInvocationHandlerImpl(orderServiceTarget);
OrderService proxy1 = (OrderService) Proxy.newProxyInstance(
orderServiceTarget.getClass().getClassLoader(),
orderServiceTarget.getClass().getInterfaces(),
handler1);
proxy1.createOrder();
System.out.println();
proxy1.closeOrder();
System.out.println("----------------------");
//最后分别查看两个代理类
System.out.println("UserServiceImpl代理类:"+proxy.getClass());
System.out.println("OrderServiceImpl代理类:"+proxy1.getClass());
}
}
输出结果
动态代理是通过Proxy创建代理对象,然后将接口方法“代理”给InvocationHandler完成的