JAVA进阶学习15
文章目录
- 一、反射的基础知识
- 1.1 class对象的获取
- 1.2 通过class对象获取构造方法
- 1.3 通过class对象获取成员变量
- 1.4 通过class对象获取成员方法
- 二、动态代理
一、反射的基础知识
反射允许对封装类的字段,方法,构造函数的信息进行编程访问,可以获取到类中封装的所有信息
可以从类的字节码文件中获取类的基本信息,再进行后续的解剖
1.1 class对象的获取
可以用三种方式来获取类的字节码文件如下:
public class MyReflectDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
/*
* 获取class对象的三种方式:
* 1. Class.forName("全类名");
* 2. 类名.class
* 3. 对象.getClass();
*
* */
//1. 第一种方式
//全类名 : 包名 + 类名
//最为常用的
Class clazz1 = Class.forName("com.myreflect1.Student");
//2. 第二种方式
//一般更多的是当做参数进行传递,直接用类名来调用属性获取class文件
Class clazz2 = Student.class;
//在之前的同步代码快的使用中已经用到过
//3.第三种方式
//当我们已经有了这个类的对象时,才可以使用。
//使用这种方法不但可以获取到字节码文件还能获取对象中成员变量的值
Student s = new Student();
Class clazz3 = s.getClass();
System.out.println(clazz1 == clazz2);//true
System.out.println(clazz2 == clazz3);//true
}
}
总结:前两种方式可以直接通过类名来调用,第三种需要创建对象后使用
注意:第一种方式中获取全类名时可以选中类名后右键,复制类的引用
在粘贴时需要粘贴在字符串中或者注释里才能显示出全类名
1.2 通过class对象获取构造方法
Class clazz = Class.forName("com.myreflect1.Student");
获取函数名及形参
- Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数组
此处返回的是一个数组,在函数名末尾带s即可以返回多个构造函数
Constructor[] cons1 = clazz.getConstructors();
for (Constructor con : cons1) {
System.out.println(con);
}
- Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组
方法同上一次可以返回多个构造函数
Constructor[] cons2 = clazz.getDeclaredConstructors();
for (Constructor con : cons2) {
System.out.println(con);
}
- Constructor getConstructor(Class<?>… parameterTypes) 返回单个公共构造方法对象
Constructor con2 = clazz.getDeclaredConstructor(String.class);
- Constructor getDeclaredConstructor(Class<?>… parameterTypes) 返回单个构造方法对象
Constructor con4 = clazz.getDeclaredConstructor(String.class,int.class);
在返回单个构造函数时需要给出形参的字节码文件,以区别函数
构造方法的一些简单应用:
//获取方法中的权限修饰符
int modifiers = con4.getModifiers();
System.out.println(modifiers);
//获取构造方法中的形参
Parameter[] parameters = con4.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter);
}
//用构造方法创建对象
//暴力反射:setAccessible表示临时取消权限校验,可以直接使用私有的方法创建对象
con4.setAccessible(true);
Student stu = (Student) con4.newInstance("张三", 23);
System.out.println(stu);
注意:权限修饰符的返回值是整型,在java中已经将所有权限与整型值一一对应
1.3 通过class对象获取成员变量
public class MyReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
/*
Class类中用于获取成员变量的方法
Field[] getFields(): 返回所有公共成员变量对象的数组
Field[] getDeclaredFields(): 返回所有成员变量对象的数组
Field getField(String name): 返回单个公共成员变量对象
Field getDeclaredField(String name):返回单个成员变量对象
Field类中用于创建对象的方法
void set(Object obj, Object value):赋值
Object get(Object obj) 获取值
*/
//1.获取class字节码文件的对象
Class clazz = Class.forName("com.itheima.myreflect3.Student");
//2.获取所有的成员变量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
//3.获取单个的成员变量,通过变量的名字获取成员变量
Field name = clazz.getDeclaredField("name");
System.out.println(name);
//获取权限修饰符
int modifiers = name.getModifiers();
System.out.println(modifiers);
//获取成员变量的名字
String n = name.getName();
System.out.println(n);
//获取成员变量的数据类型
Class<?> type = name.getType();
System.out.println(type);
//获取成员变量记录的值
Student s = new Student("zhangsan",23,"男");
name.setAccessible(true);//暴力反射
String value = (String) name.get(s);
System.out.println(value);
//修改对象里面记录的值
name.set(s,"lisi");
System.out.println(s);
}
}
1.4 通过class对象获取成员方法
public class MyReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
/*
Class类中用于获取成员方法的方法
Method[] getMethods():返回所有公共成员方法对象的数组,包括继承的
Method[] getDeclaredMethods():返回所有成员方法对象的数组,不包括继承的
Method getMethod(String name, Class<?>... parameterTypes) :返回单个公共成员方法对象
Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回单个成员方法对象
Method类中用于创建对象的方法
Object invoke(Object obj, Object... args):运行方法
参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(如果没有就不写)
返回值:方法的返回值(如果没有就不写)
获取方法的修饰符
获取方法的名字
获取方法的形参
获取方法的返回值
获取方法的抛出的异常
*/
//1. 获取class字节码文件对象
Class clazz = Class.forName("com.itheima.myreflect4.Student");
//2. 获取里面所有的方法对象(包含父类中所有的公共方法)
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
//3. 获取里面所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法)
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
//4. 获取指定的单一方法
//形参列表中需要加入方法的名字和参数的类型
Method m = clazz.getDeclaredMethod("eat", String.class);
System.out.println(m);
成员方法的应用:
// 获取方法的修饰符
int modifiers = m.getModifiers();
System.out.println(modifiers);
// 获取方法的名字
String name = m.getName();
System.out.println(name);
// 获取方法的形参
Parameter[] parameters = m.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter);
}
//获取方法的抛出的异常
Class[] exceptionTypes = m.getExceptionTypes();
for (Class exceptionType : exceptionTypes) {
System.out.println(exceptionType);
}
//方法运行
/*Method类中用于创建对象的方法
Object invoke(Object obj, Object... args):运行方法
参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(如果没有就不写)
返回值:方法的返回值(如果没有就不写)*/
Student s = new Student();
m.setAccessible(true);
//参数一s:表示方法的调用者
//参数二"汉堡包":表示在调用方法的时候传递的实际参数
String result = (String) m.invoke(s, "汉堡包");
System.out.println(result);
}
}
二、动态代理
1.为什么会需要代理?
当一个类的事务过于繁杂时需要一个代理来帮他处理一部分事务;或者后期 在对类进行添加事务时可以避免改动原始的代码,而通过代理来执行。
总结:代理可以无侵入地为对象增加功能
2.代理是什么呢?
代理也是一个类,其中存着需要被代理的方法
3.对象和代理之间有什么联系?
对象需要实现一个接口,在接口中就是需要被代理的方法
以明星需要代理为例:
明星的实现类:
public class BigStar implements Star {
private String name;
public BigStar() {
}
public BigStar(String name) {
this.name = name;
}
//唱歌
@Override
public String sing(String name){
System.out.println(this.name + "正在唱" + name);
return "谢谢";
}
//跳舞
@Override
public void dance(){
System.out.println(this.name + "正在跳舞");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return "BigStar{name = " + name + "}";
}
}
需要被代理的方法定义成接口:
public interface Star {
//我们可以把所有想要被代理的方法定义在接口当中
//唱歌
public abstract String sing(String name);
//跳舞
public abstract void dance();
}
代理的实现:
public class ProxyUtil {
/*
*
* 方法的作用:
* 传入明星的对象,为其创建一个代理
*
* 形参:
* 被代理的明星对象
*
* 返回值:
* 给明星创建的代理
* */
public static Star createProxy(BigStar bigStar){
/* java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
*/
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类(写需要加载的类))
new Class[]{Star.class},//参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法(写被加载的接口)
//参数三:用来指定生成的代理对象要干什么事情,通过匿名内部类来实现
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
* 参数一:代理的对象
* 参数二:需要明星运行的方法
* 参数三:调用方法时,传递的实参
* */
if("sing".equals(method.getName())){
System.out.println("准备话筒,收钱");
}else if("dance".equals(method.getName())){
System.out.println("准备场地,收钱");
}
//去找大明星开始唱歌或者跳舞
//代码的表现形式:调用大明星里面唱歌或者跳舞的方法
return method.invoke(bigStar,args);
}
}
);
return star;
}
}
测试类:
public class Test {
public static void main(String[] args) {
/*
需求:
外面的人想要大明星唱歌、跳舞
1. 获取代理的对象
代理对象 = ProxyUtil.createProxy(大明星的对象);
2. 再调用代理的唱歌方法
代理对象.唱歌的方法("只因你太美");
*/
//1. 创建被代理的对象
BigStar bigStar = new BigStar("鸡哥");
//2. 获取代理的对象
Star proxy = ProxyUtil.createProxy(bigStar);
//3. 调用唱歌的方法
String result = proxy.sing("只因你太美");
System.out.println(result);
}
}