Java反射详细学习笔记

动态代理

特点 : 无侵入式的给代码增加额外的功能 ;

代理里面就是对象要被代理的方法 ;

通过接口保证,后面的对象和代理需要实现同一个接口 , 接口中就是被代理的所有方法 ;

如何为java对象创建一个代理 :

  • java.lang.reflect.Proxy类 : 提供了为对象产生代理对象的方法 :

    public static Object newProxyInstance(ClassLoader,Class<?>[] interfaces,InvocationHandler h)
    参数一 : 用于指定用那个类加载器,去加载生成的代理类
    参数二 : 指定接口,这些接口用于指定生成的代理长什么,也就是什么方法
    参数三 : 用来指定生成的代理对象要干什么事情

实例代码 :

首先是一个需要代理的对象 : BigStar :

package com.it.yss;
​
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;
    }
}

定义接口Star,指定需要被代理的方法 :

package com.it.yss;
​
public interface Star {
    // 我们可以把所有想被代理的方法定义在接口当中
​
    // 唱歌
    public abstract String sing(String name) ;
​
    // 跳舞
    public abstract void dance() ;
​
}

创建代理ProxyUtil :

package com.it.yss;
​
​
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
​
/**
 *
 * 类的作用 :
 *  创建一个代理
 *
 */
public class ProxyUtil {
    /**
     * 方法的作用 :
     *  给一个明星的对象 ,创建一个代理
     *
     * 形参 :
     *  被代理的明星对象
     *
     *  返回值 :
     *      给明星创建的代理
     *
     *  需求 :
     *      外面的人想要大明星唱一首歌
     *      1 . 获取代理的对象
     *          代理对象 = ProxyUtil.createProxy(大明星的对象)
     *      2 . 再 调用代理的唱歌对象
     *          代理对象.唱歌的方法("歌曲的名称") ;
     */
    public static Star createProxy(Bigstar bigstar){
        /*
         java.lang.reflect.Proxy类 : 提供了为对象产生代理对象的方法
​
         public static Object newProxyInstance(ClassLoader,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 {
                        /**
                         * 参数一 : 代理的对象
                         * 参数二 : 要运行的方法 sing
                         * 参数三 : 调用sing方法时 , 传递的实参
                         */
                        if("sing".equals(method.getName())){
                            System.out.println("准备话筒 , 收钱");
                        }else if("dance".equals(method.getName())){
                            System.out.println("准备场地 , 收钱");
                        }
                        // 去找大明星唱歌或者跳舞
                        // 代码的表现形式 : 调用大明星里面唱歌或者跳舞的方法
                        return method.invoke(bigstar,args);
                    }
                }
        ) ;
​
        return star ;
​
​
​
    }
}

测试 :

package com.it.yss;
​
public class test {
    public static void main(String[] args) {
//     *  需求 :
//     *      外面的人想要大明星唱一首歌
//                *      1 . 获取代理的对象
//                *          代理对象 = ProxyUtil.createProxy(大明星的对象)
//                *      2 . 再 调用代理的唱歌对象
//                *          代理对象.唱歌的方法("歌曲的名称") ;
        // 1 . 获取代理的对象
        Bigstar bigstar = new Bigstar("鸡哥") ;
        Star proxy = ProxyUtil.createProxy(bigstar) ;
​
        // 2 . 调用唱歌的方法
        String result = proxy.sing("只因你太美") ;
        System.out.println(result);
​
        // 3 . 调用跳舞的方法
        proxy.dance();
    }
}

调用步骤 :

反射

概念 :

反射 :

反射允许对封装类的成员变量,成员方法和构造方法的信息进行编程访问 ;

一个类包括 :

  • 字段(成员变量)

  • 构造方法

  • 成员方法

而反射就可以将这些获取出来 , 例如idea中的类方法或构造函数参数提示就是依靠反射实现的 :

获取class对象的三种方式 :

  1. Class.forName("全类名")

  2. 类名.class

  3. 对象.getClass() ;

代码实例 :

