Java Reflection(反射)

一、Java反射机制

在学习Java反射机制前,我们应该先明确两个概念:编译期和运行期

编译期:是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。

运行期:是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。

 Java反射机制是指在程序的运行状态中,可以得到任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。

Java 反射机制主要提供了以下功能,这些功能都位于java.lang.reflect包。
● 在运行时判断任意一个对象所属的类。
● 在运行时构造任意一个类的对象。
● 在运行时判断任意一个类所具有的成员变量和方法。
● 在运行时调用任意一个对象的方法。
● 生成动态代理。

二、Class类

1、Class类

要想知道一个类的属性和方法,必须先获取到该类的字节码文件对象。获取类的信息时,使用的就是 Class 类中的方法。所以先要获取到每一个字节码文件(.class)对应的 Class 类型的实例(JVM内部创建,我们自己的Java程序是无法创建Class实例的)。由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段(成员变量)等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。这种通过Class实例获取class信息的方法称为反射。

获取一个class的Class实例(以String类为例)的三种方法:

(1)直接通过一个class的静态变量class获取:

Class cls = String.class;

(2)如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取:

String str = "Java";
Class cls = str.getClass();

(3) 如果知道一个class的完整类名,可以通过静态方法Class.forName()获取:

Class cls = Class.forName("java.lang.String");

Class实例在JVM中是唯一的,所以,上述方法获取的Class实例是同一个实例。可以用==比较两个Class实例: 

Class cls1 = String.class;
String str = "Java";
Class cls2 = str.getClass();
boolean sameClass = cls1 == cls2; // true

Class常用方法:

public class class01 {
	public static void main(String[] args) {
		Class cls = String.class;
		printClassInfo(cls);
	}
	public static void printClassInfo(Class cls) {
		System.out.println("类的名称:"+cls.getSimpleName());
		System.out.println("完全限定名:"+cls.getName());
		System.out.println("类的类型名称:"+cls.getTypeName());
		Class[] clsArr=cls.getInterfaces();
		System.out.println("当前类实现的接口:");
		for(Class clss:clsArr) {
			System.out.println(clss);
		}
		System.out.println("类所在包的名称:"+cls.getPackageName());
		System.out.println("是否为接口:"+cls.isInterface());
		System.out.println("是否为数组:"+cls.isArray());
		System.out.println("是否为枚举:"+cls.isEnum());
		System.out.println("是否为基本类型:"+cls.isPrimitive());
	}
}

 

注意,数组(例如String[])也是一种类,而且不同于String.class,它的类名是[Ljava.lang.String;。此外,JVM为每一种基本类型如int也创建了Class实例,通过int.class访问。 

动态加载机制:JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是第一次需要用到class时才加载。

2、调用构造方法

通过反射来创建新的实例,可以调用Class提供的newInstance()方法(只能调用该类的public修饰的无参数构造方法):

Student stu = Student.class.newInstance();

Constructor类(调用任意的构造方法):

public class class02 {
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
		Class cls=Example.class;
		Example ex1=(Example) cls.newInstance();
		System.out.println(ex1);
		//输出Example类中的所有构造方法
		System.out.println("Example类中的所有构造方法:");
		Constructor[] constructorArr=cls.getConstructors();
		for(Constructor constructor:constructorArr) {
			System.out.println(constructor);
		}
		//调用Example类的有一个参数的有参构造
		Constructor constructor=cls.getConstructor(int.class);
		Example ex2=(Example) constructor.newInstance(123);
		System.out.println(ex2);
		//调用Example类的有两个参数的有参构造
		Constructor constructor2=cls.getConstructor(int.class,double.class);
		Example ex3=(Example) constructor2.newInstance(123,12.3);
		System.out.println(ex3);
	}
}

class Example{
	public Example() {
		System.out.println("Example类的无参构造!");
	}
	public Example(int a) {
		System.out.println("Example类的有一个参数的有参构造!");
	}
	public Example(int a,double b) {
		System.out.println("Example类的有两个参数的有参构造!");
	}
}

通过Class实例获取Constructor的方法如下:
● getConstructor(Class...):获取某个public的构造方法;
● getDeclaredConstructor(Class...):获取某个定义的构造方法;
● getConstructors():获取所有public的构造方法;
● getDeclaredConstructors():获取所有定义的构造方法。 

3、获取继承关系

获取父类的Class(getSuperclass()方法):

