java进阶学习笔记

学习java深度学习,提升编程思维,适合掌握基础知识的工作者学习

  • 1.反射和代理
    • 1.1 概念介绍
    • 1.2应用场景
    • 1.3 反射-reflect
      • 1.3.1 获得类-Class
      • 1.3.2 获得类的字段-Field
      • 1.3.3 动态访问和修改对象实例的字段
      • 1.3.4 获得类方法-Method
      • 1.3.5 调用方法.invoke
      • 1.3.6 类实例化-构造函数Constructor
      • 1.3.7 instanceof
      • 1.3.8 利用反射来解析spring配置
    • 1.4 代理-proxy
      • 1.4.1 代理模式
      • 1.4.2 准备工作
      • 1.4.3 静态代理
    • 1.5 动态代理
      • 1.5.1 InvocationHandler介绍
      • 1.5.2 Proxy类
      • 1.5.3动态代理类的实现
      • 1.5.4动态实现接口
      • 1.5.4替换某个方法
    • 1.5 动态代理框架CGLIB
      • 1.5.1 使用介绍
      • 1.5.2 cglib说明
    • 1.6 总结
  • 2. java小干货
    • 2.1循环遍历
    • 2.2可变参数
    • 2.3 list和数组转化
    • 2.4 地址引用
    • 2.5集合
    • 2.6文件流
    • 2.7java代码块、内部类和匿名类
    • 2.8 java泛型及通配符
    • 2.9 日期类LocalDate
    • 2.10枚举
    • 2.11 java常见数据结构
  • 3. java注解
  • 4. lambda语言
  • 5.http网络
  • 6.java线程

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);

    }

jdk内置对象的方法调用的另外一种写法

  @Test
    public void t6() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
       String s="abcde";
       String sub=s.substring(0,2);
        System.out.println(sub);
        //通过类加载
        Class   cls=String.class;
        Method method=cls.getMethod("substring",int.class,int.class);
       sub=(String) method.invoke(s, 0,2);
        System.out.println(sub);

    }

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

其实就相当于

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

1.3.6 类实例化-构造函数Constructor

实例化对象有3中方法
1.我们都知道的new 对象Father father=new Father();
2.通过类class:newInstance
3.构造方法:Constructor

准备类

public class Const {
    private Integer id;
    private String name;

    public Const() {
        System.out.println("无参数构造方法");
    }

    public Const(Integer id, String name) {
        System.out.println("有参数构造方法");
        this.id = id;
        this.name = name;
    }
}

在很多框架里,我们为了足够的扩展,都通过配置来实现类的实例化

Class.newInstance()

  @Test
    public void t7() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
       Class cls =Class.forName("com.jsoft.reflection.Const");
        Const aConst = (Const) cls.newInstance();
        
    }

Class.newInstance()最大的问题是只能实例化无参的构造方法,如果我们把无参构造方法屏蔽掉,代码会出错,所以我们需要使用Constructor类来实现有参的构造方法

这里是引用一共有4种方法,全部都在Class类中:

  1. getConstructors():获取类中的公共方法
  2. getConstructor(Class[] params): 获取类的特定构造方法,params参数指定构造方法的参数类型
  3. getDeclaredConstructors(): 获取类中所有的构造方法(public、protected、default、private)
  4. getDeclaredConstructor(Class[] params): 获取类的特定构造方法,params参数指定构造方法的参数类型

代码示例

    @Test
    public void t8() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class cls =Class.forName("com.jsoft.reflection.Const");
        //得到所有构造函数
        Constructor[] constructors=  cls.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println("修饰符: " + Modifier.toString(constructor.getModifiers()));
            System.out.println("构造函数名: " + constructor.getName());
            System.out.println("参数列表: " );
            Class[] cs= constructor.getParameterTypes();
            for (Class c : cs) {
                System.out.println(c.getName());
            }
        }

        //获得具体一个(无参)
        Constructor cst=cls.getConstructor();
        //newInstance实例化
        Const const1=(Const) cst.newInstance();

        //获得具体一个(有参)
         cst=cls.getConstructor(Integer.class,String.class);//传入构造函数的参数的类型
        //newInstance实例化
         const1=(Const) cst.newInstance(1,"蒋增奎");
    }

