Java 动态代理

为什么需要动态代理? 我们一般使用代理模式来增强函数的功能,比如在函数执行前记录log信息,进行安全性检测等。 常见的代理有静态代理和动态代理。在静态代理中,一个被代理的对象需要一个相应的代理类,当程序中所需代理的对象数量较多时,代理类的数量也会随之增多,这就导致了程序的体积膨胀,在运行时会占用更多的内存。除此之外,被代理的对象添加新的函数时,代理类也要随之添加对应的函数,这就导致了后期维护的不方便。 所谓动态代理,即通过字节码生成的方式动态的组装一个代理类(.class),无需在源码中单独编写代理类文件(.java)。与静态代理相比,动态代理中的代理类是在运行时产生的,而静态代理是在编译时产生的。 Java内置的动态代理 首先提供所需代理的接口与其对应的实现类: Hello.java public interface Hello { void sayHola(); void sayHello(); } HelloImpl.java public class HelloImpl implements Hello { @Override public void sayHola() { System.out.println("Hola"); } @Override public void sayHello() { System.out.println("Hello"); } } 之后实现内置的InvocationHandler接口: MyInvocationHandler.java public class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { this....

April 15, 2021 · Huo Haodong

Java 注解

为什么要引入注解? 在没有注解前,在描述程序中的元数据(配置参数,函数信息等)时大多通过XML文件的方式实现。XML文件将元数据描述和代码完全解耦,当所需描述的元数据过多时,程序出错时难定位,维护起来很困难。 注解则将代码和元数据耦合到了一起,程序出错时定位方便,维护起来也很方便,只要找到其对应的注解去配置即可。 借用Stack Overflow上的一个例子来理解,如果我们想要介绍一个类,可以在类的头部添加一些描述信息(作者,版本,上次修改日期等): public class Generation3List extends Generation2List { // Author: John Doe // Date: 3/17/2002 // Current revision: 6 // Last modified: 4/12/2004 // By: Jane Doe // Reviewers: Alice, Bill, Cindy // class code goes here } 如果我们想要获取该类的上述信息,则只能肉眼去看,不能通过程序来获取。 引入注解后,可以先自定义一个注解: @interface ClassPreamble { String author(); String date(); int currentRevision() default 1; String lastModified() default "N/A"; String lastModifiedBy() default "N/A"; // Note use of array String[] reviewers(); } 然后把这个注解放到所需描述的类的头部:...

April 14, 2021 · Huo Haodong

Java 和 C 语言实现回调的方式及区别

阅读本篇文章需要的前置知识 C语言函数指针的定义方式 指针的概念和基本用法 Java中接口的实现和定义 Java匿名类的使用 C语言实现回调 C语言中的回调指的是回调函数,即通过函数指针的方式将函数指针传递给调用者(某个函数),调用者在使用时就如同使用一个参数那样去调用函数。 下面的示例展示了函数指针的基本用法以及回调的实现 #include <stdio.h> // 自定义的函数,作为参数传入调用者的函数中 void my_callback_func() { printf("我是回调函数,以函数指针的形式传入别的函数中以供其他函数调用我\n"); } // 回调函数的调用者,通过参数中传递来的函数指针去调用回调函数 void invoker(void(*callback)(void)) { callback(); } int main() { // 函数指针的定义与用法 void (*callback)(void) = my_callback_func; callback(); // 传入函数指针实现回调 invoker(callback); return 0; } Java实现回调 C语言通过函数指针实现回调的方式十分便捷,然而Java中没有指针这一概念,更何谈指针函数。因此,想要在Java中实现回调只能另辟蹊径。 Java想要传入函数供其他对象的函数去调用,最容易想到的方法即把包含函数的对象本身作为字段定义在调用者的类中,使用时调用类内的该字段对应的方法即可。 下面是这种实现的一个示例 CallbackProvider类 package callback.example; // CallbackInvoker类,回调函数的提供者。 public class CallbackProvider { public void myCallbackMethod() { System....

November 18, 2020 · Huo Haodong

Java 泛型的局限

由于java使用擦拭法来实现泛型,导致编译器把类型<T>视为``Object,同时编译器根据`的实际情况来实现安全的强制类型转换。也就是说,虚拟机看到字节码的时候对有没有泛型毫不知情,编译器编译的时候自动的完成了类型转换和检查的工作。 <T>不能是基本类型,比如int、double等,因为泛型的实际类型是Object,而Object类型无法持有基本类型。 无法取得带泛型的Class,因为<T>是Object类型,在编译后,所有泛型实例的getClass返回的是同一个Class实例。 无法判断带泛型的Class。 例如: Pair<Integer> p = new Pair<>(123, 456); // Compile error: if (p instanceof Pair<String>.class) { } // 并不存在Pair<String>.class,而是只有唯一的Pair.class。 不能实例化T类型。 例如: public class Pair<T> { private T first; private T last; public Pair() { // Compile error: first = new T(); last = new T(); } } // first = new T();在擦拭后实际变成了first = new Object();这样的话,创建的所有泛型到头来全成Object了。 如果要实例化的话,需要借助Class<T>参数:...

April 14, 2020 · Huo Haodong

打印菱形

问题:输入一个奇数n,打印高度为n的菱形字符。 一日闲来无事,脑中灵光一现,突然想试试打印字符玩玩。传统的打印菱形字符的方法无外乎1.依次打印菱形的4个角。2.先打印菱形的上半部分,然后再打印下半部分。3.暴力徒手打印。网上的资料也大多依此思路实现。但是实现的代码往往雷同且有多个for循环,看起来十分别扭。 这里提供一个相对优雅的方法: * *** ***** *** * 观察上面的图像,可以看到每一行的空格数(只打印左边的空格,右边的无需打印)的规律符合函数:y = abs(x + 2),其中x从0~n-1,n为菱形的高度,x为行号,y为第x行对应的左边的空格数。 更一般的,将公式推广到一般的n也是成立的,这些通过在草纸上画出函数图像即可得出。这里不再赘述。 现在有了空格数,求所需打印的字符也便容易了,想象成一个正方形的盒子,每一行除了空格就是字符,现在知道了每一行左边的空格数y,那么这一行总共有2 * y个空格,且这一行总共有n个字符,那么所需打印的字符数便为n - 2 * y。有了公式,打印菱形的代码便很容易就可以写出: //打印菱形 public static void printRhombus(int height, char charShape) { for (int row = 0; row < height; row++) { int blankCount = Math.abs(-1 * row + (height - 1) / 2); int charCount = height - 2 * blankCount; for (int col = 0; col < blankCount; col++) { System....

April 12, 2020 · Huo Haodong