io流
Stream流
流是Java8 API添加的,以一种声明性方式处理数据集合(侧重对于源数据计算能力的封装,并且支持序列与并行两种操作方式),Stream流是从支持数据处理操作的源生成的元素序列,源可以是数组、文件、集合、函数。流不是集合元素,它不是数据结构并不保存数据,它的主要目的在于计算。Stream流是对集合(Collection)对象功能的增强,与Lambda表达式结合,可以提高编程效率、间接性和程序可读性。
特点
1、代码简洁:函数式编程写出的代码简洁且意图明确,使用stream接口让你从此告别for循环
2、多核友好:Java函数式编程使得编写并行程序如此简单,就是调用一下方法
流程
1、将集合转换为Stream流(或者创建流)
2、操作Stream流(中间操作,终端操作)
创建Stream流
1.单列集合
2.双列集合
3.数组
4.零散数据
操作Stream流
中间方法
过滤
限制
跳过
去重
合并
转换
终结方法
遍历方法
统计方法
收集方法
序列化
注解
注解(Annotation)是Java语言中的一种特殊语法结构,它可以在代码中嵌入元数据(metadata),用于一些特殊的标记和说明。注解可以在编译时被读取,并在运行时使用。
注解语法:
@注解名称(属性名1=属性值1, 属性名2=属性值2, ...)
注解分为内置注解、元注解、自定义注解
内置注解
Java语言提供了一些内置注解,这些注解可以用来标记特定的场景,从而帮助编译器和开发者更好地理解代码的含义。以下是一些常见的内置注解及其使用场景和用法:
@Override:表示重写方法
@Deprecated:修饰方法、属性、类,表示不鼓励使用,通常因为很危险或有更好的选择
@SuppressWarnings:抑制编译时的警告信息
元注解
元注解(meta-annotation)是指用来注解其他注解的注解。Java语言中提供了4种元注解,分别是@Retention、@Target、@Inherited和@Documented。
- @Retention:用于指定注解的保留策略,即注释信息保存的级别。它有一个属性value,可以设置为RetentionPolicy.SOURCE、RetentionPolicy.CLASS或RetentionPolicy.RUNTIME,分别表示注解保留在源代码中、保留在class文件中或保留在运行时。
- @Target:用于指定注解的作用目标。它有一个属性value,可以设置为ElementType.TYPE、ElementType.FIELD、ElementType.METHOD等,表示注解可以作用于类、字段、方法等不同的目标上。
- @Inherited:用于指定注解是否可以被继承。如果一个注解被@Inherited注解,则它被用来注解的类的子类也会继承这个注解。
- @Documented:用于指定注解是否包含在JavaDoc中。如果一个注解被@Documented注解,则它会被包含在JavaDoc中,方便开发者查看。
自定义注解
public @Interface 注解名{ }
//元注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface myAnnotation{
//定义属性、方法
}
反射
反射机制:是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意属性和方法;这种动态获取信息以及动态调用对象方法的功能称为 java 语言的反射机制
工作原理是这样的:当一个字节码文件加载到内存的时候, jvm会对该字节码进行解剖,然后创建一个对象的Class对象,jvm把字节码文件的信息全部都存储到该Class对象中,我们只要获取到Class对象,我们就可以使用该对象设置对象的属性或者调用对象的方法等操作。
通过反射,可以实现如下的操作
- 程序运行时,可以通过反射获得任意一个类的Class对象,并通过这个对象查看这个类的信息;
- 程序运行时,可以通过反射创建任意一个类的实例,并访问该实例的成员;
- 程序运行时,可以通过反射机制生成一个类的动态代理类或动态代理对象。
获取Class类对象
- 类名.class属性
- 对象名.getClass()方法
- Class.forName(全类名)方法(包名+类名)
分别在程序不同阶段调用,获取类名
获取构造方法
get表示获取,Declared
表示私有,最后的s
表示所有,即复数形式
如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
方法名 | 说明 |
---|---|
Constructor<?>[ ] getConstructors() | 获得所有的构造(只能public修饰) |
Constructor<?>[ ] getDeclaredConstructors() | 获得所有的构造(包含private修饰) |
Constructor getConstructor(Class<?>… parameterTypes) | 获取指定构造(只能public修饰) |
Constructor getDeclaredConstructor(Class<?>… parameterTypes) | 获取指定构造(包含private修饰) |
创建对象:
newInstance
获取成员变量
get表示获取,Declared
表示私有,最后的s
表示所有,即复数形式
如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
方法名 | 说明 |
---|---|
Field[ ] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
Field[ ] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
获取变量值和修改值
方法 | 说明 |
---|---|
void set(Object obj, Object value) | 赋值 |
Object get(Object obj) | 获取值 |
获取成员方法
方法名 | 说明 |
---|---|
Method[ ] getMethods() | 返回所有成员方法对象的数组(只能拿public的) |
Method[ ] getDeclaredMethods() | 返回所有成员方法对象的数组,存在就能拿到 |
Method getMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象(只能拿public的) |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象,存在就能拿到 |
运行方法
Object invoke(Object obj, Object... args)
参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(如果没有就不写)
返回值:方法的返回值(如果没有就不写)
代理模式
java当中有三种方式来创建代理对象:静态代理,基于jdk(接口)的动态代理,基于CGLLIB(父类)的动态代理。代理底层基于反射。
优点:
1. 隐藏实现细节:代理模式可以隐藏实现对象的细节,客户端只需要与代理对象交互便可,无需了解实现细节。
2. 增强安全性:代理模式可以增强安全性,代理对象可以对客户真个要求进行过滤和验证,从而确保要求的合法性。
3. 提高性能:代理模式可以提高性能,代理对象可以缓存要求结果,从而减少对实现对象的访问次数,提高系统的响应速度。
4. 实现松耦合:代理模式可以实现松耦合,代理对象可以在客户端和实现对象之间起到中介的作用,使得系统的结构更加灵活和可扩大。
静态代理
编写代理类:实现目标的接口或者直接继承目标类,完成逻辑的修改。
优点 :在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展。缺点 :因为代理对象需要与目标对象实现一样的接口,所以会很多代理类 ,一旦接口增加方法,目标对象与代理对象都要维护
静态代理对象的要求: 1.被代理对象和代理对象实现相同的接口 2.被代理对象依赖于代理对象
实现步骤:实现类和代理类同时实现接口,代理类调用实现类并添加功能,外部调用代理类。
动态代理
- JDK动态代理:JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。
- cglib动态代理:通过继承被代理的目标类(认干爹模式)实现代理,所以不需要目标类实现接口。(CGLIB 通过动态生成一个需要被代理类的子类(即被代理类作为父类),该子类重写被代理类的所有不是 final 修饰的方法,并在子类中采用方法拦截的技术拦截父类所有的方法调用,进而织入横切逻辑。)
基于jdk(接口)的动态代理:
程序运行的过程中,通过jdk提供代理技术动态的为某个类产生动态代理对象的过程
动态代理对象的要求:
1.代理对象不需要实现接口,但是被代理对象要实现接口,否则不能用JDK动态代理。
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象。
InvocationHandler:是一个接口,可以通过实现该接口定义横切逻辑,在并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起。
通过Proxy.newProxyInstance()创建代理对象
参数1:类加载器,动态代理对象没有类加载器 需要借助别的类的加载器实现类的加载,1.加载class文件到JVM虚拟机 2.创建类的class对象,进而创建类的实例化对象。
参数2:原始类实现的所有接口 基于jdk的动态代理要求代理对象和原始对象要实现相同的接口。
参数3:代理对象额外增加的功能 InvocationHandler。
invoke():每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
步骤:
1.调用Proxy.newProxyInstance()方法创建代理对象
2.重写参数里的匿名内部类new InvocationHandler()里的invoke方法
// 该方法代理类完整逻辑的集中体现。
// 第一个参数既是代理类实例,我们所需要 代理的那个真实对象,
// 第二个参数是我们所要调用真实对象的某个方法的Method对象
// 第三个方法是调用真实对象某个方法时接受的参数。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
/**
* 产生被代理对象实例代理对象,参数1:要被代理的对象的类的类加载器
* 参数2:被代理类的所有接口
* 参数3:代理类的实例要实现的方法
* */
T proxy = (T) Proxy.newProxyInstance(
objForProxy.getClass().getClassLoader(),
objForProxy.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
//方法执行前,添加需要执行的额外的内容
before(method,args);
//执行被代理对象的方法,返回值代表被代理对象方法的返回值
Object rtvs = method.invoke(objForProxy,args);
//方法执行前,添加需要执行的额外的内容
after(rtvs,method,args);
return rtvs;
}
});
使用泛型封装 ->获取jdk动态代理对象
//jdk动态代理的方法封装
public T getProxy(){
/**
* 产生被代理对象
* */
T proxy = (T) Proxy.newProxyInstance(
objForProxy.getClass().getClassLoader(),
objForProxy.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
//方法执行前,添加需要执行的额外的内容
before(method,args);
Object rtvs = method.invoke(objForProxy,args);
//方法执行前,添加需要执行的额外的内容
after(rtvs,method,args);
return rtvs;
}
});
return proxy;
}
基于CGLib(父类)的动态代理:
开发代理对象的要求:
1.代理对象无需和原始类对象实现相同的接口
2.代理对象和被代理对象要存在父子类关系,是构造一个被代理对象的子类作为代理类
步骤:
1.创建Enhancer对象,Enhancer enhancer = new Enhancer();
2.设置类加载器和父子类关系
enhancer.setClassLoader(this.getClass().getClassLoader());
enhancer.setSuperclass(userService.getClass());
3.创建MethodInterceptor对象增强功能,并在添加到Enhancer对象里
//设置额外的功能
MethodInterceptor callback = new MethodInterceptor() {
//等价于 jdk代理中 invocationHandler 中的 invoke 方法
/**
* @param o 代理对象
* @param method 原始对象中的方法
* @param objects 原始对象中的参数
* @param methodProxy 代理方法
* @return 原始方法的返回值
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy
methodProxy) throws Throwable {
System.out.println("---------cglib log------------");
return method.invoke(userService, objects);
}
};
enhancer.setCallback(callback);
4.使用create()创建代理对象,调用代理对象方法
// 创建代理对象
被代理类对象 =(被代理类类型) enhancer.create();
cglib的第二种实现方法
也可向jdk动态代理一样,只是一个是create()一个是Proxy.newProxyInstance();三个参数最后一个是new MethodInterceptor()一个是new InvocationHandler();重写的一个是intercept()一个是invoke(),这两个方法参数也有不同
//创建cglib动态代理对象
public T getProxy(){
Enhancer en = new Enhancer();
T proxy = (T) en.create(
objForProxy.getClass(),
objForProxy.getClass().getInterfaces(),
new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
before(method,objects);
Object rtvs = method.invoke(objForProxy, objects);
after(rtvs,method,objects);
return rtvs;
}
});
return proxy;
}
Spring么默认情况下使用的是JDK代理
Spring Boot2.0 开始,如果用户什么都没有配置,那么默认情况下使用的是 Cglib 代理