1.3.7 instanceof

instanceof是判断一个实例对象是不是一个类的实例,是则返回true
对象 instanceof 类

class Person{
}

class Teacher extends Person{
}

class Student extends Person{
}

测试

public class test01 {
    public static void main(String[] args) {
 
        Object obj = new Student(); // 主要看这个对象是什么类型与实例化的类名
        System.out.println(obj instanceof Student); // true
        System.out.println(obj instanceof Person); // true
        System.out.println(obj instanceof Object); // true
        System.out.println(obj instanceof String); // false
        System.out.println(obj instanceof Teacher); // false  无关系
        System.out.println("========================");

        Person person = new Student();
        System.out.println(person instanceof Person); // true
        System.out.println(person instanceof Object); // true
        // System.out.println(person instanceof String); // 编译错误
        System.out.println(person instanceof Teacher); // false 无关系

    }
}

1.3.8 利用反射来解析spring配置

<bean id="employee" class="com.jsoft.po.Employee">
    <property name="id" value="1" />
    <property name="name" value="蒋增奎" />
    <property name="deptId" value="001" />
</bean>

解析思路

1.用dom4j解析xml得到xml数据
2.创建类:Class cls=Class.forName(“Employee”);
3.创建对象:Employee obj=(Employee)cls.newInstance();
4.调用修改字段或者方法给字段注入值

1.4 代理-proxy

1.4.1 代理模式

代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。

代理模式作用:
代理模式的主要作用是扩展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一>些自定义的操作。

代理模式价值
代理模式的价值在于不修改代理类代码,增加其他功能,我们理解成我们通常所说的赋能
(1)比如日常生活中的品牌加盟,我们忽悠一个饭店加盟一个品牌连锁,我们忽悠饭店,你只需要开店,营销、菜品、供应链我们全部帮你搞定。
(2)比如老板要求我们所有方法都要加入日志,优雅的方法,不是修改以前代码,而是增加一个代理,把在代理类去处理日志,这就是spring框架的日志、事务原理

举个例子:
boy向girl求婚
(1)现代:boy和girl直接发生通信,girl迫不及待的同意了
(2)古代:boy不能直接和girl发生关系,需要通过媒婆,boy向媒婆发起请求,媒婆转告girl,gilr同意,媒婆觉得女孩如果同意,婚前应该收彩礼10万,婚后不能家暴,媒婆就是代理,在原始的反馈上增加了自己的要求,提升了框架的健壮性。
在这里插入图片描述
代理分为静态代理和动态代理两种类型

1.4.2 准备工作

public interface Love {

    /***
     * @Description: 结婚请求
     */
    public void marray();
      /***
     * @Description: 睡觉请求
     */
    public void sleep();
    public String like(int type);
    public int saveMoney(int money);
}


/**
接口实现类
**/
public class LoveImpl implements Love {
    @Override
    public void marray() {
        System.out.println("我同意");
    }
 	public void sleep() {
        System.out.println("我同意");
    }
    
    public String like(int type){
    	if(type==1)
            return "69";
        return "96";
    }
   @Override
    public int saveMoney(int money) {
        return money*10;
    }
}



1.4.3 静态代理

静态代理实现步骤:
1.定义一个接口及其实现类;
2.创建一个代理类同样实现这个接口
3.将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。

代码:

//代理类要实现接口
public class LoveProxy implements Love {
    private Love target;//代理的接口
    /***
     * @Description: 构造方法注入代理的接口
     */
    public LoveProxy(Love love){
        target=love;
    }

    @Override
    public void marray(){
        System.out.println("============代理marray方法");
        System.out.println("要彩礼10万");
        target.marray();
        System.out.println("不能家暴");
    }

