首页
归档
留言
广告合作
友链
美女主播
Search
1
博瑞GE车机升级/降级
5,146 阅读
2
Mac打印机设置黑白打印
4,517 阅读
3
修改elementUI中el-table树形结构图标
4,516 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,351 阅读
5
intelliJ Idea 2022.2.X破解
4,060 阅读
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Spring Cloud
Mac
mybatis
WordPress
Nacos
Spring Cloud Alibaba
Mybatis-Plus
jQuery
Java Script
asp.net
微信小程序
Sentinel
UniApp
MySQL
asp.net core
IntelliJ IDEA
Jpa
树莓派
Laughing
累计撰写
570
篇文章
累计收到
1,424
条评论
首页
栏目
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
广告合作
友链
美女主播
搜索到
33
篇与
的结果
2022-07-30
Java中Synchronized的用法
Synchronized是Java中的关键字,是一种同步锁。他修饰的对象有以下几种。修饰一个代码块被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象。修饰一个方法被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象。修饰一个静态方法其作用的范围是整个静态方法,作用的对象是这个类的所有对象。修饰一个类其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。修饰一个代码块当修饰一个代码块时,我们一定要记住,作用的对象是调用这个代码块的对象。我们下面通过一段代码看一下。public class SynchronizedThread implements Runnable { /** * 计数 */ private static int count; @Override public void run() { synchronized (this) { for (int i = 0; i < 5; i++) { count++; System.out.println(Thread.currentThread().getName() + ":" + count); } } } }这段代码很简单,就是对对类的一个静态变量进行计数。接下来我们看一下调用这段代码的地方。SynchronizedThread synchronizedThread = new SynchronizedThread(); Thread thread1 = new Thread(synchronizedThread,"Thread1"); Thread thread2 = new Thread(synchronizedThread,"Thread2"); thread1.start(); thread2.start();运行上面的代码,查看控制台的输出信息不论我们怎么运行,Thead1与Thread2都是顺序执行的,这正是我们期望的。接下来,我们对上面的代码稍微做一下改动,改动后如下Thread thread3 = new Thread(new SynchronizedThread(),"Thread3"); Thread thread4 = new Thread(new SynchronizedThread(),"Thread4"); thread3.start(); thread4.start();再次运行,查看控制台输出信息可以看到Thead3与Thread4不再是顺序执行。why?Synchronized修饰一个代码块时,作用的对象是调用这个代码块的对象,我们可以看到Thead3与Thread4中是new了一个新的对象,不再是同一个对象,所以Synchronized不再起作用。修饰一个方法修饰一个方法跟修饰一个代码块类似,只是作用域不同。我们继续修改SynchronizedThread,修改后如下:public class SynchronizedThread implements Runnable { /** * 计数 */ private static int count; @Override public void run() { increment(); } public synchronized void increment(){ for (int i = 0; i < 15; i++) { count++; System.out.println(Thread.currentThread().getName() + ":" + count); } } }再次调用上面的两次代码,会发现结果是一样的。修饰静态方法由于静态方法是属于类而不是属于对象,因此synchronized修饰的静态方法锁定的是这个类的所有对象。我们再次对上面的SynchronizedThread进行修改,将increment()方法改成静态方法public class SynchronizedThread implements Runnable { /** * 计数 */ private static int count; @Override public void run() { increment(); } public synchronized static void increment(){ for (int i = 0; i < 15; i++) { count++; System.out.println(Thread.currentThread().getName() + ":" + count); } } }我们再次通过以下代码进行调用Thread thread3 = new Thread(new SynchronizedThread(),"Thread3"); Thread thread4 = new Thread(new SynchronizedThread(),"Thread4"); thread3.start(); thread4.start();查看控制台,可以看到之前不是顺序执行的方法,现在也是按照线程顺序执行了修饰一个类我们再次对上面的SynchronizedThread进行修改public class SynchronizedThread implements Runnable { /** * 计数 */ private static int count; @Override public void run() { increment(); } public static void increment() { synchronized (SynchronizedThread.class) { for (int i = 0; i < 15; i++) { count++; System.out.println(Thread.currentThread().getName() + ":" + count); } } } }我们再次通过以下代码进行调用Thread thread3 = new Thread(new SynchronizedThread(),"Thread3"); Thread thread4 = new Thread(new SynchronizedThread(),"Thread4"); thread3.start(); thread4.start();查看控制台,现在也是按照线程顺序执行的。
2022年07月30日
944 阅读
0 评论
0 点赞
2021-12-19
Java故障诊断与性能优化-栈上分配
正常情况下,Java对象是存储到堆上的,为了优化系统性能,Java虚拟体提供了一种栈上分配的技术,栈上分配的基本思想是:对于那些线程私有的对象(不能被其他线程访问的对象),Java虚拟机将对象打散分配到栈上,而不是分配到堆上。之所以分配到栈上,是因为在函数调用结束后,可以将对象信息自行销毁,而不需要垃圾回收器介入。栈上分配技术基础是逃逸分析,逃逸分析的目的是检查对象的作用域是否有可能逃出函数体。能够逃逸的对象下面演示一个能够逃逸的对象,因为能够逃逸,所以是无法进行栈上分配的。public class Test{ public static class User{ public String name; public int age; public User(String name,int age){ this.name=name; this.age=age; } } public static User alloc(){ User user = new User("Test",30); user.name = "张三"; user.age=40; return user; } public static void main(String[] args) throws InterruptedException{ long startTime = System.currentTimeMillis(); User user = new User("Test",30); for(int i=0;i<100000000;i++){ user= alloc(); } System.out.println(user.age); long endTime = System.currentTimeMillis(); System.out.println(endTime - startTime); } }执行java -server -Xmx20m -Xms20m -XX:+PrintGC Test可以看到代码发生了大量的GC。不能逃逸的对象相同的代码,我们只是设置对象不能逃逸,进行一下改造。public class Test{ public static class User{ public String name; public int age; public User(String name,int age){ this.name=name; this.age=age; } } public static void alloc(){ User user = new User("Test",30); user.name = "张三"; user.age=40; } public static void main(String[] args) throws InterruptedException{ long startTime = System.currentTimeMillis(); for(int i=0;i<100000000;i++){ alloc(); } long endTime = System.currentTimeMillis(); System.out.println(endTime - startTime); } }执行java -server -Xmx20m -Xms20m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations Test输出可以看到并没有任何GC程序就执行完了。参数说明第二段启用逃逸分析时,我们参数明显是比第一段多了。-server:Server模式下,才可以启用逃逸分析。-XX:+DoEscapeAnalysis: 启用逃逸分析。-XX:-UseTLAB:TLAB全称是Thread Local Allocation Buffer,也就是线程级别的内存分配,每个线程独享一块内存,主要是针对Eden里的一块小内存,这样线程在分配内存的时候,因为这块小内存是这个线程独享的,因此不会出现锁竞争的问题,默认是开启该功能的,不建议关闭此功能,主要是保证内存分配的高效性-XX:+EliminateAllocations:开启标量替换(默认开启),运行将对象打散分配到栈上,比如我们上面User对象有id、name两个字段,那么这两个字段将会被视为两个独立的局部变量进行分配。
2021年12月19日
836 阅读
0 评论
0 点赞
2021-12-12
Spring Boot中ThreadLocal踩坑
ThreadLocal作用ThreadLocal的作用:用来存当前线程的局部变量,不同线程间互不干扰。拿完数据记得需要移除数据,不然JVM不会将ThreadLocal回收(可能还会被引用),多了就会出现内存泄漏的情况。解析ThreadLocal类ThreadLocal包含几个方法:public T get() { } public void set(T value) { } public void remove() { } protected T initialValue() { }T get()get()方法是用来获取ThreadLocal在当前线程中保存的变量副本set(T value)set()用来设置当前线程中变量的副本void remove()remove()用来移除当前线程中变量的副本T initialValue()initialValue()是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法。ThreadLocal变量污染ThreadLocal会在每个线程存储一个副本,但是如果我们使用的是比如Tomcat,Tomcat自身会维护一个线程池,线程结束后,并不会马上销毁,而是会重新进入线程池,下次有请求时,有可能会复用当前线程,如果我们每次使用ThreadLocal之前,没有进行Set(T value),那么就有可能导致不同线程之间变量污染,比如下面的代码@RestController @RequestMapping(value = "book") @Slf4j public class BookController { private static final ThreadLocal<String> THREAD_LOCAL_TEST = new ThreadLocal<>(); @Resource private IBookService bookService; /** * 构造函数 */ public BookController() { log.info("构造函数,此时bookService :" + bookService); } @PostConstruct public void init() { log.info("PostConstruct,此时bookService :" + bookService); } @PreDestroy public void destroy() { log.info("PreDestroy"); } @GetMapping(value = "set") public void set() { if (StringUtils.isEmpty(THREAD_LOCAL_TEST.get())) { THREAD_LOCAL_TEST.set(UUID.randomUUID().toString()); } } @GetMapping(value = "search") public List<Book> search() { log.error(THREAD_LOCAL_TEST.get()); return bookService.search(); } }使用jmeter进行请求,可以看到同一线程,输出的内容永远不会发生改变。可以在内次使用之前进行set(T value),但是set(T value)可能会导致内存无法释放。ThreadLocal可能导致的内存泄露ThreadLocal为了避免内存泄露,不仅使用了弱引用维护key,还会在每个操作上检查key是否被回收,进而再回收value。但是从中也可以看到,ThreadLocal并不能100%保证不发生内存泄漏。比如,很不幸的,你的get()方法总是访问固定几个一直存在的ThreadLocal,那么清理动作就不会执行,如果你没有机会调用set()和remove(),那么这个内存泄漏依然会发生。一个良好的习惯依然是:当你不需要这个ThreadLocal变量时,主动调用remove(),这样对整个系统是有好处的。@RestController @RequestMapping(value = "book") @Slf4j public class BookController { private static final ThreadLocal<String> THREAD_LOCAL_TEST = new ThreadLocal<>(); @Resource private IBookService bookService; /** * 构造函数 */ public BookController() { log.info("构造函数,此时bookService :" + bookService); } @PostConstruct public void init() { log.info("PostConstruct,此时bookService :" + bookService); } @PreDestroy public void destroy() { log.info("PreDestroy"); } @GetMapping(value = "set") public void set() { THREAD_LOCAL_TEST.set(UUID.randomUUID().toString()); } @GetMapping(value = "search") public List<Book> search() { log.error(THREAD_LOCAL_TEST.get()); THREAD_LOCAL_TEST.remove(); return bookService.search(); } }ThreadLocal与局部变量局部变量和ThreadLocal起到的作用是一样的,保证了并发环境下数据的安全性。那就是说,完全可以用局部变量来代替ThreadLocal咯?This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its {@code get} or {@code set} method) has its own, independently initialized copy of the variable. {@code ThreadLocal} instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).[typing]翻译过来[/typing] ThreadLocal提供的是一种线程局部变量。这些变量不同于其它变量的点在于每个线程在获取变量的时候,都拥有它自己相对独立的变量初始化拷贝。ThreadLocal的实例一般是私有静态的,可以做到与一个线程绑定某一种状态。所以就这段话而言,我们知道ThreadLocal不是为了满足多线程安全而开发出来的,因为局部变量已经足够安全。ThreadLocal是为了方便线程处理自己的某种状态。 可以看到ThreadLocal实例化所处的位置,是一个线程共有区域。好比一个银行和个人,我们可以把钱存在银行,也可以把钱存在家。存在家里的钱是局部变量,仅供个人使用;存在银行里的钱也不是说可以让别人随便使用,只有我们以个人身份去获取才能得到。所以说ThreadLocal封装的变量我们是在外面某个区域保存了处于我们个人的一个状态,只允许我们自己去访问和修改的状态。
2021年12月12日
1,852 阅读
0 评论
0 点赞
2021-12-05
@SpringBootApplication标注非引导类
一般情况下,@SpringBootApplication一般都是标注在项目引导类上。像下面这样:@Slf4j @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoConfiguration.class, args); } }但是@SpringBootApplication一定要标注到引导类上吗?答案是否定的。我们可以将@SpringBootApplication标注到任意的类上。比如我们增加以下类package net.xiangcaowuyu.demo.config; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoConfiguration { }然后改造引导类@Slf4j public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoConfiguration.class, args); } }再次运行项目,可以发现项目可以正常运行但是如果我们访问我们的接口,会发现提示404注意我们DemoConfiguration所在的包net.xiangcaowuyu.demo.config,@SpringBootApplication只会扫描当前包及下级包,所以,我们的接口它扫描不到,就提示404。如果希望我们其他的类被扫描到,我们就需要添加scanBasePackages属性。package net.xiangcaowuyu.demo.config; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication(scanBasePackages = {"net.xiangcaowuyu.demo"}) public class DemoConfiguration { }重启项目,在访问接口,发现能正常访问
2021年12月05日
947 阅读
0 评论
0 点赞
2021-12-05
理解SpringBoot 中的@AliasFor注解
感觉Spring Boot中的@AliasFor注解是一个既熟悉又陌生的注解。说熟悉,是因为我们经常使用的比如@Service、@RestController、@Repository甚至@SpringBootApplication中都有他们的身影。说陌生,是因为其实从来没有真正用过这个注解。@AliasFor注解有两个作用:定义一个注解中的两个属性互为别名。桥接其他注解的属性。一、定义一个注解中的两个属性互为别名。我们以ComponentScan注解为例,先来看下ComponentScan的定义@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Repeatable(ComponentScans.class) public @interface ComponentScan { @AliasFor("basePackages") String[] value() default {}; @AliasFor("value") String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class; ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT; String resourcePattern() default "**/*.class"; boolean useDefaultFilters() default true; ComponentScan.Filter[] includeFilters() default {}; ComponentScan.Filter[] excludeFilters() default {}; boolean lazyInit() default false; @Retention(RetentionPolicy.RUNTIME) @Target({}) public @interface Filter { FilterType type() default FilterType.ANNOTATION; @AliasFor("classes") Class<?>[] value() default {}; @AliasFor("value") Class<?>[] classes() default {}; String[] pattern() default {}; } }以上代码,我们关注点在于 @AliasFor("basePackages") String[] value() default {}; @AliasFor("value") String[] basePackages() default {};这段代码,代表着,在ComponentScan注解中,basePackages属性与value是一样的,可以相互调用。我们可以测试一下@ComponentScan(basePackages = {"net.xiangcaowuyu.demo"}) @Component @EnableAutoConfiguration @Slf4j public class DemoApplication { public static void main(String[] args) { ComponentScan componentScan = AnnotatedElementUtils.getMergedAnnotation(DemoApplication.class, ComponentScan.class); assert componentScan != null; System.out.println(Arrays.toString(componentScan.basePackages())); System.out.println(Arrays.toString(componentScan.value())); SpringApplication.run(DemoApplication.class, args); } }二、桥接其他注解的属性这次,我们看下@Service的代码@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Service { @AliasFor( annotation = Component.class ) String value() default ""; }可以看到,@Service将value属性,桥接到了@Component注解的value属性。@ComponentScan(basePackages = {"net.xiangcaowuyu.demo"}) @Component @EnableAutoConfiguration @Slf4j public class DemoApplication { public static void main(String[] args) { Component component = AnnotatedElementUtils.getMergedAnnotation(BookService.class, Component.class); assert component != null; System.out.println(component.value()); SpringApplication.run(DemoApplication.class, args); } }
2021年12月05日
1,103 阅读
0 评论
1 点赞
2021-10-23
java PreDestroy注解使用
被@PreDestroy修饰的方法会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法。被@PreDestroy修饰的方法会在destroy()方法之后运行,在Servlet被彻底卸载之前。比如我们可以通过此注解实现:程序停止时做一些清理工作。 @PreDestroy public void destroy(){ log.info("PreDestroy"); }当我们停止程序,会输出以下信息
2021年10月23日
899 阅读
0 评论
0 点赞
2021-10-22
java泛型反序列化
其实不管前后端交互还有API接口,我个人更倾向于使用实体(DTO、VO)啥的,但是,有时候也不得不用Map等进行数据交互。一般而言,前后端或者接口之间交互都是通过JSON进行的,而我们在使用Map的时候,一般都是使用的泛型类,而不是使用原始类型,比如Map<String,string>,那么我们来看下下面一段代码。Map<String,String> bookMap = new HashMap<>(); bookMap.put("author","张三"); bookMap.put("name","山海经"); String bookMapString = JSONObject.toJSONString(bookMap); log.info(bookMapString);我们创建一个Map<String,String>,并将其序列化成字符串,如果我们放过来,想将字符串在反序列化成Map,我们首先想到的可能就像下面这样。Map bookMapNew = JSONObject.parseObject(bookMapString); log.info(String.valueOf(bookMapNew));程序有错吗?当然没错,但是优雅嘛,肯定是不优雅。其实这个时候,idea会给我们一个提示Raw use of parameterized class 'Map',大概意思就是说这个类要使用泛型,如果不使用泛型,我们其实丢失了类型的安全性。这个时候,我们就想到了parseObject()的重载方法,通过第二个参数,传递进去类型,如果我们是普通的类,传递一个class进去是没问题的。比如我们改写一下上面反序列化的方法Map<String,String> bookMapNew = JSONObject.parseObject(bookMapString,Map.class);这个时候,idea又会给我们一个提示大意就是类型转换没有校验。那么有没有更加完美的方法呢,有的,那就是TypeReference类,我们继续改造代码如下Map<String,String> bookMapNew = JSONObject.parseObject(bookMapString,new TypeReference<Map<String,String>>(){});这回idea终于没有提示了运行一下,结果也是正确的
2021年10月22日
1,789 阅读
0 评论
1 点赞
2021-08-29
java8 Optional<T>简介
Optional<T>是Java8增加的一个用于处理NullPointException异常的类。Optional<T>是一个容器类,代表一个值存在或不存在。如果用过Spring Boot Data Jpa,会发现里面好多返回值都是Optional<T>类型的。下面我们介绍一下Optional<T>类的常见使用方法。作为演示,我们创建了一个Dish类,代码如下:public class Dish { /** * 构造函数 * @param name 名称 * @param isVegetarian 是否蔬菜 */ public Dish(String name, boolean isVegetarian) { this.name = name; this.isVegetarian = isVegetarian; } /** * 名称 */ private String name; /** * 是否蔬菜 */ private boolean isVegetarian; @Override public String toString(){ return name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isVegetarian() { return isVegetarian; } public void setVegetarian(boolean isVegetarian) { this.isVegetarian = isVegetarian; } } 创建一个Optional<T>实例创建Optional<T>实例,有两个方法,分别是of、ofNullable,ofNullable与是of的区别在于,如果传递的参数是null,那么会创建一个空的(empty)Optional<T>实例。 Optional<Dish> dishOptionalofNullable = Optional.ofNullable(null); dishOptionalofNullable.ifPresent(System.out::println); System.out.println("准备输出of方法的值"); Optional<Dish> dishOptionalof = Optional.of(null); dishOptionalof.ifPresent(System.out::println);运行代码,我们可以看到如下输出我们可以看到ofNullable创建的实例,即使传递的参数是null调用实例方法时,系统也会抛出异常,而of传递null值时,创建的Optional<T>实例也是null。判断是否为空通过isPresent()方法可以判断Optional<T>包含的类是否是null。返回true代表不是null,返回false代表是null。 Optional<Dish> emptyOptional = Optional.empty(); if (emptyOptional.isPresent()) { System.out.println(emptyOptional.get()); } else { System.out.println("empty optional"); }还有一个方法是ifPresent(Consumer<? super T> action)可以传递一个Consumer接口,当类不是null。 Optional<Dish> emptyOptional = Optional.empty(); emptyOptional.ifPresent(System.out::println);还有一个方法 ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction),当类不是null时,调用action接口,否则调用emptyAction接口。 List<Dish> menu = new ArrayList<>(); Dish fish = new Dish("鱼", false); Dish tomato = new Dish("西红柿", true); menu.add(fish); menu.add(tomato); menu.stream().filter(Dish::isVegetarian).findAny().ifPresentOrElse(System.out::println, () -> { System.out.println("没找到呢"); });获取值获取值比较简单,通过get()方法即可。 Optional<Dish> fishOptional = Optional.ofNullable(new Dish("鱼", false)); if(fishOptional.isPresent()){ System.out.println(fishOptional.get()); }通过orElse(T other)可以设置默认值。 Optional<Dish> fishOptional = Optional.ofNullable(new Dish("鱼", false)); Dish defaultDish = new Dish("default", false); if (fishOptional.isPresent()) { System.out.println(fishOptional.orElse(defaultDish)); } Optional<Dish> emptyOptional = Optional.empty(); System.out.println(emptyOptional.orElse(defaultDish));上面emptyOptional是空的,那么我们可以返回一个默认的Dish实例,下面看下控制台输出。orElse(T other)还有一些变种方法,大家可以试一下。
2021年08月29日
989 阅读
0 评论
0 点赞
2021-08-25
java中对象和Map的相互转换
实体对象转map集合private static Map<String, String> obj2Map(Object obj) { Map<String, String> map = new HashMap<String, String>(); // System.out.println(obj.getClass()); // 获取f对象对应类中的所有属性域 Field[] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { String varName = field.getName(); varName = varName.toLowerCase();//将key置为小写,默认为对象的属性 try { // 获取原来的访问控制权限 boolean accessFlag = field.isAccessible(); // 修改访问控制权限 field.setAccessible(true); // 获取在对象f中属性fields[i]对应的对象中的变量 Object o = field.get(obj); if (o != null) map.put(varName, o.toString()); // System.out.println("传入的对象中包含一个如下的变量:" + varName + " = " + o); // 恢复访问控制权限 field.setAccessible(accessFlag); } catch (IllegalArgumentException | IllegalAccessException ex) { ex.printStackTrace(); } } return map; }将map集合转化成实体对象利用反射实现/** * Map转成实体对象 * @param map map实体对象包含属性 * @param clazz 实体对象类型 * @return */ public static <T> T map2Object(Map<String, Object> map, Class<T> clazz) { if (map == null) { return null; } T obj = null; try { obj = clazz.newInstance(); Field[] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { int mod = field.getModifiers(); if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) { continue; } field.setAccessible(true); field.set(obj, map.get(field.getName())); } } catch (Exception e) { e.printStackTrace(); } return obj; }
2021年08月25日
884 阅读
0 评论
0 点赞
2021-08-21
java8谓词复合和函数复合
谓词及功能复合,就是在通过组合已有的谓词或功能,形成更加复杂的谓词或功能。谓词复合谓词(Predicate)接口包含三个方法:negate、and和or,通过这三个方法,我们可以在已有谓词的基础上,组合形成更加复杂的谓词。negate代表非,and代表和,or代表或。举个例子,我们有一个苹果(Apple)类,包含颜色(color)和重量(weight)两个属性。class Apple { public Apple(String color, double weight) { this.color = color; this.weight = weight; } private String color; private double weight; @Override public String toString() { return "color:" + color + "---------" + "weight:" + weight; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public double getWeight() { return weight; } public void setWeight(double weight) { this.weight = weight; } } 然后我们创建一个苹果的列表,存储几条测试数据。List<Apple> appleList = new ArrayList<>(); appleList.add(new Apple("red", 2)); appleList.add(new Apple("red", 6)); appleList.add(new Apple("green", 2)); appleList.add(new Apple("green", 6));negate测试我们创建一个查找红色苹果的谓词Predicate<Apple> redApple = apple -> "red".equals(apple.getColor());然后创建一个查找不是红色苹果的谓词List<Apple> notRedAppleList = appleList.stream().filter(notRedApple).collect(Collectors.toList()); for (Apple apple : notRedAppleList) { System.out.println(apple); }and测试我们首先创建一个查找重量大于5的谓词Predicate<Apple> weightThan5Apple = apple -> apple.getWeight() > 5;然后组合红苹果及重量大于5的两个谓词Predicate<Apple> redAndWeightThan5Apple = redApple.and(weightThan5Apple);测试 List<Apple> redAndWeightThan5AppleList = appleList.stream().filter(redAndWeightThan5Apple).collect(Collectors.toList()); for (Apple apple : redAndWeightThan5AppleList) { System.out.println(apple); }or测试还是利用刚才的谓词,这次我们测试重量大于5或者颜色是红色的苹果Predicate<Apple> redOrWeightThan5Apple = redApple.or(weightThan5Apple);测试 List<Apple> redOrWeightThan5AppleList = appleList.stream().filter(redOrWeightThan5Apple).collect(Collectors.toList()); for (Apple apple : redOrWeightThan5AppleList) { System.out.println(apple); }函数复合函数复合跟谓词复合类似,函数复合提供了两个接口方法:andThen和compose,通过字面含义我们可以知道andThen代表先执行第一个函数,然后在执行第二个函数,compose与之相反,先执行第二个函数,在执行第一个函数。 Function<Integer, Integer> funcPlus = x -> x + 1; Function<Integer, Integer> funcMul = y -> y * 2; Function<Integer, Integer> funcAndThen = funcPlus.andThen(funcMul); Function<Integer, Integer> funcCompose = funcPlus.compose(funcMul); System.out.println("andThen:" + funcAndThen.apply(1)); System.out.println("compose:" + funcCompose.apply(1));通过测试,我们发现andThen先执行了第一个函数,也就是1+1=2,然后执行2*2=4,所以最终结果就是4。compose限制性1*2=2,然后执行1+2=3,所以结果是3。
2021年08月21日
974 阅读
0 评论
0 点赞
2021-08-21
java8函数式接口Predicate、Consumer、Function
java8在java.util.function包中给我们预留了几个泛型函数式接口。Predicate、Consumer、Function。Predicate接口里面有一个test方法,接口一个泛型T对象并返回一个boolean值。 static <T> List<T> filter(List<T> list, Predicate<T> predicate) { List<T> result = new ArrayList<>(); for (T t : list) { if (predicate.test(t)) { result.add(t); } } return result; }传递一个String类型的列表,过滤掉其中为空的元素。 List<String> list = new ArrayList<>(); list.add(""); list.add("one"); List<String> result = filter(list,s->s.length()>0); for(String s:result){ System.out.println(s); }Consumer接口里面定义了一个accept方法,接受一个泛型T对象,没有返回值。 static <T> void consumer(List<T> list, Consumer<T> consumer) { for (T t : list) { consumer.accept(t); } }输出列表元素。 consumer(list, System.out::println);Function接口里面定义了一个apply方法,接受一个泛型T,并返回泛型R。static <T,R> List<R> func(List<T> list, Function<T,R> fun){ List<R> result = new ArrayList<>(); for(T t:list){ result.add(fun.apply(t)); } return result; }获取列表每个元素的长度。List<Integer> lengthList=func(list, String::length); for(Integer integer:lengthList){ System.out.println(integer); }
2021年08月21日
1,034 阅读
0 评论
2 点赞
2021-07-31
JVM故障诊断与性能优化之常用虚拟机参数
类加载子系统负责从文件系统或网络中加载Class信息。方法区存放类加载子系统加载的Class信息及运行时常量池信息,包括字符串的字面量和数字常量Java堆在虚拟机启动时建立,是Java程序最主要的内存工作区域,几乎所有的Java对象实例都放于Java堆中。堆空间是线程共享的。直接内存直接内存是在Java堆外直接向系统申请的内存空间,通常,访问直接内存的速度会优于Java堆。因此出于性能考虑,读写频繁的场合可能会考虑使用直接内存。直接内存在Java堆外,因此它的大小不会直接首先与Xmx指定的最大堆大小。垃圾回收系统垃圾回收器可以堆方法区、Java堆和直接内存进行回收。Java栈每个一Java虚拟机线程都有一个私有的Java栈,一个线程的Java栈在线程创建的时候被创建,Java栈中保存着局部变量、方法参数,同时和Java方法的调用、返回密切相关。本地方法栈与Java栈类似,最大的不同在于Java栈用于Java方法的调用,而本地方法栈用于本地方法的调用。作为Java虚拟机的重要扩展,Java虚拟机允许Java直接调用本地的方法(通常使用C编写)PC寄存器寄存器也是每个线程私有的空间,Java虚拟机会为每一个Java线程创建PC寄存器,在任意时刻,一个Java线程总是在执行一个方法,这个正在被执行的方法称为当前方法。如果当前方法不是本地方法,PC寄存器就会只想当前正在被执行的指令,如果当前方法是本地方法,那么PC寄存器的值就是undefined执行引擎负责执行虚拟机的字节码。常见Java虚拟机参数要诊断虚拟机,我们就要学习如何对Java虚拟机进行最基本的配置和跟踪。跟踪垃圾回收可以通过-XX:+PrintGC参数启动Java虚拟机,只要遇到GC,就会打印日志。System.out.println("##########准备申请1m内存#########"); byte[] bytes = new byte[1 * 1024 * 1024]; System.gc();GC和Full GC是垃圾回收的停顿类型,而不是区分是新生代还是年老代,如果有Full说明发生了Stop-The-World。如果是调用 System.gc()触发的,那么将显示的是Full GC (System) 4878K->1576K(249344K)表示GC 前该区域已使用容量 -> GC 后该区域已使用容量 (该区域内存总容量)0.0013118 secs表示GC所用时间,单位为秒。可以通过-XX:+PrintGCDetails参数来输出更加详细的参数。PSYoungGen:代表新生代。total 75776K, used 1951K代表新生代大小及已使用大小。[0x000000076b800000, 0x0000000770c80000, 0x00000007c0000000)代表新生代的下界、当前上界和上届。eden、from、to代表新生代的三个区,当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数-XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代。ParOldGen代表老年代
2021年07月31日
573 阅读
0 评论
0 点赞
1
2
3