首页
关于
友链
Search
1
wlop 4K 壁纸 4k8k 动态 壁纸
1,509 阅读
2
Nacos持久化MySQL问题-解决方案
959 阅读
3
Docker搭建Typecho博客
763 阅读
4
滑动时间窗口算法
746 阅读
5
Nginx反向代理微服务配置
716 阅读
生活
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
安全
开发工具
百度网盘
天翼网盘
阿里网盘
登录
Search
标签搜索
java
javase
docker
java8
springboot
thread
spring
分布式
mysql
锁
linux
redis
源码
typecho
centos
git
map
RabbitMQ
lambda
stream
少年
累计撰写
189
篇文章
累计收到
24
条评论
首页
栏目
生活
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
安全
开发工具
百度网盘
天翼网盘
阿里网盘
页面
关于
友链
搜索到
49
篇与
的结果
2022-03-11
CompletableFuture异步编排
通过线程池性能稳定,也可以获取执行结果,并捕获异常。但是,在业务复杂情况下,一个异步调用可能会依赖于另一个异步调用的执行结果。因此我们可以使用completableFuture 异步编排方案。比如:一个业务场景,需要同时获取多个数据,如果同步线程挨个执行,则需要时间为所有线程执行时间的总和。如果我们使用异步线程执行,所需时间则为耗时最长那个异步线程的执行时间。如果多个异常线程之间还存在依赖关系,比如线程3需要线程1的执行结果,线程6依赖线程3、线程2,那这个问题怎么解决呢。那就可以使用completableFuture 异步编排方案实现。注意:completableFuture 是jdk1.8之后添加的一个功能。CompletableFuture接口:public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {public interface Future<V> {以前用到的FutureTask就是用到的Future可以得到返回结果public class FutureTask<V> implements RunnableFuture<V> {public interface RunnableFuture<V> extends Runnable, Future<V> { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run(); }Future可以得到返回结果CompletableFuture随便一个方法,都接受一个Function public <U> CompletableFuture<U> applyToEither( CompletionStage<? extends T> other, Function<? super T, U> fn) { return orApplyStage(null, other, fn); }@FunctionalInterface public interface Function<T, R> {Function是一个@FunctionalInterface,所以对Lambda使用要熟悉。CompletableFuture异步编排问题多个异步线程远程调用会导致丢失请求头,原因是多个线程,通过拦截器不能获取其它线程的请求的heard信息。解决方案:每个线程共享自己的requestAttributes。自定义feign拦截器:import feign.RequestInterceptor; import feign.RequestTemplate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; /** * @description: TODO * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 23:03 * @version: 1.0 */ @Configuration public class FamilyFegin { @Bean(name ="requestInterceptor") public RequestInterceptor requestInterceptor(){ return new RequestInterceptor() { @Override public void apply(RequestTemplate requestTemplate) { //RequestContextHolder拿到刚请求来的数据 ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); System.out.println("requestAttributes线程"+Thread.currentThread().getId()); HttpServletRequest request = requestAttributes.getRequest(); System.out.println("调用feign之前"); if(request != null){ //同步请求头数据 String cookie = request.getHeader("Cookie");//老数据 //给新请求同步老请求的cookie requestTemplate.header("Cookie", cookie); } } }; } }web配置:因为需要拦截器生效import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @description: WEB配置 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 22:40 * @version: 1.0 */ public class FamilyWebConfig implements WebMvcConfigurer { @Autowired LoginUserInterceptor loginUserInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginUserInterceptor).addPathPatterns("/**"); } }CompletableFuture异步编排import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @description: CompletableFuture异步编排 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 23:32 * @version: 1.0 */ public class MyCompletableFuture { public static ExecutorService executorService = Executors.newFixedThreadPool(10); public void myOpenFeign() throws ExecutionException, InterruptedException { System.out.println("主线程0"); //远程调用存在丢失请求头的问题,因为不在同一线程,导致自定义拦截器不能获取head信息。 //解决丢失请求头方案: ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); //第一个异步任务 CompletableFuture<Void> oneFuture = CompletableFuture.runAsync(() -> { System.out.println("辅线程1"); //每个线程都共享一下自己的requestAttributes RequestContextHolder.setRequestAttributes(requestAttributes); //异步线程远程调用,业务1 //异步线程调用业务代码 }, executorService); //第二个异步任务 CompletableFuture<Void> twoFuture = CompletableFuture.runAsync(() -> { System.out.println("辅线程2"); //每个线程都共享一下自己的requestAttributes RequestContextHolder.setRequestAttributes(requestAttributes); //异步线程远程调用,业务2 //异步线程调用业务代码 }, executorService); CompletableFuture.allOf(oneFuture, twoFuture).get(); } }以上就是解决CompletableFuture异步编排,异步多线程引起的远程调用请求丢失解决方案。代码中共享requestAttributes原因ThreadLocal数据:ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();RequestContextHolder主线程不一样获取的数据不一样,如下:import javax.faces.context.FacesContext; import org.springframework.core.NamedInheritableThreadLocal; import org.springframework.core.NamedThreadLocal; import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; public abstract class RequestContextHolder { private static final boolean jsfPresent = ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader()); private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes"); private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal("Request context"); public RequestContextHolder() { }
2022年03月11日
415 阅读
0 评论
5 点赞
2022-03-10
Springboot拦截器配置
Springboot拦截器配置1、拦截器配置,主要实现HandlerInterceptor接口import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @description: 拦截器,配合FamilyWebConfig 配置使用 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 22:36 * @version: 1.0 */ @Component public class LoginUserInterceptor implements HandlerInterceptor { public static ThreadLocal<Object> objUser = new ThreadLocal<>(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object loginusr = request.getSession().getAttribute("LOGIN_USR"); if(loginusr != null){ objUser.set(loginusr); //登录了才能访问 return true; }else{ //没登录,跳转去登录 return false; } } } 由于拦截器需要配合web配置使用,因此创建web配置。2、web配置主要实现WebMvcConfigurer接口import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @description: WEB配置 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 22:40 * @version: 1.0 */ public class FamilyWebConfig implements WebMvcConfigurer { @Autowired LoginUserInterceptor loginUserInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginUserInterceptor).addPathPatterns("/**"); } }
2022年03月10日
206 阅读
0 评论
2 点赞
2022-03-10
JAVA多线程4种实现方式
异步与线程池1、初始化线程的4种方式1)、继承Thread2)、实现Runnable接口3)、实现Callable接口+FutureTask(可以拿到返回结果,可以处理异常)4)、线程池方式1和方式2:主进程无法获取线程的运算结果。不适合当前场景。方式3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致服务器资源耗尽。方式4:通过如下两种方式初始化线程池Executors.newFiexed ThreadPool(3); //或者 new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,TimeUnit unit, workQueue,threadFactory,handler);方式1、继续Thread/** * @description: 继承Thread * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 21:06 * @version: 1.0 */ public class MyThread { public static void main(String[] args) { System.out.println("启动main方法开始"); OneMyThread oneMyThread = new OneMyThread(); oneMyThread.start(); System.out.println("结束main方法"); } public static class OneMyThread extends Thread{ @Override public void run() { System.out.println("当前线程:"+Thread.currentThread().getId()); int i = 10/2; System.out.println("运行结果:"+i); } } }运行结果:启动main方法开始 结束main方法 当前线程:22 运行结果:5方式2、实现Runable接口/** * @description: 实现Runnable接口 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 21:06 * @version: 1.0 */ public class MyThread { public static void main(String[] args) { System.out.println("启动main方法开始"); TwoMyThread twoMyThread = new TwoMyThread(); new Thread(twoMyThread).start(); System.out.println("结束main方法"); } /** * 实现Runnable接口 */ public static class TwoMyThread implements Runnable { @Override public void run() { System.out.println("当前线程:"+Thread.currentThread().getId()); int i = 10/2; System.out.println("运行结果:"+i); } } }运行结果:启动main方法开始 结束main方法 当前线程:22 运行结果:5方式3、实现Callable泛型接口/** * @description: 继承Thread * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 21:06 * @version: 1.0 */ public class MyThread { public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("启动main方法开始"); FutureTask<Integer> futureTask = new FutureTask<>(new ThreeMyThread()); new Thread(futureTask).start(); //等待线程执行完成,返回结果 Integer i = futureTask.get(); System.out.println("线程执行完,返回结果:"+i); System.out.println("结束main方法"); } /** * 实现Callable<T>泛型接口 */ public static class ThreeMyThread implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println("当前线程:"+Thread.currentThread().getId()); int i = 10/2; System.out.println("运行结果:"+i); return i; } } }运行结果:启动main方法开始 当前线程:22 运行结果:5 线程执行完,返回结果:5 结束main方法注意:结果的顺序,可以看到是一个阻塞等待通过FutureTask类源码可以看到,FutureTask不仅可以接受Callable还可以接收Runnable.FutureTask源码如下:public class FutureTask<V> implements RunnableFuture<V> { /** * Creates a {@code FutureTask} that will, upon running, execute the * given {@code Runnable}, and arrange that {@code get} will return the * given result on successful completion. * * @param runnable the runnable task * @param result the result to return on successful completion. If * you don't need a particular result, consider using * constructions of the form: * {@code Future<?> f = new FutureTask<Void>(runnable, null)} * @throws NullPointerException if the runnable is null */ public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }FutureTask实现RunnableFuture接口:RunnableFuture最终是继承的Runnable。package java.util.concurrent; /** * A {@link Future} that is {@link Runnable}. Successful execution of * the {@code run} method causes completion of the {@code Future} * and allows access to its results. * @see FutureTask * @see Executor * @since 1.6 * @author Doug Lea * @param <V> The result type returned by this Future's {@code get} method */ public interface RunnableFuture<V> extends Runnable, Future<V> { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run(); }方式4、直接提交线程池,线程池会自动开启任务。每次通过new Thread()创建线程问题:导致资源耗尽。以上三种启动方式都不用。应该将所有异步多线程任务交给线程池执行。线程池:package com.yanxizhu.family.booking; import java.util.concurrent.*; /** * @description: 继承Thread * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 21:06 * @version: 1.0 */ public class MyThread { //应保证,当前系统中线程池只有一两个,每个异步任务,提交给线程池自己执行。 public static ExecutorService executorService = Executors.newFixedThreadPool(10); public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("启动main方法开始"); executorService.submit(new TwoMyThread()); //有返回值 //每个异步任务,提交给线程池自己执行。 executorService.execute(new TwoMyThread());//无返回值 System.out.println("结束main方法"); } /** * 实现Runnable接口 */ public static class TwoMyThread implements Runnable { @Override public void run() { System.out.println("当前线程:"+Thread.currentThread().getId()); int i = 10/2; System.out.println("运行结果:"+i); } } } 运行结果:启动main方法开始 结束main方法 当前线程:22 运行结果:5使用线程池带来的好处:1、降低资源的消耗通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗2、提高响应速度因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行3、提高线程的可管理性线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配总结区别1、直接继承Thread、实现Runnable接口:不能直接得到返回值。2、实现Callable泛型接口:可以得到返回值。3、继承Thread、实现Runnable接口、实现Callable泛型接口,都不能达到控制资源的效果。4、只有线程池可以控制资源,性能稳定。
2022年03月10日
173 阅读
0 评论
1 点赞
2022-03-08
JAVA面向对象有那些特征
JAVA面向对象有那些特征?面向对象编程是利用类和对象编程的一种思想。万物可归类,类是对于世界事物的高度抽象,不同的事物之间有不同的关系,一个类自身与外界的封装关系,一个父类和子类的继承关系,一个类和多个类的多态关系。万物皆对象,对象是具体的世界事物,面向对象的三大特征封装,继承,多态。封装,封装说明一个类行为和属性与其他类的关系,低耦合,高内聚;继承是父类和子类的关系,多态说的是类与类的关系。封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。属性的封装:使用者只能通过事先定制好的方法来访问数据,可以方便地加入逻辑控制,限制对属性的不合理操作;方法的封装:使用者按照既定的方式调用方法,不必关心方法的内部实现,便于使用;便于修改,增强代码的可维护性;继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。在本质上是特殊~一般的关系,即常说的is-a关系。子类继承父类,表明子类是一种特殊的父类,并且具有父类所不具有的一些属性或方法。从多种实现类中抽象出一个基类,使其具备多种实现类的共同特性,当实现类用extends关键字继承了基类(父类)后,实现类就具备了这些相同的属性。继承的类叫做子类(派生类或者超类),被继承的类叫做父类(或者基类)。比如从猫类、狗类、虎类中可以抽象出一个动物类,具有和猫、狗、虎类的共同特性(吃、跑、叫等)。Java通过extends关键字来实现继承,父类中通过private定义的变量和方法不会被继承,不能在子类中直接操作父类通过private定义的变量以及方法。继承避免了对一般类和特殊类之间共同特征进行的重复描述,通过继承可以清晰地表达每一项共同特征所适应的概念范围,在一般类中定义的属性和操作适应于这个类本身以及它以下的每一层特殊类的全部对象。运用继承原则使得系统模型比较简练也比较清晰。相比于封装和继承,Java多态是三大特性中比较难的一个,封装和继承最后归结于多态,多态指的是类和类的关系,两个类由继承关系,存在有方法的重写,故而可以在调用时有父类引用指向子类对象。多态必备三个要素:继承,重写,父类引用指向子类对象。继承、封装:增强了代码的可复用性,多态:增加了可移植性、健壮性、灵活性。
2022年03月08日
183 阅读
0 评论
1 点赞
2022-03-08
ArrayList和LinkedList的区别
ArrayList和LinkedList都实现了List接口,他们有以下的不同点:ArrayList是基于索引的数据接口,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)。相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。LinkedListt比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。也可以参考ArrayList vs.LinkedList。1)因为Array是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。Array 获取数据的时间复杂度是O(1),但是要删除数据却是开销很大的,因为这需要重排数组中的所有数据。2)相对于Arraylist,LinkedList插入是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O(1)。ArrayList 在插入数据时还需要更新索引(除了插入数组的尾部)。3)类似于插入数据,删除数据时,LinkedList 也优于Arraylist。4)LinkedList 需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置(一个LinkedList实例存储了两个值:Nodefirst和Nodelast分别表示链表的其实节点和尾节点,每个Node 实例存储了三个值:Eitem,Node next,Node pre)。什么场景下更适宜使用LinkedList,而不用ArrayList1)你的应用不会随机访问数据。因为如果你需要LinkedList中的第n个元素的时候,你需要从第一个元素顺序数到第n个数据,然后读取数据。2)你的应用更多的插入和删除元素,更少的读取数据。因为插入和删除元素不涉及重排数据,所以它要比ArrayList要快。
2022年03月08日
145 阅读
0 评论
1 点赞
2022-03-08
JAVA中抽象类和接口有什么区别
JAVA中抽象类和接口有什么区别?相同:1.不能够实例化2.可以将抽象类和接口类型作为引用类型3.一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类不同:抽象类:1.抽象类中可以定义构造器2.可以有抽象方法和具体方法3.接口中的成员全都是public的4.抽象类中可以定义成员变量5.有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法6.抽象类中可以包含静态方法7.一个类只能继承一个抽象类接口:1.接口中不能定义构造器2.方法全部都是抽象方法3.抽象类中的成员可以是private、默认、protected、public4.接口中定义的成员变量实际上都是常量5.接口中不能有静态方法6.一个类可以实现多个接口
2022年03月08日
147 阅读
0 评论
1 点赞
2022-03-07
JDK1.8新特性
JDK1.8的新特性一、接口的默认方法Java8允许我们给接口添加一个非抽象的方法实现,只需要使用default关键字即可,这个特征又叫做扩展方法,示例如下:代码如下:interface Formula{ double calculate(int a); default double sqrt(int a){ return Math.sqrt(a); } }Formula接口在拥有calculate方法之外同时还定义了sqrt方法,实现了Formula接口的子类只需要实现一个calculate方法,默认方法sqrt将在子类上可以直接使用。代码如下:Formula formula=new Formula){@Override public double calculate(int a){return sqrt(a100);}];formula.calculate(100);/100.0 formula.sqrt(16);//4.0文中的formula被实现为一个匿名类的实例,该代码非常容易理解,6行代码实现了计算sqrt(a*100)。在下一节中,我们将会看到实现单方法接口的更简单的做法。译者注:在Java中只有单继承,如果要让一个类赋予新的特性,通常是使用接口来实现,在C++中支持多继承,允许一个子类同时具有多个父类的接口与功能,在其他语言中,让一个类同时具有其他的可复用代码的方法叫做mixin。新的Java8的这个特新在编译器实现的角度上来说更加接近Scala的trait。在C#中也有名为扩展方法的概念,允许给已存在的类型扩展方法,Java8的这个在语义上有差别。二、Lambda表达式首先看看在老版本的Java中是如何排列字符串的:代码如下:List names=Arrays.asList("peterF","anna","mike","xenia");Collections.sort(names,new Comparator){@Override public int compare(String a,String b){return b.compareTo(a);}});只需要给静态方法Collections.sort传入一个List对象以及一个比较器来按指定顺序排列。通常做法都是创建一个匿名的比较器对象然后将其传递给sort方法。在Java8中你就没必要使用这种传统的匿名对象的方式了,Java8提供了更简洁的语法,lambda表达式:代码如下:Collections.sort(names,(String a,String b)->{return b.compareTo(a);});看到了吧,代码变得更段且更具有可读性,但是实际上还可以写得更短:代码如下:Collections.sort(names,(String a,String b)->b.compareTo(a);对于函数体只有一行代码的,你可以去掉大括号0以及return关键字,但是你还可以写得更短点:代码如下:Collections.sort(names,(a,b)>b.compareTo(a);Java编译器可以自动推导出参数类型,所以你可以不用再写一次类型。接下来我们看看lambda表达式还能作出什么更方便的东西来:三、函数式接口Lambda表达式是如何在java的类型系统中表示的呢?每一个lambda表达式都对应一个类型,通常是接口类型。而“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为默认方法不算抽象方法,所以你也可以给你的函数式接口添加默认方法。我们可以将lambda表达式当作任意只包含一个抽象方法的接口类型,确保你的接口一定达到这个要求,你只需要给你的接口添加@Functionallnterface注解,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的。示例如下:代码如下:@Functionallnterface interface Converter<F,T>{T convert(F from);}Converter<String,Integer>converter=from)->Integer.valueOf(from);Integer converted=converter.convert("123");System.out.printin(converted);//123需要注意如果@Functionallnterface如果没有指定,上面的代码也是对的。译者注将lambda表达式映射到一个单方法的接口上,这种做法在Java8之前就有别的语言实现,比如Rhino JavaScript解释器,如果一个函数参数接收一个单方法的接口而你传递的是一个function,Rhino 解释器会自动做一个单接口的实例到function的适配器,典型的应用场景有 org.w3c.dom.events.EventTarget的addEventListener 第二个参数EventListener。四、方法与构造函数引用前一节中的代码还可以通过静态方法引用来表示:代码如下:Converter<String,Integer>converter=Integer:valueOf;Integer converted=converter.convert("123");System.out.printin(converted);//123Java8允许你使用:关键字来传递方法或者构造函数引用,上面的代码展示了如何引用一个静态方法,我们也可以引用一个对象的方法:代码如下:converter=something::startsWith;String converted=converter.convert("Java");System.out.printin(converted);//"接下来看看构造函数是如何使用:关键字来引用的,首先我们定义一个包含多个构造函数的简单类:代码如下:代码如下:Converter<String,Integer> converter=Integer::valueOf;Integer converted=converter.convert("123");System.out.println(converted);//123Java8允许你使用::关键字来传递方法或者构造函数引用,上面的代码展示了如何引用一个静态方法,我们也可以引用一个对象的方法:代码如下:converter=something:startsWith;String converted=converter.convert("Java");system.out.println(converted);/∥"y"接下来看看构造函数是如何使用:关键字来引用的,首先我们定义一个包含多个构造函数的简单类:代码如下:class Person{String firstName;String lastName;Person)}Person(String firstName,String lastName){this.firstName =firstName;this.lastName=lastName;}}接下来我们指定一个用来创建Person对象的对象工厂接口:代码如下:interface PersonFactory{P create(String firstName,String lastName);}这里我们使用构造函数引用来将他们关联起来,而不是实现一个完整的工厂:代码如下:PersonFactorypersonFactory=Person::new;Person person=personFactory.create("Peter","Parker");我们只需要使用Person::new来获取Person类构造函数的引用,Java编译器会自动根据PersonFactory.create方法的签名来选择合适的构造函数。五、Lambda作用域在lambda表达式中访问外层作用域和老版本的匿名对象中的方式很相似。你可以直接访问标记了final的外层局部变量,或者实例的字段以及静态变量。六、访问局部变量我们可以直接在lambda表达式中访问外层的局部变量:代码如下:final int num=1;Converter<Integer,String>stringConverter=(from)->String.valueof(from +num);stringConverter.convert(2);//3但是和匿名对象不同的是,这里的变量num可以不用声明为final,该代码同样正确:代码如下:int num=1;Converter<Integer,String>stringConverter=(from)->String.valueof(from+num);stringConverter.conver(2);//3不过这里的num必须不可被后面的代码修改(即隐性的具有fina的语义),例如下面的就无法编译:代码如下:int num=1;Converter<Integer,String>stringConverter =(from)->string.valueof(from +num);num=3;在lambda表达式中试图修改num同样是不允许的。七、访问对象字段与静态变量和本地变量不同的是,lambda内部对于实例的字段以及静态变量是即可读又可写。该行为和匿名对象是一致的:代码如下:class Lambda4{static int outerStaticNum;int outerNum;void testScopes(){Converter<Integer,String>stringConverter1=(from)->{outerNum=23;return String.valueof(from);};Converter<Integer,String>stringConverter2=(from)->{outerStaticNum=72;return String.valueof(from);};}}八、访问接口的默认方法还记得第一节中的formula例子么,接口Formula定义了一个默认方法sqrt可以直接被formula的实例包括匿名对象访问到,但是在lambda表达式中这个是不行的。Lambda表达式中是无法访问到默认方法的,以下代码将无法编译:代码如下:Formula formula=(a)->sqrt(a*100);Built-in Functional InterfacesJDK 1.8API包含了很多内建的函数式接口,在老Java中常用到的比如Comparator或者Runnable接口,这些接口都增加了@Functionallnterface注解以便能用在lambda上。Java8APl同样还提供了很多全新的函数式接口来让工作更加方便,有一些接口是来自Google Guava库里的,即便你对这些很熟悉了,还是有必要看看这些是如何扩展到lambda上使用的。Predicate接口Predicate 接口只有一个参数,返回boolean类型。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非):代码如下:Predicatepredicate=(s)->s.length()>0;predicate.test("foo");//true predicate.negate().test("foo");//false Predicate nonNull =Objects:nonNull;PredicateisNull=Objects:isNull;PredicateisEmpty=String:isEmpty;PredicateisNotEmpty=isEmpty.negate();Function接口Function 接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法(compose,andThen):代码如下:Function<String,Integer> tolnteger=Integer:valueOf;Function<String,String>backToString=tolnteger.andThen(String:valueOf);backToString.apply("123");//"123"Supplier 接口Supplier 接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数代码如下:Supplier personSupplier=Person:new;personSupplier get();//new PersonConsumer 接口Consumer接口表示执行在单个参数上的操作。代码如下:Consumergreeter=(p)->System.out.println("Hello,"+p.firstName);greeter.accept(new Person("Luke","Skywalker"));Comparator 接口Comparator是老ava中的经典接口,Java8在此之上添加了多种默认方法:代码如下:Comparatorcomparator =(p1,p2)->p1.firstName.compareTo(p2.firstName);Person p1=new Person("John","Doe");Person p2=new Person("Alice","Wonderland");comparator.compare(p1,p2);//>0 comparator.reversed().compare(p1,p2);//<0optional接口Optional 不是函数是接口,这是个用来防止NullPointerException异常的辅助类型,这是下一届中将要用到的重要概念,现在先简单的看看这个接口能干什么:Optional被定义为一个简单的容器,其值可能是null或者不是null。在Java8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java8中,不推荐你返回null而是返回Optional。代码如下:Optionaloptional=Optional.of("bam");optional.isPresent();//true optional.get();//"bam"optional.orElse("fallback");//"bam"optional.ifPresent((s)->System.out.println(s.charAt(O));//"b"Stream接口java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。Stream 的创建需要指定一个数据源,比如java.util.Collection的子类,List或者Set,Map不支持。Stream的操作可以串行执行或者并行执行。首先看看Stream是怎么用,首先创建实例代码的用到的数据List:代码如下:List stringCollection=new ArrayList<>();;stringCollection.add("ddd2");stringCollection.add("aaa2");stringCollection.add("bbb1");stringCollection.add("aaa1");stringCollection.add("bbb3");stringCollection.add("ccc");stringCollection.add("bbb2");stringCollection.add("ddd1");Java 8扩展了集合类,可以通过Collection.stream()或者Collection.parallelStream()来创建一个Stream。下面几节将详细解释常用的Stream操作:Filter 过滤过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作,所以我们可以在过滤后的结果来应用其他Stream操作(比如forEach)。forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以我们不能在forEach之后来执行其他Stream操作。代码如下:stringCollection.stream().filter(s)->s.startsWith("a").forEach(System.out::println);//"aaa2","aaal"Sort 排序排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。代码如下:stringCollection.stream().sorted().filter(s)->s.startsWith("a")).forEach(System.out:printin);//"aaa1","aaa2"需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,排序之后原数据stringCollection是不会被修改的:代码如下:System.out.printin(stringCollection);//ddd2,aaa2,bbb1,aaal,bbb3,ccc,bbb2,ddd1Map映射中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。代码如下:stringCollection.stream().map(String:toUpperCase).sorted(a,b)->b.compareTo(a)).forEach(System.out:printin);//"DDD2","DDD1","CCC","BBB3","BBB2","AAA2","AAA1"Map映射中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。代码如下:stringCollection.stream().map(String:toUpperCase).sorted((a,b)->b.compareTo(a)).forEach(System.out:printin);//"DDD2","DDD1","CCC","BBB3","BBB2","AAA2","AAA1"Match 匹配Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个boolean类型的值。代码如下:boolean anyStartsWithA=stringCollection.stream().anyMatch(s)->s.startsWith("a");System.out.printin(anystartsWithA);/∥true boolean allStartsWithA=stringCollection.stream().allMatch(s)->s.startsWith("a"));System.out.printin(allStartsWithA);//false boolean noneStartsWithZ=stringCollection.stream().noneMatch(s)->s.startsWith("z");System.out.printin(noneStartsWithZ);//trueCount计数计数是一个最终操作,返回Stream中元素的个数,返回值类型是long。代码如下:long startsWithB=stringCollection.stream().filter((s)->s.startsWith("b")).count();System.out.printin(startsWithB);//3Reduce规约这是一个最终操作,允许通过指定的函数来讲stream中的多个元素规约为一个元素,规越后的结果是通过Optional接口表示的:代码如下:Optional reduced=stringCollection.stream().sorted).reduce((51,52)->51+"#"+52);reduced.ifPresent(System.out::println);//"aaal#aaa2#bbb1#bbb2#bbb3#cCC#ddd1#ddd2"并行Streams前面提到过Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。下面的例子展示了是如何通过并行Stream来提升性能:首先我们创建一个没有重复元素的大表:代码如下:int max=1000000; List values=new Arraylist<>(max); for(inti=0;i< max;i++){ UUID uuid=UUID. randomUUID(); values. add(uuid. toString());}然后我们计算一下排序这个Stream要耗时多久,串行排序:代码如下:long to=System. nanoTime(); long count=values. stream(). sorted(). count(); System. out. println(count); long t1=System. nanoTime(); long millis =TimeUnit. NANOSECONDS. toMillis(t1-to); System. out. println(String. format("sequential sort took:%d ms", millis);∥串行耗时:899ms并行排序:代码如下:long to=System.nanoTime();long count=values.parallelStream().sorted).count();System.out.println(count);long t1=System.nanoTime();long mills=TimeUnit.NANOSECONDS.toMillis(t1-to);System.out.printin(String.format("parallel sort took:%d ms",millis));//并行排序耗时:472ms上面两个代码几乎是一样的,但是并行版的快了50%之多,唯一需要做的改动就是将stream()改为parallelStream()。Map前面提到过,Map类型不支持stream,不过Map提供了一些新的有用的方法来处理一些日常任务。代码如下:Map<Integer,String>map=new HashMap<>);for(inti=0;i<10;i++){map.putlfAbsent(i,"val"+i);}map.forEach(id,val)>System.out.println(val);以上代码很容易理解,putlfAbsent不需要我们做额外的存在性检查,而forEach则接收一个Consumer接口来对map里的每一个键值对进行操作。下面的例子展示了map上的其他有用的函数:代码如下:map. computelfPresent(3,(num, val)->val+num); map. get(3);//val33map. computelfPresent(9,(num, val)->nul); map. containsKey(9);//falsemap. computelfAbsent(23, num->"val"+num); map. containsKey(23);//true map. computelfAbsent(3, num->"bam"); map. get(3);//val33接下来展示如何在Map里删除一个键值全都匹配的项:代码如下:map.remove(3,"val3");map.get(3);//val33map.remove(3,"val33");map.get(3);/∥null另外一个有用的方法:代码如下:map.getOrDefault(42,"not found");//not found对Map的元素做合并也变得很容易了:代码如下:map.getOrDefault(42,"not found");//not found对Map的元素做合并也变得很容易了:代码如下:map.merge(9,"val9",(value,newValue)->value.concat(newValue));map.get(9);//val9map.merge(9,"concat",(value,newValue)->value.concat(newValue);map.get(9);//val9concat Merge做的事情是如果键名不存在则插入,否则则对原键对应的值做合并操作并重新插入到map中。九、Date API Java 8在包java.time下包含了一组全新的时间日期APl。新的日期APl和开源Joda-Time库差不多,但又不完全一样,下面的例子展示了这组新APl里最重要的一些部分:Clock时钟Clock类提供了访问当前日期和时间的方法,Clock是时区敏感的,可以用来取代System.currentTimeMils0来获取当前的微秒数。某一个特定的时间点也可以使用Instant类来表示,Instant类也可以用来创建老的java.util.Date对象。代码如下:Clock clock=Clock.systemDefaultZone();long millis =clock.mils();Instant instant=clock.instant();Date legacyDate=Date.from(instant);//legacy java.util.DateTimezones时区在新API中时区使用Zoneld来表示。时区可以很方便的使用静态方法of来获取到。时区定义了到UTS时间的时间差,在Instant时间点对象到本地日期对象之间转换的时候是极其重要的。代码如下:System.out.println(Zoneld.getAvailableZonelds());//prints all available timezone ids Zoneld zone1=Zoneld.of("Europe/Berlin");Zoneld zone2=Zoneld.of("Brazil/East");System.out.println(zone1.getRules();System.out.printin(zone2.getRules());LocalTime本地时间LocalTime 定义了一个没有时区信息的时间,例如晚上10点,或者17:30:15。下面的例子使用前面代码创建的时区创建了两个本地时间。之后比较时间并以小时和分钟为单位计算两个时间的时间差:代码如下:LocalTime now1=LocalTime.now(zone1);LocalTime now2=LocalTime.now(zone2);System.out.println(now1.isBefore(now2);//false long hoursBetween=ChronoUnit.HOURS.between(now1,now2);long minutesBetween=ChronoUnit.MINUTES.between(now1,now2);System.out.println(hoursBetween);//-3 System.out.printin(minutesBetween);//-239LocalTime 提供了多种工厂方法来简化对象的创建,包括解析时间字符串。代码如下:LocalTime late =LocalTime.of(23,59,59);System.out.println(late);//23:59:59DateTimeFormatter germanFormatter=Date TimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withLocale(Locale.GERMAN);LocalTime leetTime=LocalTime.parse("13:37",germanFormatter);System.out.printin(leetTime);//13:37LocalDate本地日期LocalDate表示了一个确切的日期,比如2014-03-11。该对象值是不可变的,用起来和LocalTime基本一致。下面的例子展示了如何给Date对象加减天/月/年。另外要注意的是这些对象是不可变的,操作返回的总是一个新实例。代码如下:LocalDate today=LocalDate.now();LocalDate tomorrow =today.plus(1,Chronounit.DAYS);LocalDate yesterday=tomorrow.minusDays(2);LocalDate independenceDay =LocalDate.of(2014,Month.JULY,4);DayofWeek dayofweek=independenceDay.getDayOfWeek();System.out.printin(dayofWeek);//FRIDAY 从字符串解析一个LocalDate类型和解析LocalTime一样简单:代码如下:DateTimeFormatter germanFormatter=Date TimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.GERMAN);LocalDate xmas =LocalDate.parse("24.12.2014",germanFormatter);System.out.println(xmas);//2014-12-24LocalDateTime 本地日期时间LocalDateTime同时表示了时间和日期,相当于前两节内容合并到一个对象上了。LocalDateTime和LocalTime还有LocalDate一样,都是不可变的。LocalDateTime提供了一些能访问具体字段的方法。代码如下:LocalDate Time sylvester=LocalDate Time.of(2014,Month.DECEMBER,31,23,59,59);DayofWeek dayofWeek=sylvester.getDayofWeek();System.out.println(dayOfWeek);//WEDNESDAY Month month=sylvester.getMonth();System.out.println(month);//DECEMBER long minuteOfDay=sylvester getLong(ChronoField.MINUTE_OF_DAY);System.out.println(minuteOfDay);//1439只要附加上时区信息,就可以将其转换为一个时间点Instant对象,Instant时间点对象可以很容易的转换为老式的java.util.Date。代码如下:Instant instant=sylvester.atZone(Zoneld.systemDefault().tolnstant();Date legacyDate=Date.from(instant);System.out.printin(legacyDate);//Wed Dec 31 23:59:59 CET 2014格式化LocalDateTime和格式化时间和日期一样的,除了使用预定义好的格式外,我们也可以自己定义格式:代码如下:DateTimeFormatter formatter=DateTimeFormatter.ofPattern("MMM dd,yyyy-HH:mm");LocalDateTime parsed=LocalDateTime.parse("Nov 03,2014-07:13",formatter);String string=formatter.format(parsed);System.out.println(string);//Nov 03,2014-07:13和java.text.NumberFormat不一样的是新版的DateTimeFormatter是不可变的,所以它是线程安全的。十、Annotation 注解在Java8中支持多重注解了,先看个例子来理解一下是什么意思。首先定义一个包装类Hints注解用来放置一组具体的Hint注解:代码如下:@interface Hints{Hint]value();}@Repeatable(Hints.class)@interface Hint{String value();}Java8允许我们把同一个类型的注解使用多次,只需要给该注解标注一下@Repeatable即可。例1:使用包装类当容器来存多个注解(老方法)代码如下:@Hints({@Hint("hint1"),@Hint("hint2")})class Person}例2:使用多重注解(新方法)代码如下:@Hint("hint1")@Hint("hint2")class Person}第二个例子里java编译器会隐性的帮你定义好@Hints注解,了解这一点有助于你用反射来获取这些信息:代码如下:Hint hint=Person. class. getAnnotation(Hint. class); System. out. println(hint);//null Hints hints1=Person. class. getAnnotation(Hints. class); System. out. printin(hints1. value(). length);//2Hint[]hints2=Person.class.getAnnotationsBy Type(Hint.class);System.out.printin(hints2.length);//2即便我们没有在Person类上定义@Hints注解,我们还是可以通过 getAnnotation(Hints.class)来获取@Hints注解,更加方便的方法是使用getAnnotationsBy Type 可以直接获取到所有的@Hint注解。另外ava8的注解还增加到两种新的target上了:代码如下:@Target{ElementType.TYPE_PARAMETER,ElementType.TYPE_USE})@interface MyAnnotation}关于Java8的新特性就写到这了,肯定还有更多的特性等待发掘。JDK1.8里还有很多很有用的东西,比如Arrays.parallelSort,StampedLock和CompletableFuture等等。
2022年03月07日
504 阅读
0 评论
7 点赞
2022-02-28
Google Nexus私服搭建
第一步骤:下载nexus私服软件nexus-2.12.0-01-bundle第二不:安装进入nexus解压目录进入到bin目录:1、安装:执行nexus.bat install 2、启动:nexus.bat start第三步:查看访问路径进入config目录,打开nexus.properties即可看到端口号,项目命令nexus。第四步:浏览器打开http://127.0.0.1:8081/nexus/默认登录账号:admin/admin123
2022年02月28日
299 阅读
0 评论
2 点赞
2022-02-28
JAVA map遍历
Map集合的2种循环方式。一种是通过map.entrySet()。另一种是map.keySet()。所有的集合都可以通过迭代器Iterator,进行遍历。package learn.javase; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * 重要 * 遍历map的两种方式map.entrySet() map.keySet() * @author Jole * */ public class MapDemo { public static void main(String[] args) { Map<Integer ,String > map = new HashMap(); map.put(1, "111"); map.put(2, "2222"); //方式一 entrySet() Set<Map.Entry <Integer, String>> sets = map.entrySet(); //获取值1,通过for for(Map.Entry <Integer, String> entry : sets) { System.out.println("ssss:"+entry); System.out.println("key:"+entry.getKey()+"value:"+entry.getValue()); } //获取值2,通过iterator Iterator <Map.Entry <Integer, String>> itt = sets.iterator(); while(itt.hasNext()) { Map.Entry <Integer, String> entrys = itt.next(); System.out.println(entrys.getKey()+"----"+entrys.getValue()); } System.out.println(map.put(3, "222")); //方式二 keySet() Set<Integer> set = map.keySet(); Iterator it = set.iterator(); while(it.hasNext()) { System.out.println("Iterator="+it.next()); System.out.println("key:"+map.get(it.next())); } for(Integer in : set) { System.out.println(in); } } }
2022年02月28日
303 阅读
0 评论
2 点赞
1
...
3
4
5
6