    public void sleep(){
        System.out.println("============代理sleep方法");
        System.out.println("安全措施");
        target.sleep();

    }

    public String like(int type){
        System.out.println("============代理like方法");
        System.out.println("注意身体");
        return target.like(type);
    }


    public static void main(String[] args) {
        //实际应用:代理的应用
        Love love=new LoveImpl();
        LoveProxy proxy=new LoveProxy(love);
        proxy.marray();
        proxy.sleep();
        System.out.println( proxy.like(1));

    }


}

效果:在以前的需求上扩展了功能

============代理marray方法
要彩礼10万
我同意
不能家暴
============代理sleep方法
安全措施
我同意
============代理like方法
注意身体
69

总结:代理模式的实现

1.给一个要代理的类升级一个接口
2.代理目标类和代理类都实现这个接口
3.把目标类传入代理类。代理类是方法里面去封装目标类的实现

1.5 动态代理

静态代理:通过手动编写代理类来实现,需要为每个目标对象编写一个代理类。
动态代理:通过Java提供的相关接口和类,在运行时动态生成代理类,无需手动编写代理类。

我们仍然先定义了接口,但是我们并不去编写实现类,而是直接通过JDK提供的一个Proxy.newProxyInstance()创建了一个Hello接口对象。这种没有实现类但是在运行期动态创建了一个接口对象的方式,我们称为动态代码。JDK提供的动态创建接口对象的方式,就叫动态代理。

动态代理三个重要的java类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

1.5.1 InvocationHandler介绍

代理实例的调用处理器需要实现InvocationHandler接口,并且每个代理实例都有一个关联的调用处理器。当一个方法在代理实例上被调用时,这个方法调用将被编码并分派到其调用处理器的invoke方法上。
接口里最重要的方法invoke

  public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

其有三个参数,分别为:
proxy:是调用该方法的代理实例,是java在编译环节自动生成的,对开发者没有意义,如:jdk.proxy1.$Proxy0,注意不是代理的目标类,也不是接口。

method:是在代理实例上调用的接口方法对应的Method实例。

args:一个对象数组,其中包含在代理实例上的方法调用中传递的参数值,如果接口方法不带任何参数,则为 null。基元类型的参数包装在相应基元包装类的实例中,例如 java.lang.Integer 或 java.lang.Boolean

返回值:调用代理实例上的方法的返回值。
如果接口方法声明的返回类型是基元类型,那么该方法返回的值必须是对应基元包装类的实例;
否则,它必须是可分配给声明的返回类型的类型。如果此方法返回的值为 null,并且接口方法的返回类型为基元,则代理实例上的方法调用将引发 NullPointerException。如果此方法返回的值与接口方法的声明返回类型不兼容(如上所述),则代理实例上的方法调用将引发 ClassCastException

这里最重要的参数是method,可以通过moth.invote调用目标对象的方法

1.5.2 Proxy类

Proxy类提供了创建动态代理类及其实例的静态方法,该类也是动态代理类的超类,其最主要的方法是一个newProxyInstance的静态方法

   public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
                                          {......}

参数说明:
ClassLoader :
对应代理目标类对应的类加载器
Class<?>[] interfaces
目标代理类的接口类对应的CLASS,代理目标类的接口可以是多个
InvocationHandler h
要调用的处理器
返回对象:
返回实例,Object可以转化成对应的目标代理类的接口

1.5.3动态代理类的实现

准备代码:为了增加效果,我们在增加一个类和接口

public interface LifeService {
    public String eat(String name);

    public String drink(String name);
    public int like(int type);

}

public class Life implements LifeService {

    public String eat(String name){
        String ret="I like eat:"+name;
        System.out.println(ret);
        return ret;
    }

    public String drink(String name){
        String ret="I like drink:"+name;
        System.out.println(ret);
        return ret;
    }

    public int like(int type){
        String ret="mylike:"+type;
        System.out.println(ret);
        return type;
    }


}

自己做一个调度类,继承InvocationHandler

//自己编写处理器
public class MyHandler implements InvocationHandler {
    private  Object target;//代理的目标类

