java进阶学习笔记

2023-12-23 18:23:56

1.反射和代理

1.1 概念介绍

在Java编程中,反射和代理是两个强大的特性,能够在运行时动态地操作和扩展类的行为。通过反射,我们可以在不知道类的具体信息的情况下操作类的属性和方法;而代理则允许我们创建一个代理类来拦截并增强目标对象的行为

1.2应用场景

像咱们平时大部分时候都是在写业务代码,很少会接触到直接使用反射机制的场景。
但是,这并不代表反射没有用。相反,正是因为反射,你才能这么轻松地使用各种框架。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制,掌握反射和代理机制,有利于我们了解这些框架的内核原理,提升编程思维

  • xml的bean配置的注入
  • AOP拦截
  • 数据库事务
  • springMVC
  • Java注解
  • 开发工具如IDEA,提供一个类的属性方法展示给我们智能快捷选择

这些内核都是反射和代理机制在其作用

1.3 反射-reflect

由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。这种通过Class实例获取class信息的方法称为反射(Reflection)。

我们正常得到一个对象实例是通过new方法创建对象;反射就是通过class来实现,
1.获得类class
2.获得类的字段属性和方法
3.获得类的实例对象
4.获得对方法的调用

准备工作

Food.java

public class Food {
    private Integer id;
    private  String name;
   // ....省略get set 等
}

1.3.1 获得类-Class

获得类有三种方式,看代码演示

类.class
对象.getClass();
Class.forName(“类名”)

 /***
     * @Description: 得到Class
     */
    @Test
    public void t1() throws ClassNotFoundException {
        Class cls=null;
        //1.通过类名直接获得
        cls= Food.class;
        //2.通过实例获得
        cls=new Food(1, "火锅").getClass();
        //3.通过路径+类名的字符串获得
        cls=Class.forName("com.jsoft.reflection.Food");
        printClassInfo(cls);


    }

    /***
     * @Description: 读取类的信息

     */
    static void printClassInfo(Class cls) {
        System.out.println("Class name: " + cls.getName());
        System.out.println("Simple name: " + cls.getSimpleName());
        if (cls.getPackage() != null) {
            System.out.println("Package name: " + cls.getPackage().getName());
        }
        System.out.println("is interface: " + cls.isInterface());
        System.out.println("is enum: " + cls.isEnum());
        System.out.println("is array: " + cls.isArray());
        System.out.println("is primitive: " + cls.isPrimitive());
    }

打印效果

Class name: com.jsoft.reflection.Food
Simple name: Food
Package name: com.jsoft.reflection
is interface: false
is enum: false
is array: false
is primitive: false

1.3.2 获得类的字段-Field

演示代码

public class Father  extends Man{
    private Integer age;
    public  String job;

}

public class Man{
    private Integer id;
    private String name;
    public String address;
    }

对任意的一个Object实例,只要我们获取了它的Class,就可以获取它的一切信息。
我们先看看如何通过Class实例获取字段信息。Class类提供了以下几个方法来获取字段:

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

注意:
Declared修饰就只管当前类字段,和private、public无关
否则就包含父类的public

测试代码

 @Test
    public void t2() throws NoSuchFieldException {
       Class cls= Father.class;
       //1.得到当前类的所有字段,不包含父类
       Field[] fs=cls.getDeclaredFields();
        System.out.println("1.得到当前类的所有字段,不包含父类");
        for (Field f : fs) {
            printFeild(f);
        }
        //2.得到当前类某个字段
        System.out.println("2.得到当前类某个字段,不包含父类");
       Field f=cls.getDeclaredField("job");
        printFeild(f);

        //3.得到当前类及父类的所有public字段
        System.out.println("3.得到当前类及父类的所有public字段");
        fs=cls.getFields();
        for (Field f1 : fs) {
            printFeild(f);
        }

        //4.得到当前类及父类的public字段
        System.out.println("4.得到当前类及父类的public字段,如果是private,则要报错");
        f=cls.getField("address");
        printFeild(f);

    }

    /***
     * @Description: 打印字段信息
     */
    public  void printFeild(  Field f){
        System.out.println("//------------字段信息 start");
        System.out.println("字段名称:"+f.getName());
        System.out.println("字段类型:"+f.getType().getName());

        int m = f.getModifiers();
        System.out.println("field is final:"+Modifier.isFinal(m));; // true
        System.out.println("field is Public:"+Modifier.isPublic(m));; // true
        System.out.println("field is Protected:"+Modifier.isProtected(m));; // true
        System.out.println("field is Private:"+Modifier.isPrivate(m));; // true
        System.out.println("field is Static:"+Modifier.isStatic(m));; // true

        System.out.println("//------------字段信息 end");
    }

