递归算法
模拟单向链表
package com.zz;
/**
* 单向链表 结构
* @author jiyu
*
*/
public class Node {
//指向, 下一个节点
Node next;
//节点的名字
String name;
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试:
package com.zz;
import java.util.*;
public class Test2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Node node1=new Node();
node1.setName("祖宗");
Node node2=new Node();
node2.setName("a");
Node node3=new Node();
node3.setName("b");
node1.setNext(node2);
node2.setNext(node3);
printNodeName(node1);
}
public static void printNodeName(Node node){
System.out.println(node.name);
if(node.next!=null){
printNodeName(node.next);
}
}
}
测试数据结构图
运行结果:
祖宗
a
b
单向 ,多个子节点
package com.zz;
import java.util.List;
/**
* 单向链表 结构
* @author jiyu
*
*/
public class Node2 {
//指向, 下一个节点
List<Node2> next;
//节点的名字
String name;
public List<Node2> getNext() {
return next;
}
public void setNext(List<Node2> next) {
this.next = next;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试:
package com.zz;
import java.util.ArrayList;
import java.util.List;
public class Test3 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Node2 n1=new Node2();
n1.setName("根节点");
Node2 n2=new Node2();
n2.setName("家电");
Node2 n3=new Node2();
n3.setName("服装");
Node2 n21=new Node2();
n21.setName("洗衣机");
Node2 n22=new Node2();
n22.setName("电视机");
Node2 n31=new Node2();
n31.setName("女装");
Node2 n32=new Node2();
n32.setName("男装");
List<Node2> ls1=new ArrayList();
ls1.add(n2);
ls1.add(n3);
n1.setNext(ls1);
List<Node2> ls2=new ArrayList();
ls2.add(n21);
ls2.add(n22);
n2.setNext(ls2);
List<Node2> ls3=new ArrayList();
ls3.add(n31);
ls3.add(n32);
n3.setNext(ls3);
printNodeName(n1.next);
}
public static void printNodeName(List<Node2> nodelist){
for(Node2 node:nodelist){
System.out.println(node.name);
if(node.next!=null){
//自己调用了自己
printNodeName(node.next);
}
}
}
}
代理模式
Java 的代理就是客户类不再直接和委托类打交道, 而是通过一个中间层来访问, 这个中间层就是代理。为啥要这样呢, 是因为使用代理有 2 个优势:
-
可以隐藏委托类的实现
-
可以实现客户与委托类之间的解耦, 在不修改委托类代码的情况下能够做一些额外的处理。
从上面图中列子,我们知道,消费类,不知道工厂类的具体情况, 代理类,可以对目标类的方法结果进行包装修改。
代理模式一个有三种
- 静态代理
- 动态代理
- CGLIB代理
静态代理的实现关键点
- 目标类和代理类必须实现同一接口
- 代理类里面,需要有目标类的对象引用,然后代理类的方法里面,会执行目标类的对象的方法。
- 消费类,调用代理类的方法,但是代理类方法的里面,又调用了目标类的方法,并且可以添加额外的功能,从而达到消费类间接的执行目标类方法,并且代理类还提供额外功能的目的。
// 委托接口
public interface IHelloService {
/**
* 定义接口方法
* @param userName
* @return
*/
String sayHello(String userName);
}
// 委托类实现
public class HelloService implements IHelloService {
@Override
public String sayHello(String userName) {
System.out.println("helloService" + userName);
return "HelloService" + userName;
}
}
// 代理类
public class StaticProxyHello implements IHelloService {
private IHelloService helloService = new HelloService();
@Override
public String sayHello(String userName) {
/** 代理对象可以在此处包装一下*/
System.out.println("代理对象包装礼盒...");
return helloService.sayHello(userName);
}
}
// 测试静态代理类
public class MainStatic {
public static void main(String[] args) {
StaticProxyHello staticProxyHello = new StaticProxyHello();
staticProxyHello.sayHello("isole");
}
}
JAVA 动态代理的实现关键点
- 目标类一定实现了某个接口
- 中间类一定要实现InvocationHandler接口
- 中间类通过反射,动态的生成代理类
- 动态生成的代理类,执行任何方法,都会先执行invoke方法
代理类在程序运行时创建的代理方式被成为 动态代理。在了解动态代理之前, 我们先简回顾一下 JVM 的类加载机制中的加载阶段要做的三件事情
( 附 Java 中的类加载器 )
通过一个类的全名或其它途径来获取这个类的二进制字节流
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
在内存中生成一个代表这个类的 Class 对象, 作为方法区中对这个类访问的入口
而我们要说的动态代理,主要就发生在第一个阶段, 这个阶段类的二进制字节流的来源可以有很多, 比如 zip 包、网络、运行时计算生成、其它文件生成 (JSP)、数据库获取。其中运行时计算生成就是我们所说的动态代理技术,在 Proxy 类中, 就是运用了 ProxyGenerator.generateProxyClass 来为特定接口生成形式为 *$Proxy 的代理类的二进制字节流。所谓的动态代理就是想办法根据接口或者目标对象计算出代理类的字节码然后加载进 JVM 中。实际计算的情况会很复杂,我们借助一些诸如 JDK 动态代理实现、CGLIB 第三方库来完成的
例子:
// 委托类接口
public interface IHelloService {
/**
* 方法1
* @param userName
* @return
*/
String sayHello(String userName);
/**
* 方法2
* @param userName
* @return
*/
String sayByeBye(String userName);
}
// 委托类
public class HelloService implements IHelloService {
@Override
public String sayHello(String userName) {
System.out.println(userName + " hello");
return userName + " hello";
}
@Override
public String sayByeBye(String userName) {
System.out.println(userName + " ByeBye");
return userName + " ByeBye";
}
}
// 中间类
public class JavaProxyInvocationHandler implements InvocationHandler {
/**
* 中间类持有委托类对象的引用,这里会构成一种静态代理关系
*/
private Object obj ;
/**
* 有参构造器,传入委托类的对象
* @param obj 委托类的对象
*/
public JavaProxyInvocationHandler(Object obj){
this.obj = obj;
}
/**
* 动态生成代理类对象,Proxy.newProxyInstance
* @return 返回代理类的实例
*/
public Object newProxyInstance() {
return Proxy.newProxyInstance(
//指定代理对象的类加载器
obj.getClass().getClassLoader(),
//代理对象需要实现的接口,可以同时指定多个接口
obj.getClass().getInterfaces(),
//方法调用的实际处理者,代理对象的方法调用都会转发到这里
this);
}
/**
*
* @param proxy 代理对象
* @param method 代理方法
* @param args 方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoke before");
Object result = method.invoke(obj, args);
System.out.println("invoke after");
return result;
}
}
// 测试动态代理类
public class MainJavaProxy {
public static void main(String[] args) {
JavaProxyInvocationHandler proxyInvocationHandler = new JavaProxyInvocationHandler(new HelloService());
IHelloService helloService = (IHelloService) proxyInvocationHandler.newProxyInstance();
helloService.sayByeBye("paopao");
helloService.sayHello("yupao");
}
}
CGLIB 动态代理
JDK 动态代理依赖接口实现,而当我们只有类没有接口的时候就需要使用另一种动态代理技术 CGLIB 动态代理。首先 CGLIB 动态代理是第三方框架实现的,在 maven 工程中我们需要引入 cglib 的包, 如下:
cglib cglib 2.2CGLIB 代理是针对类来实现代理的,原理是对指定的委托类生成一个子类并重写其中业务方法来实现代理。代理类对象是由 Enhancer 类创建的。CGLIB 创建动态代理类的模式是:
查找目标类上的所有非 final 的 public 类型的方法 (final 的不能被重写)
将这些方法的定义转成字节码
将组成的字节码转换成相应的代理的 Class 对象然后通过反射获得代理类的实例对象
实现 MethodInterceptor 接口, 用来处理对代理类上所有方法的请求
// 委托类,是一个简单类
public class CglibHelloClass {
/**
* 方法1
* @param userName
* @return
*/
public String sayHello(String userName){
System.out.println(“目标对象的方法执行了”);
return userName + " sayHello";
}
public String sayByeBye(String userName){
System.out.println("目标对象的方法执行了");
return userName + " sayByeBye";
}
}
/**
- CglibInterceptor 用于对方法调用拦截以及回调
/
public class CglibInterceptor implements MethodInterceptor {
/*
* CGLIB 增强类对象,代理类对象是由 Enhancer 类创建的,
* Enhancer 是 CGLIB 的字节码增强器,可以很方便的对类进行拓展
*/
private Enhancer enhancer = new Enhancer();
/**
*
* @param obj 被代理的对象
* @param method 代理的方法
* @param args 方法的参数
* @param proxy CGLIB方法代理对象
* @return cglib生成用来代替Method对象的一个对象,使用MethodProxy比调用JDK自身的Method直接执行方法效率会有提升
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("方法调用之前");
Object o = proxy.invokeSuper(obj, args);
System.out.println("方法调用之后");
return o;
}
/**
* 使用动态代理创建一个代理对象
* @param c
* @return
*/
public Object newProxyInstance(Class<?> c) {
/**
* 设置产生的代理对象的父类,增强类型
*/
enhancer.setSuperclass(c);
/**
* 定义代理逻辑对象为当前对象,要求当前对象实现 MethodInterceptor 接口
*/
enhancer.setCallback(this);
/**
* 使用默认无参数的构造函数创建目标对象,这是一个前提,被代理的类要提供无参构造方法
*/
return enhancer.create();
}
}
//测试类
public class MainCglibProxy {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
CglibHelloClass cglibHelloClass = (CglibHelloClass) cglibProxy.newProxyInstance(CglibHelloClass.class);
cglibHelloClass.sayHello(“isole”);
cglibHelloClass.sayByeBye(“sss”);
}
}
对于需要被代理的类,它只是动态生成一个子类以覆盖非 final 的方法,同时绑定钩子回调自定义的拦截器。值得说的是,它比 JDK 动态代理还要快。值得注意的是,我们传入目标类作为代理的父类。不同于 JDK 动态代理,我们不能使用目标对象来创建代理。目标对象只能被 CGLIB 创建。在例子中,默认的无参构造方法被使用来创建目标对象。
JAVA 动态代理和GCLIB代理的区别
- JAVA 动态代理 基于接口动态生成实现类
- GCLIB代理 基于继承,动态生成子类