    //通过构造方法注入代理的目标
    public MyHandler(Object target){
        this.target=target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //给所有方法加上日志
        System.out.println("给大家增加一个log日志...........");
        String mName=method.getName();
        //给以save开头的方法名添加事务
        if(mName.length()>=4 && "save".equals(mName.substring(0,4))){
            System.out.println("开启事务...............");
        }
        //方法名是sleep的增加安全措施
        if("sleep".equals(mName)){
            System.out.println("注意安全措施...............");
        }
        //调用目标代理类的方法并返回方法返回值
        return method.invoke(target,args);
    }


}

封装一个调度工厂

public class ProxyFactory {

    /***
     * @Description: 动态代理工厂
     * @Create:2023/12/24 12:43
     * @Param: [target:代理的对象实例
     * @Return: java.lang.Object  代理接口对象
     */
    public static Object creatProxy(Object target){
        //动态调用代理
        return  Proxy.newProxyInstance(
                target.getClass().getClassLoader(),  //目标的加载
               target.getClass().getInterfaces(), //目标类的接口的class,可以是多个,数组,如果指定,则要使用new Class[...]
                new MyHandler(target));
    }
    public static void main(String[] args) {

        //应用1
        Love love=(Love)creatProxy(new LoveImpl() );
        love.marray();
        love.sleep();
        int money=love.saveMoney(10);
        System.out.println("存钱:"+money);

        //应用2
        LifeService lifeService=(LifeService)creatProxy(new Life());
        lifeService.eat("火锅");
        lifeService.drink("可乐");

    }
}

执行效果:

给大家增加一个log日志...........
我同意
给大家增加一个log日志...........
注意安全措施...............
我同意
给大家增加一个log日志...........
开启事务...............
存钱:100
给大家增加一个log日志...........
I like eat:火锅
给大家增加一个log日志...........
I like drink:可乐

1.5.4动态实现接口

有时候,我们不想为接口单独做一个实现类,我们可以通过动态代理来实现,这个在springboot里面非常常见的编程思想

public class DynamicProxy {
    public static void main(String[] args) {
        //定义一个InvocationHandler对象,但不传入实现类,相当于
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              
                if (method.getName().equals("marray")) { //这个方法去实现,和做一个实现类原理一致
                    System.out.println("我同意个锤子");
                }
                return null;
            }
        };
        Love love = (Love) Proxy.newProxyInstance(
                Love.class.getClassLoader(), // 传入ClassLoader
                new Class[] { Love.class }, // 传入要实现的接口
                handler); // 传入处理调用方法的InvocationHandler
        System.out.println("3333");
        love.marray();
        

    }
}

1.5.4替换某个方法

在大量的java框架中,某种功能有默认实现,我们想替换掉,自己定义实现,其他方法不变,我们也可以使用动态代理技术实现。

public class ProxyReplcae {
    public static void main(String[] args) {
        LoveImpl loveImp=new LoveImpl();//默认实现,在spring里一般采用注入
        //定义一个InvocationHandler对象
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //这个对象不想使用以前实现,自定义实现
                if (method.getName().equals("marray")) {
                    System.out.println("不同意结婚");
                    return null;
                }
                else
                    return method.invoke(loveImp,args); //其他的依然使用默认实现

            }
        };

        Love love = (Love) Proxy.newProxyInstance(
                Love.class.getClassLoader(), // 传入ClassLoader
                new Class[] { Love.class }, // 传入要实现的接口
                handler); // 传入处理调用方法的InvocationHandler

        love.marray();  //已经使用自定义
        love.sleep();//依然是默认实现
    }


}

1.5 动态代理框架CGLIB

JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。
为了解决这个问题,我们可以用 CGLIB 动态代理机制来避免。

CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。很多知名的开源框架都使用到了CGLIB, 例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。

1.5.1 使用介绍

1.引入框架

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.3.0</version>
</dependency>

2.准备好一个普通类

