最近复习期末考,都好久没更了。还剩最后一科计算机视觉,2号考完就结束啦,趁着复习空档更新一波。
目录
Part01:使用过滤器通过动态代理模式解决网站字符集编码乱码问题
1、面试题:增强一个对象的方法有几种?
* 继承(条件:要知道父类是谁才能继承)
* 装饰者模式
- 不需要知道父类,只需要知道接口即可
- 装饰者和被装饰者要实现同一个接口
- 装饰者有被装饰者的引用
- 缺点:工作量大,要实现接口的所有方法(可以用适配器类或动态代理解决)
/**
* 不知道实现类,用装饰者模式对NormalPerson的run方法增强
* 要求:1、装饰者和被装饰者实现同一个接口;
* 2、装饰者要有被装饰者的引用
*/
public class SuperPerson implements Person {
//被装饰者的引用
private final NormalPerson p;
public SuperPerson(NormalPerson p) {
this.p = p;
}
@Override
public void run() {
//被装饰者的方法
p.run();
//对被装饰者方法的增强
System.out.println("飞。。。");
}
}
* 动态代理模式:
- 必须得知道要被代理的类/对象是谁
- 动态的给我们生成代理类/代理对象
- JDK的动态代理:通过Proxy类的newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)方法生成,返回一个指定接口的代理类实例;
- 第一个参数:和要被代理的对象一样的类加载器
- 第二个参数:和要被增强对象一样的接口
- 根据指定传递的接口,返回一个该接口下的实例
- 传递的接口就是可以被增强的所有方法,(实现类中多于接口的方法不能被增强)
- 第三个参数:所有的增强业务逻辑实现(方法),通过创建匿名内部类实例实现
- CGLIB的动态代理
- JDK的动态代理:通过Proxy类的newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)方法生成,返回一个指定接口的代理类实例;
/**
* 使用动态代理的方式对NormalPerson进行增强
* 1.必须得知道要被代理的类/对象是谁
* 2.动态的给我们生成代理类/代理对象
*/
public class PersonTest {
@Test
public void test1(){
//需要被增强的对象
NormalPerson p = new NormalPerson();
/**
* 第一个参数:和要被增强的对象一样的类加载器
* 第二个参数:要和被增强对象一样的接口
* 第三个参数:所有的增强业务逻辑的实现(通过匿名内部类new InvocationHandler(){}实现)
*/
Person proxyPerson = (Person) Proxy.newProxyInstance(p.getClass().getClassLoader(), p.getClass().getInterfaces(), new InvocationHandler() {
/**
*InvocationHandler接口中只有一个invoke方法,
* 里面的第一个参数proxy为:代理对象的类型
* 第二个参数method为要被增强的方法;
* 第三个参数args为要被增强的方法运行过程中需要的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//通过判断对特定方法加强,如果没有判断,对proxyPerson调用的所有方法都会加强
if("run".equals(method)){
//让以前的方法执行
/**
* 第一个参数:本身应该执行这个方法的对象
* 第二个参数:执行这个方法需要的参数
*/
method.invoke(p, args);
//在这个方法里面写对要被增强对象方法的增强
System.out.println("飞。。。");
}
//谁调用就返回给谁(如果原方法没有返回值就不会接收)
return null;
}
});
//执行哪个方法,invoke都会执行一遍,执行的内容就是针对该方法的增强
proxyPerson.run();
}
}
2、通过动态代理模式解决网站字符集编码乱码问题案例实现
- Filter代码
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@WebFilter(filterName = "MyFilter",urlPatterns = {"/*"})
public class MyFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//要增强的对象
final HttpServletRequest request = (HttpServletRequest)req;
HttpServletRequest proxyRequest = (HttpServletRequest)Proxy.newProxyInstance(request.getClass().getClassLoader(), request.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//通过判断对特定的方法增强
if("getParameter".equals(method.getName())){
String m = request.getMethod();
//判断getParameter使用的是哪个提交方式
//get方式
if("get".equalsIgnoreCase(m)){
//获取以前的方法调用后的乱码
String string = (String)method.invoke(request, args);
//处理乱码
String s = new String(string.getBytes("iso-8859-1"), "utf-8");
//返回处理之后的
return s;
//post方式
}else if("post".equalsIgnoreCase(m)){
//设置编码格式为utf-8
request.setCharacterEncoding("utf-8");
return method.invoke(request,args);
}
}
return method.invoke(request,args);
}
});
//把通过动态代理增强的request传过去
chain.doFilter(proxyRequest, resp);
}
public void init(FilterConfig config) throws ServletException {
}
}
Part02:注解
1、什么是注解:
- 本质为接口。
- 可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理,通过注解,开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。
2、注解的作用:
- 告诉程序,当前应该怎么执行功能
3、注解的使用:
* JDK的三个常用注解:
- @Override:标记方法重写
- 功能:会在程序的预编译阶段进行重写检查,如果发现方法不是重写,那么会在预编译时期报错
- 使用:只能标记在方法上
- @SuppressWarnings:压制警告
- 取值:
- unchecked:忽略安全检查
- unused:忽略不使用
- null:忽略空指针
- all:忽略所有
- 一般使用:@SuppressWarnings(value=“all”)
- 功能:会在程序的预编译阶段进行警告的压制
- 使用:可以标注在方法、变量或类上
- 取值:
- @Deprecated:标记方法过时
- 功能:告诉程序这个方法是过时的,如果有程序员调用这样的过时方法,会在方法上做删除提示。
- 使用:标记在方法上
* 自定义注解:
- 注解的属性名:注解方法的方法名
- 注解的属性值:注解方法的返回值
- 如果自定义注解里面只有一个方法且该方法的名字叫做value,那么属性名可以省略不写。
- 使用IDEA自定义注解类
- 选择包—>右键new—>选择JavaClass
- 在Kind中选择Annotation
- 选择包—>右键new—>选择JavaClass
//自定义注解
public @interface MyAnnotation {
String test();
}
//自定义注解的使用
@MyAnnotation(test = "abc")
public class Test {
}
* 元注解:修饰注解的注解
- @Target:该注解可以放在哪些地方
- @Retention:指定注解在什么时候有效
- SOURCE:源码时期
- CLASS:编译时期
- RUNTIME:运行时期
Part03:类加载器
1、面试题:什么是类加载器:
- Class文件加载到内存中形成Class对象,这个过程就是类加载
- 做这个事情的东西就是类加载器
2、类加载器的组成:
- 引导类加载器(负责加载 rt.jar)(用的C语言,加载器打印出来为null)
- 扩展类加载器(负责加载 ext*.jar)
- 应用类加载器(负责加载 自己编写的类)
Part04:全盘负责托管机制
1、面试题:如何保证一个类只被加载一次?
- 使用全盘负责托管机制
- 作用:一个类只加载一次
- 原理:每个类加载器只加载自己的部分
- 应用类加载器拿到xxx.class文件,但是没有做任何的事情,给扩展类加载器
- 扩展类拿到了xxx.class文件,也没有做任何事情,给引导类加载器
- 引导类加载器拿到了xxx.class文件,它把属于它自己的内容给加载了,剩下的给扩展类加载器
- 扩展类加载器拿到剩下的,它把属于它的内容给加载了,把最终的部分给了应用类加载器
- 应用类加载器拿到最终剩下的,全部加载