Class cls = String.class;
System.out.println(cls.getSuperclass());

注意:Object的父类是null 

 获取interface(getInterfaces()方法):

Class cls = String.class;
Class[] is = cls.getInterfaces();

继承关系:

当我们判断一个实例是否是某个类型时,可以使用instanceof操作符: 

Object obj=Integer.valueOf(7);
System.out.println("是否为Integer:"+(obj instanceof Integer)); //true
System.out.println("是否为Object:"+(obj instanceof Object)); //true
System.out.println("是否为Double:"+(obj instanceof Double)); //false
System.out.println("是否为Comparable:"+(obj instanceof Comparable)); //true
System.out.println("是否为Double:"+(obj instanceof Double)); //false
System.out.println("是否为Number:"+(obj instanceof Number)); //true

如果是两个Class实例,要判断一个向上转型是否成立,可以调用isAssignableFrom()方法:

Object obj=Integer.valueOf(7);
System.out.println("Integer<=Integer:"+Integer.class.isAssignableFrom(Integer.class)); //true
System.out.println("Integer<=NUmber:"+Integer.class.isAssignableFrom(Number.class)); //false
System.out.println("Integer<=Double:"+Integer.class.isAssignableFrom(Double.class)); //false
System.out.println("Integer<=Comparable:"+Integer.class.isAssignableFrom(Comparable.class); //false
System.out.println("NUmber<=Integer:"+Number.class.isAssignableFrom(Integer.class)); //true

4、访问字段(成员变量) :

获取Field 字段:

● Field getField(name):根据字段名获取当前类中某个public的field(包括父类)
● Field getDeclaredField(name):根据字段名获取当前类中定义的某个field(不包括父类)
● Field[] getFields():获取所有public的field(包括父类)
● Field[] getDeclaredFields():获取当前类中定义的所有field(不包括父类)

public class class03 {
	public static void main(String[] args) throws NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException {
		Class cls=Book.class;
		Field[] fields=cls.getDeclaredFields(); //获取Book类中定义的所有field(不包括父类)
		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();
		}
        //设置成员变量authorName的值
        Field field=cls.getDeclaredField("authorName");
		Object obj=cls.newInstance();
		field.set(obj, "南派三叔");
		System.out.println(obj.toString());
	}
}

class Book{
	public String authorName;
	public double price;
	private String bookName;
	private String publishTime;
	public String getName() {
		return authorName;
	}
	public void setName(String authorName) {
		this.authorName = authorName;
	}
	public double getPrice() {
		return price;
	}
	public void setPrice(double price) {
		this.price = price;
	}
	public String getBookName() {
		return bookName;
	}
	public void setBookName(String bookName) {
		this.bookName = bookName;
	}
	public String getTime() {
		return publishTime;
	}
	public void setTime(String publishTime) {
		this.publishTime = publishTime;
	}
    //重写toString()方法
    public String toString() {
		return String.format("书名:%s,姓名:%s,价格:%f,出版时间:%s", bookName,authorName,price,publishTime);
	}
    public int createRandomNumber(int i) {
	    Random ran=new Random();
		int a=ran.nextInt(i);
		System.out.println(a);
		return a;
	}
	public int createRandomNumber2() {
		int a=new Random().nextInt(100);
		System.out.println(a);
		return a;
	}
}

获取字段值(成员变量的值):

public class class04 {
	public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
		Book book=new Book();
		book.authorName="南派三叔";
		book.price=44.4;
		book.setBookName("盗墓笔记");
		book.setTime("2012-2-2");
		
		printInfo(book);
	}
	public static void printInfo(Object obj) throws IllegalArgumentException, IllegalAccessException {
		Class cls=obj.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(obj)); //获取字段值
		}
	}
}  

设置字段值:

 代码在class03类中。注意:修改非public字段,需要首先调用setAccessible(true),然后在调用set()方法。

5、调用方法

Methond类:我们可以通过Class实例获取所有方法(Method类型的对象)。

● Method getMethod(name, Class...):获取某个public的Method(包括父类)
● Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
● Method[] getMethods():获取所有public的Method(包括父类)
● Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)

public class class05 {
	public static void main(String[] args) {
		Class cls = Book.class;
		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[] pArr = method.getParameterTypes();
			for (Class p : pArr) {
				System.out.println("方法的参数类型:"+p.getName());
			}
			System.out.println();
		}
	}
}

调用方法 :