public class MyDay {
    public void work(int hour){
        System.out.println(hour+"点开始工作");
    }

    public void sleep(int hour){
        System.out.println(hour+"点开始睡觉");
    }
}

代码


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

/**
 * @class: com.jsoft.reflection.CglibProxyFacory
 * @description:
 * @author: jiangzengkui
 * @company: 教育家
 * @create: 2023-12-24 15:29
 */
public class CglibProxyFacory {
    /***
     clazz:代理目标类的class
     */
    public static Object getProxy(Class<?> clazz) {
        //自定义一个拦截器

        MethodInterceptor me=new MethodInterceptor(){

            /**
             * @param o           被代理的对象(需要增强的对象)
             * @param method      被拦截的方法(需要增强的方法)
             * @param args        方法入参
             * @param methodProxy 用于调用原始方法
             */
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy)
                    throws Throwable {
                System.out.println("before method " + method.getName());
                Object object = methodProxy.invokeSuper(o, args);
                //调用方法之后,我们同样可以添加自己的操作
                System.out.println("after method " + method.getName());
                return object; // 方法的返回值,如果void则为null
            }
        };
        // 创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置被代理类
        enhancer.setSuperclass(clazz);
        // 设置方法拦截器
        enhancer.setCallback(me);
        // 创建代理类
        return enhancer.create();
    }

    public static void main(String[] args) {
        MyDay myDay= (MyDay) CglibProxyFacory.getProxy(MyDay.class);
        myDay.work("早上8");
        myDay.sleep("晚上23");

    }

}

效果:

before method work
早上8点开始工作
after method work
before method sleep
晚上23点开始睡觉
after method sleep

1.5.2 cglib说明

主要要用到的3个类

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

net.sf.cglib.proxy.MethodInterceptor

这个就是拦截器处理接口,其重要接口方法


  /**
             * @param obj           被代理的对象(需要增强的对象)
             * @param method      被拦截的方法(需要增强的方法)
             * @param args        方法入参
             * @param proxy 用于调用原始方法
             * renturn  方法的返回值,如果void则为null
             */
   public Object intercept(
   Object obj, 
   java.lang.reflect.Method method,
   Object[] args,
   MethodProxy proxy
   ) throws Throwable;

net.sf.cglib.proxy.MethodProxy

这个方法为调用代理目标类的原始方法,其调用函数:MethodProxy.invokeSuperr(Object obj, Object[] args) ,其自动为代理目标类创建一个接口

/**
Object obj:传入代理目标类对象实例
 Object[] args:代理目标类的方法参数

**/
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

net.sf.cglib.proxy.Enhancer

这个类用于调用拦截器,创建增强的代理目标类

 		// 创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置被代理类
        enhancer.setSuperclass(clazz);
        // 设置方法拦截器
        enhancer.setCallback(me);
        // 创建代理类-增强后的目标代理类
        Object obj=enhancer.create();

1.6 总结

反射机制:

  • 根据类的名称或者class本身,可以进行实例化对象、构造方法、获得和修改字段、获得方法名和调用方法

代理机制:

  • 代理机制就是代理本身目标类,增强其功能
  • 静态代理,就是继承目标类对应的接口,实现所有的接口方法,通过接口的封装,增强目标类
  • 动态代理:就是创造一个拦截器,拦截器里去增强目标类的方法,前提:必须有接口类
  • Cglib:增对目标类没有接口这种情况,自动创建一个接口,实现没有接口类也可以做代理

2. java小干货

2.1循环遍历

2.2可变参数

2.3 list和数组转化

2.4 地址引用

2.5集合

2.6文件流

2.7java代码块、内部类和匿名类

2.8 java泛型及通配符

2.9 日期类LocalDate

2.10枚举

2.11 java常见数据结构

3. java注解

4. lambda语言

5.http网络

6.java线程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/277656.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c 语言, 随机数,一个不像随机数的随机数