1.3.3 动态访问和修改对象实例的字段

我们可以通过一个对象对应的class去动态访问或修改对象的字段
Field.set(vo对象,修改值) //修改字段值
Feild.get(vo对象) //获得字段值
Feild.setAccessible(true); //私有字段,需要设置这个允许访问,否则报异常

  @Test
    public void t3() throws NoSuchFieldException, IllegalAccessException {
        Food food=new Food(1, "火锅");
        System.out.println("food.getName:"+food.getName());
        Class cls=food.getClass();
        Field f=cls.getDeclaredField("name");
        f.setAccessible(true); //不设置这个private 字段要抛出异常
        //通过field获得值
        Object name= f.get(food);
        System.out.println("name:"+name);
        //通过field设置值
        f.set(food, "西北风");
        System.out.println("name:"+food.getName());
    }

1.3.4 获得类方法-Method

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

示例代码

@Data
public class Father  extends Man{
    private Integer age;
    public  String job;
   public  String play(int type){
        System.out.println("param value:"+type);
        return type+"";
    }

    public  String sleep(String type, Date date){
        System.out.println("param value type:"+type+";date:"+date);
        return type+"";
    }
    private  void self(){
        System.out.println("this is private method");
    }
	public void dd(){
        System.out.println("没有参数方法()");
    }


}

@Data
public class Man{
    private Integer id;
    private String name;
    public String address;

    @Override
    public Food eat() {

        return new Food(1,"火锅");
    }
}

测试代码

@Test
    public void t4() throws IllegalAccessException, NoSuchMethodException {
        Class cls= Father.class;
        System.out.println("1. 获取所有public的Method(包括父类)");
         Method[] methods=cls.getMethods();
        for (Method method : methods) {
            printMethod(method);
        }
        System.out.println("2. 获取某个public的Method(包括父类)");
        //第一个参数为方法名,第二参数是可变参数,传递方法的参数类型,如果无参数则不传递
        Method m= cls.getMethod("getName");
        printMethod(m);
        //方法有1个参数
        //注意int foat double这些基本类型对用的类是int.class,不是Integer.class
        m=cls.getMethod("play", int.class);
        printMethod(m);
        //方法有多个参数
        m=cls.getMethod("sleep", String.class, Date.class);
        printMethod(m);

        System.out.println("3. 获取当前类的的Method(不包括父类)");
        m= cls.getDeclaredMethod("getJob");
        printMethod(m);
        //私有方法也可以
        m= cls.getDeclaredMethod("self");
        //父类方法不可以,要报java.lang.NoSuchMethodException
        m= cls.getDeclaredMethod("eat");
        printMethod(m);

     printMethod(m);
    }

    public void printMethod(Method addMethod){
        System.out.println("---------方法名称: 【" + addMethod.getName()+"】---相关属性------------");
        System.out.println("修饰符: " + Modifier.toString(addMethod.getModifiers())); //public private等
        System.out.println("返回值: " + addMethod.getReturnType()); //返回值类型的class数组
        Class[] paramsCls= addMethod.getParameterTypes(); //参数值类型对应的class数组
    }

1.3.5 调用方法.invoke

我们可以用Method.invoke(vo,参数)来调用方法

代码

@Test
    public void t5() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Father father=new Father();
        Class cls= father.getClass();
        Method m=null;

        //无参无返回方法
        m=cls.getMethod("dd");
        m.invoke(father);

        //有参数,有返回值
        m=cls.getMethod("sleep", String.class,Date.class);
       String ret=(String)m.invoke(father, "1",new Date());
        System.out.println("返回值:"+ret);

        //private方法调用
        m=cls.getDeclaredMethod("self");
        //私有方法必须设置为m.setAccessible(true);
        m.setAccessible(true);
        m.invoke(father);

    }

Method m =Father.class.getMethod(“dd”);
m.invoke(new Father());

其实就相当于

Father f=new Father();
f.dd();

1.3.6 构造函数

文章来源:https://blog.csdn.net/weixin_41158378/article/details/135152916
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。