public class class06 {
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
			IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		Class cls = Book.class;
		Object obj = cls.newInstance();
        //调用Book类的有参方法
		Method method = cls.getMethod("createRandomNumber", int.class);
		method.invoke(obj, 1000);
        //调用Book类的无参方法
		Method method2 = cls.getMethod("createRandomNumber2");
		method2.invoke(obj);
	}
}

 调用静态方法(调用静态方法时,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null):

public class class07 {
    public static void main(String[] args) throws Exception {
        //获取Integer.parseInt(String)方法,参数为String:
        Method m = Integer.class.getMethod("parseInt", String.class);
        //调用该静态方法并获取结果:
        Integer n = (Integer) m.invoke(null, "12345");
        System.out.println(n);
    }
}

调用非public方法:

在调用非public方法时,与修改非public字段,我们需要首先调用setAccessible(true),然后在调用invoke()方法。

多态:

public class class08 {
    public static void main(String[] args) throws Exception {
        //获取Person的hello方法:
        Method h = Person.class.getMethod("hello");
        //对Student实例调用hello方法:
        h.invoke(new Student());
    }
}

class Person {
    public void hello() {
        System.out.println("Person:hello");
    }
}

class Student extends Person {
    public void hello() {
        System.out.println("Student:hello");
    }
}

上述代码输出的结果是Student:hello,因此,使用反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法。 

三、静态代理和动态代理

1、代理模式:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。代理模式角色分为 3 种:
● Subject(抽象主题角色):定义代理类和真实主题的公共对外方法,通常被设计成接口;
● RealSubject(真实主题角色):真正实现业务逻辑的类;
● Proxy(代理主题角色):用来代理和封装真实主题;
代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层。

2、静态代理:所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就确定了。

案例:假设有UserService接口及其实现类UserServiceImpl,我们需要在不改变实现类代码的基础上,增加日志记录的功能(select和update)。

UserService接口:

public interface UserService {
	void select();
	void update();
}

 UserServiceImpl类(真正的实现类):

//真正的实现类
public class UserServiceImpl implements UserService {
	public void select() {
		System.out.println("select......");
		System.out.println("数据库中完成用户信息的查询执行!");
	}
	
	public void update() {
		System.out.println("update......");
		System.out.println("数据库中用户状态的更新执行!");
	}
}

UserServiceProxy类(代理类):

public class UserServiceProxy implements UserService{
	private UserService target;
	
	public UserServiceProxy() {
		target=new UserServiceImpl();
	}
	
	public void select() {
		before();
		target.select();
		after();
	}
	
	public void update() {
		before();
		target.update();
		after();
	}
	
	private void before() {
		System.out.println("------方法执行前!------");
	}
	
	private void after() {
		System.out.println("------方法执行后!------");
		System.out.println();
	}
}

Client类:

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

3、动态代理:动态代理的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件。JDK动态代理主要涉及两个类:Proxy类 和 InvocationHandler类。我们通过编写一个调用逻辑处理器 LogInvocationHandlerImpl类案例来提供日志增强功能,并实现 InvocationHandler 接口。(由JDK帮我们创建代理类)

案例:同静态代理

 UserService接口:

public interface UserService {
	void select();
	void update();
}

 UserServiceImpl类(真正的实现类):

//真正的实现类
public class UserServiceImpl implements UserService {
	public void select() {
		System.out.println("select......");
		System.out.println("数据库中完成用户信息的查询执行!");
	}
	
	public void update() {
		System.out.println("update......");
		System.out.println("数据库中用户状态的更新执行!");
	}
}

LogInvocationHandlerImpl类:

public class LogInvocationHandlerImpl implements InvocationHandler {
	private Object traget;
	public LogInvocationHandlerImpl(Object target) {
		this.traget = target;
	}
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.printf("方法%s开始执行、、、、、、、\n",method.getName());
		Object returnValue =method.invoke(traget, args);
		System.out.printf("方法%s结束执行、、、、、、、\n",method.getName());
		System.out.println();
		return returnValue;
	}
}

Client类:

public class Client {
	public static void main(String[] args) {
        //创建目标对象和InvocationHandler
		LogInvocationHandlerImpl handler = new LogInvocationHandlerImpl(new UserServiceImpl());
        //创建UserService接口的动态代理对象
		UserService proxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),
				new Class[] { UserService.class }, handler);
		proxy.select();	
		proxy.update();
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值