c 语言&#xff0c; 随机数&#xff0c;一个不像随机数的随机数 使用两种方式获取随机数&#xff0c;总感觉使用比例的那个不太像随机数。 方法一&#xff1a; rand() 获取一个随机数&#xff0c;计算这个随机数跟最大可能值 RAND_MAX&#xff08;定义在 stdlib.h 中&#xf…

PostgreSQL 数据库归档最近被问及的问题问题 与 4 毋 处世学

开头还是介绍一下群&#xff0c;如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, Oceanbase, Sql Server等有问题&#xff0c;有需求都可以加群群内&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;&#xff08;共1790人左右 1 2 3 4 5&#xff0…

Unity 通过鼠标框选绘制矩形区域

鼠标拖动的同时绘制一块同等大小的区域:如下 using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// 通过鼠标框选绘制矩形区域 /// </summary> /// public enum MouseType {left = 0,right = 1,middle = 2 } publi…

关于<取消对 NULL 指针“r”的引用><从 “a“ 读取无效数据>两个问题的解决办法

【取消对 NULL 指针“r”的引用】 修改&#xff1a; 必须要检查malloc的返回值&#xff0c;避免空间不够 &#xff08;nullptr只能用于指针类型&#xff0c;不能用于整数类型&#xff09; 【从 "a" 读取无效数据】 修改&#xff1a; 用指针法来表示&#xff0c;…

三张表看懂POE POE+ POE++ 三个协议的相关参数

Hqst华强盛&#xff08;盈盛电子&#xff09;导读&#xff1a;三张表看懂POE POE POE 三个协议的相关参数。 一 ̖ POE协议区分&#xff1a; 802.3af&#xff08;PoE) 百兆网络变压器H81621S 二 ̖ POE协议与受电设备&#xff08;PD&#xff09;工作功率分级 802.3at&#xf…

Yapi接口管理平台Centos7部署

文章目录 1.环境准备1.1 关闭透明大页THP1.2 设置最大文件打开数最大进程数 2.Nodejs安装3.安装Mongodb3.1 下载安装3.2 配置3.3 配置环境变量3.4 启动3.5 关闭 4.安装YAPI4.1 离线安装4.2 页面安装&#xff08;本次采用&#xff09;4.3 访问 1.环境准备 1.1 关闭透明大页THP …

小米SU7正式亮相,媒介盒子多家媒体报道

哈喽大家好啊&#xff0c;今天 媒介盒子来和大家分享媒体推广的干货知识&#xff0c;本篇分享的主要内容是新车上市&#xff0c;小米SU7的营销逻辑 在12月28日下午的发布会上&#xff0c;小米C级轿车SU7正式亮相&#xff0c;SU7的发布&#xff0c;也意味着小米智能科技“人车家…

【Shell编程练习】通过位置变量创建 Linux 系统账户及密码

系列文章目录 输出Hello World 系列文章目录位置变量代码实现运行结果 位置变量 位置变量将以数字方式对变量进行命名&#xff0c;可将命令行参数的值存储到脚本中。要从命令行、函数或脚本执行等处传递参数时&#xff0c;就需要在 Shell 脚本中使用位置参数变量。下表为常用…

2020年认证杯SPSSPRO杯数学建模A题(第一阶段)听音辨位全过程文档及程序

2020年认证杯SPSSPRO杯数学建模 A题 听音辨位 原题再现&#xff1a; 把若干 (⩾ 1) 支同样型号的麦克风固定安装在一个刚性的枝形架子上 (架子下面带万向轮&#xff0c;在平地上可以被水平推动或旋转&#xff0c;但不会歪斜)&#xff0c;这样的设备称为一个麦克风树。不同的麦…

LaTeX 符合 GB/T 7714-2015 标准的 biblatex 参考文献样式

需求&#xff1a;用LaTeX写学位论文&#xff0c;想得到符合 GB/T 7714-2015 标准的 biblatex 参考文献样式&#xff08;按照GB/T7714-2015的格式要求&#xff0c;英文作者名的字母全部大写&#xff09;。 命令&#xff1a; %在导言区加载宏包和文献文件&#xff0c;注意gb7714…