package com.it.yss;
​
public class MyReflectDemo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        /**
         *  获取class对象的三种方式 :
         *         1. Class.forName("全类名")
         *         2. 类名.class
         *         3. 对象.getClass() ;
         */
        // 1. 第一种方式
        // 全类名 : 包名 + 类名 com.it.yss.Student
        // 最为常用的
        Class Clazz = Class.forName("com.it.yss.Student");
        // 打印 :
        System.out.println(Clazz);
​
        // 2 .第二种方式
        // 一般更多的是当作参数进行传递
        Class Clazz2 = Student.class ;
        System.out.println(Clazz2==Clazz);
​
        // 3 . 第三种方式
        // 当有了这个类的对象的时候 , 才能使用
        Student stu = new Student();
        Class Clazz3 = stu.getClass();
        System.out.println(Clazz3==Clazz2);
    }
}

可以看到全是相等的 ,输出结果 :

class com.it.yss.Student
true
true

在java中 , 万事皆可看作对象 :

获取构造方法 :

实例 :

Student类 :

package com.it.myreflect2;
​
public class Student {
    private String name ;
    private int age;
​
    public Student() {}
​
    public Student(String name){
        this.name = name;
    }
​
    public Student(int age){
        this.age = age;
    }
​
    public Student(String name, int age) {
        this.name = name;
        this.age = age ;
    }
​
    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{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

1 . 获取字节码文件对象 :

Class clazz = Class.forName("com.it.myreflect2.Student");

2 . 获取构造方法

        // 2 . 2 获取所有构造方法
        Constructor[] cons2 = clazz.getDeclaredConstructors();
        for (Constructor c : cons2) {
            System.out.println(c);
        }
​
        System.out.println("------------------------");
​
        // 2 . 3 获取空参构造方法
        Constructor con1 = clazz.getDeclaredConstructor() ;
        System.out.println(con1);
​
        System.out.println("------------------------");
​
        // 2 . 4 获取对应的构造方法(传入对应参数的字节码文件)
        Constructor con2 = clazz.getDeclaredConstructor(String.class);
        System.out.println(con2);
        Constructor con3 = clazz.getDeclaredConstructor(int.class);
        System.out.println(con3);
        Constructor con4 = clazz.getDeclaredConstructor(String.class,int.class);
        System.out.println(con4);
​
        System.out.println("------------------------");

输出结果 :

public com.it.myreflect2.Student()
public com.it.myreflect2.Student(java.lang.String)
------------------------
public com.it.myreflect2.Student()
public com.it.myreflect2.Student(java.lang.String)
protected com.it.myreflect2.Student(int)
private com.it.myreflect2.Student(java.lang.String,int)
------------------------
public com.it.myreflect2.Student()
------------------------
public com.it.myreflect2.Student(java.lang.String)
protected com.it.myreflect2.Student(int)
private com.it.myreflect2.Student(java.lang.String,int)
------------------------

3 . 获取构造方法修饰权限符

// 3 获取方法修饰权限符
int modifiers = con4.getModifiers();
System.out.println(modifiers);
​
System.out.println("------------------------");

这里输出结果是2 , 看api文档可以发现2对应的是private ;

4 . 获取构造方法参数

用con.getParameters()来获得对应的参数列表 :

Parameter[] parameters = con4.getParameters();
for (Parameter p : parameters) {
    System.out.println(p);
}

输出结果 :

java.lang.String arg0
int arg1

5 . 用反射创建对象

// 暴力反射 : 临时取消权限校验(原本是私有构造方法)
con4.setAccessible(true);
// 创建对象
Student stu = (Student) con4.newInstance("张三",23);
System.out.println(stu);

输出结果 :

Student{name='张三', age=23}

利用反射获取成员变量

Student类 :

package com.it.myreflect3;
public class Student {
    private String name ;
    private int age;
    public String gender ;
    public Student() {}
    public Student(int age, String gender, String name) {
        this.age = age;
        this.gender = gender;
        this.name = name;
    }
    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{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                '}';
    }
}

1 . 获取成员变量 :

        // 2 . 获取成员变量
        Field[] fields1 = clazz.getFields() ;//获取公共
        Field[] fields2 = clazz.getDeclaredFields() ;// 获取所有
        for(Field f : fields1){
            System.out.println(f);
        }
        System.out.println("--------------");
        for(Field f : fields2){
            System.out.println(f);
        }
        System.out.println("--------------");
        // 获取单个成员变量
        Field gender = clazz.getDeclaredField("gender");
        System.out.println(gender);

输出结果 :

public java.lang.String com.it.myreflect3.Student.gender
--------------
private java.lang.String com.it.myreflect3.Student.name
private int com.it.myreflect3.Student.age
public java.lang.String com.it.myreflect3.Student.gender
--------------
public java.lang.String com.it.myreflect3.Student.gender

2 . 获取权限修饰符

int modifiers = gender.getModifiers();
System.out.println(modifiers);

输出结果 :

1

1代表public

3 . 获取成员变量,类型

String ys = gender.getName() ;
System.out.println(ys);
Class<?> type = gender.getType();
System.out.println(type);

输出结果 :

gender
class java.lang.String

4 . 获取成员变量记录的值

Student s = new Student(23,"男","张三");
Object value = gender.get(s) ;
System.out.println(value);

输出结果 :

5 . 修改对象里面记录的值

gender.set(s,"女");
System.out.println(s);

输出结果 :

Student{name='张三', age=23, gender='女'}

获取成员方法

Student类 :

package com.it.myreflect4;
​
import java.io.IOException;
​
public class Student {
    private String name ;
    private int age;
​
    public Student() {}
​
    private Student(String name, int age) {
        this.name = name;
        this.age = age ;
    }
​
    public void sleep(){
        System.out.println("睡觉");
    }
​
    private String eat(String food) throws IOException {
        System.out.println("在吃"+food);
        return "奥里给" ;
    }
​
    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{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

注 : 多加了eat()和sleep();

完整代码 :

package com.it.myreflect4;
​
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
​
public class test {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // 1 . 获取字节码文件对象
        Class clazz = Class.forName("com.it.myreflect4.Student");
​
        // 2 . 获取所有的方法对象(包含父类中所有的公共方法)
        Method[] methods= clazz.getMethods();
        for(Method method:methods){
            System.out.println(method);
        }
​
        //获取所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法)
        Method[] methods1= clazz.getDeclaredMethods();
        for(Method method:methods1){
            System.out.println(method);
        }
​
        // 获取指定的单一方法
        Method m = clazz.getDeclaredMethod("eat", String.class) ;
        System.out.println(m);
​
        // 获取方法的修饰符
        int modify = m.getModifiers();
        System.out.println(modify);
​
        // 获取方法的名字
        String name = m.getName() ;
        System.out.println(name);
​
        // 获取方法的形参
        Parameter[] ps = m.getParameters();
        for(Parameter p : ps){
            System.out.println(p);
        }
​
        // 获取方法抛出的异常 :
        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);
        String ts = (String) m.invoke(s, "汉堡包");
        System.out.println(ts);
​
    }
}

输出结果 :

public java.lang.String com.it.myreflect4.Student.getName()
public java.lang.String com.it.myreflect4.Student.toString()
public void com.it.myreflect4.Student.sleep()
public void com.it.myreflect4.Student.setName(java.lang.String)
public int com.it.myreflect4.Student.getAge()
public void com.it.myreflect4.Student.setAge(int)
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
public java.lang.String com.it.myreflect4.Student.getName()
public java.lang.String com.it.myreflect4.Student.toString()
public void com.it.myreflect4.Student.sleep()
public void com.it.myreflect4.Student.setName(java.lang.String)
private java.lang.String com.it.myreflect4.Student.eat(java.lang.String) throws java.io.IOException
public int com.it.myreflect4.Student.getAge()
public void com.it.myreflect4.Student.setAge(int)
private java.lang.String com.it.myreflect4.Student.eat(java.lang.String) throws java.io.IOException
2
eat
java.lang.String arg0
class java.io.IOException
在吃汉堡包
奥里给

反射的作用

  1. 获取一个类里面的所有的信息 , 获取到之后,再执行其它的业务逻辑

  2. 结合配置文件,动态的创建对象并调用方法

总结 :

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值