一文详解Cookie以及Selenium自动获取Cookie

前言 以后数据获取途径以及数据资产绝对会是未来核心要素生产工具和资源之一&#xff0c;每个大模型都离不开更加精细化数据的二次喂养训练。不过现在来看收集大量数据的方法还是有很多途径的&#xff0c;有些垂直领域的专业数据是很难获取得到的&#xff0c;靠人力去搜寻相当…

深眸科技创新工业AI视觉系统,与机械臂协同工作实现视觉引导功能

工业AI视觉系统&#xff1a;工业AI视觉系统能够在工业环境中进行缺陷检测、视觉分拣、物流供包、拆码垛、工业上料等应用。 随着国内工业企业不断进步和发展&#xff0c;传统机器视觉无法满足企业对复杂操作流程的需求&#xff0c;多数制造企业对于机器视觉系统的需求增长。而…

在SpringBoot中自定义指标并使用Prometheus监控报警

公众号「架构成长指南」&#xff0c;专注于生产实践、云原生、分布式系统、大数据技术分享 在10 分钟教你使用Prometheus监控Spring Boot工程中介绍了如何使用Prometheus监控Spring Boot提供的默认指标&#xff0c;这篇介绍如何自定义业务指标&#xff0c;并使用Prometheus进行…

Fedora操作系统有哪些优势

Fedora是一种基于Linux内核的开源操作系统&#xff0c;被广泛的认为是一款先进的、功能丰富的操作系统。它有许多的优势&#xff0c;从最新的软件版本到社区驱动的开发&#xff0c;以及安全性和稳定性等方面。下面我简单的介绍有哪些优势。 最新的软件版本 Fedora以当前最新的…

内网渗透之Vulnstack4靶场的全方位打法

靶场考察点 MS14-068漏洞 MS14-068是一个著名的Windows Kerberos安全漏洞&#xff0c;允许攻击者篡改Kerberos票据&#xff0c;从而获取非法提权。这个漏洞特别影响Windows域控制器&#xff0c;能让攻击者伪造Kerberos票据&#xff0c;获取域内几乎任意账户的权限&#xff0c…

鸿蒙系列--组件介绍之容器组件

一、Badge 描述&#xff1a;给其他组件添加标记 子组件&#xff1a;支持单个子组件 1.创建数字标记 Badge(value: {count: number, position?: BadgePosition, maxCount?: number, style: BadgeStyle}) 2.创建字符串标记 Badge(value: {value: string, position?: Badge…

关于Python里xlwings库对Excel表格的操作(二十三)

这篇小笔记主要记录如何【用“.number_format ”函数设置单元格的文本各种属性】。前面的小笔记已整理成目录&#xff0c;可点链接去目录寻找所需更方便。 【目录部分内容如下】【点击此处可进入目录】 &#xff08;1&#xff09;如何安装导入xlwings库&#xff1b; &#xff0…

批量抠图软件哪个好用?推荐这三款抠图工具给你

在数字图像处理的世界里&#xff0c;抠图是个不可或缺的环节。对于那些经常需要从复杂背景中提取主体的设计师和摄影师来说&#xff0c;抠图技巧无疑是一项宝贵的职业技能。然而&#xff0c;当面对大量的抠图需求时&#xff0c;手动处理不仅耗时&#xff0c;而且效率低下。因此…

Junit在多线程测试时的坑

Junit单元测试主线程退出&#xff0c;子线程也会退出 Testpublic void test() throws InterruptedException {Thread t1 new Thread(() -> {try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.…

PowerShell Instal 一键部署gitea

gitea 前言 Gitea 是一个轻量级的 DevOps 平台软件。从开发计划到产品成型的整个软件生命周期,他都能够高效而轻松的帮助团队和开发者。包括 Git 托管、代码审查、团队协作、软件包注册和 CI/CD。它与 GitHub、Bitbucket 和 GitLab 等比较类似。 Gitea 最初是从 Gogs 分支而来…