关于java:Nginx-静态压缩和代码压缩实战大大提高网站访问速度

原文:juejin.cn/post/7101663374957608974 基于目前大部分的利用,都应用了前后端拆散的框架,vue的前端利用,也是非常的风行。不晓得大家有无遇到这样的问题: 随着前端框架的页面,性能开发一直的迭代;装置的依赖,一直的增多; 这样导致的问题,就是咱们的前端框架,会变得非常宏大,打包进去的dist目录会变得很大了喔!!! 这样就会导致一个问题:首次拜访利用,会变得很慢!!! 这的确是一个重大的问题!!!T_T 对于这个问题,咋们明天就从代码压缩+nginx动态资源压缩,两个方面进行钻研解决!!! nginx动态资源动静压缩 nginx开启gzip压缩server { listen 80; listen [::]:80; server_name localhost; server_tokens off; # 开启gzip压缩 gzip on; gzip_disable "msie6"; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_buffers 16 8k; gzip_http_version 1.1; gzip_types text/plain application/css text/css application/xml text/javascript application/javascript application/x-javascript; #llsydn 前端 location /jxbp { alias /opt/llsydn/jxbp; index index.html; try_files $uri $uri/ /dist/index.html; } }而后重启nginx:nginx -s reload 看看是否曾经失效!!! 压缩的效果图 没有压缩的效果图 从下面两张图能够看到: 第一张通过压缩:finish工夫为2.36s,transferred为7.6MB第二张没有压缩:finish工夫为6.32s,transferred为24.6MB很显著,通过了nginx的动态资源压缩,访问速度进步了,显著的!!! 看到这,咋们会有这样的一个问题,这压缩都是nginx干的活,那当访问量变大,那对于nginx的压力,是非常大的,毕竟都是动静压缩的!!! 那有无解决办法呢? nginx动态资源动静压缩的原理,无非就帮咱们将js、css等文件,压缩为一个.gz文件,而后传输给前端浏览器,进行解析。说到这,咋们不禁会说:那咋们先将js、css等文件,生成对应的.gz文件,不就不须要nginx动静压缩了嘛?如同是这么一回事!!! 蠢才呀!!! ...

May 10, 2023 · 1 min · jiezi

关于java:Java-Spark-RDD算子示例

Tranform(转换算子)mappackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function;import java.util.ArrayList;import java.util.List;/** * 将解决的数据逐条进行映射转换,这里的转换能够是类型的转换,也能够是指的转换 */public class MapRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("MapRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Integer> nums = new ArrayList<>(); nums.add(1); nums.add(2); nums.add(3); nums.add(4); JavaRDD<Integer> numsRDD = sc.parallelize(nums); JavaRDD<Integer> mapRDD = numsRDD.map(new Function<Integer, Integer>() { @Override public Integer call(Integer value) throws Exception { return value * 2; } }); mapRDD.collect().forEach(System.out::println); JavaRDD<String> fileRDD = sc.textFile("datas/apache.log"); JavaRDD<String> urlRDD = fileRDD.map(new Function<String, String>() { @Override public String call(String line) throws Exception { return line.split(" ")[6]; } }); urlRDD.collect().forEach(System.out::println); sc.stop(); }}mapPartitionspackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.FlatMapFunction;import org.apache.spark.api.java.function.Function;import java.util.ArrayList;import java.util.Iterator;import java.util.List;/** * 将解决的数据以分区为单位发送给计算节点进行解决,这里的解决是指能够进行任意的解决,哪怕是过滤数据 * * map和mapPartitions的区别? * 数据处理角度 * Map算子是分区内一个数据一个数据的执行,相似于串行操作。而mapPartitions算子是以分区为单位进行批处理操作 * * 性能的角度 * Map算子次要目标将数据源中的数据进行转换和扭转。然而不会缩小或增多数据。MapPartitions算子须要传递一个迭代器,返回一个迭代器,没有要求的元素的个数 * 放弃不变,所以能够减少或缩小数据 * * 性能角度 * Map算子因为相似于串行操作,所以性能比拟低,而mapPartitions算子相似于批处理,所以性能较高。然而mapPartitions算子会长工夫占用内存,那么这样会导致 * 内存可能不够用,呈现内存溢出的谬误。所以在内存无限的状况下,不举荐应用。应用map操作 */public class MapPartitionsRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("MapPartitionsRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Integer> nums = new ArrayList<>(); nums.add(1); nums.add(2); nums.add(3); nums.add(4); JavaRDD<Integer> numsRDD = sc.parallelize(nums, 2); JavaRDD<Integer> mapPartitionsRDD = numsRDD.mapPartitions(new FlatMapFunction<Iterator<Integer>, Integer>() { @Override public Iterator<Integer> call(Iterator<Integer> iterator) throws Exception { // 留神,这里只会打印两遍,为什么呢?是因为有两个分区,每个分区解决一次 System.out.println("xxxxxxxxxxx"); List<Integer> result = new ArrayList<>(); while (iterator.hasNext()) { Integer num = iterator.next(); result.add(num * 2); } return result.iterator(); } }); mapPartitionsRDD.collect().forEach(System.out::println); // 计算每个分区的最大值 JavaRDD<Integer> maxPartitionValueRDD = mapPartitionsRDD.mapPartitions(new FlatMapFunction<Iterator<Integer>, Integer>() { @Override public Iterator<Integer> call(Iterator<Integer> iterator) throws Exception { List<Integer> result = new ArrayList<>(); Integer maxValue = Integer.MIN_VALUE; while (iterator.hasNext()) { Integer value = iterator.next(); if (value > maxValue) { maxValue = value; } } result.add(maxValue); return result.iterator(); } }); maxPartitionValueRDD.collect().forEach(System.out::println); sc.stop(); }}mapPartitionsWithIndexpackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function;import org.apache.spark.api.java.function.Function2;import java.util.ArrayList;import java.util.Collections;import java.util.Iterator;import java.util.List;/** * 将解决的数据以分区为单位发送到计算节点进行解决,这里解决的是指能够进行任意的解决,哪怕是过滤数据,在解决时同时能够获取以后分区的索引 */public class MapPartitionsWithIndexRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("MapPartitionsWithIndexRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Integer> nums = new ArrayList<>(); nums.add(1); nums.add(2); nums.add(3); nums.add(4); JavaRDD<Integer> numsRDD = sc.parallelize(nums, 2); Function2 mpIndexFunction = new Function2<Integer, Iterator<Integer>, Iterator<Integer>>(){ @Override public Iterator<Integer> call(Integer index, Iterator<Integer> iterator) throws Exception { if(index == 0){ return iterator; } // 返回一个空的迭代器 return Collections.emptyIterator(); } }; // mapPartitionsWithIndex 的时候须要留神,preservesPartitioning是否保留 partitioner // 函数内部申明 JavaRDD mpRDD = numsRDD.mapPartitionsWithIndex(mpIndexFunction, true); mpRDD.collect().forEach(System.out::println); sc.stop(); }}flatMappackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.FlatMapFunction;import org.apache.spark.api.java.function.Function;import java.util.ArrayList;import java.util.Arrays;import java.util.Iterator;import java.util.List;/** * 将解决的数据进行扁平化后再进行映射解决,所以算子也称之为扁平映射,说白了其实就是能够一对多的输入 */public class FlatMapRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("FlatMapRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); JavaRDD<String> fileRDD = sc.textFile("datas/wc"); JavaRDD<String> wordRDD = fileRDD.flatMap(new FlatMapFunction<String, String>() { @Override public Iterator<String> call(String line) throws Exception { return Arrays.stream(line.split(" ")).iterator(); } }); wordRDD.collect().forEach(System.out::println); List<ArrayList<Integer>> nums = new ArrayList<>(); ArrayList<Integer> nums1 = new ArrayList<>(); nums1.add(1); nums1.add(2); nums.add(nums1); ArrayList<Integer> nums2 = new ArrayList<>(); nums2.add(3); nums2.add(4); nums.add(nums2); JavaRDD<ArrayList<Integer>> numsRDD = sc.parallelize(nums); JavaRDD<Integer> numsFlatMapRDD = numsRDD.flatMap(new FlatMapFunction<ArrayList<Integer>, Integer>() { @Override public Iterator<Integer> call(ArrayList<Integer> integers) throws Exception { return integers.iterator(); } }); numsFlatMapRDD.collect().forEach(System.out::println); sc.stop(); }}mapValuespackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function;import scala.Tuple2;import java.util.ArrayList;import java.util.List;/** * 只对value进行操作 */public class MapValuesRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("MapValuesRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Tuple2<String, Integer>> userInfos = new ArrayList<>(); userInfos.add(Tuple2.apply("Alice", 300)); userInfos.add(Tuple2.apply("zhangsan", 200)); userInfos.add(Tuple2.apply("lisi", 309)); userInfos.add(Tuple2.apply("wagnwu", 201)); userInfos.add(Tuple2.apply("mayun", 234)); userInfos.add(Tuple2.apply("haha", 223)); JavaPairRDD<String, Integer> userInfosRDD = sc.parallelizePairs(userInfos, 2); // 都涨薪100 JavaPairRDD<String, Integer> userInfosSalaryAdd100 = userInfosRDD.mapValues(new Function<Integer, Integer>() { @Override public Integer call(Integer v1) throws Exception { return v1 + 100; } }); userInfosSalaryAdd100.collect().forEach(System.out::println); sc.stop(); }}glompackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function;import java.util.ArrayList;import java.util.Collections;import java.util.List;/** * 将同一个分区的数据间接转换为雷同类型的内存数组进行解决,分区不变 */public class GlomRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("GlomRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Integer> nums = new ArrayList<>(); nums.add(1); nums.add(2); nums.add(3); nums.add(4); JavaRDD<Integer> numsRDD = sc.parallelize(nums, 2); JavaRDD<List<Integer>> glomRDD = numsRDD.glom(); JavaRDD<Integer> mapRDD = glomRDD.map(new Function<List<Integer>, Integer>() { @Override public Integer call(List<Integer> nums) throws Exception { return Collections.max(nums); } }); List<Integer> resultList = mapRDD.collect(); Integer result = resultList.stream().reduce(Integer::sum).orElse(0); System.out.println(result); sc.stop(); }}groupBypackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function;import org.apache.spark.api.java.function.PairFunction;import scala.Tuple2;import java.util.ArrayList;import java.util.Collection;import java.util.List;/** * reduceByKey和groupByKey的区别? * 从shuffle角度 : reduceByKey和groupByKey都存在shuffle操作,然而reduceByKey能够在shuffle前对分区内雷同的key进行预聚合(combine)性能, * 这样会缩小落盘的数据量,而groupByKey只是进行分组,不存在数据量缩小的问题,reduceByKey性能比拟高 * * 从性能角度: reduceByKey其实蕴含分区和聚合的性能。GroupByKey只能分组,不能聚合,所以分组聚合场景下,举荐应用reduceByKey,如果仅仅是分组而 * 不须要聚合。那么还是只能应用reduceByKey */public class GroupByKeyRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("GroupByKeyRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<String> words = new ArrayList<>(); words.add("Hello"); words.add("Spark"); words.add("Spark"); words.add("World"); JavaRDD<String> wordsRDD = sc.parallelize(words); JavaPairRDD<String, Integer> wordToPairRDD = wordsRDD.mapToPair(new PairFunction<String, String, Integer>() { @Override public Tuple2<String, Integer> call(String word) throws Exception { return Tuple2.apply(word, 1); } }); JavaPairRDD<String, Iterable<Integer>> wordGroupByRDD = wordToPairRDD.groupByKey(); JavaPairRDD<String, Integer> wordCountRDD = wordGroupByRDD.mapValues(new Function<Iterable<Integer>, Integer>() { @Override public Integer call(Iterable<Integer> iterable) throws Exception { return ((Collection<?>) iterable).size(); } }); wordCountRDD.collect().forEach(System.out::println); sc.stop(); }}filterpackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function;import org.apache.spark.api.java.function.PairFunction;import scala.Tuple2;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Collection;import java.util.Date;import java.util.List;/** * 将数据依据指定的规定进行筛选过滤,合乎规定的数据保留,不合乎规定的数据抛弃。当数据进行筛选过滤过,分区不变,然而分区内的数据可能不平衡 * 生成环境下,可能会呈现数据歪斜,所以个别filter之后能够repartition */public class FilterRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("FilterRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); JavaRDD<String> logFileRDD = sc.textFile("datas/apache.log"); JavaRDD<String> filterRDD = logFileRDD.filter(new Function<String, Boolean>() { @Override public Boolean call(String value) throws Exception { return value.contains("7/05/2015"); } }); JavaRDD<String> mapRDD = filterRDD.map(new Function<String, String>() { @Override public String call(String value) throws Exception { String[] fields = value.split(" "); return fields[6]; } }); mapRDD.collect().forEach(System.out::println); sc.stop(); }}samplepackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function;import java.util.ArrayList;import java.util.List;/** * 其实次要查看一下数据的散布 */public class SampleRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("SampleRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Integer> nums = new ArrayList<>(); nums.add(1); nums.add(2); nums.add(3); nums.add(4); JavaRDD<Integer> numsRDD = sc.parallelize(nums); /** * 第一个参数 : 抽取的数据是否放回,false : 不放回,true : 放回 * 第二个参数 : 抽取的几率,范畴在[0,1]之间,抽取呈现的概率,大于1,反复几率 * 第三个参数 : 随机种子 */ JavaRDD<Integer> sampleRDD1 = numsRDD.sample(false, 0.5); JavaRDD<Integer> sampleRDD2 = numsRDD.sample(true, 3); sampleRDD1.collect().forEach(System.out::println); System.out.println("**************************"); sampleRDD2.collect().forEach(System.out::println); sc.stop(); }}distinctpackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import java.util.ArrayList;import java.util.List;/** * 将数据集中反复的数据去重 */public class DistinctRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("DistinctRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Integer> nums = new ArrayList<>(); nums.add(1); nums.add(1); nums.add(2); nums.add(3); nums.add(3); nums.add(1); JavaRDD<Integer> numsRDD = sc.parallelize(nums, 2); JavaRDD<Integer> distinctRDD = numsRDD.distinct(2); distinctRDD.collect().forEach(System.out::println); sc.stop(); }}coalescepackage com.journey.core.rdd.transform;import com.clearspring.analytics.util.Lists;import org.apache.commons.collections.IteratorUtils;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.FlatMapFunction;import java.util.ArrayList;import java.util.Iterator;import java.util.List;/** * 依据数据量缩减分区,用于大数据集过滤后,进步小数据集的执行效率 * 当Spark程序中,存在过多的小工作的时候,能够通过coalesce办法,缩减合并分区,缩小分区的个数,缩小任务调度老本 */public class CoalesceRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("CoalesceRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Integer> nums = new ArrayList<>(); nums.add(1); nums.add(2); nums.add(3); nums.add(4); nums.add(5); nums.add(6); JavaRDD<Integer> numsRDD = sc.parallelize(nums, 6); /** * coalesce其实须要留神一点,就是默认shuffle为false,也就是在缩减分区的时候,是进行分区的合并的 * coalesce 在不shuffle的状况下,不能减少分区 */ JavaRDD<Integer> coalesceRDD = numsRDD.coalesce(2); coalesceRDD.saveAsTextFile("datas/output"); sc.stop(); }}repartitionpackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import java.util.ArrayList;import java.util.List;/** * 该操作外部其实执行的是coalesce操作,参数shuffle的默认值为true。无论是将分区数多的RDD转换为分区少的RDD,还是将分区少的RDD * 转换为分区多的RDD,repartition都能够实现,因为无论如何都会通过shuffle过程 */public class RepartitionRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("RepartitionRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Integer> nums = new ArrayList<>(); nums.add(1); nums.add(2); nums.add(3); nums.add(4); nums.add(5); nums.add(6); JavaRDD<Integer> numsRDD = sc.parallelize(nums, 6); JavaRDD<Integer> coalesceRDD = numsRDD.repartition(10); coalesceRDD.saveAsTextFile("datas/output"); sc.stop(); }}intersection & union & subtract & zippackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function;import java.util.ArrayList;import java.util.List;/** * 该操作用于排序数据。在排序之前,能够将数据通过f函数进行解决,之后依照f函数解决的后果进行排序,默认是升序排序。排序后新产生的RDD的分区数 * 与原RDD分区数始终。两头存在shuffle的过程 */public class IntersectionRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("SortByRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Integer> nums1 = new ArrayList<>(); nums1.add(1); nums1.add(2); nums1.add(3); nums1.add(4); List<Integer> nums2 = new ArrayList<>(); nums2.add(3); nums2.add(4); nums2.add(5); nums2.add(6); List<String> nums3 = new ArrayList<>(); nums3.add("3"); JavaRDD<Integer> nums1RDD = sc.parallelize(nums1,1); JavaRDD<Integer> nums2RDD = sc.parallelize(nums2,1); // 必须雷同类型 JavaRDD<Integer> intersectionRDD = nums1RDD.intersection(nums2RDD); JavaRDD<Integer> unionRDD = nums1RDD.union(nums2RDD); // 必须雷同类型 JavaRDD<Integer> subtractRDD = nums1RDD.subtract(nums2RDD); // 必须雷同类型,雷同分区个数 JavaPairRDD<Integer, Integer> zipRDD = nums1RDD.zip(nums2RDD); intersectionRDD.collect().forEach(System.out::println); System.out.println("******************************"); unionRDD.collect().forEach(System.out::println); System.out.println("******************************"); subtractRDD.collect().forEach(System.out::println); System.out.println("******************************"); zipRDD.collect().forEach(System.out::println); sc.stop(); }}partitionBypackage com.journey.core.rdd.transform;import org.apache.spark.Partitioner;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function;import scala.Tuple2;import java.util.ArrayList;import java.util.List;/** * 将数据依照指定Partitioner从新进行分区。Spark默认的分区器是HashPartitioner */public class PartitionerByRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("PartitionerByRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Tuple2<String, String>> infos = new ArrayList<>(); infos.add(Tuple2.apply("1305261989234", "zhangsan")); infos.add(Tuple2.apply("1505261989234", "lisi")); infos.add(Tuple2.apply("1305261982343", "wagnwu")); infos.add(Tuple2.apply("1505261382343", "zhaoliu")); // 将130结尾的放入一个分区,将150结尾放入一个分区中 // TODO 留神,如果是pairs,须要调用的是parallelizePairs JavaPairRDD<String, String> infosRDD = sc.parallelizePairs(infos, 2); JavaPairRDD<String, String> partitionByRDD = infosRDD.partitionBy(new Partitioner() { @Override public int numPartitions() { return 2; } @Override public int getPartition(Object key) { String item = key.toString(); if (item.startsWith("130")) { return 0; } else if (item.startsWith("150")) { return 1; } return 0; } }); partitionByRDD.collect().forEach(System.out::println); sc.stop(); }}reduceByKeypackage com.journey.core.rdd.transform;import org.apache.spark.Partitioner;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function2;import org.apache.spark.api.java.function.PairFunction;import scala.Tuple2;import java.util.ArrayList;import java.util.List;/** * 能够将雷同的key对应的value进行聚合 */public class ReduceByKeyRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("ReduceByKeyRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<String> words = new ArrayList<>(); words.add("Hello"); words.add("Spark"); words.add("Spark"); words.add("World"); JavaRDD<String> wordsRDD = sc.parallelize(words); JavaPairRDD<String, Integer> wordToPairRDD = wordsRDD.mapToPair(new PairFunction<String, String, Integer>() { @Override public Tuple2<String, Integer> call(String word) throws Exception { return Tuple2.apply(word, 1); } }); JavaPairRDD<String, Integer> wordCountRDD = wordToPairRDD.reduceByKey(new Function2<Integer, Integer, Integer>() { @Override public Integer call(Integer v1, Integer v2) throws Exception { return v1 + v2; } }); wordCountRDD.collect().forEach(System.out::println); sc.stop(); }}groupByKeypackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function;import org.apache.spark.api.java.function.PairFunction;import scala.Tuple2;import java.util.ArrayList;import java.util.Collection;import java.util.List;/** * reduceByKey和groupByKey的区别? * 从shuffle角度 : reduceByKey和groupByKey都存在shuffle操作,然而reduceByKey能够在shuffle前对分区内雷同的key进行预聚合(combine)性能, * 这样会缩小落盘的数据量,而groupByKey只是进行分组,不存在数据量缩小的问题,reduceByKey性能比拟高 * * 从性能角度: reduceByKey其实蕴含分区和聚合的性能。GroupByKey只能分组,不能聚合,所以分组聚合场景下,举荐应用reduceByKey,如果仅仅是分组而 * 不须要聚合。那么还是只能应用reduceByKey */public class GroupByKeyRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("GroupByKeyRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<String> words = new ArrayList<>(); words.add("Hello"); words.add("Spark"); words.add("Spark"); words.add("World"); JavaRDD<String> wordsRDD = sc.parallelize(words); JavaPairRDD<String, Integer> wordToPairRDD = wordsRDD.mapToPair(new PairFunction<String, String, Integer>() { @Override public Tuple2<String, Integer> call(String word) throws Exception { return Tuple2.apply(word, 1); } }); JavaPairRDD<String, Iterable<Integer>> wordGroupByRDD = wordToPairRDD.groupByKey(); JavaPairRDD<String, Integer> wordCountRDD = wordGroupByRDD.mapValues(new Function<Iterable<Integer>, Integer>() { @Override public Integer call(Iterable<Integer> iterable) throws Exception { return ((Collection<?>) iterable).size(); } }); wordCountRDD.collect().forEach(System.out::println); sc.stop(); }}aggregateByKeypackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function;import org.apache.spark.api.java.function.Function2;import org.apache.spark.api.java.function.PairFunction;import scala.Tuple2;import java.util.ArrayList;import java.util.Collection;import java.util.List;/** * 第一个参数示意初始值 * 第二个参数分区内的计算规定 * 第三个参数分区间的计算规定 */public class AggregateByKeyRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("AggregateByKeyRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Tuple2<String, Integer>> words = new ArrayList<>(); words.add(Tuple2.apply("Hello", 3)); words.add(Tuple2.apply("Spark", 2)); words.add(Tuple2.apply("Hello", 10)); words.add(Tuple2.apply("Spark", 17)); JavaPairRDD<String, Integer> wordsRDD = sc.parallelizePairs(words, 2); // aggregateByKey 的初始值只会参加分区内的计算 JavaPairRDD<String, Integer> aggregateByKeyRDD = wordsRDD.aggregateByKey(10, new Function2<Integer, Integer, Integer>() { @Override public Integer call(Integer v1, Integer v2) throws Exception { return v1 + v2; } }, new Function2<Integer, Integer, Integer>() { @Override public Integer call(Integer v1, Integer v2) throws Exception { return v1 + v2; } }); aggregateByKeyRDD.collect().forEach(System.out::println); // aggregateByKey 的初始值只会参加分区内的计算 JavaPairRDD<String, Integer> aggregateByKeyRDD2 = wordsRDD.aggregateByKey(10, new Function2<Integer, Integer, Integer>() { @Override public Integer call(Integer v1, Integer v2) throws Exception { // 分区内计算最大值 return Math.max(v1, v2); } }, new Function2<Integer, Integer, Integer>() { @Override public Integer call(Integer v1, Integer v2) throws Exception { return v1 + v2; } }); aggregateByKeyRDD2.collect().forEach(System.out::println); sc.stop(); }}foldByKeypackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function2;import scala.Tuple2;import java.util.ArrayList;import java.util.List;/** * 第一个参数示意初始值 * 第二个参数示意分区内和分区间的计算规定,雷同 */public class FoldByKeyRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("FoldByKeyRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Tuple2<String, Integer>> words = new ArrayList<>(); words.add(Tuple2.apply("Hello", 3)); words.add(Tuple2.apply("Spark", 2)); words.add(Tuple2.apply("Hello", 10)); words.add(Tuple2.apply("Spark", 17)); JavaPairRDD<String, Integer> wordsRDD = sc.parallelizePairs(words, 2); JavaPairRDD<String, Integer> foldByKeyRDD = wordsRDD.foldByKey(10, new Function2<Integer, Integer, Integer>() { @Override public Integer call(Integer v1, Integer v2) throws Exception { return v1 + v2; } }); foldByKeyRDD.collect().forEach(System.out::println); sc.stop(); }}combineByKeypackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function;import org.apache.spark.api.java.function.Function2;import scala.Tuple2;import java.util.ArrayList;import java.util.List;/** * 求平均数 * 第一个参数只做数据的转换 * 第二个参数分区内的计算 * 第三个参数分区间的计算 * * reduceByKey : 雷同key的第一个数据不过程任何计算,分区内和分区间计算规定雷同 * foldByKey : 雷同key的第一个数据和初始值进行分区内计算,分区内和分区间计算规定雷同 * aggregateByKey : 雷同key的第一个数据和初始值进行分区内计算,分区内和分区间计算规定能够不雷同 * combineByKey : 当计算时,发现数据结构不满足时,能够让第一个数据转换构造。分区内和分区间计算规定能够不雷同 */public class CombineByKeyRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("CombineByKeyRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Tuple2<String, Integer>> words = new ArrayList<>(); words.add(Tuple2.apply("Hello", 3)); words.add(Tuple2.apply("Spark", 2)); words.add(Tuple2.apply("Hello", 3)); words.add(Tuple2.apply("Spark", 2)); words.add(Tuple2.apply("Spark", 2)); words.add(Tuple2.apply("Spark", 2)); JavaPairRDD<String, Integer> wordsRDD = sc.parallelizePairs(words, 2); JavaPairRDD<String, Tuple2<Integer, Integer>> combineByKeyRDD = wordsRDD.combineByKey(new Function<Integer, Tuple2<Integer, Integer>>() { @Override public Tuple2<Integer, Integer> call(Integer v1) throws Exception { return Tuple2.apply(v1, 1); } }, new Function2<Tuple2<Integer, Integer>, Integer, Tuple2<Integer, Integer>>() { @Override public Tuple2<Integer, Integer> call(Tuple2<Integer, Integer> v1, Integer v2) throws Exception { return Tuple2.apply(v1._1 + v2, v1._2 + 1); } }, new Function2<Tuple2<Integer, Integer>, Tuple2<Integer, Integer>, Tuple2<Integer, Integer>>() { @Override public Tuple2<Integer, Integer> call(Tuple2<Integer, Integer> v1, Tuple2<Integer, Integer> v2) throws Exception { return Tuple2.apply(v1._1 + v2._1, v1._2 + v2._2); } }); combineByKeyRDD.collect().forEach(t -> { String key = t._1; Tuple2<Integer, Integer> tuple = t._2; System.out.println(key + ":" + tuple._1 / tuple._2); }); JavaPairRDD<String, Integer> wordCountRDD = wordsRDD.combineByKey(new Function<Integer, Integer>() { @Override public Integer call(Integer v1) throws Exception { return v1; } }, new Function2<Integer, Integer, Integer>() { @Override public Integer call(Integer v1, Integer v2) throws Exception { return v1 + v2; } }, new Function2<Integer, Integer, Integer>() { @Override public Integer call(Integer v1, Integer v2) throws Exception { return v1 + v2; } }); wordCountRDD.collect().forEach(System.out::println); sc.stop(); }}sortByKeypackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.Function;import org.apache.spark.api.java.function.Function2;import scala.Tuple2;import java.util.ArrayList;import java.util.List;/** * 对key进行排序 */public class SortByKeyRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("CombineByKeyRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Tuple2<String, Integer>> words = new ArrayList<>(); words.add(Tuple2.apply("Alice", 3)); words.add(Tuple2.apply("zhangsan", 2)); words.add(Tuple2.apply("lisi", 3)); words.add(Tuple2.apply("wagnwu", 2)); words.add(Tuple2.apply("mayun", 2)); words.add(Tuple2.apply("haha", 2)); JavaPairRDD<String, Integer> wordsRDD = sc.parallelizePairs(words, 2); // 默认是升序,能够指定降序排序,也能够指定自定义排序规定 JavaPairRDD<String, Integer> sortWordsRDD = wordsRDD.sortByKey(true); sortWordsRDD.collect().forEach(System.out::println); sc.stop(); }}join & leftOuterJoinpackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.Optional;import scala.Tuple2;import java.util.ArrayList;import java.util.List;/** * 在类型为(K,V)和(K,W)的RDD上调用,返回一个雷同key对应的所有元素连贯在一起的(K,(V,W))的RDD */public class JoinRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("JoinRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Tuple2<Integer, String>> userInfos = new ArrayList<>(); userInfos.add(Tuple2.apply(1, "zhagnsan")); userInfos.add(Tuple2.apply(2, "lisi")); userInfos.add(Tuple2.apply(3, "lisi")); List<Tuple2<Integer, String>> orders = new ArrayList<>(); orders.add(Tuple2.apply(1, "iphone pad")); orders.add(Tuple2.apply(1, "mac pad")); orders.add(Tuple2.apply(2, "java book")); JavaPairRDD<Integer, String> userInfosRDD = sc.parallelizePairs(userInfos, 2); JavaPairRDD<Integer, String> ordersRDD = sc.parallelizePairs(orders, 2); JavaPairRDD<Integer, Tuple2<String, String>> joinRDD = userInfosRDD.join(ordersRDD); joinRDD.collect().forEach(System.out::println); // 左连贯,就是右边都显示,左边没有为empty JavaPairRDD<Integer, Tuple2<String, Optional<String>>> leftOuterJoinRDD = userInfosRDD.leftOuterJoin(ordersRDD); leftOuterJoinRDD.collect().forEach(System.out::println); sc.stop(); }}cogrouppackage com.journey.core.rdd.transform;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.Optional;import scala.Tuple2;import java.util.ArrayList;import java.util.List;/** * 雷同的key汇聚合在一起,value是一个汇合 */public class CogroupRDD { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("CogroupRDD") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); List<Tuple2<Integer, String>> userInfos = new ArrayList<>(); userInfos.add(Tuple2.apply(1, "zhagnsan")); userInfos.add(Tuple2.apply(2, "lisi")); userInfos.add(Tuple2.apply(3, "lisi")); List<Tuple2<Integer, String>> orders = new ArrayList<>(); orders.add(Tuple2.apply(1, "iphone pad")); orders.add(Tuple2.apply(1, "mac pad")); orders.add(Tuple2.apply(2, "java book")); JavaPairRDD<Integer, String> userInfosRDD = sc.parallelizePairs(userInfos, 2); JavaPairRDD<Integer, String> ordersRDD = sc.parallelizePairs(orders, 2); JavaPairRDD<Integer, Tuple2<Iterable<String>, Iterable<String>>> cogroupRDD = userInfosRDD.cogroup(ordersRDD); cogroupRDD.collect().forEach(System.out::println); sc.stop(); }}Top N 案例package com.journey.core.rdd.transform;import org.apache.commons.collections.IteratorUtils;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.Optional;import org.apache.spark.api.java.function.Function;import org.apache.spark.api.java.function.Function2;import org.apache.spark.api.java.function.PairFunction;import org.apache.spark.api.java.function.VoidFunction;import scala.Tuple2;import scala.Tuple3;import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.Iterator;import java.util.List;/** * Serialization stack: * - object not serializable (class: java.util.ArrayList$SubList, value: [(16,26), (26,25), (1,23)]) * - field (class: scala.Tuple2, name: _2, type: class java.lang.Object) * - object (class scala.Tuple2, (7,[(16,26), (26,25), (1,23)])) * - element of array (index: 0) * - array (class [Lscala.Tuple2;, size 5) * at org.apache.spark.serializer.SerializationDebugger$.improveException(SerializationDebugger.scala:41) * at org.apache.spark.serializer.JavaSerializationStream.writeObject(JavaSerializer.scala:47) * at org.apache.spark.serializer.JavaSerializerInstance.serialize(JavaSerializer.scala:101) * at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:489) * at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) * at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) * at java.base/java.lang.Thread.run(Thread.java:835) * 23/05/09 20:29:01 ERROR Executor: Exception in task 0.0 in stage 2.0 (TID 4) * java.io.NotSerializableException: java.util.ArrayList$SubList * Serialization stack: * * 解决之法 : * It's because, List returned by subList() method is an instance of 'RandomAccessSubList' which is not serializable. * Therefore you need to create a new ArrayList object from the list returned by the subList(). */public class Demo { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("Demo") .setMaster("local[*]"); JavaSparkContext sc = new JavaSparkContext(conf); JavaRDD<String> logRDD = sc.textFile("datas/agent.log"); JavaPairRDD<Tuple2<String, String>, Integer> proviceAdRDD = logRDD.mapToPair(new PairFunction<String, Tuple2<String, String>, Integer>() { @Override public Tuple2<Tuple2<String, String>, Integer> call(String line) throws Exception { String[] fields = line.split(" "); String provice = fields[1]; String ad = fields[4]; return Tuple2.apply(Tuple2.apply(provice, ad), 1); } }); JavaPairRDD<Tuple2<String, String>, Integer> proviceAdToCountRDD = proviceAdRDD.reduceByKey(new Function2<Integer, Integer, Integer>() { @Override public Integer call(Integer v1, Integer v2) throws Exception { return v1 + v2; } }); JavaPairRDD<String, Tuple2<String, Integer>> proviceToAdCountRDD = proviceAdToCountRDD.mapToPair(new PairFunction<Tuple2<Tuple2<String, String>, Integer>, String, Tuple2<String, Integer>>() { @Override public Tuple2<String, Tuple2<String, Integer>> call(Tuple2<Tuple2<String, String>, Integer> value) throws Exception { return Tuple2.apply(value._1._1, Tuple2.apply(value._1._2, value._2)); } }); JavaPairRDD<String, Iterable<Tuple2<String, Integer>>> proviceToAdGroupRDD = proviceToAdCountRDD.groupByKey(); // 在分组内进行排序,取分组内的 top N JavaPairRDD<String , Iterable<Tuple2<String , Integer>>> proviceToAdTop3RDD = proviceToAdGroupRDD.mapToPair(new PairFunction<Tuple2<String, Iterable<Tuple2<String, Integer>>>, String, Iterable<Tuple2<String, Integer>>>() { @Override public Tuple2<String, Iterable<Tuple2<String, Integer>>> call(Tuple2<String, Iterable<Tuple2<String, Integer>>> iterable) throws Exception { List<Tuple2<String, Integer>> result = IteratorUtils.toList(iterable._2.iterator()); Collections.sort(result, new Comparator<Tuple2<String, Integer>>() { @Override public int compare(Tuple2<String, Integer> o1, Tuple2<String, Integer> o2) { return o2._2 - o1._2; } }); // 肯定要次要,这里须要的是new ArrayList<>(result.subList(0, 3)),封装一下 return Tuple2.apply(iterable._1, new ArrayList<>(result.subList(0, 3))); } });// proviceToAdTop3RDD.foreach(new VoidFunction<Tuple2<String, Iterable<Tuple2<String, Integer>>>>() {// @Override// public void call(Tuple2<String, Iterable<Tuple2<String, Integer>>> stringIterableTuple2) throws Exception {// System.out.println(stringIterableTuple2);// }// }); proviceToAdTop3RDD.collect().forEach(System.out::println); sc.stop(); }}

May 9, 2023 · 13 min · jiezi

关于java:Mybatis的log机制

Mybatis的一个十分有用的附加性能就是能帮忙咱们打印执行过程中的log:sql语句、参数、执行后果等。这一个性在开发过程中十分有用,能够帮忙咱们疾速高效定位开发过程中的问题。 明天咱们从源码的角度钻研一下Mybatis的log机制,次要包含两局部: Mybatis的日志框架日志打印源码剖析,也就是找到Mybatis源码中打印日志的中央日志框架#Log接口及其实现目前java世界曾经有如下十分优良的日志框架: Slf4jCommonsLogLog4J2Log4JJdkLogMybatis当然不会本人再实现一套日志框架,所谓Mybatis的日志框架,指的是Mybatis针对日志解决的外部实现,从而非常简单不便的反对上述各种支流日志框架。 首先,Mybatis定义了一个Log接口: public interface Log { boolean isDebugEnabled(); boolean isTraceEnabled(); void error(String s, Throwable e); void error(String s); void debug(String s); void trace(String s); void warn(String s);}Log接口定义了4种log级别,和下面提到的Java支流log框架的日志级别对应。 Log接口有以下实现: 咱们能够看到,除了下面提到的针对java支流log框架的实现之外,Mybatis还提供了其余的Log接口的实现,都非常简单,比方StdOutImpl就是间接System.out.println或者System.err.println,NoLoggingImpl就是没有log的意思。 java支流日志框架的实现类其实也非常简单:创立相应java日志框架的log对象作为Mybatis日志对象的代理对象,Mybatis须要记录日志的时候,委派给代理对象执行。 日志框架#Log对象初始化既然Mybatis反对各种java支流日志框架,那他到底是怎么决定具体应用哪个日志框架呢?其实就是Mybatis的log对象的初始化过程。 这个工作是LogFactory实现的。 private static Constructor<? extends Log> logConstructor; //... private LogFactory() { // disable construction }logFactory定义了一个动态Constructor变量logConstructor,并且把构造方法定义为private来阻止通过构造方法初始化,对外通过getLog办法提供Log对象: public static Log getLog(String logger) { try { return logConstructor.newInstance(logger); } catch (Throwable t) { throw new LogException("Error creating logger for logger " + logger + ". Cause: " + t, t); } }getLog办法也非常简单,间接通过结构器logConstructor调用newInstance办法获取Log对象。所以,LogFactory的外围其实就应该是这个logConstructor的初始化过程了。 ...

May 9, 2023 · 4 min · jiezi

关于java:从零搭建SpringBoot3一手动编写一套属于自己风格的代码生成器一键生成系统

简介尽管 java 的代码生成工具有很多,可是很多时候不是本人喜爱的格调,改起来比拟艰难,所以我筹备从零和大家一起搭建一套基于 springboot3.0 的框架,这次就先搞定一套代码生成性能,后续再一直的欠缺其它 咱们应用到的三方库: beelt 模版引擎,用于生成代码。官网:http://ibeetl.commybatis-plug 官网:https://www.baomidou.com/开始第一步,创立一个 maven 我的项目,而后在 pom.xml 中引入相干依赖,用到数据库驱动,阿里的数据库连接池等 <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>springboot-generate</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>20</maven.compiler.source> <maven.compiler.target>20</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.6</version> </parent> <!-- Add typical dependencies for a web application --> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.17</version> </dependency> <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>3.5.3.1</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <!-- https://mvnrepository.com/artifact/com.ibeetl/beetl --> <dependency> <groupId>com.ibeetl</groupId> <artifactId>beetl-springboot-starter-jdk17</artifactId> <version>3.15.0.RELEASE</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>provided</scope> </dependency> </dependencies> <!-- Package as an executable jar --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>第二步,在 java 文件夹下创立个包 com.light,并在下创立启动入口 Application.java,留神不要间接在 java 下创立,代码 ...

May 9, 2023 · 3 min · jiezi

关于java:一种新的流为-Java-加入生成器Generator特性

前言这篇文章不是工具举荐,也不是利用案例分享。其主题思想,是介绍一种全新的设计模式。它既领有形象的数学美感,仅仅从一个简略接口登程,就能推演出宏大的个性汇合,引出许多全新概念。同时也有扎实的工程实用价值,由其实现的工具,性能均可显著超过同类的头部开源产品。 这一设计模式并非因Java而生,而是诞生于一个非常简陋的脚本语言。它对语言个性的要求十分之低,因此其价值对泛滥古代编程语言都是普适的。 对于Stream首先大略回顾下Java里传统的流式API。自Java8引入lambda表达式和Stream以来,Java的开发便捷性有了质的飞跃,Stream在简单业务逻辑的解决上让人效率倍增,是每一位Java开发者都应该把握的根底技能。但排除掉parallelStream也即并发流之外,它其实并不是一个好的设计。 第一、封装过重,实现过于简单,源码极其难读。我能了解这或者是为了兼容并发流所做的斗争,但毕竟耦合太深,显得艰深晦涩。每一位初学者被源码吓到之后,想必都会产生流是一种非常高级且实现简单的个性的印象。实际上并不是这样,流其实能够用非常简单的形式构建。 第二、API过于简短。简短体现在stream.collect这一部分。作为比照,Kotlin提供的toList/toSet/associate(toMap)等等丰盛操作是能够间接作用在流上的。Java直到16才抠抠索索加进来一个Stream能够间接调用的toList,他们甚至不肯把toSet/toMap一起加上。 第三、API性能简陋。对于链式操作,在最后的Java8里只有map/filter/skip/limit/peek/distinct/sorted这七个,Java9又加上了takeWhile/dropWhile。然而在Kotlin中,除了这几个之外人还有许多额定的实用功能。 例如: mapIndexed,mapNotNull,filterIndexed,filterNotNull,onEachIndexed,distinctBy, sortedBy,sortedWith,zip,zipWithNext等等,翻倍了不止。这些货色实现起来并不简单,就是个棘手的事,但对于用户而言有和没有的体验差别堪称微小。 在这篇文章里,我将提出一种全新的机制用于构建流。这个机制极其简略,任何能看懂lambda表达式(闭包)的同学都能亲手实现,任何反对闭包的编程语言都能利用该机制实现本人的流。也正是因为这个机制足够简略,所以开发者能够以相当低的老本撸出大量的实用API,应用体验甩开Stream两条街,不是问题。 对于生成器生成器(Generator)[1]是许多古代编程语言里一个广受好评的重要个性,在Python/Kotlin/C#/Javascript等等语言中均有间接反对。它的外围API就是一个yield关键字(或者办法)。 有了生成器之后,无论是iterable/iterator,还是一段乌七八糟的闭包,都能够间接映射为一个流。举个例子,假如你想实现一个下划线字符串转驼峰的办法,在Python里你能够利用生成器这么玩 def underscore_to_camelcase(s): def camelcase(): yield str.lower while True: yield str.capitalize return ''.join(f(sub) for sub, f in zip(s.split('_'), camelcase()))这短短几行代码能够说处处体现出了Python生成器的奇妙。首先,camelcase办法里呈现了yield关键字,解释器就会将其看作是一个生成器,这个生成器会首先提供一个lower函数,而后提供有数的capitalize函数。因为生成器的执行始终是lazy的,所以用while true的形式生成有限流是非常常见的伎俩,不会有性能或者内存上的节约。其次,Python里的流是能够和list一起进行zip的,无限的list和有限的流zip到一起,list完结了流天然也会完结。 这段代码中,开端那行join()括号里的货色,Python称之为生成器推导(Generator Comprehension)[2],其本质上仍然是一个流,一个zip流被map之后的string流,最终通过join办法聚合为一个string。 以上代码里的操作, 在任何反对生成器的语言里都能够轻易实现,然而在Java里你恐怕连想都不敢想。Java有史以来,无论是历久弥新的Java8,还是最新的引入了Project Loom[3]的OpenJDK19,连协程都有了,仍然没有间接反对生成器。 实质上,生成器的实现要依赖于continuation[4]的挂起和复原,所谓continuation能够直观了解为程序执行到指定地位后的断点,协程就是指在这个函数的断点挂起后跳到另一个函数的某个断点继续执行,而不会阻塞线程,生成器亦如是。 Python通过栈帧的保留与复原实现函数重入以及生成器[5],Kotlin在编译阶段利用CPS(Continuation Passing Style)[6]技术对字节码进行了变换,从而在JVM上模仿了协程[7]。其余的语言要么大体如此,要么有更间接的反对。 那么,有没有一种方法,能够在没有协程的Java里,实现或者至多模拟出一个yield关键字,从而动静且高性能地创立流呢。答案是,有。 注释Java里的流叫Stream,Kotlin里的流叫Sequence。我切实想不出更好的名字了,想叫Flow又被用了,简略起见权且叫Seq。 概念定义首先给出Seq的接口定义 public interface Seq<T> { void consume(Consumer<T> consumer);}它实质上就是一个consumer of consumer,其实在含意我后边会讲。这个接口看似形象,实则十分常见,java.lang.Iterable人造自带了这个接口,那就是大家耳熟能详的forEach。利用办法推导,咱们能够写出第一个Seq的实例 List<Integer> list = Arrays.asList(1, 2, 3);Seq<Integer> seq = list::forEach;能够看到,在这个例子里consume和forEach是齐全等价的,事实上这个接口我最早就是用forEach命名的,几轮迭代之后才改成含意更精确的consume。 利用单办法接口在Java里会自动识别为FunctionalInteraface这一平凡个性,咱们也能够用一个简略的lambda表达式来结构流,比方只有一个元素的流。 static <T> Seq<T> unit(T t) { return c -> c.accept(t);}这个办法在数学上很重要(实操上其实用的不多),它定义了Seq这个泛型类型的单位元操作,即T -> Seq<T>的映射。 ...

May 9, 2023 · 7 min · jiezi

关于java:使用-SaToken-完成踢人下线功能

一、需要在企业级我的项目中,踢人下线是一个很常见的需要,如果要设计比较完善的话,至多须要以下性能点: 能够依据用户 userId 踢出指定会话,对方再次拜访零碎会被提醒:您已被踢下线,请从新登录。能够查问出一个账号共在几个设施端登录,并返回其对应的 Token 凭证,以便后续操作。能够只踢出一个账号某一个端的会话,其余端不受影响。例如在某电商APP上能够看到以后账号共在几个手机上登录,并登记指定端的会话,以后端不受影响。手动从零开始设计满足需要的会话架构,还是须要肯定的代码量的。本篇将介绍如何应用 Sa-Token 不便的实现上述需要,Sa-Token 框架对踢人下线做了较为残缺的封装,咱们能够应用极少的代码就实现踢人下线性能。 Sa-Token 是一个轻量级 java 权限认证框架,次要解决登录认证、权限认证、单点登录、OAuth2、微服务网关鉴权 等一系列权限相干问题。Gitee 开源地址:https://gitee.com/dromara/sa-token首先在我的项目中引入 Sa-Token 依赖: <!-- Sa-Token 权限认证 --><dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-boot-starter</artifactId> <version>1.34.0</version></dependency>注:如果你应用的是 SpringBoot 3.x,只须要将 sa-token-spring-boot-starter 批改为 sa-token-spring-boot3-starter 即可。 二、踢人下线 API 一览先看看 Sa-Token 为咱们提供的与踢人下线无关的API。 强制登记: StpUtil.logout(10001); // 强制指定账号登记下线 StpUtil.logout(10001, "PC"); // 强制指定账号指定端登记下线 StpUtil.logoutByTokenValue("token"); // 强制指定 Token 登记下线 踢人下线: StpUtil.kickout(10001); // 将指定账号踢下线 StpUtil.kickout(10001, "PC"); // 将指定账号指定端踢下线StpUtil.kickoutByTokenValue("token"); // 将指定 Token 踢下线强制登记 和 踢人下线 的区别在于: 强制登记等价于对方被动调用了登记办法,再次拜访会提醒:Token有效。踢人下线不会革除Token信息,而是将其打上特定标记,再次拜访会提醒:Token已被踢下线。动态图演示: 上面开始进行代码实战。 三、依据账号踢人下线在实现踢人下线之前,咱们须要先让会话实现登录。失常的登录须要依据 username + password 判断账号合法性,因为咱们本篇的重点是 踢人下线,所以此处简化一下登录操作,间接填入 userId 进行登录。 ...

May 9, 2023 · 2 min · jiezi

关于java:Java-网络编程详解

文章和代码曾经归档至【Github仓库:https://github.com/timerring/java-tutorial 】或者公众号【AIShareLab】回复 java 也可获取。网络的相干概念网络通信概念:两台设施之间通过网络实现数据传输网络通信:将数据通过网络从一台设施传输到另一台设施java.net包下提供了一系列的类或接口,供程序员应用,实现网络通信 网络概念:两台或多台设施通过肯定物理设施连接起来形成了网络依据网络的覆盖范围不同,对网络进行分类:局域网:覆盖范围最小,仅仅笼罩一个教室或一个机房城域网:覆盖范围较大,能够笼罩一个城市广域网:覆盖范围最大,能够覆盖全国,甚至寰球,万维网是广域网的代表 ip 地址概念:用于惟一标识网络中的每台计算机/主机查看ip地址: ipconfigip地址的示意模式: 点分十进制XX.XX.XX.XX每一个十进制数的范畴:0~255ip地址的组成=网络地址+主机地址,比方:192.168.16.69iPv6是互联网工程工作组设计的用于代替IPv4的下一代IP协定,其地址数量号称能够为全世界的每一粒沙子编上一个地址。因为IPv4最大的问题在于网络地址资源无限,重大制约了互联网的利用和倒退。IPv6的应用,不仅能解决网络地址资源数量的问题,而且也解决了多种接入设施连入互联网的阻碍。ipv4 地址分类 非凡的:127.0.0.1示意本机地址 域名www.baidu.com益处: 为了不便记忆,解决记ip的艰难概念: 将ip地址映射成域名,这里怎么映射上——HTTP协定端口号 概念: 用于标识计算机上某个特定的网络程序示意模式: 以整数模式,端口范畴0~65535 [2个字节示意端口 0\~2^16-1]0\~1024曾经被占用, 比方ssh 22, ftp 21, smtp 25 http 80常见的网络程序端口号 tomcat: 8080mysql: 3306oracle: 1521sqlserver: 1433网络通信协定 协定(tcp/ip) TCP/IP (Transmission Control Protocol/Internet Protocol) 的简写,中文译名为传输控制协议/因特网互联协定,又叫网络通讯协定,这个协定是lnternet最根本的协定、Internet国际互联网络的根底,简略地说,就是由网络层的IP协定和传输层的TCP协定组成的。 TCP 和 UDPTCP协定:传输控制协议 应用TCP协定前,须先建设TCP连贯,造成传输数据通道传输前,采纳“三次握手"形式,是牢靠的TCP协定进行通信的两个利用过程: 客户端、服务端在连贯中可进行大数据量的传输传输结束,需开释已建设的连贯, 效率低UDP协定:用户数据协定 将数据、源、目标封装成数据包,不须要建设连贯每个数据报的大小限度在64K内,不适宜传输大量数据因无需连贯,故是不牢靠的发送数据完结时无需开释资源(因为不是面向连贯的),速度快InetAddress 类相干办法获取本机InetAddress对象getLocalHost依据指定主机名/域名获取ip地址对象getByName获取InetAddress对象的主机名getHostName获取InetAddress对象的地址getHostAddress利用案例编写代码,获取计算机的主机名和IP 地址相干API package com.hspedu.api;import java.net.InetAddress;import java.net.UnknownHostException;/** * 演示InetAddress 类的应用 */public class API_ { public static void main(String[] args) throws UnknownHostException { //1. 获取本机的InetAddress 对象 InetAddress localHost = InetAddress.getLocalHost(); System.out.println(localHost);//DESKTOP-S4MP84S/192.168.12.1 //2. 依据指定主机名 获取 InetAddress对象 InetAddress host1 = InetAddress.getByName("DESKTOP-S4MP84S"); System.out.println("host1=" + host1);//DESKTOP-S4MP84S/192.168.12.1 //3. 依据域名返回 InetAddress对象, 比方 www.baidu.com 对应 InetAddress host2 = InetAddress.getByName("www.baidu.com"); System.out.println("host2=" + host2);//www.baidu.com / 110.242.68.4 //4. 通过 InetAddress 对象,获取对应的地址 String hostAddress = host2.getHostAddress();//IP 110.242.68.4 System.out.println("host2 对应的ip = " + hostAddress);//110.242.68.4 //5. 通过 InetAddress 对象,获取对应的主机名/或者的域名 String hostName = host2.getHostName(); System.out.println("host2对应的主机名/域名=" + hostName); // www.baidu.com }}Socket根本介绍套接字(Socket)开发网络应用程序被宽泛采纳,以至于成为事实上的规范。通信的两端都要有Socket,是两台机器间通信的端点。网络通信其实就是Socket间的通信。Socket容许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。个别被动发动通信的应用程序属客户端,期待通信申请的为服务端。示意图 ...

May 9, 2023 · 10 min · jiezi

关于java:自从用了-Alibaba-COLA-架构代码再也不怕腐烂了

大家好,我是不才陈某~ 本文开始前,问大家一个问题,你感觉一份业务代码,尤其是互联网业务代码,都有哪些特点? 我能想到的有这几点: 互联网业务迭代快,工期紧,导致代码构造凌乱,简直没有代码正文和文档。互联网人员变动频繁,很容易接手他人的老我的项目,新人基本没工夫吃透代码构造,紧迫的工期又只能让屎山越堆越大。多人一起开发,每个人的编码习惯不同,工具类代码各用个的,业务命名也常常抵触,影响效率。大部分团队简直没有工夫做代码重构,任由代码腐烂。每当咱们新启动一个代码仓库,都是信念满满,构造整洁。然而工夫越往后,代码就变得糜烂不堪,技术债权越来越宏大。 这种状况有解决方案吗?也是有的: 小组内定期做代码重构,解决技术债权。组内设计欠缺的利用架构,让代码的腐烂来得慢一些。(当然很难做到齐全不腐烂)设计尽量简略,让不同层级的开发都能疾速看懂并上手开发,而不是在一堆简单的没人看懂的代码上堆更多的屎山。而COLA,咱们明天的配角,就是为了提供一个可落地的业务代码构造标准,让你的代码腐烂的尽可能慢一些,让团队的开发效率尽可能快一些。 COLA是什么COLA是由阿里大佬张建飞所提出的一种业务代码架构的最佳实际,并且曾经在阿里云脚手架代码生成器中作为一个可选项,可见其曾经领有了肯定影响力。 COLA 是 Clean Object-Oriented and Layered Architecture的缩写,代表“整洁面向对象分层架构”。 在COLA 4.0,也就是目前最新的版本中,作者将COLA拆分为COLA架构(Archetype)和COLA组件(Components)两个局部: COLA架构:COLA利用的代码模板。COLA组件:提供一些十分有用的通用组件,这些组件能够帮忙咱们晋升研发效率。两者互不烦扰,能够独立应用。 COLA整体架构首先次要谈谈COLA架构,COLA的官网博文中是这么介绍的: 在平时咱们的业务开发中,大部分的零碎都须要: 接管request,响应response;做业务逻辑解决,像校验参数,状态流转,业务计算等等;和内部零碎有联动,像数据库,微服务,搜索引擎等;正是有这样的共性存在,才会有很多普适的架构思维呈现,比方分层架构、六边形架构、洋葱圈架构、整洁架构(Clean Architecture)、DDD架构等等。 这些利用架构思维尽管很好,但咱们很多同学还是“不讲Co德,明确了很多情理,可还是过不好这毕生”。问题就在于不足实际和领导。COLA的意义就在于,他不仅是思维,还提供了可落地的实际。应该是为数不多的利用架构层面的开源软件。 COLA提供了一整套代码架构,拿来即用。 其中蕴含了很多架构设计思维,包含探讨度很高的畛域驱动设计DDD等。 留神:每个人对于架构设计都有着本人的了解。所以对于COLA的架构,本篇文章也仅仅只是我本人对于COLA的浅显了解,大家能够批评对待。 COLA分层架构先来看两张官网介绍图 其次,还有一个官网的表格,介绍了COLA中每个层的命名和含意: 档次包名性能必选Adapter层web解决页面申请的Controller否Adapter层wireless解决无线端的适配否Adapter层wap解决wap端的适配否App层executor解决request,包含command和query是App层consumer解决内部message否App层scheduler解决定时工作否Domain层model畛域模型否Domain层ability畛域能力,包含DomainService否Domain层gateway畛域网关,解耦利器是Infra层gatewayimpl网关实现是Infra层mapperibatis数据库映射否Infra层config配置信息否Client SDKapi服务对外透出的API是Client SDKdto服务对外的DTO是这两张图和一个表格曾经把整个COLA架构的绝大部分内容展示给了大家,然而一下子这么多信息量可能很难消化。 既然整个示例架构我的项目是一个Maven父子构造,那咱们就从父模块一个个好好过一遍。 首先父模块的pom.xml蕴含了如下子模块: <modules> <module>demo-web-client</module> <module>demo-web-adapter</module> <module>demo-web-app</module> <module>demo-web-domain</module> <module>demo-web-infrastructure</module> <module>start</module></modules>start层该模块作为整个利用的启动模块(通常是一个SpringBoot利用),只承当启动我的项目和全局相干配置项的寄存职责。代码目录如下: 将启动独立进去,益处是清晰简洁,也能让新人一眼就看出如何运行我的项目,以及我的项目的一些根底依赖。 adapter层接下来咱们依照之前架构图从上到下的程序,一个个看。 首先是demo-web-adapter模块,这名字是不是很陈腐?但其实,能够了解为平时咱们用的controller层(对于Web利用来说),换汤不换药。 在COLA官网博客中,也能找到如下的形容: Controller这个名字次要是来自于MVC,因为是MVC,所以自带了Web利用的烙印。然而,随着mobile的衰亡,当初很少有利用仅仅只反对Web端,通常的标配是Web,Mobile,WAP三端都要反对。 cilent层有了咱们说的“controller”层,接下来有的小伙伴必定就会想,是不是service层啦。 是,也不是。 传统的Web利用中,齐全能够只有一个service层给controller层调用,然而作为一个业务利用,除非你真的只是个前端页面的有情吐数据机器,否则很大可能性你的利用会有很多其余上下游调用方,并且你须要提供接口给他们。 这时候你给他们的不应该是一个Web接口,应该是RPC调用的服务层接口,至于起因不是本文的重点,具体就不开展了。 所以在COLA中,你的adapter层,调用了client层,client层中就是你服务接口的定义。 从上图中能够看到,client包里有: api文件夹:寄存服务接口定义dto文件夹:寄存传输实体留神,这里只是服务接口定义,而不是服务层的具体实现,所以在adapter层中,调用的其实是client层的接口: @RestControllerpublic class CustomerController { @Autowired private CustomerServiceI customerService; @GetMapping(value = "/customer") public MultiResponse<CustomerDTO> listCustomerByName(@RequestParam(required = false) String name){ CustomerListByNameQry customerListByNameQry = new CustomerListByNameQry(); customerListByNameQry.setName(name); return customerService.listByName(customerListByNameQry); }}而最终接口的具体实现逻辑放到了app层。 ...

May 9, 2023 · 2 min · jiezi

关于java:springboot和mybatis整合

我的项目目录 src/main/resourcesapplication.yml:spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root url: jdbc:mysql://localhost:3306/blog1?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8mybatis: type-aliases-package: app.repository mapper-locations: classpath:/mapping/*.xmlmapping/userMapper.xml<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mapper namespace="app.repository.UserMapper"> <insert id="insert" parameterType="app.model.User" keyColumn="id"> insert into user (id,username,password) values (#{id},#{username},#{password}) </insert></mapper>app/controller/UserController;import app.model.User;import app.service.imp.UserServiceImp;import app.utils.ResultEnum;import app.utils.ResultInfo;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestControllerpublic class UserController { @Resource private UserServiceImp userServiceImp; @PostMapping("/insert") public ResultInfo insert(User user){ int num = userServiceImp.insert(user); System.out.println("n:"+num); ResultInfo resultInfo = new ResultInfo(); resultInfo.setMsg(ResultEnum.SUCCESS_CODE.getMsg()); return resultInfo; }}app/model/User;import lombok.Data;import lombok.Getter;import lombok.Setter;/** * <p> * * </p> * * @author huang * @since 2023-05-03 */@Getter@Setter@Datapublic class User { private Integer id; private String username; private String password;}app/repository/UserMapper.javaimport app.model.User;public interface UserMapper { public int insert(User user);}app/serviceUser;import app.model.User;public interface UserService { public int insert(User user);}UserServiceImppackage;import app.model.User;import app.repository.UserMapper;import app.service.UserService;import org.springframework.stereotype.Service;import javax.annotation.Resource;@Servicepublic class UserServiceImp implements UserService { @Resource private UserMapper userMapper; @Override public int insert(User user) { int num = userMapper.insert(user); return num; }}app/utilsResultEnum.java;public enum ResultEnum { SUCCESS_CODE(200,"申请胜利"), SUCCESS_FAIL(500,"申请失败"); private Integer code; private String msg; ResultEnum(Integer code, String msg) { this.code = code; this.msg = msg; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; }}ResultInfo;public class ResultInfo { private Integer code=200; private String msg; private Object result; public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Object getResult() { return result; } public void setResult(Object result) { this.result = result; }}app/config/SwaggerConfig;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import springfox.documentation.builders.ApiInfoBuilder;import springfox.documentation.builders.PathSelectors;import springfox.documentation.builders.RequestHandlerSelectors;import springfox.documentation.spi.DocumentationType;import springfox.documentation.spring.web.plugins.Docket;import springfox.documentation.swagger2.annotations.EnableSwagger2;//@Configuration是通知Spring Boot须要加载这个配置类;@EnableSwagger2是启用Swagger2.@Configuration@EnableSwagger2public class SwaggerConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2). select(). apis(RequestHandlerSelectors. basePackage("app.controller")). //扫码 paths(PathSelectors.any()).build().apiInfo(new ApiInfoBuilder().title("springboot blog app").version("1.0").build()); }}Applicationpackage app;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication@MapperScan("app.repository")public class Application { public static void main(String[] args) { SpringApplication.run(Application.class,args); }}启动数据库创立数表 ...

May 9, 2023 · 2 min · jiezi

关于java:京东面经总结

非科班,经验了无数场秋招,现将面试京东的题目记录如下: 一面kafka在利用场景以及 我的项目 里的实现bitmap底层object里有哪些办法hashmap相干sychronized和reentrantlock相干问题以及锁降级cas和volatile线程几种状态以及转化jvm内存模型mybatis相干问题Redis数据结构,问了下跳表的底层RDB和AOFMySQL索引有哪些b+树底层实现最左前缀原理二面线程的状态cms增量更新法GcRoots是哪些java根底mysql索引我的项目具体实现三面索引谈谈多线程jvm如何调优mq在我的项目中的用法遇到的多线程问题,如何解决最长无反复字串找到A^2+B^2 = C四面数据库乐观锁、乐观锁为啥用Redissql语句执行程序SpringMVC长处,原理aop长处,原理ioc长处,原理面向对象概念封装我的项目中封装如何体现高内聚,低耦合啥意思,如何去设计设计一个电梯场景,实现面向对象,高内聚,低耦合的状况统计学校内共享单车数量,你有啥想法(凋谢题)本文曾经收录到Github,该仓库蕴含计算机根底、Java根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等外围知识点,欢送star~ Github地址 如果拜访不了Github,能够拜访gitee地址。 gitee地址

May 8, 2023 · 1 min · jiezi

关于java:Java后端真实靠谱强大的面试题网站面试梯

本文分享一个给力的Java后端面试题网站:面试梯。 网址:https://offer.skyofit.com 这套题实在、高频、全面、有具体答案、保你稳过面试,让你成为offer收割机。题目包含:Java根底、多线程、JVM、数据库、Redis、Shiro、Spring、SpringBoot、MyBatis、MQ、ELK、分布式、SpringCloud、设计模式、线上问题排查等。 面试的痛点你要跳槽时是否遇到过如下问题? 没有题库,不晓得如何开始温习有题库,但题目超级多,基本看不完背完题库中的面试题了,却发现面试官的问题大部分都不在题库中,导致面试老是挂,拿不到offer此面试梯的特点这是一套实在、靠谱的题目。它的特点是: 实在、靠谱(这套题是我在几十场面试中被面试官问到的问题)精选、高频(数量适合,很快就能看完)有难度和频率的标记(焦急跳槽的能够只看频率为三颗星及以上的题目)很弱小,面试官的问题根本都在这套题里(看过这套题的敌人都说,面试时85%以上的题目都在这些题目里,成为了offer收割机)此面试题的适用人群此网站蕴含从简略到艰难、从高频到低频的题目,适宜所有Java求职者,包含:应届生、转行的、三年以内教训的、三到五年教训的、五到十年教训的等。 此面试题的威力看过此网站的敌人和共事加入了面试后说,他们面试被问到的问题大部分(85%以上)都在这个网站里。 我在面试时也常常会遇到这种状况:95%的题目在此网站中,我对答如流,不晓得面试官是否会狐疑泄题了。这些面试也都很稳地拿到了offer。 难度与频率这套面试题采纳五角星来评判题目的难度和被问到的频率,最高为五颗星。 如果是三年以上的教训,只看难度为三颗星及以上的题目即可。如果是一两年教训,看集体程度,程度好些的能够看一颗星到五颗星所有题目(我晓得有这样的敌人:尽管教训少,但爱学习,一两年的教训领有普通人四五年的技术能力)。

May 8, 2023 · 1 min · jiezi

关于java:Java实现坦克大战30

IO 流-利用到坦克大战 坦克大战0.5版减少性能 避免敌人坦克重叠静止记录玩家的总成绩(累积击毁敌方坦克数),存盘退出【io流】记泉退出游戏时敌人坦克坐标/方向,存盘退出【io流】玩游戏时,能够抉择是开新游戏还是持续上局游戏 package com.hspedu.tankgame5;/** * 炸弹 */public class Bomb { int x, y; //炸弹的坐标 int life = 9; //炸弹的生命周期 boolean isLive = true; //是否还存活 public Bomb(int x, int y) { this.x = x; this.y = y; } //缩小生命值 public void lifeDown() { //配合呈现图片的爆炸成果 if(life > 0) { life--; } else { isLive = false; } }}package com.hspedu.tankgame5;import java.util.Vector;/** * 敌人的坦克 */@SuppressWarnings({"all"})public class EnemyTank extends Tank implements Runnable { //在敌人坦克类,应用Vector 保留多个Shot Vector<Shot> shots = new Vector<>(); //减少成员,EnemyTank 能够失去敌人坦克的Vector //剖析 //1. Vector<EnemyTank> 在 Vector<EnemyTank> enemyTanks = new Vector<>(); boolean isLive = true; public EnemyTank(int x, int y) { super(x, y); } //这里提供一个办法,能够将MyPanel 的成员 Vector<EnemyTank> enemyTanks = new Vector<>(); //设置到 EnemyTank 的成员 enemyTanks public void setEnemyTanks(Vector<EnemyTank> enemyTanks) { this.enemyTanks = enemyTanks; } //编写办法,判断以后的这个敌人坦克,是否和 enemyTanks 中的其余坦克产生的重叠或者碰撞 public boolean isTouchEnemyTank() { //判断以后敌人坦克(this) 方向 switch (this.getDirect()) { case 0: //上 //让以后敌人坦克和其它所有的敌人坦克比拟 for (int i = 0; i < enemyTanks.size(); i++) { //从vector 中取出一个敌人坦克 EnemyTank enemyTank = enemyTanks.get(i); //不和本人比拟 if (enemyTank != this) { //如果敌人坦克是上/下 // //1. 如果敌人坦克是上/下 x的范畴 [enemyTank.getX(), enemyTank.getX() + 40] // y的范畴 [enemyTank.getY(), enemyTank.getY() + 60] if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) { //2. 以后坦克 左上角的坐标 [this.getX(), this.getY()] if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 40 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 60) { return true; } //3. 以后坦克 右上角的坐标 [this.getX() + 40, this.getY()] if (this.getX() + 40 >= enemyTank.getX() && this.getX() + 40 <= enemyTank.getX() + 40 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 60) { return true; } } //如果敌人坦克是 右/左 //1. 如果敌人坦克是右/左 x的范畴 [enemyTank.getX(), enemyTank.getX() + 60] // y的范畴 [enemyTank.getY(), enemyTank.getY() + 40] if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) { //2. 以后坦克 左上角的坐标 [this.getX(), this.getY()] if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 60 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 40) { return true; } //3. 以后坦克 右上角的坐标 [this.getX() + 40, this.getY()] if (this.getX() + 40 >= enemyTank.getX() && this.getX() + 40 <= enemyTank.getX() + 60 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 40) { return true; } } } } break; case 1: //右 //让以后敌人坦克和其它所有的敌人坦克比拟 for (int i = 0; i < enemyTanks.size(); i++) { //从vector 中取出一个敌人坦克 EnemyTank enemyTank = enemyTanks.get(i); //不和本人比拟 if (enemyTank != this) { //如果敌人坦克是上/下 //1. 如果敌人坦克是上/下 x的范畴 [enemyTank.getX(), enemyTank.getX() + 40] // y的范畴 [enemyTank.getY(), enemyTank.getY() + 60] if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) { //2. 以后坦克 右上角的坐标 [this.getX() + 60, this.getY()] if (this.getX() + 60 >= enemyTank.getX() && this.getX() + 60 <= enemyTank.getX() + 40 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 60) { return true; } //3. 以后坦克 右下角的坐标 [this.getX() + 60, this.getY() + 40] if (this.getX() + 60 >= enemyTank.getX() && this.getX() + 60 <= enemyTank.getX() + 40 && this.getY() + 40 >= enemyTank.getY() && this.getY() + 40 <= enemyTank.getY() + 60) { return true; } } //如果敌人坦克是 右/左 //1. 如果敌人坦克是右/左 x的范畴 [enemyTank.getX(), enemyTank.getX() + 60] // y的范畴 [enemyTank.getY(), enemyTank.getY() + 40] if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) { //2. 以后坦克 右上角的坐标 [this.getX() + 60, this.getY()] if (this.getX() + 60 >= enemyTank.getX() && this.getX() + 60 <= enemyTank.getX() + 60 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 40) { return true; } //3. 以后坦克 右下角的坐标 [this.getX() + 60, this.getY() + 40] if (this.getX() + 60 >= enemyTank.getX() && this.getX() + 60 <= enemyTank.getX() + 60 && this.getY() + 40 >= enemyTank.getY() && this.getY() + 40 <= enemyTank.getY() + 40) { return true; } } } } break; case 2: //下 //让以后敌人坦克和其它所有的敌人坦克比拟 for (int i = 0; i < enemyTanks.size(); i++) { //从vector 中取出一个敌人坦克 EnemyTank enemyTank = enemyTanks.get(i); //不和本人比拟 if (enemyTank != this) { //如果敌人坦克是上/下 //1. 如果敌人坦克是上/下 x的范畴 [enemyTank.getX(), enemyTank.getX() + 40] // y的范畴 [enemyTank.getY(), enemyTank.getY() + 60] if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) { //2. 以后坦克 左下角的坐标 [this.getX(), this.getY() + 60] if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 40 && this.getY() + 60 >= enemyTank.getY() && this.getY() + 60 <= enemyTank.getY() + 60) { return true; } //3. 以后坦克 右下角的坐标 [this.getX() + 40, this.getY() + 60] if (this.getX() + 40 >= enemyTank.getX() && this.getX() + 40 <= enemyTank.getX() + 40 && this.getY() + 60 >= enemyTank.getY() && this.getY() + 60 <= enemyTank.getY() + 60) { return true; } } //如果敌人坦克是 右/左 //1. 如果敌人坦克是右/左 x的范畴 [enemyTank.getX(), enemyTank.getX() + 60] // y的范畴 [enemyTank.getY(), enemyTank.getY() + 40] if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) { //2. 以后坦克 左下角的坐标 [this.getX(), this.getY() + 60] if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 60 && this.getY() + 60 >= enemyTank.getY() && this.getY() + 60 <= enemyTank.getY() + 40) { return true; } //3. 以后坦克 右下角的坐标 [this.getX() + 40, this.getY() + 60] if (this.getX() + 40 >= enemyTank.getX() && this.getX() + 40 <= enemyTank.getX() + 60 && this.getY() + 60 >= enemyTank.getY() && this.getY() + 60 <= enemyTank.getY() + 40) { return true; } } } } break; case 3: //左 //让以后敌人坦克和其它所有的敌人坦克比拟 for (int i = 0; i < enemyTanks.size(); i++) { //从vector 中取出一个敌人坦克 EnemyTank enemyTank = enemyTanks.get(i); //不和本人比拟 if (enemyTank != this) { //如果敌人坦克是上/下 //1. 如果敌人坦克是上/下 x的范畴 [enemyTank.getX(), enemyTank.getX() + 40] // y的范畴 [enemyTank.getY(), enemyTank.getY() + 60] if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) { //2. 以后坦克 左上角的坐标 [this.getX(), this.getY() ] if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 40 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 60) { return true; } //3. 以后坦克 左下角的坐标 [this.getX(), this.getY() + 40] if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 40 && this.getY() + 40 >= enemyTank.getY() && this.getY() + 40 <= enemyTank.getY() + 60) { return true; } } //如果敌人坦克是 右/左 //1. 如果敌人坦克是右/左 x的范畴 [enemyTank.getX(), enemyTank.getX() + 60] // y的范畴 [enemyTank.getY(), enemyTank.getY() + 40] if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) { //2. 以后坦克 左上角的坐标 [this.getX(), this.getY() ] if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 60 && this.getY() >= enemyTank.getY() && this.getY() <= enemyTank.getY() + 40) { return true; } //3. 以后坦克 左下角的坐标 [this.getX(), this.getY() + 40] if (this.getX() >= enemyTank.getX() && this.getX() <= enemyTank.getX() + 60 && this.getY() + 40 >= enemyTank.getY() && this.getY() + 40 <= enemyTank.getY() + 40) { return true; } } } } break; } return false; } @Override public void run() { while (true) { //这里咱们判断如果shots size() =0, 创立一颗子弹,放入到 //shots汇合,并启动 if (isLive && shots.size() < 1) { Shot s = null; //判断坦克的方向,创立对应的子弹 switch (getDirect()) { case 0: s = new Shot(getX() + 20, getY(), 0); break; case 1: s = new Shot(getX() + 60, getY() + 20, 1); break; case 2: //向下 s = new Shot(getX() + 20, getY() + 60, 2); break; case 3://向左 s = new Shot(getX(), getY() + 20, 3); break; } shots.add(s); //启动 new Thread(s).start(); } //依据坦克的方向来持续冲动 switch (getDirect()) { case 0: //向上 //让坦克放弃一个方向,走30步 for (int i = 0; i < 30; i++) { if (getY() > 0 && !isTouchEnemyTank()) { moveUp(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 1: //向右 for (int i = 0; i < 30; i++) { if (getX() + 60 < 1000 && !isTouchEnemyTank()) { moveRight(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 2: //向下 for (int i = 0; i < 30; i++) { if (getY() + 60 < 750 && !isTouchEnemyTank()) { moveDown(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 3: //向左 for (int i = 0; i < 30; i++) { if (getX() > 0 && !isTouchEnemyTank()) { moveLeft(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; } //而后随机的扭转坦克方向 0-3 setDirect((int) (Math.random() * 4)); //写并发程序,肯定要思考分明,该线程什么时候完结 if (!isLive) { break; //退出线程. } } }}package com.hspedu.tankgame5;import java.util.Vector;/** * 本人的坦克 */public class Hero extends Tank { //定义一个Shot对象, 示意一个射击(线程) Shot shot = null; //能够发射多颗子弹 //Vector<Shot> shots = new Vector<>(); public Hero(int x, int y) { super(x, y); } //射击 public void shotEnemyTank() { //发多颗子弹怎么办, 管制在咱们的面板上,最多只有5颗// if(shots.size() == 5) {// return;// } //创立 Shot 对象, 依据以后Hero对象的地位和方向来创立Shot switch (getDirect()) {//失去Hero对象方向 case 0: //向上 shot = new Shot(getX() + 20, getY(), 0); break; case 1: //向右 shot = new Shot(getX() + 60, getY() + 20, 1); break; case 2: //向下 shot = new Shot(getX() + 20, getY() + 60, 2); break; case 3: //向左 shot = new Shot(getX(), getY() + 20, 3); break; } //把新创建的shot放入到shots //shots.add(shot); //启动咱们的Shot线程 new Thread(shot).start(); }}package com.hspedu.tankgame5;import javax.swing.*;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import java.util.Scanner;public class HspTankGame05 extends JFrame { // 定义MyPanel MyPanel mp = null; static Scanner scanner = new Scanner(System.in); public static void main(String[] args) { HspTankGame05 hspTankGame01 = new HspTankGame05(); } public HspTankGame05() { System.out.println("请输出抉择 1: 新游戏 2: 持续上局"); String key = scanner.next(); mp = new MyPanel(key); //将mp 放入到Thread ,并启动 Thread thread = new Thread(mp); thread.start(); this.add(mp);//把面板(就是游戏的绘图区域) this.setSize(1300, 950); this.addKeyListener(mp);//让JFrame 监听mp的键盘事件 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); //在JFrame 中减少相应敞开窗口的解决 this.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { Recorder.keepRecord(); System.exit(0); } }); }}package com.hspedu.tankgame5;import javax.swing.*;import java.awt.*;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.io.File;import java.io.FileInputStream;import java.util.Vector;/** * 坦克大战的绘图区域 *///为了监听 键盘事件, 实现KeyListener//为了让Panel 不停的重绘子弹,须要将 MyPanel 实现Runnable ,当做一个线程应用@SuppressWarnings({"all"})public class MyPanel extends JPanel implements KeyListener, Runnable { //定义我的坦克 Hero hero = null; //定义敌人坦克,放入到Vector Vector<EnemyTank> enemyTanks = new Vector<>(); //定义一个寄存Node 对象的Vector, 用于复原敌人坦克的坐标和方向 Vector<Node> nodes = new Vector<>(); //定义一个Vector ,用于寄存炸弹 //阐明,当子弹击中坦克时,退出一个Bomb对象到bombs Vector<Bomb> bombs = new Vector<>(); int enemyTankSize = 3; //定义三张炸弹图片,用于显示爆炸成果 Image image1 = null; Image image2 = null; Image image3 = null; public MyPanel(String key) { nodes = Recorder.getNodesAndEnemyTankRec(); //将MyPanel对象的 enemyTanks 设置给 Recorder 的 enemyTanks Recorder.setEnemyTanks(enemyTanks); hero = new Hero(500, 100);//初始化本人坦克 switch (key) { case "1": //初始化敌人坦克 for (int i = 0; i < enemyTankSize; i++) { //创立一个敌人的坦克 EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0); //将enemyTanks 设置给 enemyTank !!! enemyTank.setEnemyTanks(enemyTanks); //设置方向 enemyTank.setDirect(2); //启动敌人坦克线程,让他动起来 new Thread(enemyTank).start(); //给该enemyTank 退出一颗子弹 Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect()); //退出enemyTank的Vector 成员 enemyTank.shots.add(shot); //启动 shot 对象 new Thread(shot).start(); //退出 enemyTanks.add(enemyTank); } break; case "2": //持续上局游戏 //初始化敌人坦克 for (int i = 0; i < nodes.size(); i++) { Node node = nodes.get(i); //创立一个敌人的坦克 EnemyTank enemyTank = new EnemyTank(node.getX(), node.getY()); //将enemyTanks 设置给 enemyTank !!! enemyTank.setEnemyTanks(enemyTanks); //设置方向 enemyTank.setDirect(node.getDirect()); //启动敌人坦克线程,让他动起来 new Thread(enemyTank).start(); //给该enemyTank 退出一颗子弹 Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect()); //退出enemyTank的Vector 成员 enemyTank.shots.add(shot); //启动 shot 对象 new Thread(shot).start(); //退出 enemyTanks.add(enemyTank); } break; default: System.out.println("你的输出有误..."); } //初始化图片对象 image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif")); image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif")); image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif")); } //编写办法,显示我方击毁敌方坦克的信息 public void showInfo(Graphics g) { //画出玩家的总成绩 g.setColor(Color.BLACK); Font font = new Font("宋体", Font.BOLD, 25); g.setFont(font); g.drawString("您累积击毁敌方坦克", 1020, 30); drawTank(1020, 60, g, 0, 0);//画出一个敌方坦克 g.setColor(Color.BLACK);//这里须要从新设置成彩色 g.drawString(Recorder.getAllEnemyTankNum() + "", 1080, 100); } @Override public void paint(Graphics g) { super.paint(g); g.fillRect(0, 0, 1000, 750);//填充矩形,默认彩色 showInfo(g); if (hero != null && hero.isLive) { //画出本人坦克-封装办法 drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1); } //画出hero射击的子弹 if (hero.shot != null && hero.shot.isLive == true) { g.draw3DRect(hero.shot.x, hero.shot.y, 1, 1, false); } //将hero的子弹汇合 shots ,遍历取出绘制// for(int i = 0; i < hero.shots.size(); i++) {// Shot shot = hero.shots.get(i);// if (shot != null && shot.isLive) {// g.draw3DRect(shot.x, shot.y, 1, 1, false);//// } else {//如果该shot对象曾经有效 ,就从shots汇合中拿掉// hero.shots.remove(shot);// }// } //如果bombs 汇合中有对象,就画出 for (int i = 0; i < bombs.size(); i++) { //取出炸弹 Bomb bomb = bombs.get(i); //依据以后这个bomb对象的life值去画出对应的图片 if (bomb.life > 6) { g.drawImage(image1, bomb.x, bomb.y, 60, 60, this); } else if (bomb.life > 3) { g.drawImage(image2, bomb.x, bomb.y, 60, 60, this); } else { g.drawImage(image3, bomb.x, bomb.y, 60, 60, this); } //让这个炸弹的生命值缩小 bomb.lifeDown(); //如果bomb life 为0, 就从bombs 的汇合中删除 if (bomb.life == 0) { bombs.remove(bomb); } } //画出敌人的坦克, 遍历Vector for (int i = 0; i < enemyTanks.size(); i++) { //从Vector 取出坦克 EnemyTank enemyTank = enemyTanks.get(i); //判断以后坦克是否还存活 if (enemyTank.isLive) {//当敌人坦克是存活的,才画出该坦克 drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0); //画出 enemyTank 所有子弹 for (int j = 0; j < enemyTank.shots.size(); j++) { //取出子弹 Shot shot = enemyTank.shots.get(j); //绘制 if (shot.isLive) { //isLive == true g.draw3DRect(shot.x, shot.y, 1, 1, false); } else { //从Vector 移除 enemyTank.shots.remove(shot); } } } } } //编写办法,画出坦克 /** * @param x 坦克的左上角x坐标 * @param y 坦克的左上角y坐标 * @param g 画笔 * @param direct 坦克方向(上下左右) * @param type 坦克类型 */ public void drawTank(int x, int y, Graphics g, int direct, int type) { //依据不同类型坦克,设置不同色彩 switch (type) { case 0: //敌人的坦克 g.setColor(Color.cyan); break; case 1: //我的坦克 g.setColor(Color.yellow); break; } //依据坦克方向,来绘制对应形态坦克 //direct 示意方向(0: 向上 1 向右 2 向下 3 向左 ) // switch (direct) { case 0: //示意向上 g.fill3DRect(x, y, 10, 60, false);//画出坦克右边轮子 g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克左边轮子 g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克盖子 g.fillOval(x + 10, y + 20, 20, 20);//画出圆形盖子 g.drawLine(x + 20, y + 30, x + 20, y);//画出炮筒 break; case 1: //示意向右 g.fill3DRect(x, y, 60, 10, false);//画出坦克上边轮子 g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边轮子 g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克盖子 g.fillOval(x + 20, y + 10, 20, 20);//画出圆形盖子 g.drawLine(x + 30, y + 20, x + 60, y + 20);//画出炮筒 break; case 2: //示意向下 g.fill3DRect(x, y, 10, 60, false);//画出坦克右边轮子 g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克左边轮子 g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克盖子 g.fillOval(x + 10, y + 20, 20, 20);//画出圆形盖子 g.drawLine(x + 20, y + 30, x + 20, y + 60);//画出炮筒 break; case 3: //示意向左 g.fill3DRect(x, y, 60, 10, false);//画出坦克上边轮子 g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边轮子 g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克盖子 g.fillOval(x + 20, y + 10, 20, 20);//画出圆形盖子 g.drawLine(x + 30, y + 20, x, y + 20);//画出炮筒 break; default: System.out.println("临时没有解决"); } } //如果咱们的坦克能够发射多个子弹 //在判断我方子弹是否击中敌人坦克时,就须要把咱们的子弹汇合中 //所有的子弹,都取出和敌人的所有坦克,进行判断 //老韩给的局部代码.. public void hitEnemyTank() {// //遍历咱们的子弹// for(int j = 0;j < hero.shots.size();j++) {// Shot shot = hero.shots.get(j);// //判断是否击中了敌人坦克// if (shot != null && hero.shot.isLive) {//当我的子弹还存活//// //遍历敌人所有的坦克// for (int i = 0; i < enemyTanks.size(); i++) {// EnemyTank enemyTank = enemyTanks.get(i);// hitTank(hero.shot, enemyTank);// }//// }// } //单颗子弹。 if (hero.shot != null && hero.shot.isLive) {//当我的子弹还存活 //遍历敌人所有的坦克 for (int i = 0; i < enemyTanks.size(); i++) { EnemyTank enemyTank = enemyTanks.get(i); hitTank(hero.shot, enemyTank); } } } //编写办法,判断敌人坦克是否击中我的坦克 public void hitHero() { //遍历所有的敌人坦克 for (int i = 0; i < enemyTanks.size(); i++) { //取出敌人坦克 EnemyTank enemyTank = enemyTanks.get(i); //遍历enemyTank 对象的所有子弹 for (int j = 0; j < enemyTank.shots.size(); j++) { //取出子弹 Shot shot = enemyTank.shots.get(j); //判断 shot 是否击中我的坦克 if (hero.isLive && shot.isLive) { hitTank(shot, hero); } } } } //编写办法,判断我方的子弹是否击中敌人坦克. //什么时候判断 我方的子弹是否击中敌人坦克 ? run办法 //前面咱们将 enemyTank 改成 tank名称 public void hitTank(Shot s, Tank enemyTank) { //判断s 击中坦克 switch (enemyTank.getDirect()) { case 0: //坦克向上 case 2: //坦克向下 if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 40 && s.y > enemyTank.getY() && s.y < enemyTank.getY() + 60) { s.isLive = false; enemyTank.isLive = false; //当我的子弹击中敌人坦克后,将enemyTank 从Vector 拿掉 enemyTanks.remove(enemyTank); //当我方击毁一个敌人坦克时,就对数据allEnemyTankNum++ //解读, 因为 enemyTank 能够是 Hero 也能够是 EnemyTank if (enemyTank instanceof EnemyTank) { Recorder.addAllEnemyTankNum(); } //创立Bomb对象,退出到bombs汇合 Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY()); bombs.add(bomb); } break; case 1: //坦克向右 case 3: //坦克向左 if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 60 && s.y > enemyTank.getY() && s.y < enemyTank.getY() + 40) { s.isLive = false; enemyTank.isLive = false; //当我的子弹击中敌人坦克后,将enemyTank 从Vector 拿掉 enemyTanks.remove(enemyTank); //解读, 因为 enemyTank 能够是 Hero 也能够是 EnemyTank if (enemyTank instanceof EnemyTank) { Recorder.addAllEnemyTankNum(); } //创立Bomb对象,退出到bombs汇合 Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY()); bombs.add(bomb); } break; } } @Override public void keyTyped(KeyEvent e) { } //解决wdsa 键按下的状况 @Override public void keyPressed(KeyEvent e) { System.out.println(e.getKeyCode()); if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键 //扭转坦克的方向 hero.setDirect(0);// //批改坦克的坐标 y -= 1 if (hero.getY() > 0) { hero.moveUp(); } } else if (e.getKeyCode() == KeyEvent.VK_D) {//D键, 向右 hero.setDirect(1); if (hero.getX() + 60 < 1000) { hero.moveRight(); } } else if (e.getKeyCode() == KeyEvent.VK_S) {//S键 hero.setDirect(2); if (hero.getY() + 60 < 750) { hero.moveDown(); } } else if (e.getKeyCode() == KeyEvent.VK_A) {//A键 hero.setDirect(3); if (hero.getX() > 0) { hero.moveLeft(); } } //如果用户按下的是J,就发射 if (e.getKeyCode() == KeyEvent.VK_J) { //判断hero的子弹是否销毁,发射一颗子弹 if (hero.shot == null || !hero.shot.isLive) { hero.shotEnemyTank(); } //发射多颗子弹 //hero.shotEnemyTank(); } //让面板重绘 this.repaint(); } @Override public void keyReleased(KeyEvent e) { } @Override public void run() { //每隔 100毫秒,重绘区域, 刷新绘图区域, 子弹就挪动 while (true) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //判断是咱们子弹否击中了敌人坦克 hitEnemyTank(); //判断敌人坦克是否击中咱们 hitHero(); this.repaint(); } }}package com.hspedu.tankgame5;/** * 一个Node 对象,示意一个敌人坦克的信息 */public class Node { private int x; private int y; private int direct; public Node(int x, int y, int direct) { this.x = x; this.y = y; this.direct = direct; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public int getDirect() { return direct; } public void setDirect(int direct) { this.direct = direct; }}package com.hspedu.tankgame5;import java.io.*;import java.nio.Buffer;import java.util.Vector;/** * 该类用于记录相干信息的.和文件交互 */@SuppressWarnings({"all"})public class Recorder { //定义变量,记录我方击毁敌人坦克数 private static int allEnemyTankNum = 0; //定义IO对象, 筹备写数据到文件中 private static BufferedWriter bw = null; private static BufferedReader br = null; private static String recordFile = "e:\\myRecord.txt"; //定义Vector ,指向 MyPanel 对象的 敌人坦克Vector private static Vector<EnemyTank> enemyTanks = null; //定义一个Node 的Vector ,用于保留敌人的信息node private static Vector<Node> nodes = new Vector<>(); public static void setEnemyTanks(Vector<EnemyTank> enemyTanks) { Recorder.enemyTanks = enemyTanks; } public static String getRecordFile() { return recordFile; } //减少一个办法,用于读取recordFile, 复原相干信息 //该办法,在持续上局的时候调用即可 public static Vector<Node> getNodesAndEnemyTankRec() { try { br = new BufferedReader(new FileReader(recordFile)); allEnemyTankNum = Integer.parseInt(br.readLine()); //循环读取文件,生成nodes 汇合 String line = "";//255 40 0 while ((line = br.readLine()) != null) { String[] xyd = line.split(" "); Node node = new Node(Integer.parseInt(xyd[0]), Integer.parseInt(xyd[1]), Integer.parseInt(xyd[2])); nodes.add(node); //放入nodes Vector } } catch (IOException e) { e.printStackTrace(); } finally { try { if (br != null) { br.close(); } } catch (IOException e) { e.printStackTrace(); } } return nodes; } //减少一个办法,当游戏退出时,咱们将allEnemyTankNum 保留到 recordFile //对keepRecord 进行降级, 保留敌人坦克的坐标和方向 public static void keepRecord() { try { bw = new BufferedWriter(new FileWriter(recordFile)); bw.write(allEnemyTankNum + "\r\n"); //遍历敌人坦克的Vector ,而后依据状况保留即可. //OOP, 定义一个属性 ,而后通过setXxx失去 敌人坦克的Vector for (int i = 0; i < enemyTanks.size(); i++) { //取出敌人坦克 EnemyTank enemyTank = enemyTanks.get(i); if (enemyTank.isLive) { //倡议判断. //保留该enemyTank信息 String record = enemyTank.getX() + " " + enemyTank.getY() + " " + enemyTank.getDirect(); //写入到文件 bw.write(record + "\r\n"); } } } catch (IOException e) { e.printStackTrace(); } finally { try { if (bw != null) { bw.close(); } } catch (IOException e) { e.printStackTrace(); } } } public static int getAllEnemyTankNum() { return allEnemyTankNum; } public static void setAllEnemyTankNum(int allEnemyTankNum) { Recorder.allEnemyTankNum = allEnemyTankNum; } //当我方坦克击毁一个敌人坦克,就该当 allEnemyTankNum++ public static void addAllEnemyTankNum() { Recorder.allEnemyTankNum++; }}package com.hspedu.tankgame5;/** * 射击子弹 */public class Shot implements Runnable { int x; //子弹x坐标 int y; //子弹y坐标 int direct = 0; //子弹方向 int speed = 2; //子弹的速度 boolean isLive = true; //子弹是否还存活 //结构器 public Shot(int x, int y, int direct) { this.x = x; this.y = y; this.direct = direct; } @Override public void run() {//射击 while (true) { //休眠 50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } //依据方向来扭转x,y坐标 switch (direct) { case 0://上 y -= speed; break; case 1://右 x += speed; break; case 2://下 y += speed; break; case 3://左 x -= speed; break; } //老师测试,这里咱们输入x,y的坐标 System.out.println("子弹 x=" + x + " y=" + y); //当子弹挪动到面板的边界时,就应该销毁(把启动的子弹的线程销毁) //当子弹碰到敌人坦克时,也应该完结线程 if (!(x >= 0 && x <= 1000 && y >= 0 && y <= 750 && isLive)) { System.out.println("子弹线程退出"); isLive = false; break; } } }}package com.hspedu.tankgame5;public class Tank { private int x;//坦克的横坐标 private int y;//坦克的纵坐标 private int direct = 0;//坦克方向 0 上1 右 2下 3左 private int speed = 1; boolean isLive = true; public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } //上右下左挪动办法 public void moveUp() { y -= speed; } public void moveRight() { x += speed; } public void moveDown() { y += speed; } public void moveLeft() { x -= speed; } public int getDirect() { return direct; } public void setDirect(int direct) { this.direct = direct; } public Tank(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; }}坦克大战0.6版减少性能 ...

May 8, 2023 · 31 min · jiezi

关于java:9种ElasticSearch分词器详解一文get-博学谷狂野架构师

ElasticSearch 分词器 作者: 博学谷狂野架构师GitHub:GitHub地址 (有我精心筹备的130本电子书PDF) 只分享干货、不吹水,让咱们一起加油!概述分词器的次要作用将用户输出的一段文本,依照肯定逻辑,剖析成多个词语的一种工具什么是分词器顾名思义,文本剖析就是把全文本转换成一系列单词(term/token)的过程,也叫分词。在 ES 中,Analysis 是通过分词器(Analyzer) 来实现的,可应用 ES 内置的分析器或者按需定制化分析器。举一个分词简略的例子:比方你输出 Mastering Elasticsearch,会主动帮你分成两个单词,一个是 mastering,另一个是 elasticsearch,能够看出单词也被转化成了小写的。 分词器的形成分词器是专门解决分词的组件,分词器由以下三局部组成:组成部分character filter接管原字符流,通过增加、删除或者替换操作扭转原字符流例如:去除文本中的html标签,或者将罗马数字转换成阿拉伯数字等。一个字符过滤器能够有零个或者多个 tokenizer简略的说就是将一整段文本拆分成一个个的词。例如拆分英文,通过空格能将句子拆分成一个个的词,然而对于中文来说,无奈应用这种形式来实现。在一个分词器中,有且只有一个tokenizeer token filters将切分的单词增加、删除或者扭转例如将所有英文单词小写,或者将英文中的停词a删除等,在token filters中,不容许将token(分出的词)的position或者offset扭转。同时,在一个分词器中,能够有零个或者多个token filters. 分词程序 同时 Analyzer 三个局部也是有程序的,从图中能够看出,从上到下顺次通过 Character Filters,Tokenizer 以及 Token Filters,这个程序比拟好了解,一个文本进来必定要先对文本数据进行解决,再去分词,最初对分词的后果进行过滤。 索引和搜寻分词文本分词会产生在两个中央:创立索引:当索引文档字符类型为text时,在建设索引时将会对该字段进行分词。搜寻:当对一个text类型的字段进行全文检索时,会对用户输出的文本进行分词。配置分词器默认ES应用standard analyzer,如果默认的分词器无奈合乎你的要求,能够本人配置分词器测试能够通过_analyzerAPI来测试分词的成果。COPY# 过滤html 标签POST _analyze{ "tokenizer":"keyword", #原样输入 "char_filter":["html_strip"], # 过滤html标签 "text":"<b>hello world<b>" # 输出的文本} 指定分词器应用中央分词器的应用中央有两个:创立索引时进行搜寻时创立索引时指定分词器如果设置手动设置了分词器,ES将依照上面程序来确定应用哪个分词器:先判断字段是否有设置分词器,如果有,则应用字段属性上的分词器设置如果设置了analysis.analyzer.default,则应用该设置的分词器如果下面两个都未设置,则应用默认的standard分词器字段指定分词器为title属性指定分词器COPYPUT my_index{ "mappings": { "properties": { "title":{ "type":"text", "analyzer": "whitespace" } } }}设置默认分词器COPYPUT my_index{ "settings": { "analysis": { "analyzer": { "default":{ "type":"simple" } } } }}搜寻时如何确定分词器在搜寻时,通过上面参数顺次查看搜寻时应用的分词器:搜寻时指定analyzer参数创立mapping时指定字段的search_analyzer属性创立索引时指定setting的analysis.analyzer.default_search查看创立索引时字段指定的analyzer属性如果下面几种都未设置,则应用默认的standard分词器。指定analyzer搜寻时指定analyzer查问参数COPYGET my_index/_search{ "query": { "match": { "message": { "query": "Quick foxes", "analyzer": "stop" } } }}指定字段analyzerCOPYPUT my_index{ "mappings": { "properties": { "title":{ "type":"text", "analyzer": "whitespace", "search_analyzer": "simple" } } }}指定默认default_seachCOPYPUT my_index{ "settings": { "analysis": { "analyzer": { "default":{ "type":"simple" }, "default_seach":{ "type":"whitespace" } } } }}内置分词器es在索引文档时,会通过各种类型 Analyzer 对text类型字段做剖析,不同的 Analyzer 会有不同的分词后果,内置的分词器有以下几种,基本上内置的 Analyzer 包含 Language Analyzers 在内,对中文的分词都不够敌对,中文分词须要装置其它 Analyzer ...

May 8, 2023 · 4 min · jiezi

关于java:9种ElasticSearch分词器详解一文get-博学谷狂野架构师

ElasticSearch 分词器 作者: 博学谷狂野架构师GitHub:GitHub地址 (有我精心筹备的130本电子书PDF) 只分享干货、不吹水,让咱们一起加油!概述分词器的次要作用将用户输出的一段文本,依照肯定逻辑,剖析成多个词语的一种工具什么是分词器顾名思义,文本剖析就是把全文本转换成一系列单词(term/token)的过程,也叫分词。在 ES 中,Analysis 是通过分词器(Analyzer) 来实现的,可应用 ES 内置的分析器或者按需定制化分析器。举一个分词简略的例子:比方你输出 Mastering Elasticsearch,会主动帮你分成两个单词,一个是 mastering,另一个是 elasticsearch,能够看出单词也被转化成了小写的。 分词器的形成分词器是专门解决分词的组件,分词器由以下三局部组成:组成部分character filter接管原字符流,通过增加、删除或者替换操作扭转原字符流例如:去除文本中的html标签,或者将罗马数字转换成阿拉伯数字等。一个字符过滤器能够有零个或者多个 tokenizer简略的说就是将一整段文本拆分成一个个的词。例如拆分英文,通过空格能将句子拆分成一个个的词,然而对于中文来说,无奈应用这种形式来实现。在一个分词器中,有且只有一个tokenizeer token filters将切分的单词增加、删除或者扭转例如将所有英文单词小写,或者将英文中的停词a删除等,在token filters中,不容许将token(分出的词)的position或者offset扭转。同时,在一个分词器中,能够有零个或者多个token filters. 分词程序 同时 Analyzer 三个局部也是有程序的,从图中能够看出,从上到下顺次通过 Character Filters,Tokenizer 以及 Token Filters,这个程序比拟好了解,一个文本进来必定要先对文本数据进行解决,再去分词,最初对分词的后果进行过滤。 索引和搜寻分词文本分词会产生在两个中央:创立索引:当索引文档字符类型为text时,在建设索引时将会对该字段进行分词。搜寻:当对一个text类型的字段进行全文检索时,会对用户输出的文本进行分词。配置分词器默认ES应用standard analyzer,如果默认的分词器无奈合乎你的要求,能够本人配置分词器测试能够通过_analyzerAPI来测试分词的成果。COPY# 过滤html 标签POST _analyze{ "tokenizer":"keyword", #原样输入 "char_filter":["html_strip"], # 过滤html标签 "text":"<b>hello world<b>" # 输出的文本} 指定分词器应用中央分词器的应用中央有两个:创立索引时进行搜寻时创立索引时指定分词器如果设置手动设置了分词器,ES将依照上面程序来确定应用哪个分词器:先判断字段是否有设置分词器,如果有,则应用字段属性上的分词器设置如果设置了analysis.analyzer.default,则应用该设置的分词器如果下面两个都未设置,则应用默认的standard分词器字段指定分词器为title属性指定分词器COPYPUT my_index{ "mappings": { "properties": { "title":{ "type":"text", "analyzer": "whitespace" } } }}设置默认分词器COPYPUT my_index{ "settings": { "analysis": { "analyzer": { "default":{ "type":"simple" } } } }}搜寻时如何确定分词器在搜寻时,通过上面参数顺次查看搜寻时应用的分词器:搜寻时指定analyzer参数创立mapping时指定字段的search_analyzer属性创立索引时指定setting的analysis.analyzer.default_search查看创立索引时字段指定的analyzer属性如果下面几种都未设置,则应用默认的standard分词器。指定analyzer搜寻时指定analyzer查问参数COPYGET my_index/_search{ "query": { "match": { "message": { "query": "Quick foxes", "analyzer": "stop" } } }}指定字段analyzerCOPYPUT my_index{ "mappings": { "properties": { "title":{ "type":"text", "analyzer": "whitespace", "search_analyzer": "simple" } } }}指定默认default_seachCOPYPUT my_index{ "settings": { "analysis": { "analyzer": { "default":{ "type":"simple" }, "default_seach":{ "type":"whitespace" } } } }}内置分词器es在索引文档时,会通过各种类型 Analyzer 对text类型字段做剖析,不同的 Analyzer 会有不同的分词后果,内置的分词器有以下几种,基本上内置的 Analyzer 包含 Language Analyzers 在内,对中文的分词都不够敌对,中文分词须要装置其它 Analyzer ...

May 8, 2023 · 4 min · jiezi

关于java:高并发场景下的-HttpClient-优化QPS-大大提升

HttpClient优化思路: 池化长连贯httpclient和httpget复用正当的配置参数(最大并发申请数,各种超时工夫,重试次数)异步 6、多读源码1.背景咱们有个业务,会调用其余部门提供的一个基于http的服务,日调用量在千万级别。应用了httpclient来实现业务。之前因为qps上不去,就看了一下业务代码,并做了一些优化,记录在这里。 先比照前后:优化之前,均匀执行工夫是250ms;优化之后,均匀执行工夫是80ms,升高了三分之二的耗费,容器不再动不动就报警线程耗尽了,清新~ 2.剖析我的项目的原实现比拟粗略,就是每次申请时初始化一个httpclient,生成一个httpPost对象,执行,而后从返回后果取出entity,保留成一个字符串,最初显式敞开response和client。咱们一点点剖析和优化: 2.1 httpclient重复创立开销httpclient是一个线程平安的类,没有必要由每个线程在每次应用时创立,全局保留一个即可。 2.2 重复创立tcp连贯的开销tcp的三次握手与四次挥手两大裹脚布过程,对于高频次的申请来说,耗费切实太大。试想如果每次申请咱们须要破费5ms用于协商过程,那么对于qps为100的单零碎,1秒钟咱们就要花500ms用于握手和挥手。又不是高级领导,咱们程序员就不要搞这么大做派了,改成keep alive形式以实现连贯复用! 2.3 反复缓存entity的开销本来的逻辑里,应用了如下代码: HttpEntity entity = httpResponse.getEntity();String response = EntityUtils.toString(entity);这里咱们相当于额定复制了一份content到一个字符串里,而本来的httpResponse依然保留了一份content,须要被consume掉,在高并发且content十分大的状况下,会耗费大量内存。并且,咱们须要显式的敞开连贯,ugly。 3.实现按下面的剖析,咱们次要要做三件事:一是单例的client,二是缓存的保活连贯,三是更好的解决返回后果。一就不说了,来说说二。 提到连贯缓存,很容易联想到数据库连接池。httpclient4提供了一个PoolingHttpClientConnectionManager 作为连接池。接下来咱们通过以下步骤来优化: 3.1 定义一个keep alive strategy对于keep-alive,本文不开展阐明,只提一点,是否应用keep-alive要依据业务状况来定,它并不是灵丹妙药。还有一点,keep-alive和time_wait/close_wait之间也有不少故事。 在本业务场景里,咱们相当于有多数固定客户端,长时间极高频次的拜访服务器,启用keep-alive十分适合 再多提一嘴,http的keep-alive 和tcp的KEEPALIVE不是一个货色。回到注释,定义一个strategy如下: ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() { @Override public long getKeepAliveDuration(HttpResponse response, HttpContext context) { HeaderElementIterator it = new BasicHeaderElementIterator (response.headerIterator(HTTP.CONN_KEEP_ALIVE)); while (it.hasNext()) { HeaderElement he = it.nextElement(); String param = he.getName(); String value = he.getValue(); if (value != null && param.equalsIgnoreCase ("timeout")) { return Long.parseLong(value) * 1000; } } return 60 * 1000;//如果没有约定,则默认定义时长为60s }};3.2 配置一个PoolingHttpClientConnectionManagerPoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();connectionManager.setMaxTotal(500);connectionManager.setDefaultMaxPerRoute(50);//例如默认每路由最高50并发,具体根据业务来定也能够针对每个路由设置并发数。 ...

May 8, 2023 · 4 min · jiezi

关于java:Arrays点燃你的数组操作技巧的隐秘武器

前言 数组在 Java 中是一种罕用的数据结构,用于存储和操作大量数据。然而在解决数组中的数据,可能会变得复杂和繁琐。Arrays 是咱们在解决数组时的一把利器。它提供了丰盛的办法和性能,使得数组操作变得更加简略、高效和牢靠。无论是排序、搜寻、比拟还是复制,Arrays 都可能满足咱们的需要,来帮忙咱们充分发挥数组的后劲。接下来咱们一起看看 Arrays 的各种性能和用法,以帮忙咱们更好地利用这个弱小的工具。 asList() - 转换为固定大小列表:将指定的元素转换为一个固定大小的列表。binarySearch() - 二分查找:在指定数组中应用二分查找算法查找指定元素。copyOf() - 复制数组:将指定数组的正本复制到一个新数组中。copyOfRange() - 范畴复制数组:将指定数组的指定范畴内的元素复制到一个新数组中。fill() - 填充数组:将指定的值填充到数组的每个元素中。equals() - 比拟数组相等性:比拟两个数组是否相等。setAll() - 设置所有元素:应用指定的生成器函数设置数组的元素值。sort() - 排序数组:对指定数组进行升序排序。stream() - 转换为流:将数组转换为流。spliterator() - 宰割迭代器:创立一个用于遍历数组元素的宰割迭代器。parallelPrefix() - 并行前缀:对数组中的元素进行累积操作,将后果存储在数组中。parallelSetAll() - 并行设置所有元素:应用指定的生成器函数并行地设置数组的元素值。parallelSort() - 并行排序:对指定数组进行并行排序。hashCode() - 哈希码值:返回数组的哈希码值。deepHashCode() - 多维数组的哈希码值:返回多维数组的哈希码值。deepToString() - 多维数组的字符串示意模式:返回多维数组的字符串示意模式。toString() - 数组的字符串示意模式:返回数组的字符串示意模式。代码示例1. asList() - 转换为固定大小列表:import java.util.Arrays;import java.util.List;public class AsListExample { public static void main(String[] args) { String[] array = {"A", "B", "C"}; System.out.println("原始数组: " + Arrays.toString(array)); List<String> list = Arrays.asList(array); System.out.println("转换后的列表: " + list); }}输入后果: ...

May 7, 2023 · 4 min · jiezi

关于java:java-线程学习

感觉本人java基础知识学的太差,记录一下本人学习 深入浅出多线程 的笔记。 笔记连贯:https://flowus.cn/share/d4a68486-0162-4759-816e-551b275218f3【FlowUs 息流】 1.过程和线程基本概念过程产生的背景: 最后计算机只能输出一次指令而后执行一次,效率较低。起初有了批处理零碎,用户能够将一串的指令交给计算机,由计算机顺次解决。然而还是串行解决,随程序的阻塞而阻塞。 为了解决这个问题,呈现了过程的概念:指正在运行的一个程序或应用程序,内存中能够存在多个过程。CPU采纳工夫片轮转的形式运行过程。 每个过程的工夫片完结后,把CPU调配给下一个过程,如果以后过程工作没完结,则保留以后过程的信息,期待下一次调配; 然而,人们并不满足于此。如果一个过程蕴含多个子工作,过程也只能一一执行,效率不高。 那么能不能让这些子工作同时执行呢?于是人们又提出了线程的概念,那么能不能让这些子工作同时执行呢?于是人们又提出了线程的概念,让一个线程执行一个子工作,这样一个过程就蕴含了多个线程,每个线程负责一个独自的子工作。例如: 一个下载软件能够应用多个线程同时下载多个文件,从而大大缩短下载工夫。 线程和过程: 2. Thread类和Runnable接口自定义Thread类: public class Demo { public static class MyThread extends Thread { @Override public void run() { System.out.println("MyThread"); } } public static void main(String[] args) { Thread myThread = new MyThread(); myThread.start(); }}start()办法后,该线程才算启动调用了start()办法后,虚构机会创立一个线程,而后等到这个线程第一次失去工夫片时再调用run()办法。第二次调用start()办法会抛出IllegalThreadStateException异样。、Thread 类是一个 Runnable 接口的实现类。 @FunctionalInterfacepublic interface Runnable { public abstract void run();}Runnable接口只有一个 run 办法, 并且有 @FunctionalInterface 注解。意思是:函数式接口, 接口外面只能有一个形象办法 阐明咱们能够应用 Lambda 表达式 public static void main(String[] args) { new Thread(new MyThread()).start(); // Java 8 函数式编程,能够省略MyThread类 new Thread(() -> { System.out.println("Java 8 匿名外部类"); }).start(); }2.1 线程优先级每个Java程序都有一个默认的主线程,就是通过JVM启动的第一个线程main线程。Java中线程优先级能够指定,范畴是1~10。Java默认的线程优先级为5,Java中的优先级来说不是特地的牢靠,Java程序中对线程所设置的优先级只是给操作系统一个倡议,操作系统不肯定会驳回。而真正的调用程序,是由操作系统的线程调度算法决定的。例如咱们能够应用如下的代码测试一下: ...

May 7, 2023 · 3 min · jiezi

关于java:美团面经总结2023最新

分享一份读者面试美团的面经,比拟有参考性,感兴趣的能够看看~ 一面音讯队列如何保障可靠性音讯队列如何保障音讯幂等性音讯队列的优缺点为什么用b+树汇集索引和主键区别,其余引擎怎么做的平时数据库编码explain参数http报文参数有哪些吗?做题,链表奇偶有序输入二面自我介绍有哪些排序算法?介绍下快排/堆排/归并排序。数据库中的索引应该如何设计?有哪些索引生效的状况?你们用到的HTTP接口用到了什么提交形式?GET/POST的区别?除了GET/POST还有哪些?面向对象的根本准则?再具体说下依赖倒转。介绍下策略模式和观察者模式?如何保障用户申请的等幂性?等幂性指的是用户可能连点提交三次领取申请,返回同样的后果(领取胜利),但理论后盾只执行一次,放弃一致性。介绍下TCP四次挥手?第四次挥手后客户端是立即就敞开了吗?是什么状态?两个大文件,别离每行都存一个url,查找两个文件中反复的url。一个大文件中,每一行有一个整数,怎么找第100大的数?一个大文件中,每一行有一个整数,怎么找中位数?redis的根本数据结构?zset是怎么实现的?有哪些命令?算法题 力扣221. 最大正方形三面我的项目相干(模块划分,我的项目需要,技术计划,数据库设计,表的构造及关系,负责角色)http协定的关键字段,比方request和response头部信息有哪些关键字段,有什么含意http状态码:100,200,502,504http和https的区别,https是为了解决什么问题三次握手、四次挥手(具体过程+状态变动)呈现大量的close_wait可能是什么起因,解决方案,通过什么工具看进去网络有问题等等Java中常见的汇合有哪些,List、Set、Map初始容量加载因子理解吗Java中线程通信的形式有哪些,大略的原理MySQL如果遇到性能不好的问题,比如说慢查问,怎么做数据库优化计划(索引 | 分库分表)有哪些索引,数据结构,建设索引的准则分库分表的准则,说说场景(程度 | 垂直、热数据 | 冷数据 blabla)算法题:两数之和四面自我介绍、我的项目介绍,问了数据量理解微服务吗?(有没有本人在做我的项目时进行调研,理解企业目前罕用的工具、办法)理解springcloud吗?一台机器无奈满足运载需要,怎么办呢?答:多搞几台机器,问:多台机器如何协同工作?解释一下mapreduce如果有一个很大的文件,TB级别,文件里是乱序的数字,如何排序?mapreduce如何实现?排序过程中的归并排序,请形容一下其过程?工夫复杂度过程、线程区别,问应用Java时,外面多线程的概念和os里的线程过程的区别是什么?真正应用时,Java里的线程和过程是如何调度?多线程的同步互斥的办法?答了信号量,问具体怎么实现,答pv操作,给了具体的场景,问变量如何初始化(等同于口述代码)有哪些索引?(mysql为例)b树、b+树是什么样的树结构,查问复杂度?是均衡二叉树吗?应用过redis吗?具体做什么?手撕代码:LRU算法;正反序层序遍历二叉树本文曾经收录到Github仓库,该仓库蕴含计算机根底、Java根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等外围知识点,欢送star~ Github地址 如果拜访不了Github,能够拜访gitee地址。 gitee地址

May 7, 2023 · 1 min · jiezi

关于java:Java-IO流详解

文章和代码曾经归档至【Github仓库:https://github.com/timerring/java-tutorial 】或者公众号【AIShareLab】回复 java 也可获取。文件文件,对咱们并不生疏,文件是保留数据的中央。文件在程序中是以流的模式来操作的。 流:数据在数据源(文件)和程序(内存)之间经验的门路 输出流:数据从数据源(文件)到程序(内存)的门路 输入流:数据从程序(内存)到数据源(文件)的门路 罕用的文件操作创立文件对象相干结构器和办法new File(String pathname) //依据门路构建一个File对象new File(File parent, String child) //依据父目录文件+子门路构建newFile(String parent, String child) //依据父目录+子门路构建package com.hspedu.file;import org.junit.jupiter.api.Test;import java.io.*;/** * 演示创立文件 */public class FileCreate { public static void main(String[] args) { } //形式1 new File(String pathname) @Test public void create01() { String filePath = "e:\\news1.txt"; File file = new File(filePath); try { file.createNewFile(); System.out.println("文件创建胜利"); } catch (IOException e) { e.printStackTrace(); } } //形式2 new File(File parent,String child) //依据父目录文件+子门路构建 //e:\\news2.txt @Test public void create02() { File parentFile = new File("e:\\"); String fileName = "news2.txt"; //这里的file对象,在java程序中,只是一个对象 //只有执行了createNewFile 办法,才会真正的,在磁盘创立该文件 File file = new File(parentFile, fileName); try { file.createNewFile(); System.out.println("创立胜利~"); } catch (IOException e) { e.printStackTrace(); } } //形式3 new File(String parent,String child) //依据父目录+子门路构建 @Test public void create03() { //String parentPath = "e:\\"; String parentPath = "e:\\"; String fileName = "news4.txt"; File file = new File(parentPath, fileName); try { file.createNewFile(); System.out.println("创立胜利~"); } catch (IOException e) { e.printStackTrace(); } } //上面四个都是抽象类 //InputStream //OutputStream //Reader //字符输出流 //Writer //字符输入流}获取文件的相干信息getName、getAbsolutePath、getParent、length、exists、isFile、isDirectory ...

May 7, 2023 · 15 min · jiezi

关于java:Java实现坦克大战20

文章和代码曾经归档至【Github仓库:https://github.com/timerring/java-tutorial 】或者公众号【AIShareLab】回复 java 也可获取。线程-利用到坦克大战坦克大战0.3剖析如何实现当用户按下J键,咱们的坦克就发射一颗子弹,思路: 当发射一颗子弹后,就相当于启动一个线程Hero有子弹的对象,当按下J时,咱们就启动一个发射行为(线程),让子弹不停的挪动,造成一个射击的成果。咱们MyPanel须要不停的重绘子弹,能力呈现该成果.当子弹挪动到面板的边界时,就应该销毁(把启动的子弹的线程销毁)坦克大战0.4减少性能 让敌人的坦克也可能发射子弹(能够有多颗子弹) 在敌人坦克类,应用Vector保留多个Shot当每创立一个敌人坦克对象,给该敌人坦克对象初始化一个Shot对象,同时启动Shot在绘制敌人坦克时,须要遍历敌人坦克对象Vector,绘会制所有的子弹,当子弹isLive == false时,就从Vector移除当我方坦克击中敌人坦克时,敌人的坦克就隐没,如果能做出爆炸成果更好.让敌人的坦克也能够自在随机的上下左右挪动 因为要求敌人的坦克,能够自在挪动,因而须要将敌人坦克当做线程应用咱们须要Enemy Tank implements Runnable在run办法写上咱们相应的业务代码.在创立敌人坦克对象时,启动线程管制我方的坦克和敌人的坦克在规定的范畴挪动剖析->解决减少性能 我方坦克在发射的子弹沦亡后,能力发射新的子弹.=>扩大(发多颗子弹怎么办,管制在咱们的面板上,最多只有5颗)-》在课后欠缺 1.在按下J键,咱们判断以后hero对象的子弹,是否曾经销毁 2.如果没有销毁,就不去触发shotEnemyTank 3.如果曾经销毁,才去触发shotEnemyTank 4.如果要发射多颗子弹,就应用Vector保留 5.在绘制我方子弹时,须要遍历该Vector汇合 让敌人坦克发射的子弹沦亡后,能够再发射子弹当敌人的坦克击中我方坦克时,我方坦克隐没,并呈现爆炸成果思路:编写办法,判断敌人的坦克是否击中我的坦克课后练习:让敌人坦克能够最多发射3颗(在面板E),咱们的坦克能够发射3颗.并且可能呈现失常的爆炸成果即可.package com.hspedu.tankgame4;/** * 炸弹 */public class Bomb { int x, y; //炸弹的坐标 int life = 9; //炸弹的生命周期 boolean isLive = true; //是否还存活 public Bomb(int x, int y) { this.x = x; this.y = y; } //缩小生命值 public void lifeDown() { //配合呈现图片的爆炸成果 if(life > 0) { life--; } else { isLive = false; } }}package com.hspedu.tankgame4;import java.util.Vector;/** * 敌人的坦克 */@SuppressWarnings({"all"})public class EnemyTank extends Tank implements Runnable { //在敌人坦克类,应用Vector 保留多个Shot Vector<Shot> shots = new Vector<>(); boolean isLive = true; public EnemyTank(int x, int y) { super(x, y); } @Override public void run() { while (true) { //这里咱们判断如果shots size() =0, 创立一颗子弹,放入到 //shots汇合,并启动 if (isLive && shots.size() < 1) { Shot s = null; //判断坦克的方向,创立对应的子弹 switch (getDirect()) { case 0: s = new Shot(getX() + 20, getY(), 0); break; case 1: s = new Shot(getX() + 60, getY() + 20, 1); break; case 2: //向下 s = new Shot(getX() + 20, getY() + 60, 2); break; case 3://向左 s = new Shot(getX(), getY() + 20, 3); break; } shots.add(s); //启动 new Thread(s).start(); } //依据坦克的方向来持续动 switch (getDirect()) { case 0: //向上 //让坦克放弃一个方向,走30步 for (int i = 0; i < 30; i++) { if (getY() > 0) { moveUp(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 1: //向右 for (int i = 0; i < 30; i++) { if (getX() + 60 < 1000) { moveRight(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 2: //向下 for (int i = 0; i < 30; i++) { if (getY() + 60 < 750) { moveDown(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 3: //向左 for (int i = 0; i < 30; i++) { if (getX() > 0) { moveLeft(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; } // 而后随机的扭转坦克方向 0-3 setDirect((int) (Math.random() * 4)); // 写并发程序,肯定要思考分明,该线程什么时候完结 if (!isLive) { break; //退出线程. } } }}package com.hspedu.tankgame4;import java.util.Vector;/** * 本人的坦克 */public class Hero extends Tank { //定义一个Shot对象, 示意一个射击(线程) Shot shot = null; //能够发射多颗子弹 //Vector<Shot> shots = new Vector<>(); public Hero(int x, int y) { super(x, y); } //射击 public void shotEnemyTank() { //发多颗子弹怎么办, 管制在咱们的面板上,最多只有5颗// if(shots.size() == 5) {// return;// } //创立 Shot 对象, 依据以后Hero对象的地位和方向来创立Shot switch (getDirect()) {//失去Hero对象方向 case 0: //向上 shot = new Shot(getX() + 20, getY(), 0); break; case 1: //向右 shot = new Shot(getX() + 60, getY() + 20, 1); break; case 2: //向下 shot = new Shot(getX() + 20, getY() + 60, 2); break; case 3: //向左 shot = new Shot(getX(), getY() + 20, 3); break; } //把新创建的shot放入到shots //shots.add(shot); //启动咱们的Shot线程 new Thread(shot).start(); }}package com.hspedu.tankgame4;import javax.swing.*;public class HspTankGame04 extends JFrame { //定义MyPanel MyPanel mp = null; public static void main(String[] args) { HspTankGame04 hspTankGame01 = new HspTankGame04(); } public HspTankGame04() { mp = new MyPanel(); //将mp 放入到Thread ,并启动 Thread thread = new Thread(mp); thread.start(); this.add(mp);//把面板(就是游戏的绘图区域) this.setSize(1200, 950); this.addKeyListener(mp);//让JFrame 监听mp的键盘事件 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); }}package com.hspedu.tankgame4;import javax.swing.*;import java.awt.*;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.util.Vector;/** * 坦克大战的绘图区域 *///为了监听 键盘事件, 实现KeyListener//为了让Panel 不停的重绘子弹,须要将 MyPanel 实现Runnable ,当做一个线程应用public class MyPanel extends JPanel implements KeyListener, Runnable { //定义我的坦克 Hero hero = null; //定义敌人坦克,放入到Vector Vector<EnemyTank> enemyTanks = new Vector<>(); //定义一个Vector ,用于寄存炸弹 //阐明,当子弹击中坦克时,退出一个Bomb对象到bombs Vector<Bomb> bombs = new Vector<>(); int enemyTankSize = 3; //定义三张炸弹图片,用于显示爆炸成果 Image image1 = null; Image image2 = null; Image image3 = null; public MyPanel() { hero = new Hero(500, 100);//初始化本人坦克 //初始化敌人坦克 for (int i = 0; i < enemyTankSize; i++) { //创立一个敌人的坦克 EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0); //设置方向 enemyTank.setDirect(2); //启动敌人坦克线程,让他动起来 new Thread(enemyTank).start(); //给该enemyTank 退出一颗子弹 Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect()); //退出enemyTank的Vector 成员 enemyTank.shots.add(shot); //启动 shot 对象 new Thread(shot).start(); //退出 enemyTanks.add(enemyTank); } //初始化图片对象 image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif")); image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif")); image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif")); } @Override public void paint(Graphics g) { super.paint(g); g.fillRect(0, 0, 1000, 750);//填充矩形,默认彩色 if(hero != null && hero.isLive) { //画出本人坦克-封装办法 drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1); } //画出hero射击的子弹 if (hero.shot != null && hero.shot.isLive == true) { g.draw3DRect(hero.shot.x, hero.shot.y, 1, 1, false); } //将hero的子弹汇合 shots ,遍历取出绘制// for(int i = 0; i < hero.shots.size(); i++) {// Shot shot = hero.shots.get(i);// if (shot != null && shot.isLive) {// g.draw3DRect(shot.x, shot.y, 1, 1, false);//// } else {//如果该shot对象曾经有效 ,就从shots汇合中拿掉// hero.shots.remove(shot);// }// } //如果bombs 汇合中有对象,就画出 for (int i = 0; i < bombs.size(); i++) { //取出炸弹 Bomb bomb = bombs.get(i); //依据以后这个bomb对象的life值去画出对应的图片 if (bomb.life > 6) { g.drawImage(image1, bomb.x, bomb.y, 60, 60, this); } else if (bomb.life > 3) { g.drawImage(image2, bomb.x, bomb.y, 60, 60, this); } else { g.drawImage(image3, bomb.x, bomb.y, 60, 60, this); } //让这个炸弹的生命值缩小 bomb.lifeDown(); //如果bomb life 为0, 就从bombs 的汇合中删除 if (bomb.life == 0) { bombs.remove(bomb); } } //画出敌人的坦克, 遍历Vector for (int i = 0; i < enemyTanks.size(); i++) { //从Vector 取出坦克 EnemyTank enemyTank = enemyTanks.get(i); //判断以后坦克是否还存活 if (enemyTank.isLive) {//当敌人坦克是存活的,才画出该坦克 drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0); //画出 enemyTank 所有子弹 for (int j = 0; j < enemyTank.shots.size(); j++) { //取出子弹 Shot shot = enemyTank.shots.get(j); //绘制 if (shot.isLive) { //isLive == true g.draw3DRect(shot.x, shot.y, 1, 1, false); } else { //从Vector 移除 enemyTank.shots.remove(shot); } } } } } //编写办法,画出坦克 /** * @param x 坦克的左上角x坐标 * @param y 坦克的左上角y坐标 * @param g 画笔 * @param direct 坦克方向(上下左右) * @param type 坦克类型 */ public void drawTank(int x, int y, Graphics g, int direct, int type) { //依据不同类型坦克,设置不同色彩 switch (type) { case 0: //敌人的坦克 g.setColor(Color.cyan); break; case 1: //我的坦克 g.setColor(Color.yellow); break; } //依据坦克方向,来绘制对应形态坦克 //direct 示意方向(0: 向上 1 向右 2 向下 3 向左 ) // switch (direct) { case 0: //示意向上 g.fill3DRect(x, y, 10, 60, false);//画出坦克右边轮子 g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克左边轮子 g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克盖子 g.fillOval(x + 10, y + 20, 20, 20);//画出圆形盖子 g.drawLine(x + 20, y + 30, x + 20, y);//画出炮筒 break; case 1: //示意向右 g.fill3DRect(x, y, 60, 10, false);//画出坦克上边轮子 g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边轮子 g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克盖子 g.fillOval(x + 20, y + 10, 20, 20);//画出圆形盖子 g.drawLine(x + 30, y + 20, x + 60, y + 20);//画出炮筒 break; case 2: //示意向下 g.fill3DRect(x, y, 10, 60, false);//画出坦克右边轮子 g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克左边轮子 g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克盖子 g.fillOval(x + 10, y + 20, 20, 20);//画出圆形盖子 g.drawLine(x + 20, y + 30, x + 20, y + 60);//画出炮筒 break; case 3: //示意向左 g.fill3DRect(x, y, 60, 10, false);//画出坦克上边轮子 g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克下边轮子 g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克盖子 g.fillOval(x + 20, y + 10, 20, 20);//画出圆形盖子 g.drawLine(x + 30, y + 20, x, y + 20);//画出炮筒 break; default: System.out.println("临时没有解决"); } } //如果咱们的坦克能够发射多个子弹 //在判断我方子弹是否击中敌人坦克时,就须要把咱们的子弹汇合中 //所有的子弹,都取出和敌人的所有坦克,进行判断 //老韩给的局部代码.. public void hitEnemyTank() {// //遍历咱们的子弹// for(int j = 0;j < hero.shots.size();j++) {// Shot shot = hero.shots.get(j);// //判断是否击中了敌人坦克// if (shot != null && hero.shot.isLive) {//当我的子弹还存活//// //遍历敌人所有的坦克// for (int i = 0; i < enemyTanks.size(); i++) {// EnemyTank enemyTank = enemyTanks.get(i);// hitTank(hero.shot, enemyTank);// }//// }// } //单颗子弹。 if (hero.shot != null && hero.shot.isLive) {//当我的子弹还存活 //遍历敌人所有的坦克 for (int i = 0; i < enemyTanks.size(); i++) { EnemyTank enemyTank = enemyTanks.get(i); hitTank(hero.shot, enemyTank); } } } //编写办法,判断敌人坦克是否击中我的坦克 public void hitHero() { //遍历所有的敌人坦克 for (int i = 0; i < enemyTanks.size(); i++) { //取出敌人坦克 EnemyTank enemyTank = enemyTanks.get(i); //遍历enemyTank 对象的所有子弹 for (int j = 0; j < enemyTank.shots.size(); j++) { //取出子弹 Shot shot = enemyTank.shots.get(j); //判断 shot 是否击中我的坦克 if (hero.isLive && shot.isLive) { hitTank(shot, hero); } } } } //编写办法,判断我方的子弹是否击中敌人坦克. //什么时候判断 我方的子弹是否击中敌人坦克 ? run办法 //前面咱们将 enemyTank 改成 tank名称 public void hitTank(Shot s, Tank enemyTank) { //判断s 击中坦克 switch (enemyTank.getDirect()) { case 0: //坦克向上 case 2: //坦克向下 if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 40 && s.y > enemyTank.getY() && s.y < enemyTank.getY() + 60) { s.isLive = false; enemyTank.isLive = false; //当我的子弹击中敌人坦克后,将enemyTank 从Vector 拿掉 enemyTanks.remove(enemyTank); //创立Bomb对象,退出到bombs汇合 Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY()); bombs.add(bomb); } break; case 1: //坦克向右 case 3: //坦克向左 if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 60 && s.y > enemyTank.getY() && s.y < enemyTank.getY() + 40) { s.isLive = false; enemyTank.isLive = false; //创立Bomb对象,退出到bombs汇合 Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY()); bombs.add(bomb); } break; } } @Override public void keyTyped(KeyEvent e) { } //解决wdsa 键按下的状况 @Override public void keyPressed(KeyEvent e) { System.out.println(e.getKeyCode()); if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键 //扭转坦克的方向 hero.setDirect(0);// //批改坦克的坐标 y -= 1 if (hero.getY() > 0) { hero.moveUp(); } } else if (e.getKeyCode() == KeyEvent.VK_D) {//D键, 向右 hero.setDirect(1); if (hero.getX() + 60 < 1000) { hero.moveRight(); } } else if (e.getKeyCode() == KeyEvent.VK_S) {//S键 hero.setDirect(2); if (hero.getY() + 60 < 750) { hero.moveDown(); } } else if (e.getKeyCode() == KeyEvent.VK_A) {//A键 hero.setDirect(3); if (hero.getX() > 0) { hero.moveLeft(); } } //如果用户按下的是J,就发射 if (e.getKeyCode() == KeyEvent.VK_J) { //判断hero的子弹是否销毁,发射一颗子弹 if (hero.shot == null || !hero.shot.isLive) { hero.shotEnemyTank(); } //发射多颗子弹 //hero.shotEnemyTank(); } //让面板重绘 this.repaint(); } @Override public void keyReleased(KeyEvent e) { } @Override public void run() { //每隔 100毫秒,重绘区域, 刷新绘图区域, 子弹就挪动 while (true) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //判断是咱们子弹否击中了敌人坦克 hitEnemyTank(); //判断敌人坦克是否击中咱们 hitHero(); this.repaint(); } }}package com.hspedu.tankgame4;/** * 射击子弹 */public class Shot implements Runnable { int x; //子弹x坐标 int y; //子弹y坐标 int direct = 0; //子弹方向 int speed = 2; //子弹的速度 boolean isLive = true; //子弹是否还存活 //结构器 public Shot(int x, int y, int direct) { this.x = x; this.y = y; this.direct = direct; } @Override public void run() {//射击 while (true) { //休眠 50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } //依据方向来扭转x,y坐标 switch (direct) { case 0://上 y -= speed; break; case 1://右 x += speed; break; case 2://下 y += speed; break; case 3://左 x -= speed; break; } //老师测试,这里咱们输入x,y的坐标 System.out.println("子弹 x=" + x + " y=" + y); //当子弹挪动到面板的边界时,就应该销毁(把启动的子弹的线程销毁) //当子弹碰到敌人坦克时,也应该完结线程 if (!(x >= 0 && x <= 1000 && y >= 0 && y <= 750 && isLive)) { System.out.println("子弹线程退出"); isLive = false; break; } } }}package com.hspedu.tankgame4;public class Tank { private int x;//坦克的横坐标 private int y;//坦克的纵坐标 private int direct = 0;//坦克方向 0 上1 右 2下 3左 private int speed = 1; boolean isLive = true; public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } //上右下左挪动办法 public void moveUp() { y -= speed; } public void moveRight() { x += speed; } public void moveDown() { y += speed; } public void moveLeft() { x -= speed; } public int getDirect() { return direct; } public void setDirect(int direct) { this.direct = direct; } public Tank(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; }}

May 7, 2023 · 9 min · jiezi

关于java:Java-线程池原理总结

本文首发自「慕课网」,想理解更多IT干货内容,程序员圈内热闻,欢送关注"慕课网"! 作者:大能|慕课网讲师 Java 线程池原理总结 (一)什么是线程池 线程池做的工作次要是管制运行的线程的数量,解决过程中将工作放入队列,而后在线程创立后启动这些工作,如果线程数量超过了最大数量超出数量的线程排队等待,等其它线程执行结束,再从队列中取出工作来执行。他的次要特点为: 升高资源耗费。通过反复利用已创立的线程升高线程创立和销毁造成的耗费。进步响应速度。当工作达到时,工作能够不须要的等到线程创立就能立刻执行。进步线程的可管理性。线程是稀缺资源,如果无限度的创立,不仅会耗费系统资源,还会升高零碎 的稳定性,应用线程池能够进行对立的调配,调优和监控。(二)线程池的基本原理线程池的组成个别的线程池次要分为以下 4 个组成部分: 线程池管理器:用于创立并治理线程池工作线程:线程池中的线程工作接口:每个工作必须实现的接口,用于工作线程调度其运行工作队列:用于寄存待处理的工作,提供一种缓冲机制ThreadPoolExecutor 的构造方法如下: public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { }结构函数参数阐明: 线程池都有哪几种工作队列1、ArrayBlockingQueue:是一个基于数组构造的有界阻塞队列,此队列按 FIFO(先进先出)准则对元素进行排序。2、LinkedBlockingQueue:一个基于链表构造的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。动态工厂办法Executors.newFixedThreadPool()应用了这个队列3、SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作始终处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,动态工厂办法Executors.newCachedThreadPool应用了这个队列。4、PriorityBlockingQueue:一个具备优先级的有限阻塞队列。 回绝策略 :线程池中的线程曾经用完了,无奈持续为新工作服务,同时,期待队列也曾经排满了,再也塞不下新工作了。这时候咱们就须要回绝策略机制正当的解决这个问题。 JDK 内置的回绝策略如下: 以上内置回绝策略均实现了 RejectedExecutionHandler 接口,若以上策略仍无奈满足理论须要,齐全能够本人扩大 RejectedExecutionHandler 接口。 Java线程池工作过程 1.线程池刚创立时,外面没有一个线程。工作队列是作为参数传进来的。不过,就算队列外面有工作,线程池也不会马上执行它们。 2.当调用 execute() 办法增加一个工作时,线程池会做如下判断:a) 如果正在运行的线程数量小于 corePoolSize,那么马上创立线程运行这个工作;b) 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个工作放入队列;c) 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创立非核心线程立即运行这个工作;d) 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异样 RejectExecutionException。 3.当一个线程实现工作时,它会从队列中取下一个工作来执行。 4.当一个线程无事可做,超过肯定的工夫(keepAliveTime)时,线程池会判断,如果以后运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有工作实现后,它最终会膨胀到 corePoolSize 的大小。 (三) Executor框架Executor框架同java.util.concurrent.Executor 接口在Java 5中被引入Executor框架是一个依据一组执行策略调用,调度,执行和管制的异步工作的框架。 无限度的创立线程会引起应用程序内存溢出。所以创立一个线程池是个更好的的解决方案,因为能够限度线程的数量并且能够回收再利用这些线程。利用Executor框架能够十分不便的创立一个线程池。 Executors类是什么? Executors能够用于不便的创立线程池。 ...

May 6, 2023 · 1 min · jiezi

关于java:12种接口优化的通用方案

接口优化计划总结:1.批处理批量思维:批量操作数据库,在循环插入场景的接口中,能够在批处理执行实现后一次性插入或更新数据库,防止屡次IO.//批量入库 batchInsert();mybatis <foreach> 进行批处理`2.异步解决异步思维:针对耗时比拟长且不是后果必须的逻辑,能够思考放到异步执行,这样能升高接口耗时.留神:异步不能实时获取后果,异步过程中报错无奈晓得例如一个理财的申购接口,入账和写入申购文件是同步执行的,因为是T+1交易,前面这两个逻辑其实不是后果必须的,咱们并不需要关注它的实时后果,所以咱们思考把入账和写入申购文件改为异步解决。如图所示:实现形式:能够应用线程池,音讯队列,还能够应用一些调度工作框架我的项目案例: 异步导出大数量报表数据3.空间换工夫一个很好了解的空间换工夫的例子是正当应用缓存,针对一些频繁应用且不频繁变更的数据,能够提前缓存起来,须要时间接查缓存,防止频繁地查询数据库或者反复计算。须要留神的事,这里用了正当二字,因为空间换工夫也是一把双刃剑,须要综合思考你的应用场景,毕竟缓存带来的数据一致性问题也挺令人头疼。这里的缓存能够是R2M,也能够是本地缓存、memcached,或者Map。举一个股票工具的查问例子:因为策略轮动的调仓信息,每周只更新一次,所以原来的调接口就去查库的逻辑并不合理,而且拿到调仓信息后,须要通过简单计算,最终得出回测收益和跑赢沪深指数这些咱们想要的后果。如果咱们把查库操作和计算结果放入缓存,能够节俭很多的执行工夫。如图:4.预处理也就是预取思维,就是提前要把查问的数据,提前计算好,放入缓存或者表中的某个字段,用的时候会大幅提高接口性能。跟下面那个例子很像,然而关注点不同。举个简略的例子:理财产品,会有依据净值计算年化收益率的数据展现需要,利用净值去套用年化收益率计算公式计算的逻辑咱们能够采纳预处理,这样每一次接口调用间接取对应字段就能够了。留神: 3,4用于条件少且不常常变动的 例如: 大盘5.池化思维咱们都用过数据库连接池,线程池等,这就是池思维的体现,它们解决的问题就是防止反复创建对象或创立连贯,能够反复利用,防止不必要的损耗,毕竟创立销毁也会占用工夫。池化思维蕴含但并不局限于以上两种,总的来说池化思维的实质是预调配与循环应用,明确这个原理后,咱们即便是在做一些业务场景的需要时,也能够利用起来。比方:对象池6.串行改并行串行就是,以后执行逻辑必须等上一个执行逻辑完结之后才执行,并行就是两个执行逻辑互不烦扰,所以并行相对来说就比拟节省时间,当然是建设在没有后果参数依赖的前提下。比方,理财的持仓信息展现接口,咱们既须要查问用户的账户信息,也须要查问商品信息和banner位信息等等来渲染持仓页,如果是串行,基本上接口耗时就是累加的。如果是并行,接口耗时将大大降低。如图:我的项目案例: 并行获取 销售报表,应收款,指标数据7.索引加索引能大大提高数据查问效率,这个在接口设计之出也会思考到,这里不再多赘述,随着需要的迭代,咱们重点整顿一下索引不失效的一些场景,心愿对小伙伴们有所帮忙。具体不失效场景不再一一举例,前面有工夫的话,独自整顿一下。8.防止大事务所谓大事务问题,就是运行工夫较长的事务,因为事务统一不提交,会导致数据库连贯被占用,影响到别的申请拜访数据库,影响别的接口性能。举个例子: @Transactional(value = "taskTransactionManager", propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = {RuntimeException.class, Exception.class}) public BasicResult purchaseRequest(PurchaseRecord record) {     BasicResult result = new BasicResult();     ...     pushRpc.doPush(record);             result.setInfo(ResultInfoEnum.SUCCESS);     return result; }所以为防止大事务问题,咱们能够通过以下计划躲避:1,RPC调用不放到事务外面2,查问操作尽量放到事务之外3,事务中防止解决太多数据9.优化程序结构程序结构问题个别呈现在屡次需要迭代后,代码叠加造成。会造成一些反复查问、屡次创建对象等耗时问题。在多人保护一个我的项目时比拟多见。解决起来也比较简单,咱们须要针对接口整体做重构,评估每个代码块的作用和用处,调整执行程序。10.深分页问题(深分页:深分页就是对于试用limit m,n这种来实现分页查问的时候,须要将满足条件的mn条数据都查问进去,而后进行排序,而后再取第(m-1)n到第m*n这n条数据。当m十分大的时候,那么须要参加排序的数据就会十分多,导致查问后果会十分慢。)深分页问题比拟常见,分页咱们个别最先想到的就是 limit ,为什么会慢,咱们能够看下这个SQL: select * from purchase_record where productCode = 'PA9044' and status=4 and id > 100000 limit 20011.SQL优化sql优化能大幅提高接口的查问性能,因为本文重点讲述接口优化的计划,具体sql优化不再一一列举,小伙伴们能够联合索引、分页、等关注点思考优化计划。12.锁粒度防止过粗锁个别是为了在高并发场景下爱护共享资源采纳的一种伎俩,然而如果锁的粒度太粗,会很影响接口性能。对于锁粒度:就是你要锁的范畴有多大,不论是synchronized还是redis分布式锁,只须要在临界资源处加锁即可,不波及共享资源的,不必要加锁,就好比你要上卫生间,只须要把卫生间的门锁上就能够,不须要把客厅的门也锁上。谬误的加锁形式:正确加锁形式: 订单号的生成,分布式锁

May 6, 2023 · 1 min · jiezi

关于java:项目打包到Docker

1,批改我的项目中admin/pc端pom文件2.批改pc/admin端yml文件,都改为服务器地址3.批改docker配置文件地址4.5.把模块别离打包上传到docker (命令打包)6.//列出本地镜像docker images //显示所有的容器docker ps -a //进行正在运行的容器 docker stop 容器id1 容器 id2//删除容器docker rm 容器id1 容器 id2//列出镜像docker images//删除镜像 docker rmi 镜像id1 镜像id2//启动admin端服务容器docker run -d -p 端口号:端口号 -v /root/logs/admin:/logs -v/etc/localtime:/etc/localtime --name admin lecshop/lecshop_web_admin//启动pc端服务容器docker run -d -p 端口号:端口号 -v /alipaycert:/alipaycert -v /root/logs/pc:/logs -v /etc/localtime:/etc/localtime --name pc lecshop/lecshop_web_pc//显示所有的容器 docker ps -a

May 6, 2023 · 1 min · jiezi

关于java:获取两个时间相隔天数

两个日期之间相隔天数 public int daysBetweenDates(String date1, String date2) { LocalDate startDate = LocalDate.parse(date1); LocalDate endDate = LocalDate.parse(date2); long daysDiff = ChronoUnit.DAYS.between(startDate, endDate); return Math.abs((int)daysDiff);

May 6, 2023 · 1 min · jiezi

关于java:Java-20-新功能介绍

➜ bin pwd/Users/darcy/develop/jdk-20.0.1.jdk/Contents/Home/bin➜ bin ./java -versionopenjdk version "20.0.1" 2023-04-18OpenJDK Runtime Environment (build 20.0.1+9-29)OpenJDK 64-Bit Server VM (build 20.0.1+9-29, mixed mode, sharing)Java 20 共带来 7 个新个性性能,其中三个是孵化提案,孵化也就是说尚在征求意见阶段,将来可能会删除此性能。 JEP形容分类429作用域值(孵化器)Project Loom,Java 开发相干432Record 模式匹配(第二次预览)Project Amber,新的语言个性433switch 的模式匹配(第四次预览)Project Amber,新的语言个性434内部函数和内存 API(第二个预览版)Project Panama,非 Java 库436虚构线程(第二个预览版)Project Loom,Java 开发相干437结构化并发(第二孵化器)Project Loom,Java 开发相干438Vector API(第五孵化器)Project Panama,非 Java 库JEP:JDK Enhancement Proposal,JDK 加强倡议,或者叫 Java 将来倒退倡议。JDK 20 不是长期反对 (LTS) 版本,因而它只会在六个月后被 JDK 21 取代之前收到更新。JDK 17( 2021 年 9 月 14 日公布)是 Java 的最新 LTS 版本。Oracle 发表打算将 LTS 版本之间的工夫从三年缩短到两年,因而 JDK 21(2023 年 9 月)打算成为下一个LTS。 ...

May 6, 2023 · 4 min · jiezi

关于java:Java多线程基础

文章和代码曾经归档至【Github仓库:https://github.com/timerring/java-tutorial 】或者公众号【AIShareLab】回复 java 也可获取。多线程根底线程相干概念程序(program)是为实现特定工作、用某种语言编写的一组指令的汇合。简略的说:就是咱们写的代码。 过程过程是指运行中的程序,比方咱们应用QQ,就启动了一个过程,操作系统就会为该过程分配内存空间。当咱们应用迅雷,又启动了一个过程,操作系统将为迅雷调配新的内存空间。过程是程序的一次执行过程,或是正在运行的一个程序。是动静过程:有它本身的产生、存在和沦亡的过程线程线程由过程创立的,是过程的一个实体一个过程能够领有多个线程,如下图 其余相干概念单线程:同一个时刻,只容许执行一个线程。多线程:同一个时刻,能够执行多个线程,比方:一个qq过程,能够同时打开多个聊天窗口,一个迅雷过程,能够同时下载多个文件。并发:同一个时刻,多个工作交替执行,造成一种“貌似同时”的错觉,简略的说单核cpu实现的多任务就是并发。 并行:同一个时刻,多个工作同时执行。多核cpu能够实现并行。 获取cpu的数量/外围数 package com.hspedu;public class CpuNum { public static void main(String[] args) { Runtime runtime = Runtime.getRuntime(); //获取以后电脑的cpu数量/外围数 int cpuNums = runtime.availableProcessors(); System.out.println("以后有cpu 个数=" + cpuNums); }}线程根本应用创立线程的两种形式在java中线程来应用有两种办法。 1.继承Thread类,重写run办法 2.实现Runnable接口,重写run办法 线程利用案例1-继承Thread 类运行程序时就相当启动了一个过程,进入main时就开启了一个main线程, 1)请编写程序,开启一个线程,该线程每隔1秒。在控制台输入“哺瞄。我是小猫咪 2)对上题改良:当输入80次啪瞄,我是小猫咪,完结该线程 3)应用JConsole 监控线程执行状况,并画出程序示意图! 在过程运行时间接在控制台输出JConsole即可。 主线程挂了然而子线程还在继续执行,这并不会导致应用程序的完结。阐明: 当main线程启动一个子线程 Thread-0, 主线程不会阻塞, 会继续执行(不会等执行结束后再往下执行),这时 主线程和子线程是交替执行。 package com.hspedu.threaduse;/** * 演示通过继承 Thread 类创立线程 */public class Thread01 { public static void main(String[] args) throws InterruptedException { //创立Cat对象,能够当做线程应用 Cat cat = new Cat(); // 读源码 /* (1) public synchronized void start() { start0(); } (2) //start0() 是本地办法,是JVM调用, 底层是c/c++实现 //真正实现多线程的成果, 是start0(), 而不是 run private native void start0(); */ cat.start();// 启动线程-> 最终会执行cat的run办法 //cat.run();//run办法就是一个一般的办法, 没有真正的启动一个线程,就会把run办法执行结束,才向下执行,因而要真正实现多线程,还是应该应用start办法。 //阐明: 当main线程启动一个子线程 Thread-0, 主线程不会阻塞, 会继续执行(不会等执行结束后再往下执行),这时 主线程和子线程是交替执行。 System.out.println("主线程继续执行" + Thread.currentThread().getName());//名字main for(int i = 0; i < 60; i++) { System.out.println("主线程 i=" + i); //让主线程休眠 Thread.sleep(1000); } }}// 阐明//1. 当一个类继承了 Thread 类, 该类就能够当做线程应用//2. 咱们会重写 run办法,写上本人的业务代码//3. run Thread 类 实现了 Runnable 接口的run办法,如下/* @Override public void run() { if (target != null) { target.run(); } } */class Cat extends Thread { int times = 0; @Override public void run() {//重写run办法,写上本人的业务逻辑 while (true) { //该线程每隔1秒。在控制台输入 “喵喵, 我是小猫咪” System.out.println("喵喵, 我是小猫咪" + (++times) + " 线程名=" + Thread.currentThread().getName()); //让该线程休眠1秒 ctrl+alt+t try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if(times == 80) { break;//当times 到80, 退出while, 这时线程也就退出.. } } }} ...

May 6, 2023 · 10 min · jiezi

关于java:Java实现坦克大战10

文章和代码曾经归档至【Github仓库:https://github.com/timerring/java-tutorial 】或者公众号【AIShareLab】回复 java 也可获取。坦克大战1.0java 绘图坐标体系坐标体系-介绍下图阐明了Java坐标系。坐标原点位于左上角,以像素为单位。在Java坐标系中,第一个是x坐标,示意以后地位为程度方向,间隔坐标原点x个像素;第二个是y坐标,示意以后地位为垂直方向,间隔坐标原点y个像素。 坐标体系-像素绘图还必须要搞清一个十分重要的概念-像素一个像素等于多少厘米? 计算机在屏幕上显示的内容都是由屏幕上的每一个像素组成的。例如,计算机显示器的分辨率是800×600,示意计算机屏幕上的每一行由800个点组成,共有600行,整个计算机屏幕共有480 000个像素。像素是一个密度单位,而厘米是长度单位,两者无奈比拟。 介绍-疾速入门绘图原理在面板上画一个小圆。 Component类提供了两个和绘图相干最重要的办法:1. paint(Graphics g)绘制组件的外观 repaint()刷新组件的外观。 当组件第一次在屏幕显示的时候,程序会主动的调用paint()办法来绘制组件。在以下状况paint(将会被调用: 1.窗口最小化.再最大化 2.窗口的大小发生变化 3.repaint办法被调用 Graphics 类Graphics类你能够了解就是画笔,为咱们提供了各种绘制图形的办法:[参考jdk帮忙文档] 1.画直线 drawLine(int x1,int y1,int x2,int y2) 2.画矩形边框 drawRect(int x, int y, int width, int height) 3.画椭圆边框 drawOval(int x, int y, int width, int height) 4.填充矩形 fillRect(int x, int y, int width, int height) 5.填充椭圆 fillOval(int x, int y, int width, int height) 6.画图片 drawlmage(Image img, int x, int y. ..) ...

May 6, 2023 · 5 min · jiezi

关于java:Java-中-Optional-的使用技巧

不要给 Optional 变量赋值为 null如果你有一个变量申明为 Optional,则不要给它赋值为 null: public Optional<Insurance> getInsurance() { // 这里不应该给 Optional 变量赋值为 null,而应该间接赋值为 Optional.empty(); Optional<Insurance> insurance = null; return insurance;}不要间接调用 Optional 的 get 办法Optional<Insurance> insuranceOpt = InsuranceService.getInsurance():// 这里不应该间接调用 get 办法,而应该在调用 get 办法之火线调用 isPresent() 办法进行判断Insurance insurance = insuranceOpt.get();if (insuranceOpt.isPresent()) { // do something} else { // do something}应用 orElse() 办法来获取默认值public String getName() { Optional<String> name = NameService.getName(); if (name.isPresent()) { return name.get(); } else { return DEFAULT_VALUE; } // 下面的代码能够简化为一行 return NameService.getName().orElse(DEFAULT_VALUE);}不要应用 orElse() 办法来获取须要计算的值,而应该应用 orElseGet() 办法来获取public Optional<Insurance> getFromCache(String id) { // 从缓存中获取}public Insurance getFromDb(String id) { // 从数据库中获取}public Optional<Insurance> getInsurance(String id) { // 应用 orElse() 不论是否从缓存中获取到数据都会执行从数据库中查问的逻辑 return getFromCache(id).orElse(getFromDb(id)); // 应该批改为如下的形式,这样的话只有当缓存外面获取不到值的时候,才会从数据库中获取 return getFromCache(id).orElseGet(() -> getFromDb(id));}参考12 recipes for using the Optional class as it’s meant to be used (oracle.com) ...

May 5, 2023 · 1 min · jiezi

关于java:Java-给Word文档添加水印

在Word增加水印,能够无效地爱护文档的机密性和避免别人抄袭,也能够作为文档的版权申明或草稿标记,不便进行对立治理。在Word中增加水印的办法有很多种,比方应用内置的“水印”性能、应用“页眉/页脚”性能、应用插入图片/文本框并调整其透明度等形式。这里我想介绍如何通过Java程序,以编程的形式来增加水印。水印包含图片水印和文本水印。所用到的工具是Free Spire.Doc for Java。上面是具体的办法和示例代码。 程序环境:IntelliJ IDEA 2018 (jdk 1.8.0)在进行操作之前先导入jar包,请参考以下两种导入形式:办法一:如果应用的是 maven,能够增加以下代码到我的项目的 pom.xml 文件中。 <repositories> <repository> <id>com.e-iceblue</id> <name>e-iceblue</name> <url>https://repo.e-iceblue.cn/repository/maven-public/</url> </repository></repositories><dependencies> <dependency> <groupId>e-iceblue</groupId> <artifactId>spire.doc.free</artifactId> <version>5.2.0</version> </dependency></dependencies>办法二:如果没有应用 maven,则能够从此链接下载Free Spire.Doc for Java,找到lib文件夹下的Spire.doc.jar并进行解压;而后在IDEA中创立一个新我的项目,顺次点击“文件”(File),“我的项目构造”(Project Structure),“组件”(Modules),“依赖项”(Dependencies),再点击右方绿色“+”下的第一个选项“jar文件或门路”(JARs or Directories),找到解压后的Spire.doc.jar 文件,点击确认,将其导入到我的项目中。 增加文本水印具体方法:创立一个 Document 实例。应用 Document.loadFromFile() 办法加载示例 Word 文档。应用 Document.getSections().get() 办法获取第一节。创立一个 TextWatermark 实例。应用 TextWatermark 类提供的办法设置文本水印的文本、字体大小、色彩和布局。应用 Section.getDocument().setWatermark() 办法将文本水印增加到示例文档。应用 Document.saveToFile() 办法保留文件。示例代码:import com.spire.doc.*;import com.spire.doc.documents.WatermarkLayout;import java.awt.*;public class WordTextWatermark { public static void main(String[] args) { //创立一个Document实例 Document document = new Document(); //加载示例 Word 文档 document.loadFromFile("sample.docx"); //获取第一节 Section section = document.getSections().get(0); //创立一个 TextWatermark 实例 TextWatermark txtWatermark = new TextWatermark(); //设置文本水印格局 txtWatermark.setText("外部秘密"); txtWatermark.setFontSize(40); txtWatermark.setColor(Color.red); txtWatermark.setLayout(WatermarkLayout.Diagonal); //将文本水印增加到示例文档 section.getDocument().setWatermark(txtWatermark); //保留文件 document.saveToFile("result1.docx", FileFormat.Docx); }}效果图: ...

May 5, 2023 · 1 min · jiezi

关于java:基于Java开发的企业供应商电子招采管理系统

前言 洽购供应商治理的难点:沟通消耗精力,业务协同难,管控凌乱。优质的供应商,是间接可能影响洽购老本和企业洽购策略落地的,而供应商治理的终极门路是建设企业本人的供应商私域流量池。 一、供应商治理 1.供应商准入:反对多种供应商入驻形式,公开申请,别人代录,间接录入等。 2.供应商信息补充3.供应商补充完根本信息后,提交至企业方,企业方进行供应商审批。 二、企业询价、供应商报价治理 1.采购员发动询价(可发动多轮询价)。2.邀请具体供应商3.供应商能在具体通道看到公布的询价布告4.供应商抉择参加询价还是放弃5.供应商提交报价后,采购员针对计划进行中标商抉择,并提交到相应环节进行中标审批再定标。6.洽购审批程序实现之后,洽购人员在零碎确定具体供应商中标并发送中标通告,供应商在报价治理页面能看到中标信息。 三、洽购订单 1.采购员在系统生成洽购订单 2. 供应商确认订单并填写订单交付进度。 四、送货、到货治理 供应商产品送货 2.洽购方进行到货确认 总结 下面只是展现了洽购流程中一部分性能,全计划取得平台私信取得。

May 5, 2023 · 1 min · jiezi

关于java:Java泛型详解

文章和代码曾经归档至【Github仓库:https://github.com/timerring/java-tutorial 】或者公众号【AIShareLab】回复 java 也可获取。泛型泛型的了解和益处看一个需要请编写程序,在ArrayList 中,增加3个Dog对象 Dog对象含有name 和 age, 并输入name 和 age (要求应用getXxx()) package com.hspedu.generic;import java.util.ArrayList;@SuppressWarnings({"all"})public class Generic01 { public static void main(String[] args) { //应用传统的办法来解决 ArrayList arrayList = new ArrayList(); arrayList.add(new Dog("旺财", 10)); arrayList.add(new Dog("发财", 1)); arrayList.add(new Dog("小黄", 5)); //如果程序员,不小心,增加了一只猫 arrayList.add(new Cat("招财猫", 8)); // 就会报类型转换的谬误 //遍历 for (Object o : arrayList) { //向下转型Object ->Dog Dog dog = (Dog) o; System.out.println(dog.getName() + "-" + dog.getAge()); } }}/*请编写程序,在ArrayList 中,增加3个Dog对象Dog对象含有name 和 age, 并输入name 和 age (要求应用getXxx()) */class Dog { private String name; private int age; public Dog(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; }}class Cat { //Cat类 private String name; private int age; public Cat(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; }}应用传统办法的问题剖析1)不能对退出到汇合ArrayList中的数据类型进行束缚(不平安) ...

May 5, 2023 · 12 min · jiezi

关于java:探究Spring中Bean的线程安全性问题

前言 明天共事笑嘻嘻的凑过去,问了我一个问题:spring中的bean是线程平安的吗?。我心田一想必定是平安的,毕竟这样多我的项目在用。然而转念一想,他那贱兮兮的表情,多半是在给我挖坑。于是我自信的答复他:不平安。他反诘,你确定? 这一问给我整的不自信了,哈哈哈,容我去学习一下。 多线程平安嘛 在 Spring 框架中,Bean 是应用程序的外围构建块,代表了在 Spring 容器中治理的对象或组件。Spring 容器负责创立和治理 Bean,并在须要时将它们注入到其余 Bean 中。因为多个线程可能会同时拜访同一个 Bean 实例,从而导致数据竞争和并发问题。 在 Spring 中,Bean 的线程安全性次要取决于 Bean 的作用域(scope)。Spring 提供了多种作用域: 包含单例(Singleton)原型(Prototype)申请(Request)会话(Session) 上面别离来介绍一下它们的线程安全性。 单例(Singleton) 在Spring中,单例作用域默认的作用域,容器中只会存在一个该类型的实例。如果Bean的实现没有状态,并且不会因为并发拜访而产生副作用,那么该Bean就是线程平安的。因为所有线程都共享同一个实例,不会有多个线程同时批改同一个实例的状态。然而,如果Bean的实现具备状态,或者它依赖于非线程平安的内部资源,那么该Bean就不是线程平安的。 原型(Prototype) 在Spring中,原型(Prototype)作用域是指每次获取Bean时都会创立一个新的Bean实例。每个原型作用域的Bean实例都是独立的,之间互不影响,也不会共享任何状态信息。因而,原型作用域的Bean是线程平安的。 申请(Request) 在Spring中,申请(Request)作用域是指在同一个HTTP申请范畴内,多个Bean实例共享同一个申请对象。具体来说,当客户端发送一个HTTP申请时,Spring会创立一个对应的申请对象,并将其保留在ThreadLocal中。在同一个申请处理过程中,所有应用申请作用域的Bean都会共享这个申请对象,能够通过该对象来获取申请相干的信息,如申请参数、申请头等。 因为每个HTTP申请都会创立一个独立的申请对象,因而申请作用域是线程平安的。不同的HTTP申请之间应用不同的申请对象,不会产生线程平安问题。而同一个HTTP申请中,多个Bean共享同一个申请对象,也不会呈现线程平安问题,因为在同一个申请处理过程中,Spring会保障只有一个线程在解决该申请。 会话(Session) 会话(Session)作用域是指在同一个HTTP会话范畴内,多个Bean实例共享同一个会话对象。具体来说,当客户端第一次拜访Web利用时,Spring会为该会话创立一个对应的会话对象,并将其保留在HTTP会话中。在同一个HTTP会话期间,所有应用会话作用域的Bean都会共享这个会话对象,能够通过该对象来获取会话相干的信息,如会话属性、会话ID等。 因为同一个HTTP会话期间所有的申请都共享同一个会话对象,因而会话作用域也是线程平安的。不同的HTTP会话之间应用不同的会话对象,也不会产生线程平安问题。 总结 在 Spring 中,Bean 的线程安全性是取决于 Bean 的作用域和实现形式的。须要依据具体情况进行思考,抉择适合的作用域和实现形式来保障 Bean 的线程安全性。 除了作用域外,Bean 的实现形式也会影响其线程安全性。如果 Bean 的实现具备状态,那么须要思考线程平安问题。能够应用锁或其余线程同步机制来保障线程平安,然而这可能会影响应用程序的性能和可扩展性。 结尾 如果感觉对你有帮忙,能够多多评论,多多点赞哦,也能够到我的主页看看,说不定有你喜爱的文章,也能够顺手点个关注哦,谢谢。 我是不一样的科技宅,每天提高一点点,体验不一样的生存。咱们下期见!

May 4, 2023 · 1 min · jiezi

关于java:一文吃透Tomcat核心知识点

架构首先,看一下整个架构图。最全面的Java面试网站 接下来简略解释一下。 Server:服务器。Tomcat 就是一个 Server 服务器。 Service:在服务器中能够有多个 Service,只不过在咱们罕用的这套 Catalina 容器的Tomcat 中只蕴含一个 Service,在 Service 中蕴含连接器和容器。一个残缺的 Service 能力实现对申请的接管和解决。 连接器:Coyote 是连接器具体的实现。用于与新来的申请建设连贯并解析数据。因为 Tomcat 反对的 IO 模型有 NIO、NIO2、APR,而反对的应用层协定有 HTTP1.1、HTTP2、AJP。所以针对不同的 IO 模型和应用层协定申请,在一个 Service 中能够有多个连接器来实用不同的协定的IO申请。 EndPoint :Coyote 通信端点,即通信监听的接口,是具体 Socket 接管和发送处理器,是用来实现 TCP/IP 传输协定的。 Acceptor:用于接管申请的 socket。 Executor:线程池,在接管到申请的 socket 后会从线程池中调配一条来执行前面的操作。 Processor :Coyote 协定解决接口,是用来实现 HTTP 应用层协定的,接管 EndPoint 、容器传来的 Socket 字节流,解析成 request 或 response 对象。 ProtocolHandler:Coyote 协定接口,通过 EndPoint 和 Processor,实现针对具体协定的解决能力。 Adapter:容器只负责解决数据,对于申请协定不同的数据,容器会无奈解决,所以在 ProtocolHandler 解决生成的 request 对象后,还须要将其转成 Tomcat 定义好的对立格局的 ServletRequest 对象,Adapter 就是用来进行这样的操作的。 ...

May 4, 2023 · 7 min · jiezi

关于java:刺激ChatGPT给我虚构了一本书

ChatGPT很弱小,能够帮咱们解决很多问题,但这些问题的答案的正确性您是否有考据过呢? 昨晚,DD就收到了一个乏味的反馈: 发问:有什么对于数据权限设计的材料举荐吗?ChatGPT竟然介绍了一本基本不存在的书《数据权限设计与实现》,作者竟然还是我... 那么你在应用ChatGPT的时候,有碰到过相似的问题吗?在应用AI辅助工作的时候,你是否会对后果进一步验证呢?如果每次都要对后果的正确性做进一步验证,AI辅助真的有进步你的工作效率吗?还是减少了你的犯错率呢?留言区一起聊聊呗。最初,棘手给大家举荐一些乏味好用的AI利用: 把中国法律法规喂给 ChatGPT = AI 法律助手把读文章变成听文章的在线工具寰球最大的 ChatGPT 开源替代品欢送关注我的公众号:程序猿DD。第一工夫理解前沿行业音讯、分享深度技术干货、获取优质学习资源

May 4, 2023 · 1 min · jiezi

关于java:推荐一款-IntelliJ-IDEA-神级插件由-ChatGPT-团队开发免费使用堪称辅助神器

起源:https://blog.csdn.net/m0_64880608/article/details/130201349 什么是Bito?Bito是一款在IntelliJ IDEA编辑器中的插件,Bito插件是由ChatGPT团队开发的,它是ChatGPT团队为了进步开发效率而开发的一款工具。 ChatGPT团队是一支专一于自然语言解决技术的团队,他们开发了一款基于GPT的自然语言解决模型,能够用于生成自然语言文本。 Bito插件的弱小之处在于它能够帮忙开发人员更快地提交代码,同时还提供了一些有用的性能,如主动补全提交信息、疾速查看历史记录等。 如何装置Bito插件1、关上IntelliJ IDEA编辑器,在菜单栏中抉择“File” -> “Settings”。 2、在“Settings”窗口中,抉择“Plugins”选项卡,而后点击“Marketplace”按钮。 3、在搜寻框中输出“Bito”,而后点击“Search”按钮。 4、找到Bito插件后,点击“Install”按钮进行装置。 如何应用Bito插件装置实现后,你能够在编辑器的右方 or 邮件找到Bito插件的图标。点击它,就能够应用Bito插件的各种性能了。 注:如果没注册,它会提醒你注册,跟着步骤来就行,注册后能力正确应用它的性能 先查看你的邮箱验证码: 填入你的验证码: 点击Create workspace: 在输入框就能欢快的输出了: 比方让它写一个冒泡排序、讲个笑话~ 近期热文举荐: 1.1,000+ 道 Java面试题及答案整顿(2022最新版) 2.劲爆!Java 协程要来了。。。 3.Spring Boot 2.x 教程,太全了! 4.别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!! 5.《Java开发手册(嵩山版)》最新公布,速速下载! 感觉不错,别忘了顺手点赞+转发哦!

May 4, 2023 · 1 min · jiezi

关于java:基于springcloud实现的医院信息系统

拜访【WRITE-BUG数字空间】_[内附残缺源码和文档]医疗信息就诊零碎,零碎次要性能依照数据流量、流向及处理过程分为临床诊疗、药品治理、财务管理、患者治理。诊疗流动由各工作站配合实现,并将临床信息进行整顿、解决、汇总、统计、剖析等。本零碎包含以下工作站:门诊医生工作站、药房医生工作站、医技医生工作站、收费员工作站、对帐员工作站、管理员工作站。我的项目简介HIS英文全称 hospital information system(医院信息系统),零碎次要性能依照数据流量、流向及处理过程分为临床诊疗、药品治理、财务管理、患者治理。诊疗流动由各工作站配合实现,并将临床信息进行整顿、解决、汇总、统计、剖析等。本零碎包含以下工作站:门诊医生工作站、药房医生工作站、医技医生工作站、收费员工作站、对帐员工作站、管理员工作站。基于Spring cloud和Spring boot 2.x 实现我的项目演示username:演示用户password:test因为没有进行部署,首次加载须要期待1~2分钟,加载实现之后比拟流程晦涩好不容易抓到了推免的尾巴,所以最近打算更新一下部署文档,临时不打算再保护代码了,但我会持续推出其余有意思的基于Cloud的我的项目,欢送关注有问题请间接 issue(如果须要数据库和实在数据请分割自己邮箱)申明我的项目为NEU实训课设本组所作,包前缀为neu,侵权必改之前因为学校忽然减少人机交互课设,所以把该我的项目变成Private,但很多人有情fork之后并不删除,例如 TANGKUO,我曾经分割他屡次,他还是不删除。只能挂个名字在这了...继续更新中......前言HIS 我的项目致力于打造一个残缺的医疗系统,采纳现阶段风行技术实现。下一阶段打算添置部署教程进步代码品质更新技术栈为最新版本,并引入新技术本仓库蕴含零碎形容HIS-master单体利用his-cloud散布式微服务利用HIS-web诊疗前端HIS-app患者前端注:单体利用和分布式实现业务完全相同

May 3, 2023 · 1 min · jiezi

关于java:连接池-Druid-补充-removeAbandoned

removeAbandoned参数指定连接池是否移除被遗弃的连贯。 具体的逻辑分两局部:一部分是设置流动连贯、筹备未来判断是否应该被遗弃,这个动作是在getConnection获取连贯后执行的;另外一部分是在连贯回收工作DestroyTask中,咱们在剖析连贯回收的文章中疏忽了这部分内容,明天补充一下。 getConnection办法获取连贯之后判断removeAbandoned参数关上的话,就设置以后连贯的connectedTimeNano为零碎工夫,而后将以后连贯放入activeConnections中,筹备被回收线程查看、移除。 代码在getConnectionDirect中: if (removeAbandoned) { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); poolableConnection.connectStackTrace = stackTrace; poolableConnection.setConnectedTimeNano(); poolableConnection.traceEnable = true; activeConnectionLock.lock(); try { activeConnections.put(poolableConnection, PRESENT); } finally { activeConnectionLock.unlock(); } }第二局部须要回顾一下上一篇文章中的DestroyTask的run办法,在调用shrink之后判断RemoveAbandoned则调用removeAbandoned()办法: @Override public void run() { shrink(true, keepAlive); if (isRemoveAbandoned()) { removeAbandoned(); } }removeAbandoned办法源码,分段贴出: public int removeAbandoned() { int removeCount = 0; long currrentNanos = System.nanoTime(); List<DruidPooledConnection> abandonedList = new ArrayList<DruidPooledConnection>(); activeConnectionLock.lock(); try { Iterator<DruidPooledConnection> iter = activeConnections.keySet().iterator(); for (; iter.hasNext();) { DruidPooledConnection pooledConnection = iter.next(); if (pooledConnection.isRunning()) { continue; } long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000); if (timeMillis >= removeAbandonedTimeoutMillis) { iter.remove(); pooledConnection.setTraceEnable(false); abandonedList.add(pooledConnection); } } } finally { activeConnectionLock.unlock(); }首先activeConnectionLock加锁。 ...

May 3, 2023 · 1 min · jiezi

关于java:Spring-Dependency-Inject依赖注入

依赖注入常见有4种办法,setter办法:1.简略类型2.援用类型构造方法:1.简略类型2.援用类型

May 2, 2023 · 1 min · jiezi

关于java:Java-Collection与Map详解

文章和代码曾经归档至【Github仓库:https://github.com/timerring/java-tutorial 】或者公众号【AIShareLab】回复 java 也可获取。汇合的了解和益处后面咱们保留多个数据应用的是数组,那么数组有有余的中央,咱们剖析一下 数组1)长度开始时必须指定,而且一旦指定,不能更改 2)保留的必须为同一类型的元素 3)应用数组进行减少/删除元素比拟麻烦 eg: 写出Person数组扩容示意代码。 Person[] pers = new Person[1]; //大小是1per[0] = new Person(); //减少新的Person对象?Person[] pers2 = new Person[pers.length+1];//新创建数组for(){} //拷贝pers数组的元素到per2pers2[pers2.length-1] = new Person()://增加新的对象汇合1)能够动静保留任意多个对象,应用比拟不便 2)提供了一系列不便的操作对象的办法: add、remove、set、get 等 3)应用汇合增加/删除新元素简洁了 汇合的框架体系Java 的汇合类很多,次要分为两大类,如图! 汇合次要是两组(单列汇合, 双列汇合)Collection 接口有两个重要的子接口List Set , 他们的实现子类都是单列汇合 (单列数据)Map 接口的实现子类是双列汇合,寄存的K-V (双列数据)package com.hspedu.collection_;import java.util.ArrayList;import java.util.Collection;import java.util.HashMap;import java.util.Map;public class Collection_ { @SuppressWarnings({"all"}) public static void main(String[] args) { //Collection //Map ArrayList arrayList = new ArrayList(); arrayList.add("jack"); arrayList.add("tom"); HashMap hashMap = new HashMap(); hashMap.put("NO1", "北京"); hashMap.put("NO2", "上海"); }}Collection 接口和罕用办法Collection 接口实现类的特点public interface Collection <E> extends lterable <E>1) collection实现子类能够寄存多个元素,每个元素能够是Object2) 有些Collection的实现类,能够寄存反复的元素,有些不能够3) 有些Collection的实现类,有些是有序的(List),有些不是有序(Set)4) Collection接口没有间接的实现子类,是通过它的子接口 Set 和 List 来实现的 ...

May 2, 2023 · 27 min · jiezi

关于java:深入理解线程池

什么是线程池?线程池是一种利用池化技术思维来实现的线程治理技术,次要是为了复用线程、便当地治理线程和工作、并将线程的创立和工作的执行解耦开来。咱们能够创立线程池来复用曾经创立的线程来升高频繁创立和销毁线程所带来的资源耗费。在JAVA中次要是应用ThreadPoolExecutor类来创立线程池。 线程池的长处升高资源耗费,复用已创立的线程来升高创立和销毁线程的耗费。进步响应速度,工作达到时,能够不须要期待线程的创立立刻执行。进步线程的可管理性,应用线程池可能对立的调配、调优和监控。 线程池的状态及生命周期 ThreadPoolExecutor的构造方法参数corePoolSize,外围线程数量maximumPoolSize,最大线程数量keepAliveTime,线程闲暇时存活的工夫unit,闲暇存活工夫单位workQueue,工作队列,用于寄存已提交的工作(ArrayBlockingQueue,LinkedBlockingQueue,SynchronousQueue)threadFactory,线程工厂,用于创立线程执行工作handler,回绝策略,当线程池处于饱和时,应用某种策略来回绝工作提交(AbortPolicy:抛异样;DiscardPolicy:间接抛弃;DiscardOldestPolicy:丢第一个;CallerRunsPolicy:谁提交工作,谁执行) 从execute源码剖析线程池执行工作的流程线程池外围属性 // AtomicInteger,就是一个int,写操作用CAS实现,保障了原子性// ctl保护这线程池的2个核心内容:// 1:线程池状态(高3位,保护着线程池状态)// 2:工作线程数量(外围线程+非核心线程,低29位,保护着工作线程个数)private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));// COUNT_BITS=29private static final int COUNT_BITS = Integer.SIZE - 3;// 工作线程的最大个数// 00100000 00000000 00000000 00000000 - 1// 000111111111111111111111111111111 private static final int CAPACITY = (1 << COUNT_BITS) - 1;private static final int RUNNING = -1 << COUNT_BITS;private static final int SHUTDOWN = 0 << COUNT_BITS;private static final int STOP = 1 << COUNT_BITS;private static final int TIDYING = 2 << COUNT_BITS;private static final int TERMINATED = 3 << COUNT_BITS;// 拿到线程池状态// 011... // 111...private static int runStateOf(int c) { return c & ~CAPACITY; }// 拿到工作线程个数// ...0000000111111// ...1111111111111private static int workerCountOf(int c) { return c & CAPACITY; }execute() ...

May 2, 2023 · 4 min · jiezi

关于java:一天吃透Redis面试八股文

Redis连环40问,相对够全! Redis是什么?Redis(Remote Dictionary Server)是一个应用 C 语言编写的,高性能非关系型的键值对数据库。与传统数据库不同的是,Redis 的数据是存在内存中的,所以读写速度十分快,被广泛应用于缓存方向。Redis能够将数据写入磁盘中,保障了数据的平安不失落,而且Redis的操作是原子性的。 Redis优缺点?长处: 基于内存操作,内存读写速度快。反对多种数据类型,包含String、Hash、List、Set、ZSet等。反对长久化。Redis反对RDB和AOF两种长久化机制,长久化性能能够无效地防止数据失落问题。反对事务。Redis的所有操作都是原子性的,同时Redis还反对对几个操作合并后的原子性执行。反对主从复制。主节点会主动将数据同步到从节点,能够进行读写拆散。Redis命令的解决是单线程的。Redis6.0引入了多线程,须要留神的是,多线程用于解决网络数据的读写和协定解析,Redis命令执行还是单线程的。毛病: 对结构化查问的反对比拟差。数据库容量受到物理内存的限度,不适宜用作海量数据的高性能读写,因而Redis适宜的场景次要局限在较小数据量的操作。Redis 较难反对在线扩容,在集群容量达到下限时在线扩容会变得很简单。Redis为什么这么快?基于内存:Redis是应用内存存储,没有磁盘IO上的开销。数据存在内存中,读写速度快。IO多路复用模型:Redis 采纳 IO 多路复用技术。Redis 应用单线程来轮询描述符,将数据库的操作都转换成了事件,不在网络I/O上节约过多的工夫。高效的数据结构:Redis 每种数据类型底层都做了优化,目标就是为了谋求更快的速度。本文曾经收录到Github仓库,该仓库蕴含计算机根底、Java根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等外围知识点,欢送star~ Github地址 如果拜访不了Github,能够拜访gitee地址。 gitee地址 既然Redis那么快,为什么不必它做主数据库,只用它做缓存?尽管Redis十分快,但它也有一些局限性,不能齐全代替主数据库。有以下起因: 事务处理:Redis只反对简略的事务处理,对于简单的事务无能为力,比方跨多个键的事务处理。 数据长久化:Redis是内存数据库,数据存储在内存中,如果服务器解体或断电,数据可能失落。尽管Redis提供了数据长久化机制,但有一些限度。 数据处理:Redis只反对一些简略的数据结构,比方字符串、列表、哈希表等。如果须要解决简单的数据结构,比方关系型数据库中的表,那么Redis可能不是一个好的抉择。 数据安全:Redis没有提供像主数据库那样的平安机制,比方用户认证、访问控制等等。 因而,尽管Redis十分快,但它还有一些限度,不能齐全代替主数据库。所以,应用Redis作为缓存是一种很好的形式,能够进步应用程序的性能,并缩小数据库的负载。 讲讲Redis的线程模型?Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成构造为4局部:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的生产是单线程的,所以Redis才叫单线程模型。 文件事件处理器应用I/O多路复用(multiplexing)程序来同时监听多个套接字, 并依据套接字目前执行的工作来为套接字关联不同的事件处理器。当被监听的套接字筹备好执行连贯accept、read、write、close等操作时, 与操作绝对应的文件事件就会产生, 这时文件事件处理器就会调用套接字之前关联好的事件处理器来解决这些事件。尽管文件事件处理器以单线程形式运行, 但通过应用 I/O 多路复用程序来监听多个套接字, 文件事件处理器既实现了高性能的网络通信模型, 又能够很好地与 redis 服务器中其余同样以单线程形式运行的模块进行对接, 这放弃了 Redis 外部单线程设计的简略性。 Redis利用场景有哪些?缓存热点数据,缓解数据库的压力。利用 Redis 原子性的自增操作,能够实现计数器的性能,比方统计用户点赞数、用户拜访数等。分布式锁。在分布式场景下,无奈应用单机环境下的锁来对多个节点上的过程进行同步。能够应用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还能够应用官网提供的 RedLock 分布式锁实现。简略的音讯队列,能够应用Redis本身的公布/订阅模式或者List来实现简略的音讯队列,实现异步操作。限速器,可用于限度某个用户拜访某个接口的频率,比方秒杀场景用于避免用户疾速点击带来不必要的压力。好友关系,利用汇合的一些命令,比方交加、并集、差集等,实现独特好友、共同爱好之类的性能。给大家分享一个Github仓库,下面有大彬整顿的300多本经典的计算机书籍PDF,包含C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,能够star一下,下次找书间接在下面搜寻,仓库继续更新中~ Github地址 Memcached和Redis的区别?MemCached 数据结构繁多,仅用来缓存数据,而 Redis 反对多种数据类型。MemCached 不反对数据长久化,重启后数据会隐没。Redis 反对数据长久化。Redis 提供主从同步机制和 cluster 集群部署能力,可能提供高可用服务。Memcached 没有提供原生的集群模式,须要依附客户端实现往集群中分片写入数据。Redis 的速度比 Memcached 快很多。Redis 应用单线程的多路 IO 复用模型,Memcached应用多线程的非阻塞 IO 模型。(Redis6.0引入了多线程IO,用来解决网络数据的读写和协定解析,然而命令的执行依然是单线程)value 值大小不同:Redis 最大能够达到 512M;memcache 只有 1mb。为什么要用 Redis 而不必 map/guava 做缓存?应用自带的 map 或者 guava 实现的是本地缓存,最次要的特点是轻量以及疾速,生命周期随着 jvm 的销毁而完结,并且在多实例的状况下,每个实例都须要各自保留一份缓存,缓存不具备一致性。 ...

May 1, 2023 · 4 min · jiezi

关于java:基于SSM开发宿舍管理系统

拜访【WRITE-BUG数字空间】_[内附残缺源码和文档]本宿舍管理系统采纳B/S架构模式。 B/S架构的全称为Browser/Server,即浏览器/服务器构造。Browser指的是Web浏览器,与C/S架构相比,B/S模式极少数事务逻辑在前端实现,它的次要事务逻辑在服务器端实现。B/S架构的零碎毋庸特地装置,只有Web浏览器即可。宿舍治理是高校治理的重要组成部分,一套优良的管理系统不仅能够升高宿舍治理的难度,也能在肯定水平上缩小学校管理费用的收入,能是建设现代化高校管理体系的重要标记。1、零碎架构模式本宿舍管理系统采纳 B/S 架构模式。B/S 架构的全称为 Browser/Server,即浏览器/服务器构造。Browser 指的是 Web 浏览器,与 C/S 架构相比,B/S 模式极少数事务逻辑在前端实现,它的次要事务逻辑在服务器端实现。B/S 架构的零碎毋庸特地装置,只有 Web 浏览器即可。B/S 架构的分层:与 C/S 架构只有两层不同的是,B/S 架构有三层,别离为:第一层体现层:次要实现用户和后盾的交互及最终查问后果的输入性能。第二层逻辑层:次要是利用服务器实现客户端的应用逻辑性能。第三层数据层:次要是进行数据长久化存储。2、技术选型抉择适合的技术,整个我的项目就曾经胜利了一半,通过对系统需要和零碎本身特点的剖析,加上古代 B/S 模式支流架构解决方案,对本零碎技术选型如下:数据体现层: Html+JavaScript+CSS+VUE业务逻辑层 Java+Spring+SpringMVC数据长久层: MySql+MyBatis开发工具: Eclipse3、用户剖析本零碎次要利用于高校宿舍治理,应用人群如下:系统管理员:治理整个零碎的平安运行,各个性能应用。宿舍管理员:治理本人负责的宿管和学生学生:查看浏览信息,提交工作4、功能分析系统管理员:增加、批改、删除布告信息增加、批改、删除宿舍管理员信息增加、批改、删除学生信息宿舍楼治理及其宿舍管理员调配学生寝室治理公布考勤、打卡工作查看、批改个人信息

April 30, 2023 · 1 min · jiezi

关于java:一文彻底搞懂ZAB算法看这篇就够了

最近须要设计一个分布式系统,须要一个中间件来存储共享的信息,来保障多个零碎之间的数据一致性,调研了两个支流框架Zookeeper和ETCD,发现都能满足咱们的零碎需要。其中ETCD是K8s中采纳的分布式存储,而其底层采纳了RAFT算法来保障一致性,之前曾经详细分析了Raft算法的原理,明天次要仔细分析下Zookeeper的底层算法-ZAB算法。什么是ZAB 算法?ZAB的全称是 Zookeeper Atomic Broadcast (Zookeeper原子播送)。Zookeeper 是通过 Zab 算法来保障分布式事务的最终一致性。 Zab协定是为分布式协调服务Zookeeper专门设计的一种 反对解体复原 的 原子播送协定 ,是Zookeeper保证数据一致性的外围算法。Zab借鉴了Paxos算法,但又不像Paxos那样,是一种通用的分布式一致性算法。它是特地为Zookeeper设计的反对解体复原的原子播送协定。在Zookeeper中次要依赖Zab协定来实现数据一致性,基于该协定,zk实现了一种主备模型(即Leader和Follower模型)的零碎架构来保障集群中各个正本之间数据的一致性。 这里的主备零碎架构模型,就是指只有一台客户端(Leader)负责解决内部的写事务申请,而后Leader客户端将数据同步到其余Follower节点。客户端的读取流程:客户端会随机的链接到 zookeeper 集群中的一个节点,如果是读申请,就间接从以后节点中读取数据;如果是写申请,那么节点就会向 Leader 提交事务,Leader 接管到事务提交,会播送该事务,只有超过半数节点写入胜利,该事务就会被提交。 深刻ZAB算法ZAB算法分为两大块内容,音讯播送和解体复原。 音讯播送(boardcast):Zab 协定中,所有的写申请都由 leader 来解决。失常工作状态下,leader 接管申请并通过播送协定来解决。解体复原(recovery):当服务首次启动,或者 leader 节点挂了,零碎就会进入恢复模式,直到选出了有非法数量 follower 的新 leader,而后新 leader 负责将整个零碎同步到最新状态。1. 音讯播送音讯播送的过程实际上是一个简化的两阶段提交过程,这里对两阶段提交做一个简略的介绍。 两阶段提交两阶段提交算法自身是统一强一致性算法,适宜用作数据库的分布式事务,其实数据库的常常用到的TCC自身就是一种2PC。 上面以MySQL中对数据库的批改过程,来介绍下两阶段提交的具体流程,在MySQL中对一条数据的批改操作首先写undo日志,记录的数据原来的样子,接下来执行事务批改操作,把数据写到redo日志外面,万一捅娄子,事务失败了,可从undo外面复原数据。数据库通过undo与redo能保证数据的强一致性。 首先第一阶段叫筹备节点,事务的申请都发送给一个个的资源,这里的资源能够是数据库,也能够是其余反对事务的框架,他们会别离执行本人的事务,写日志到undo与redo,然而不提交事务。当事务管理器收到了所以资源的反馈,事务都执行没报错后,事务管理器再发送commit指令让资源把事务提交,一旦发现任何一个资源在筹备阶段没有执行胜利,事务管理器会发送rollback,让所有的资源都回滚。这就是2pc,非常简单。说他是强一致性的是他须要保障任何一个资源都胜利,整个分布式事务才胜利。 长处:原理简略,实现不便毛病:同步阻塞,单点问题,数据不统一,容错性不好 同步阻塞:在二阶段提交的过程中,所有的节点都在期待其余节点的响应,无奈进行其余操作。这种同步阻塞极大的限度了分布式系统的性能。单点问题:协调者在整个二阶段提交过程中很重要,如果协调者在提交阶段呈现问题,那么整个流程将无奈运行。更重要的是,其余参与者将会处于始终锁定事务资源的状态中,而无奈持续实现事务操作。数据不统一:假如当协调者向所有的参与者发送commit申请之后,产生了部分网络异样,或者是协调者在尚未发送完所有 commit申请之前本身产生了解体,导致最终只有局部参与者收到了commit申请。这将导致重大的数据不统一问题。容错性不好:二阶段提交协定没有设计较为欠缺的容错机制,任意一个节点是失败都会导致整个事务的失败。ZAB音讯播送过程Zookeeper集群中,存在以下三种角色的节点: Leader:Zookeeper集群的外围角色,在集群启动或解体复原中通过Follower参加选举产生,为客户端提供读写服务,并对事务申请进行解决*。Follower:Zookeeper集群的外围角色,在集群启动或解体复原中加入选举,没有被选上就是这个角色,为客户端提供读取服务,也就是解决非事务申请,Follower不能处理事务申请,对于收到的事务申请会转发给Leader。Observer:观察者角色,不加入选举,为客户端提供读取服务,解决非事务申请,对于收到的事务申请会转发给Leader。应用Observer的目标是为了扩大零碎,进步读取性能。Leader 接管到音讯申请后,将音讯赋予一个全局惟一的 64 位自增 id,叫做:zxid,通过 zxid 的大小比拟即可实现因果有序这一个性。Leader 通过先进先出队列(通过 TCP 协定来实现,以此实现了全局有序这一个性)将带有 zxid 的音讯作为一个提案(proposal)分发给所有 follower。当 follower 接管到 proposal,先将 proposal 写到硬盘,写硬盘胜利后再向 leader 回一个 ACK。当 leader 接管到非法数量的 ACKs 后,leader 就向所有 follower 发送 COMMIT 命令,同时会在本地执行该音讯。当 follower 收到音讯的 COMMIT 命令时,就会执行该音讯。 相比于残缺的二阶段提交,Zab 协定最大的区别就是不能终止事务,follower 要么回 ACK 给 leader,要么摈弃 leader,在某一时刻,leader 的状态与 follower 的状态很可能不统一,因而它不能解决 leader 挂掉的状况,所以 Zab 协定引入了恢复模式来解决这一问题。 ...

April 29, 2023 · 2 min · jiezi

关于java:连接池-Druid-四-连接归还

驾轻就熟,连贯偿还是通过Connection的代理对象重写close办法实现的,通过后面的学习咱们曾经晓得Connectin的代理对象是DruidPooledConnection,所以咱们间接看DruidPooledConnection的close办法。 DruidPooledConnection#close间接上代码: public void close() throws SQLException { if (this.disable) { return; } DruidConnectionHolder holder = this.holder; if (holder == null) { if (dupCloseLogEnable) { LOG.error("dup close"); } return; } DruidAbstractDataSource dataSource = holder.getDataSource(); boolean isSameThread = this.getOwnerThread() == Thread.currentThread(); if (!isSameThread) { dataSource.setAsyncCloseConnectionEnable(true); } if (dataSource.isAsyncCloseConnectionEnable()) { syncClose(); return; }判断以后Connection(DruidPooledConnection)的状态为disbale、或者connectionHolder(DruidConnectionHolder)为null(阐明连贯已敞开了),则间接返回。 而后判断连贯的ownerThread(获取connection的线程)与以后线程不是同一线程的话,则设置异步敞开asyncCloseConnectionEnable=true。调用syncClose()办法。 syncClose()办法和同线程敞开形式的代码逻辑基本一致,只不过syncClose()整个办法须要加锁,同线程敞开则不须要。 所以咱们就不贴出syncClose()办法的源码了。 在什么场景下数据库连贯会跨线程敞开?一个线程获取到数据库连贯,而后会交给另外一个线程,由另外一个线程执行连贯敞开?应用层这么做是为了实现多线程之间的事务处理? 接下来: if (!CLOSING_UPDATER.compareAndSet(this, 0, 1)) { return; } try { for (ConnectionEventListener listener : holder.getConnectionEventListeners()) { listener.connectionClosed(new ConnectionEvent(this)); } List<Filter> filters = dataSource.getProxyFilters(); if (filters.size() > 0) { FilterChainImpl filterChain = new FilterChainImpl(dataSource); filterChain.dataSource_recycle(this); } else { recycle(); } } finally { CLOSING_UPDATER.set(this, 0); } this.disable = true; }CAS形式批改以后对象的closing属性值为1(0->1),如果批改不胜利,则阐明有其余线程正在试图敞开以后连贯,间接返回。 ...

April 28, 2023 · 3 min · jiezi

关于java:用-ChatGPT-写代码效率杠杠的

起源:https://www.cnblogs.com/scy251147/p/17242557.html ChatGPT进去良久了,vscode下面因为集成了相干的插件,所以用起来很简略,通过自己深度应用后,察觉其能力的弱小之处,在书写单元测试,书写可视化小工具,代码纠错,代码标准,编程辅助方面,极其牢靠,而且能辨认一些坑并绕过,上面来看看他无能的一些事件吧。 留神:因为英文的表述能够让ChatGPT的反馈更精确,所以这里的问题我均以英文来形容。 1、单测用例生成待生成单测的测试类,此类就是一个动态工厂类,能够依据传入的数据类型和语言类型,创立出几个语言数据服务,包含ducc,mysql,配置,空语言数据服务: public class SepI18nDataFactory { /** * 配置文件数据服务 */ public static ISepI18nDataService dataService; /** * 构建数据服务 * * @param dataType * @return */ public static ISepI18nDataService buildDataService(SepI18nDataType dataType, SepI18nLanguageType languageType) { //依照数据类型判断 switch (dataType) { //从ducc中构建多语言服务 case DUCC: return getInstance(languageType, x -> new SepI18nDuccDataService(x)); //从mysql中构建多语言服务 case MYSQL: return getInstance(languageType, x -> new SepI18nMysqlDataService(x)); //从本地配置中构建多语言服务 case CONFIG: return getInstance(languageType, x -> new SepI18nConfigDataService(x)); //空数据服务,兜底逻辑 default: return getInstance(languageType, x -> new SepI18nEmptyDataService(x)); } } /** * 高并发下实例创立管控 * * @param languageType * @param callback * @return */ private static ISepI18nDataService getInstance(SepI18nLanguageType languageType, Function<SepI18nLanguageType, ISepI18nDataService> callback) { if (dataService == null) { synchronized (SepI18nDataFactory.class) { if (dataService == null) { dataService = callback.apply(languageType); } } } return dataService; }}ChatGPT问题如下(要求:为类中的所有办法创立单测并蕴含所有的场景):program unit tests for all methods for below class , you need test all scenarios, show me the code by using java language:public class SepI18nDataFactory { /** * 配置文件数据服务 */ public static SepI18nConfigDataService configDataService; /** * 数据库数据服务 */ public static SepI18nMysqlDataService mysqlDataService; /** * ducc数据服务 */ public static SepI18nDuccDataService duccDataService; /** * 空数据服务 - 兜底服务 */ public static SepI18nEmptyDataService emptyDataService; /** * 构建数据服务 * @param dataType * @return */ public static ISepI18nDataService buildDataService(SepI18nDataType dataType, SepI18nLanguageType languageType) { //区域设定 Locale locale = null; //依照数据类型判断 switch (dataType) { //从ducc中构建多语言服务 case DUCC: if (duccDataService == null) { duccDataService = new SepI18nDuccDataService(languageType); } return duccDataService; //从mysql中构建多语言服务 case MYSQL: if (mysqlDataService == null) { mysqlDataService = new SepI18nMysqlDataService(languageType); } return mysqlDataService; //从本地配置中构建多语言服务 case CONFIG: if (configDataService == null) { configDataService = new SepI18nConfigDataService(languageType); } return configDataService; //空数据服务,兜底逻辑 default: if (emptyDataService == null) { emptyDataService = new SepI18nEmptyDataService(languageType); } return emptyDataService; } }}ChatGPT答复如下:import static org.junit.Assert.assertEquals; import org.junit.Test;public class SepI18nDataFactoryTest { @Test public void testBuildDataService_DUCC() { SepI18nDataType dataType = SepI18nDataType.DUCC; SepI18nLanguageType languageType = SepI18nLanguageType.EN; ISepI18nDataService dataService = SepI18nDataFactory.buildDataService(dataType, languageType); assertEquals(SepI18nDuccDataService.class, dataService.getClass()); } @Test public void testBuildDataService_MYSQL() { SepI18nDataType dataType = SepI18nDataType.MYSQL; SepI18nLanguageType languageType = SepI18nLanguageType.EN; ISepI18nDataService dataService = SepI18nDataFactory.buildDataService(dataType, languageType); assertEquals(SepI18nMysqlDataService.class, dataService.getClass()); } @Test public void testBuildDataService_CONFIG() { SepI18nDataType dataType = SepI18nDataType.CONFIG; SepI18nLanguageType languageType = SepI18nLanguageType.EN; ISepI18nDataService dataService = SepI18nDataFactory.buildDataService(dataType, languageType); assertEquals(SepI18nConfigDataService.class, dataService.getClass()); } @Test public void testBuildDataService_Default() { SepI18nDataType dataType = SepI18nDataType.OTHER; SepI18nLanguageType languageType = SepI18nLanguageType.EN; ISepI18nDataService dataService = SepI18nDataFactory.buildDataService(dataType, languageType); assertEquals(SepI18nEmptyDataService.class, dataService.getClass());}}生成的单测能够完满运行且蕴含所有的场景,完满。 ...

April 28, 2023 · 5 min · jiezi

关于java:基于ssm分布式开发实现的电商项目聚合工程

拜访【WRITE-BUG数字空间】_[内附残缺源码和文档]注:本我的项目为开源我的项目,不能用于商业利用,仅供学习。1.应用工具:maven(构建我的项目),svn(版本控制工具),myeclipse(集成开发环境),nginx(反向代理),FastDFS (图片服务器),tomcat(web服务器),zookeeper(集群治理),mysql(数据库)Junit(测试)2.技术栈:spring,springmvc,mybatis(框架)solr(搜寻服务),redis(缓存),easyUI(后盾零碎页面)3.数据库设计tb_user用户表(id,username,password,phone,email,created,updated)tb_item商品表(id,title,sell_point,price,num,barcode,image,cid,status,created,updated)tb_cat商品分类表(id,parent_id,name,status,sort_order,is_parent,created,updated)tb_item_desc商品形容表(item_id,item_desc,created,updated)tb_item_param商品规格参数表(id,item_cat_id,param_data,created,updated)tb_item_param商品规格参数模板表(id,item_id,param_data,created,updated)tb_order订单表(payment,payment_type,post_fee,status,create_time,update_time,payment_time,consign_time,end_time,close_time,shipping_name,shipping_code,user_id,buyer_message,buyer_nick,buyer_rate)tb_order订单商品表(id,item_id,order_id,num,title,price,total_fee,pic_path)tb_order_shipping订单物流表(order_id,receiver_name,receiver_phone,receiver_mobile,receiver_state,receiver_city,receiver_district,receiver_address,receiver_zip,created,updated)tb_content_category商品目录分类表(id,parent_id,name,status,sort_order,is_parent,created,updated)tb_content商品目录表(id,category_id,title,sub_title,title_desc,url,pic,pic2,content,created,updated) 4.分布式系统4.1商品后盾管理系统shop-manager(治理后盾)商品的增加性能:1.商品类目抉择-easyui异步tree控件的应用2.图片上传(fastdfs+nginx)3.富文本编辑器应用KindEditor4.分页应用PageHelper插件,插件是基于mybatis的拦截器接口实现的 商品的展现性能:1.分页插件的应用PageHelper。2.easyUIDataGrid的应用4.2前台零碎shop-rest(公布服务)shop-search(搜寻服务)应用solr实现搜寻,内容列表应用redis缓存,应用zookeeper治理集群shop-sso (单点登录零碎)SSO英文全称Single Sign On,单点登录。SSO是在多个利用零碎中, 用户只须要登录一次就能够拜访所有相互信任的利用零碎。它包含 能够将这次次要的登录映射到其余利用中用于同一个用户的登录的机制。 它是目前比拟风行的企业业务整合的解决方案之一。用户登录:1、接管用户名和明码2、校验用户名明码3、生成token,能够应用UUID4、把用户信息写入redis,key就是token5、把token写入cookie。6、返回登录胜利须要把token返回给客户端。 Session共享的问题:1、tomcat做集群配置session复制。如果集群中节点很多,会造成网络风暴。举荐节点数量不要超过5个。2、分布式架构。拆分成多个子系统。(本我的项目应用)

April 28, 2023 · 1 min · jiezi

关于java:说一个大家都知道的-Spring-Boot-小细节

小伙伴们晓得,咱们在创立 Spring Boot 我的项目的时候,默认都会有一个 parent,这个 parent 中帮咱们定了我的项目的 JDK 版本、编码格局、依赖版本、插件版本等各种常见内容,有的小伙伴可能看过 parent 的源码,这个源码里边有这么一个配置: <resources> <resource> <directory>${basedir}/src/main/resources</directory> <filtering>true</filtering> <includes> <include>**/application*.yml</include> <include>**/application*.yaml</include> <include>**/application*.properties</include> </includes> </resource> <resource> <directory>${basedir}/src/main/resources</directory> <excludes> <exclude>**/application*.yml</exclude> <exclude>**/application*.yaml</exclude> <exclude>**/application*.properties</exclude> </excludes> </resource></resources>首先小伙伴们晓得,这个配置文件的目标次要是为了形容在 maven 打包的时候要不要带上这几个配置文件,然而咋一看,又感觉下面这段配置仿佛有点矛盾,松哥来和大家捋一捋就不感觉矛盾了: 先来看第一个 resource,directory 就是我的项目的 resources 目录,includes 中就是咱们三种格局的配置文件,另外还有一个 filtering 属性为 true,这是啥意思呢?这其实是说咱们在 maven 的 pom.xml 文件中定义的一些变量,能够在 includes 所列出的配置文件中进行援用,也就是说 includes 中列出来的文件,能够参加到我的项目的编译中。第二个 resource,没有 filter,并且将这三个文件排除了,意思是我的项目在打包的过程中,除了这三类文件之外,其余文件间接拷贝到我的项目中,不会参加我的项目编译。总结一下就是 resources 下的所有文件都会被打包到我的项目中,然而列出来的那三类,不仅会被打包进来,还会参加编译。 这下就清晰了,下面这段配置实际上并不矛盾。 那么在 properties 或者 yaml 中,该如何援用 maven 中的变量呢? 这块本来的写法是应用 $ 符号来援用,然而,咱们在 properties 配置文件中,往往用 $ 符号来援用以后配置文件的另外一个 key,所以,咱们在 Spring Boot 的 parent 中,还会看到上面这行配置: ...

April 28, 2023 · 2 min · jiezi

关于java:连接池-Druid-三-获取连接-getConnection

Druid连接池只存储在connections数组中,所以获取连贯的逻辑应该比HikariPool简略一些:间接从connectoins获取即可。 DruidDataSource.getConnection间接上代码: @Override public DruidPooledConnection getConnection() throws SQLException { return getConnection(maxWait); }调用了getConnection(maxWait),maxWait是参数设定的获取连贯的最长等待时间,超过该时长还没有获取到连贯的话,抛异样。 看getConnection(maxWait)代码: public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException { init(); if (filters.size() > 0) { FilterChainImpl filterChain = new FilterChainImpl(this); return filterChain.dataSource_connect(this, maxWaitMillis); } else { return getConnectionDirect(maxWaitMillis); } }先调用init,init办法会判断连接池是否曾经实现了初始化,如果没有实现初始化则首先进行初始化,初始化的代码咱们上一篇文章曾经剖析过了。 之后判断是否有filters,filters的内容咱们先放放,临时不论,间接看没有filters的状况下,调用getConnectionDirect办法。 getConnectionDirect办法比拟长,咱们还是老办法,分段剖析: public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException { int notFullTimeoutRetryCnt = 0; for (;;) { // handle notFullTimeoutRetry DruidPooledConnection poolableConnection; try { poolableConnection = getConnectionInternal(maxWaitMillis); } catch (GetConnectionTimeoutException ex) { if (notFullTimeoutRetryCnt <= this.notFullTimeoutRetryCount && !isFull()) { notFullTimeoutRetryCnt++; if (LOG.isWarnEnabled()) { LOG.warn("get connection timeout retry : " + notFullTimeoutRetryCnt); } continue; } throw ex; }上来之后首先有限for循环,目标是从连接池获取到连贯之后,依据参数设定可能会做必要的查看,如果查看不通过(比方连贯不可用、连贯已敞开等等)的话循环从新获取。 ...

April 27, 2023 · 4 min · jiezi

关于java:Java-给PDF添加文本水印

水印个别用于标注文档的版权信息、作者、所属机构或者文档属性等。不同于文本批注等,文字水印是绘制到页面中的元素,所以不能轻易被删除。因而,水印也是一种很好的防伪伎俩。水印又分为文本水印和图片水印两种。这里,以Java代码为例,我将分享通过代码给PDF文档增加文本水印的具体方法和示例代码。应用到的工具是Free Spire.PDF for Java(免费版),有趣味的敌人也能够试用下。 程序环境IntelliJ IDEA 2018 (jdk 1.8.0)在进行操作之前先导入JAR包,请参考以下两种导入形式:办法一:如果您应用的是 maven,先创立maven我的项目。而后通过增加以下代码来配置pom.xml 文件,再点击Import Changes将 JAR文件导入到应用程序中。 <repositories> <repository> <id>com.e-iceblue</id> <name>e-iceblue</name> <url>https://repo.e-iceblue.cn/repository/maven-public/</url> </repository></repositories><dependencies> <dependency> <groupId>e-iceblue</groupId> <artifactId>spire.pdf.free</artifactId> <version>5.1.0</version> </dependency></dependencies>办法二:如果您没有应用 maven,则能够从此链接下载Free Spire.PDF for Java,找到lib文件夹下的Spire.Pdf.jar并进行解压;而后在IDEA中创立一个新我的项目,顺次点击“文件”(File),“我的项目构造”(Project Structure),“组件”(Modules),“依赖项”(Dependencies),再点击右方绿色“+”下的第一个选项“JAR文件或门路”(JARs or Directories),找到解压后的Spire.Pdf.jar 文件,点击确认,将其导入到我的项目中。 单个文本水印代码介绍:首先,创立一个PdfDocument类的对象并加载PDF示例文档。而后,循环遍历所有页面,并在每个页面上增加水印。设置了水印文字透明度,坐标偏移量和旋转角度。通过调用translateTransform()办法和rotateTransform()办法对水印进行地位和旋转调整,而后将其绘制到该页面上。最初,应用saveToFile()办法将更改保留到文件中。 残缺代码:import com.spire.pdf.*;import com.spire.pdf.graphics.*;import java.awt.*;public class insertSingleTextWaterMark { public static void main(String[] args) { //创立PdfDocument类的对象 PdfDocument pdf = new PdfDocument(); //载入PDF文档 pdf.loadFromFile("sample.pdf"); //循环遍历 PDF 文档中的所有页面以一一增加水印 String text = "秘密"; PdfTrueTypeFont font = new PdfTrueTypeFont(new Font("黑体", Font.BOLD, 50)); float set1 = (float) (font.measureString(text).getWidth() * Math.sqrt(2)/4); float set2 = (float) (font.measureString(text).getHeight() * Math.sqrt(2)/4); for (int i = 0; i < pdf.getPages().getCount(); i++){ //获取一个页面 PdfPageBase page = pdf.getPages().get(i); //设置水印文字的透明度 page.getCanvas().setTransparency(0.8f); //设置坐标的偏移量 page.getCanvas().translateTransform(page.getCanvas().getSize().getWidth()/2 - set1 - set2, page.getCanvas().getSize().getHeight()/2 + set1 - set2); //设置水印文字的旋转角度 page.getCanvas().rotateTransform(-45); //将水印绘制在该页面上 page.getCanvas().drawString(text, font, PdfBrushes.getDarkRed(), 0, 0); } //保存文档 pdf.saveToFile("result1.pdf"); }}效果图 ...

April 27, 2023 · 2 min · jiezi

关于java:MySQL用的在溜不知道业务如何设计也白搭

MySQL业务设计 作者: 博学谷狂野架构师GitHub:GitHub地址 (有我精心筹备的130本电子书PDF) 只分享干货、不吹水,让咱们一起加油!逻辑设计范式设计范式概述第一范式:当关系模式R的所有属性都不能在合成为更根本的数据单位时,称R是满足第一范式的,简记为1NF。满足第一范式是关系模式规范化的最低要求,否则,将有很多基本操作在这样的关系模式中实现不了。 第二范式:如果关系模式R满足第一范式,并且R得所有非主属性都齐全依赖于R的每一个候选要害属性,称R满足第二范式,简记为2NF。 第三范式:设R是一个满足第一范式条件的关系模式,X是R的任意属性集,如果X非传递依赖于R的任意一个候选关键字,称R满足第三范式,简记为3NF。 第一范式数据库表中的所有字段都只具备繁多属性。繁多属性的列是由根本数据类型所形成的。设计进去的表都是简略的二维表。示例 解决办法name-age列具备两个属性,一个name,一个 age不合乎第一范式,把它拆分成两列。 第二范式要求表中只具备一个业务主键,也就是说合乎第二范式的表不能存在非主键列只对局部主键的依赖关系。示例有两张表:订单表,产品表 解决办法一个订单有多个产品,所以订单的主键为【订单ID】和【产品ID】组成的联结主键,这样2个主键不合乎第二范式,而且产品ID和订单ID没有强关联,故,把订单表进行拆分为订单表与订单与商品的两头表。 第三范式指每一个非主属性既不局部依赖于也不传递依赖于业务主键,也就是在第二范式的根底上打消了非主键对主键的传递依赖。示例 解决办法其中 客户编号 和订单编号治理 关联 客户姓名 和订单编号治理 关联 客户编号 和 客户姓名 关联 如果客户编号产生扭转,用户姓名也会扭转,这样不合乎第三大范式,应该把客户姓名这一列删除 范式设计实战按要求设计一个电子商务网站的数据库构造,本网站只销售图书类产品,须要具备以下性能: 用户登陆 商品展现 供应商治理用户治理 商品治理 订单销售用户登陆及用户治理用户必须注册并登陆零碎能力进行网上交易,用户名用来作为用户信息的业务主键同一时间一个用户只能在一个中央登陆 只有一个业务主键,肯定是合乎第二范式,没有属性和业务主键存在传递依赖的关系,合乎第三范式。 商品信息 一个商品能够属于多个分类,故,商品名称和分类应该是组合主键,会有大量冗余,不合乎第二范式。应该把分类信息独自寄存 解决办法另外再建设一个两头表把分类信息和商品信息进行关联 最初的三张表如下 供应商治理性能 合乎三大范式,不须要批改,但如果减少新的一列【银行支行】,这样随着银行账户的变动,银行支行也会编号,不合乎第三大范式 在线销售性能 有多个业务主键,不合乎第二范式,订单商品单价、订单数量、订单金额存在传递依赖关系,不合乎第三范式,须要拆解 解决办法创立一个订单关联表,将商品分类和商品名称拆解进去 这时候,【订单商品分类】与【订单商品名】有依赖关联,故合并如下 表汇总 查问练习编写SQL查问出每一个用户的订单总金额(用户名,订单总金额)COPYSELECT a.单用户名, sum(d.商品价格 * b.商品数量)FROM 订单表 aJOIN 订单分类关联表 b ON a.订单编号 = b.订单编号JOIN 商品分类关联表 c ON c.商品分类ID = b.商品分类IDJOIN 商品信息表 d ON d.商品名称 = c.商品名称GROUP BY a.下单用户名编写SQL查问出下单用户和订单详情(订单编号,用户名,手机号,商品名称,商品数量,商品价格)COPYSELECT a.订单编号, e.用户名, e.手机号, d.商品名称, c.商品数量, d.商品价格FROM 订单表 aJOIN 订单分类关联表 b ON a.订单编号 = b.订单编号JOIN 商品分类关联表 c ON c.商品分类ID = b.商品分类IDJOIN 商品信息表 d ON d.商品名称 = c.商品名称JOIN 用户信息表 e ON e.用户名 = a.下单用户存在的问题大量的表关联十分影响查问的性能完全符合范式化的设计有时并不能失去良好得SQL查问性能反范式设计什么叫反范式化设计反范式化是针对范式化而言得,在后面介绍了数据库设计得范式所谓得反范式化就是为了性能和读取效率得思考而适当得对数据库设计范式得要求进行违反容许存在大量得冗余,换句话来说反范式化就是应用空间来换取工夫商品信息反范式设计上面是范式设计的商品信息表 ...

April 27, 2023 · 2 min · jiezi

关于java:项目终于用上了xxljob真香

大家好,我是不才陈某~ 本篇文章次要记录我的项目中遇到的 xxl-job 的实战,心愿能通过这篇文章通知读者们什么是 xxl-job 以及怎么应用 xxl-job 并分享一个实战案例。 文章首发公众号:码猿技术专栏 关注公众号:码猿技术专栏,回复关键词:1111 获取阿里外部java性能调优手册! 那么上面先阐明什么是 xxl-job 以及为什么要应用它。 xxl-job 是什么?XXL-JOB 是一个分布式任务调度平台,其外围设计指标是开发迅速、学习简略、轻量级、易扩大。 设计思维 是将调度行为形象造成 调度核心 平台,平台自身不承当业务逻辑,而是负责发动 调度申请 后,由 执行器 接管调度申请并执行 工作,这里的 工作 形象为 扩散的 JobHandler。通过这种形式即可实现 调度 与 工作 互相解耦,从而进步零碎整体的稳定性和拓展性。 为了更好了解,这里放一张官网的架构图: 任务调度是什么?在开发我的项目时大家是否也遇到过相似的场景问题: 零碎须要定时在每天0点进行数据备份。零碎须要在流动开始前几小时预热执行一些前置业务。零碎须要定时对 MQ 音讯表的发送装填,对发送失败的 MQ 音讯进行弥补从新发送。这些场景问题都能够通过 任务调度 来解决,任务调度指的是零碎在约定的指定工夫主动去执行指定的工作的过程。 单体零碎 中有许多实现 任务调度 的形式,如多线程形式、Timer 类、Spring Tasks 等等。这里比拟罕用的是 Spring Tasks(通过 @EnableScheduling + @Scheduled 的注解能够自定义定时工作,有趣味的能够去理解一下) 为什么须要分布式任务调度平台?分布式下,每个服务都能够搭建为集群,这样的益处是能够将工作切片分给每一个服务从而实现并行执行,进步任务调度的解决效率。那么为什么 分布式系统 不能应用 单体零碎 的任务调度实现形式呢。 在集群服务下,如果还是应用每台机器依照单体零碎的任务调度实现形式实现的话,会呈现上面这四个问题: 怎么做到对工作的管制(如何防止工作反复执行)。如果某台机器宕机了,会不会存在工作失落。如果要减少服务实例,怎么做到弹性扩容。如何做到对任务调度的执行状况对立监测。通过下面的问题能够理解到分布式系统下须要一个满足高可用、容错治理、负载平衡等性能的任务调度平台来实现任务调度。分布式系统下,也有许多能够实现任务调度的第三方的分布式任务调度零碎,如 xxl-job、Quartz、elastic-job 等等罕用的分布式任务调度零碎。 如何应用 xxl-job作为开源软件的 xxl-job,能够在 github 或 gitee上查看和下载 xxl-job 的源码。 ...

April 27, 2023 · 4 min · jiezi

关于java:什么是死锁以及如何解决死锁

死锁是指两个或多个线程在相互期待对方开释锁的状态,从而导致程序无奈继续执行的状况。 在Java多线程中,死锁通常是因为以下四种状况的组合所导致的: 互斥:多个线程竞争同一资源(如锁),每次只能有一个线程占用,其余线程必须期待。占有且期待:线程在持有锁的同时,期待其余线程持有的锁。不可抢占:曾经获取锁的线程不能被其余线程强制中断或开释锁。循环期待:多个线程造成一个循环期待的关系,每个线程都在期待其余线程开释锁。要解决死锁问题,能够采取以下措施: 防止应用多个锁:尽量应用单个锁或者应用更高级别的同步机制,比方并发汇合。对立获取锁的程序:确保所有线程获取锁的程序统一,避免出现循环期待的状况。尽可能放大同步代码块:缩小同步代码块的长度,放大互斥的范畴。及时开释锁:尽可能早地开释锁,防止占有且期待的状况。应用定时锁:在获取锁时,应用带有超时工夫的锁,防止因为期待锁而导致死锁。强制中断线程:在发现死锁时,能够强制中断其中一个线程,突破循环期待的环。须要留神的是,死锁是一个简单的问题,解决起来也比拟艰难,须要仔细分析代码和调试。 死锁代码演示: public class DeadLock { public static void main(String[] args) { Object lockA = new Object(); Object lockB = new Object(); Thread t1 = new Thread(() -> { // 1.占有一把锁 synchronized (lockA) { System.out.println("线程1取得锁A"); // 休眠1s(让线程2有工夫先占有锁B) try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 获取线程2的锁B synchronized (lockB) { System.out.println("线程1取得锁B"); } } }); t1.start(); Thread t2 = new Thread(() -> { // 占B锁 synchronized (lockB) { System.out.println("线程2取得锁B"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 获取线程1的锁A synchronized (lockA) { System.out.println("线程2取得了锁A"); } } }); t2.start(); }}运行后果: ...

April 26, 2023 · 1 min · jiezi

关于java:Java中活锁是什么啊

活锁是与死锁对应的,是说单方没有相互期待而无奈进行,都能够动,然而动完当前仍是死锁状态。 残缺内容请点击下方链接查看: https://developer.aliyun.com/ask/463993?groupCode=learning 版权申明:本文内容由阿里云实名注册用户自发奉献,版权归原作者所有,阿里云开发者社区不领有其著作权,亦不承当相应法律责任。具体规定请查看《阿里云开发者社区用户服务协定》和《阿里云开发者社区知识产权爱护指引》。如果您发现本社区中有涉嫌剽窃的内容,填写侵权投诉表单进行举报,一经查实,本社区将立即删除涉嫌侵权内容。

April 26, 2023 · 1 min · jiezi

关于java:jdbcplus是一款基于JdbcTemplate增强工具包已实现分页多租户等插件可自定义扩展插件

jdbc-plus简介 jdbc-plus是一款基于JdbcTemplate加强工具包, 基于JdbcTemplate已实现分页、多租户等插件,可自定义扩大插件。我的项目地址: https://github.com/deeround/jdbc-plus 个性: 应用简略,对代码入侵很小可自定义任意扩大插件多租户参考mybatis-plus多租户的实现原理,实践上与mybatis-plus多租户插件反对度一样分页参考PageHelper的实现原理,应用简略,对代码入侵较小,还能够注册不反对的数据库收费开源,可任意应用批改代码疾速开始引入jdbc-plus-spring-boot-starter<dependency> <groupId>com.github.deeround</groupId> <artifactId>jdbc-plus-spring-boot-starter</artifactId> <version>${version}</version></dependency>注入须要应用的插件@Configurationpublic class JdbcPlusConfig { /** * PaginationInterceptor是内置的分页插件(分页插件肯定要注入在TenantLineHandler之后,能够通过Order来管制程序) */ @Bean @Order(9) public IInterceptor paginationInterceptor() { return new PaginationInterceptor(); } /** * TenantLineHandler是内置的多租户插件插件 */ @Bean @Order(1) public IInterceptor tenantLineInterceptor() { return new TenantLineInterceptor(new TenantLineHandler() { /** * 以后租户ID */ @Override public Expression getTenantId() { String currentTenantId = "test_tenant_1";//能够从申请上下文中获取(cookie、session、header等) return new StringValue(currentTenantId); } /** * 租户字段名 */ @Override public String getTenantIdColumn() { return "tenant_id"; } /** * 依据表名判断是否疏忽拼接多租户条件 */ @Override public boolean ignoreTable(String tableName) { return TenantLineHandler.super.ignoreTable(tableName); } }); }}多租户插件注入多租户插件 /** * TenantLineHandler是内置的多租户插件插件 */ @Bean @Order(1) public IInterceptor tenantLineInterceptor() { return new TenantLineInterceptor(new TenantLineHandler() { /** * 以后租户ID */ @Override public Expression getTenantId() { String currentTenantId = "test_tenant_1";//能够从申请上下文中获取(cookie、session、header等) return new StringValue(currentTenantId); } /** * 租户字段名 */ @Override public String getTenantIdColumn() { return "tenant_id"; } /** * 依据表名判断是否疏忽拼接多租户条件 */ @Override public boolean ignoreTable(String tableName) { return TenantLineHandler.super.ignoreTable(tableName); } }); }service层执行SQL时主动增加租户字段 @Autowired JdbcTemplate jdbcTemplate; public void insert() { this.jdbcTemplate.update("insert into test_user(id,name) values('1','wangwu')"); //最终执行SQL:insert into test_user(id,name,tenant_id) values('1','wangwu','test_tenant_1') } public void delete() { this.jdbcTemplate.update("delete from test_user"); //最终执行SQL:delete from test_user where tenant_id='test_tenant_1' } public void update() { this.jdbcTemplate.update("update test_user set name='lisi' where id='1'"); //最终执行SQL:update test_user set name='lisi' where id='1' and tenant_id='test_tenant_1' } public List<Map<String, Object>> query() { return this.jdbcTemplate.queryForList("select * from test_user"); //最终执行SQL:select * from test_user where tenant_id='test_tenant_1' }分页插件注入分页插件 /** * PaginationInterceptor是内置的分页插件(分页插件肯定要注入在TenantLineHandler之后,能够通过Order来管制程序) */ @Bean @Order(9) public IInterceptor paginationInterceptor() { return new PaginationInterceptor(); }service层执行SQL时主动对SQL进行分页查问 @Autowired JdbcTemplate jdbcTemplate; public PageInfo<Map<String, Object>> page1() { PageHelper.startPage(1, 2); List<Map<String, Object>> list = this.jdbcTemplate.queryForList("select * from test_user");//最终执行SQL:select * from test_user LIMIT 0,2 PageInfo<Map<String, Object>> page = new PageInfo<>(list);//PageInfo对象蕴含了分页信息(总行数等) return page; } public PageInfo<Map<String, Object>> page2() { PageHelper.startPage(2, 2); List<Map<String, Object>> list = this.jdbcTemplate.queryForList("select * from test_user");//最终执行SQL:select * from test_user LIMIT 2,2 PageInfo<Map<String, Object>> page = new PageInfo<>(list);//PageInfo对象蕴含了分页信息(总行数等) return page; }自定义分页当插件不反对的数据库分页,能够通过PageHelper.registerDialectAlias(String alias, Class clazz) 注册一个本人分页实现类即可,也能够笼罩已反对的数据库分页。 ...

April 26, 2023 · 2 min · jiezi

关于java:重学Java之泛型的基本使用

前言自身是打算接着写JMM、JCStress,而后这两个是在公司空闲的时候顺手写的,没有推到Github上,但写点什么能够让我取得平静的感觉,所性就从待办中拎了一篇文章,也就是这篇泛型。这篇文章来自于我敌人提出的一个问题,比方我在一个类外面申明了两个办法,两个办法只有返回类型是int,一个是Integer,像上面这样,是否通过编译: public class DataTypeTest { public int sayHello(){ return 0; } public Integer sayHello(){ return 1; }}我过后答复的时候是将Integer和int当做不同的类型来思考的,我答复的是能够,然而我的敌人说,这是不行的。前面我想到了泛型擦除,但其实这跟泛型擦除倒是没关系,问题出在主动装箱和拆箱上,Java的编译器将原始类型转为包装类,包装类转为根本类型。但对于泛型,我用起来的时候,发现有些概念凌乱,然而不影响开发速度,再加上平时感觉对我用途不大,所性就始终放在那里,不去思考。最近也在一些工具类库,用到了泛型,发现自己对泛型的了解还是有所欠缺,所以明天就重新学习泛型,顺带梳理一下本人对泛型的了解,前面发现都揉在一篇文章外面,篇幅还是有些过大,这里就分拆两篇。 泛型的根本的应用泛型擦除、实现、向前兼容、与其余语言的比照。泛型的意义我在学习Java的时候,看的是Oracle出的《Java Tutorials》,地址如下: https://docs.oracle.com/javase/tutorial/java/generics/index.html在开篇教程如是说: In any nontrivial software project, bugs are simply a fact of life. Careful planning, programming, and testing can help reduce their pervasiveness, but somehow, somewhere, they'll always find a way to creep into your code. This becomes especially apparent as new features are introduced and your code base grows in size and complexity. ...

April 26, 2023 · 8 min · jiezi

关于java:这可能是最全面的Redis面试八股文了

Redis连环40问,相对够全! Redis是什么?Redis(Remote Dictionary Server)是一个应用 C 语言编写的,高性能非关系型的键值对数据库。与传统数据库不同的是,Redis 的数据是存在内存中的,所以读写速度十分快,被广泛应用于缓存方向。Redis能够将数据写入磁盘中,保障了数据的平安不失落,而且Redis的操作是原子性的。最全面的Java面试网站 Redis优缺点?长处: 基于内存操作,内存读写速度快。反对多种数据类型,包含String、Hash、List、Set、ZSet等。反对长久化。Redis反对RDB和AOF两种长久化机制,长久化性能能够无效地防止数据失落问题。反对事务。Redis的所有操作都是原子性的,同时Redis还反对对几个操作合并后的原子性执行。反对主从复制。主节点会主动将数据同步到从节点,能够进行读写拆散。Redis命令的解决是单线程的。Redis6.0引入了多线程,须要留神的是,多线程用于解决网络数据的读写和协定解析,Redis命令执行还是单线程的。毛病: 对结构化查问的反对比拟差。数据库容量受到物理内存的限度,不适宜用作海量数据的高性能读写,因而Redis适宜的场景次要局限在较小数据量的操作。Redis 较难反对在线扩容,在集群容量达到下限时在线扩容会变得很简单。Redis为什么这么快?基于内存:Redis是应用内存存储,没有磁盘IO上的开销。数据存在内存中,读写速度快。IO多路复用模型:Redis 采纳 IO 多路复用技术。Redis 应用单线程来轮询描述符,将数据库的操作都转换成了事件,不在网络I/O上节约过多的工夫。高效的数据结构:Redis 每种数据类型底层都做了优化,目标就是为了谋求更快的速度。本文曾经收录到Github仓库,该仓库蕴含计算机根底、Java根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等外围知识点,欢送star~ Github地址 如果拜访不了Github,能够拜访gitee地址。 gitee地址 既然Redis那么快,为什么不必它做主数据库,只用它做缓存?尽管Redis十分快,但它也有一些局限性,不能齐全代替主数据库。有以下起因: 事务处理:Redis只反对简略的事务处理,对于简单的事务无能为力,比方跨多个键的事务处理。 数据长久化:Redis是内存数据库,数据存储在内存中,如果服务器解体或断电,数据可能失落。尽管Redis提供了数据长久化机制,但有一些限度。 数据处理:Redis只反对一些简略的数据结构,比方字符串、列表、哈希表等。如果须要解决简单的数据结构,比方关系型数据库中的表,那么Redis可能不是一个好的抉择。 数据安全:Redis没有提供像主数据库那样的平安机制,比方用户认证、访问控制等等。 因而,尽管Redis十分快,但它还有一些限度,不能齐全代替主数据库。所以,应用Redis作为缓存是一种很好的形式,能够进步应用程序的性能,并缩小数据库的负载。 讲讲Redis的线程模型?Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成构造为4局部:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的生产是单线程的,所以Redis才叫单线程模型。 文件事件处理器应用I/O多路复用(multiplexing)程序来同时监听多个套接字, 并依据套接字目前执行的工作来为套接字关联不同的事件处理器。当被监听的套接字筹备好执行连贯accept、read、write、close等操作时, 与操作绝对应的文件事件就会产生, 这时文件事件处理器就会调用套接字之前关联好的事件处理器来解决这些事件。尽管文件事件处理器以单线程形式运行, 但通过应用 I/O 多路复用程序来监听多个套接字, 文件事件处理器既实现了高性能的网络通信模型, 又能够很好地与 redis 服务器中其余同样以单线程形式运行的模块进行对接, 这放弃了 Redis 外部单线程设计的简略性。 Redis利用场景有哪些?缓存热点数据,缓解数据库的压力。利用 Redis 原子性的自增操作,能够实现计数器的性能,比方统计用户点赞数、用户拜访数等。分布式锁。在分布式场景下,无奈应用单机环境下的锁来对多个节点上的过程进行同步。能够应用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还能够应用官网提供的 RedLock 分布式锁实现。简略的音讯队列,能够应用Redis本身的公布/订阅模式或者List来实现简略的音讯队列,实现异步操作。限速器,可用于限度某个用户拜访某个接口的频率,比方秒杀场景用于避免用户疾速点击带来不必要的压力。好友关系,利用汇合的一些命令,比方交加、并集、差集等,实现独特好友、共同爱好之类的性能。Memcached和Redis的区别?MemCached 数据结构繁多,仅用来缓存数据,而 Redis 反对多种数据类型。MemCached 不反对数据长久化,重启后数据会隐没。Redis 反对数据长久化。Redis 提供主从同步机制和 cluster 集群部署能力,可能提供高可用服务。Memcached 没有提供原生的集群模式,须要依附客户端实现往集群中分片写入数据。Redis 的速度比 Memcached 快很多。Redis 应用单线程的多路 IO 复用模型,Memcached应用多线程的非阻塞 IO 模型。(Redis6.0引入了多线程IO,用来解决网络数据的读写和协定解析,然而命令的执行依然是单线程)value 值大小不同:Redis 最大能够达到 512M;memcache 只有 1mb。最初给大家分享一个Github仓库,下面有大彬整顿的300多本经典的计算机书籍PDF,包含C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,能够star一下,下次找书间接在下面搜寻,仓库继续更新中~ ...

April 26, 2023 · 4 min · jiezi

关于java:Spring-Boot-项目代码混淆实战来了再也不用担心代码泄露了

编译简略就是把代码跑一哈,而后咱们的代码 .java文件 就被编译成了 .class 文件 反编译就是针对编译生成的 jar/war 包 外面的 .class 文件 逆向还原回来,能够看到你的代码写的啥。 比拟罕用的反编译工具 JD-GUI ,间接把编译好的jar丢进去,大部分都能反编译看到源码: 那如果不想给他人反编译看本人写的代码呢? 怎么做? 混同该篇玩的代码混同 ,是其中一种伎俩。 我给你看,但你反编译看到的不是真正的代码。 先看一张成果示例图 : 开搞 注释先看一下咱们混同一个我的项目代码,要做啥? 一共就两步举荐一个开源收费的 Spring Boot 最全教程: https://github.com/javastacks/spring-boot-best-practice第一步, 在我的项目门路下,新增一份文件 proguard.cfg : proguard.cfg #指定Java的版本-target 1.8#proguard会对代码进行优化压缩,他会删除从未应用的类或者类成员变量等-dontshrink#是否敞开字节码级别的优化,如果不开启则设置如下配置-dontoptimize#混同时不生成大小写混合的类名,默认是能够大小写混合-dontusemixedcaseclassnames# 对于类成员的命名的混同采取惟一策略-useuniqueclassmembernames#混同时不生成大小写混合的类名,默认是能够大小写混合-dontusemixedcaseclassnames#混同类名之后,对应用Class.forName('className')之类的中央进行相应代替-adaptclassstrings#对异样、注解信息予以保留-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod# 此选项将保留接口中的所有原始名称(不混同)-->-keepnames interface ** { *; }# 此选项将保留所有软件包中的所有原始接口文件(不进行混同)#-keep interface * extends * { *; }#保留参数名,因为控制器,或者Mybatis等接口的参数如果混同会导致无奈承受参数,xml文件找不到参数-keepparameternames# 保留枚举成员及办法-keepclassmembers enum * { *; }# 不混同所有类,保留原始定义的正文--keepclassmembers class * { @org.springframework.context.annotation.Bean *; @org.springframework.beans.factory.annotation.Autowired *; @org.springframework.beans.factory.annotation.Value *; @org.springframework.stereotype.Service *; @org.springframework.stereotype.Component *; }#疏忽warn音讯-ignorewarnings#疏忽note音讯-dontnote#打印配置信息-printconfiguration-keep public class com.example.myproguarddemo.MyproguarddemoApplication { public static void main(java.lang.String[]); }留神点: ...

April 26, 2023 · 1 min · jiezi

关于java:chatgpt周刊第六期

一周资讯王小川公布守业公开信:低调入局,低调公布,与光年之外同一办公楼4月10日,搜狐网络大厦二层,搜狗创始人王小川正式对外发表开启人工智能大模型畛域守业。王小川与前搜狗 COO 茹立云联结成立人工智能公司百川智能,旨在打造中国版的OpenAI根底大模型及颠覆性下层利用。百川智能早前已取得5000万美元启动资金,来自王小川与其业内好友的集体反对。 王小川示意,面向机构的融资会在近期启动,该公司将会在年底公布面向ToB(企业端)畛域的大模型产品。他示意:“追上ChatGPT程度,我感觉往年内可能就可能实现,但对于GPT-4或者GPT-5,我认为可能须要3年左右的工夫,应该不会低于两年。” 大厂类 ChatGPT 停顿汇总:国产大模型主场来了,目迷五色的混战 即刻 AIHackathon 第一期完结:9支AI队伍入选,别离取得30万美元天使投资4月10日,即刻 AIHackathon 守业流动在上海举办 DemoDay。从 400+ 报名团队里海选出 100+ 团队参加 DemoDay,并最终颁布第一期录取的 9 支AI守业团队。 录取的团队会率先失去 Hack Engine 基金 2.5 Fund 的反对!这是一个关注AI、次要服务Hack Engine 成员的基金,将为所有录取团队,提供30万美元的天使投资。 阿里巴巴全系产品将接入大模型:进入全新的智能化时代4月11日,阿里巴巴团体董事会主席兼CEO、阿里云智能团体CEO张勇在2023年阿里云峰会上示意,阿里巴巴所有产品将来将接入「通义千问」大模型,进行全面革新,包含天猫、淘宝、钉钉、天猫精灵、闲鱼、盒马等产品。 飞书颁布智能助手MY AI:公开征求意见的告诉4月11日,飞书官网公布智能助手 My AI 演示视频,实现了包含主动汇总会议纪要、创立报告、优化/续写文字内容、主动创立日程、搜寻公司外部知识库等性能。飞书相干负责人回应,产品性能仍在推动中,暂无明确公测及上线工夫。 OpenAI公布新图像模型 Consistency Model:一步成图,1秒18张OpenAI 上个月3号低调上传了一篇图像生成畛域的论文 Consistency Models (一致性模型) ,探讨该模型地性能优于 Diffusion Model (扩散模型) 。 扩散模型 (如 OpenAI - DALL.E 2、Stability AI - Stable Diffusion、Google - Imagen) 曾经成为生成人工智能反动的根底,因为它们取代了 GAN 作为最无效的图像合成模型。然而,一致性模型被证实能够在更短的工夫内产生与扩散模型雷同品质的输入。 Databricks公布 Dolly 2.0:世界首款真开源类ChatGPT大模型4 月 12 日,Databricks 公布 Dolly 2.0,这是业内第一个开源、遵循指令的 LLM,它在通明且收费提供的数据集上进行了微调。该数据集 databricks-dolly-15k 也是开源的,可用于商业目标。 ...

April 25, 2023 · 1 min · jiezi

关于java:Linux-java环境变量配置

解压tar -zxvf /usr/local/jdk-11.0.2.tar.gz -C /usr/local配置环境变量vi /etc/profile # java Pathexport JAVA_HOME=/usr/local/jdk-11.0.2export CLASSPATH=$JAVA_HOME/lib:$CLASSPATHexport PATH=$JAVA_HOME/bin:$PATH重启环境source /etc/profile查看java --version

April 25, 2023 · 1 min · jiezi

关于java:SpringBoot-使用-SaToken-完成权限认证

一、设计思路所谓权限认证,外围逻辑就是判断一个账号是否领有指定权限: 有,就让你通过。没有?那么禁止拜访!深刻到底层数据中,就是每个账号都会领有一个权限码汇合,框架来校验这个汇合中是否蕴含指定的权限码。 例如:以后账号领有权限码汇合 ["user-add", "user-delete", "user-get"],这时候我来校验权限 "user-update",则其后果就是:验证失败,禁止拜访。 动静演示图: 所以当初问题的外围就是: 如何获取一个账号所领有的的权限码汇合?本次操作须要验证的权限码是哪个?接下来,咱们将介绍在 SpringBoot 中如何应用 Sa-Token 实现权限认证操作。 Sa-Token 是一个轻量级 java 权限认证框架,次要解决登录认证、权限认证、单点登录、OAuth2、微服务网关鉴权 等一系列权限相干问题。Gitee 开源地址:https://gitee.com/dromara/sa-token首先在我的项目中引入 Sa-Token 依赖: <!-- Sa-Token 权限认证 --><dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-boot-starter</artifactId> <version>1.34.0</version></dependency>注:如果你应用的是 SpringBoot 3.x,只须要将 sa-token-spring-boot-starter 批改为 sa-token-spring-boot3-starter 即可。 二、获取以后账号权限码汇合因为每个我的项目的需要不同,其权限设计也变幻无穷,因而 [ 获取以后账号权限码汇合 ] 这一操作不可能内置到框架中,所以 Sa-Token 将此操作以接口的形式裸露给你,以不便你依据本人的业务逻辑进行重写。 你须要做的就是新建一个类,实现 StpInterface接口,例如以下代码: /** * 自定义权限验证接口扩大 */@Component // 保障此类被SpringBoot扫描,实现Sa-Token的自定义权限验证扩大 public class StpInterfaceImpl implements StpInterface { /** * 返回一个账号所领有的权限码汇合 */ @Override public List<String> getPermissionList(Object loginId, String loginType) { // 本list仅做模仿,理论我的项目中要依据具体业务逻辑来查问权限 List<String> list = new ArrayList<String>(); list.add("101"); list.add("user.add"); list.add("user.update"); list.add("user.get"); // list.add("user.delete"); list.add("art.*"); return list; } /** * 返回一个账号所领有的角色标识汇合 (权限与角色可离开校验) */ @Override public List<String> getRoleList(Object loginId, String loginType) { // 本list仅做模仿,理论我的项目中要依据具体业务逻辑来查问角色 List<String> list = new ArrayList<String>(); list.add("admin"); list.add("super-admin"); return list; }}参数解释: ...

April 25, 2023 · 3 min · jiezi

关于java:一次说透4大服务性幂等场景架构设计方案

服务幂等性架构设计 作者: 博学谷狂野架构师GitHub:GitHub地址 (有我精心筹备的130本电子书PDF) 只分享干货、不吹水,让咱们一起加油!防重表实现幂等对于避免数据反复提交,还有一种解决方案就是通过防重表实现。防重表的实现思路也非常简单,首先创立一张表作为防重表,同时在该表中建设一个或多个字段的惟一索引作为防重字段,用于保障并发状况下,数据只有一条。在向业务表中插入数据之前先向防重表插入,如果插入失败则示意是反复数据。 为什么不必乐观锁对于防重表的解决方案,可能有人会说为什么不应用乐观锁,乐观锁在应用的过程中也是会产生死锁的。乐观锁是通过锁表的形式实现的,假如当初一个用户A拜访表A(锁住了表A),而后试图拜访表B; 另一个用户B拜访表B(锁住了表B),而后试图拜访表A。 这时对于用户A来说,因为表B曾经被用户B锁住了,所以用户A必须等到用户B开释表B能力拜访。 同时对于用户B来说,因为表A曾经被用户A锁住了,所以用户B必须等到用户A开释表A能力拜访。此时死锁就曾经产生了。 浏览业余类书籍是Java程序员必备的学习形式之一。通过一直学习和积攒,能够一直进步本人的技术能力和职业程度,实现职业倒退的指标。 十分倡议大家重视浏览,并抉择一些有深度、有价值的书籍一直晋升本人的技术水平和能力。这些书籍包含Java编程语言、数据结构和算法、面向对象设计、设计模式、框架原理与利用等等。 对于一位2-3年的Java程序员来说,浏览业余类书籍是更加的重要,因为它们能够帮忙你扩大技术广度和深度,进步你的技术能力和职业程度。以下是我给这些程序员的一些倡议: 学会寻找优良的书籍:在抉择书籍时,要抉择那些被宽泛认可和举荐的经典书籍。能够通过搜寻网上的书籍举荐列表,向其余经验丰富的程序员求教,或者参考公司外部的学习打算,来找到好的书籍。浏览无关设计模式和架构的书籍:对于Java程序员来说,把握设计模式和架构准则是十分重要的。能够抉择浏览《设计模式:可复用面向对象软件的根底》、《大话设计模式》、《Java程序员修炼之道》等书籍来深刻学习。学习新的技术和框架:Java技术一直倒退,新的技术和框架也不断涌现。因而,Java程序员应该定期浏览无关新技术和框架的书籍,比方Spring、Spring Boot、MyBatis、Netty等。学习算法和数据结构:算法和数据结构是编程根底,把握这些常识能够进步代码的品质和效率。能够抉择浏览《算法》、《算法导论》等书籍来学习算法和数据结构。参考开源我的项目和源代码:浏览开源我的项目和源代码是十分无益的,能够学习到其余程序员的编码技巧和设计思路。能够抉择一些出名的开源我的项目,如Spring、MyBatis等来进行学习。当然,我也晓得,光是倡议是不足以激发大家学习的能源的,所以,书也我也帮大家整顿好了,把饭喂到嘴里了,我只能帮你到这里了,剩下的就靠你本人了。 以下这份蕴含46本Java程序员必备经典的书单(豆瓣评分8分+),是我破费一个月工夫整顿的:GitHub:GitHub地址 惟一主键实现幂等数据库惟一主键的实现次要是利用数据库中主键惟一束缚的个性,一般来说惟一主键比拟实用于“插入”时的幂等性,其能保障一张表中只能存在一条带该惟一主键的记录。 应用数据库惟一主键实现幂等性时须要留神的是,该主键一般来说并不是应用数据库中自增主键,而是应用分布式 ID 充当主键,这样能力能保障在分布式环境下 ID 的全局唯一性。 对于一些后盾零碎,并发量并不高的状况下,对于幂等的实现非常简单,通过这种思维即可实现幂等管制。 实用场景插入操作删除操作应用限度须要生成全局惟一主键 ID;次要流程 次要流程如下:客户端执行创立申请,调用服务端接口。服务端执行业务逻辑,生成一个分布式 ID,将该 ID 充当待插入数据的主键,然 后执数据插入操作,运行对应的 SQL 语句。服务端将该条数据插入数据库中,如果插入胜利则示意没有反复调用接口。如果抛出主键反复异样,则示意数据库中曾经存在该条记录,返回错误信息到客户端。 在业务执行前,先判断是否曾经操作过,如果没有则执行,否则判断为反复操作。 成果演示在并发下拜访时,因为是基于id进行判断,那id值就必须要保障在屡次提交时,须要惟一。拜访流程如下: @Override@Transactional(rollbackFor = Exception.class)public String addOrder(Order order) { order.setCreateTime(new Date()); order.setUpdateTime(new Date()); //查问 Order orderResult = orderMapper.selectByPrimaryKey(order.getId()); Optional<Order> orderOptional = Optional.ofNullable(orderResult); if (orderOptional.isPresent()){ return "repeat request"; } int result = orderMapper.insert(order); if (result != 1){ return "fail"; } return "success";}对于上述性能实现,在并发下,并不能实现幂等性管制。通过jemeter测试,模仿50个并发,能够发现,插入了反复数据。产生了脏数据。 ...

April 25, 2023 · 3 min · jiezi

关于java:推荐一个可以提高生产力的在线游戏

很久没举荐好玩的工具了,明天给家举荐一个十分有意思的游戏:Habitica Habitica除了是个游戏之外,竟然还是一个生产力利用! 为什么说Habitica还是个生产力利用呢?因为它还能够帮忙咱们养成习惯! 通过Habitica,咱们能够用它的每日指标和代办事项列表性能来跟踪和治理你的习惯 在实现工作之后,你能够以此来降级你的虚构角色,同时解锁游戏中更多的性能,比方:取得战斗配备,宠物、技能等。 同时,Habitica 还是一个线上游戏,你能够邀请你的同学、敌人、共事一起退出进来,一起养成好习惯,一起打怪降级,一起定制工作和处分等。 目前 Habitica 社区曾经有超过 400万的用户,他们通过 Habitica 来改善本人的生存,你也能够像他们一样,通过Habitica来定制本人的生活习惯和健身打算 把学习工作和工作打算一起布局进来,跟踪治理它们,并激励本人实现以降级角色获取处分 如果你还有一些其余让你无奈保持的打算,也能够将其退出进来,比方: 每天来 http://spring4all.com/ ,公布一篇技术分享(彩蛋:社区有积分换书的流动,每天发一篇,一周换一本)每天去 https://stackoverflow.com/,答复一个技术问题每天看公众号TJ君,Get一个乏味的软件工具这么乏味且有用的在线游戏,你不来玩一下吗?上面官方网站,冲啊~ 网站地址:https://habitica.com/ 欢送关注我的公众号:程序猿DD。第一工夫理解前沿行业音讯、分享深度技术干货、获取优质学习资源

April 25, 2023 · 1 min · jiezi

关于java:Java中如何实现线程呢

Java中实现线程对象做实例化的时候有两种形式,一种形式是通过实现接口的形式,还有一种形式是间接扩大thread类。 残缺内容请点击下方链接查看: https://developer.aliyun.com/ask/463627?groupCode=learning 版权申明:本文内容由阿里云实名注册用户自发奉献,版权归原作者所有,阿里云开发者社区不领有其著作权,亦不承当相应法律责任。具体规定请查看《阿里云开发者社区用户服务协定》和《阿里云开发者社区知识产权爱护指引》。如果您发现本社区中有涉嫌剽窃的内容,填写侵权投诉表单进行举报,一经查实,本社区将立即删除涉嫌侵权内容。

April 25, 2023 · 1 min · jiezi

关于java:如何优雅的处理异常

作者:京东批发  秦浩然 一、什么是异样Java 语言依照谬误严重性,从 throwale 根类衍生出 Error 和 Exception 两大派别。 Error(谬误): 程序在执行过程中所遇到的硬件或操作系统的谬误。谬误对程序而言是致命的,将导致程序无奈运行。常见的谬误有内存溢出,jvm 虚拟机本身的非正常运行,calss 文件没有主办法。程序本生是不能处理错误的,只能依附外界干涉。Error 是零碎外部的谬误,由 jvm 抛出,交给零碎来解决。 Exception(异样): 程序失常运行中,能够意料的意外状况。比方数据库连贯中断,空指针,数组下标越界。异样呈现能够导致程序非正常终止,也能够事后检测,被捕捉解决掉,使程序持续运行。Exception(异样)依照性质,又分为编译异样(受检异样)和运行时异样(非受检异样)。 ◦ 编译异样: 又叫可查看异样,通常时由语法错和环境因素(内部资源)造成的异样。比方输入输出异样 IOException,数据库操作 SQLException。其特点是,Java 语言强制要求捕捉和解决所有非运行时异样。通过行为规范,强化程序的健壮性和安全性。 ◦ 运行时异样: 又叫不查看异样 RuntimeException,这些异样个别是由程序逻辑谬误引起的,即语义错。比方算术异样,空指针异样 NullPointerException,下标越界 IndexOutOfBoundsException。运行时异样应该在程序测试期间被裸露进去,由程序员去调试,而防止捕捉。 二、解决异样形式代码中,咱们最常见到的解决异样的形式就是:try-catch try { // 业务逻辑 } catch (Exception e) { // 捕捉到异样的逻辑 }或者是再进一步辨别下异样类型: try { // 业务逻辑 } catch (IOException ie) { // 捕捉到IO异样的逻辑 } catch (Exception e) { // 捕捉到其余异样的逻辑 }三、如何抛出异样咱们通常能够用抛出异样的形式来控制代码流程,而后在网关处对立catch异样来返回谬误code。这在肯定水平上能够简化代码流程管制,如下所示: @Override public UserVO queryUser(Long id) { UserDO userDO = userMapper.queryUserById(id); if (Objects.isNull(userDO)) { throw new RuntimeException("用户不存在"); //用户不存在抛出异样 } return userDO.toVo(); } 下面这种抛出异样的形式,尽管简化了代码流程,然而在存在多种谬误场景时,没有方法细分具体的谬误类型。如:用户不存在的谬误、用户没有权限的谬误; ...

April 25, 2023 · 3 min · jiezi

关于java:Java文件操作必备技能10个小技巧让你快速掌握

前言 在咱们日常的开发中,文件操作是一个十分重要的主题。文件读写、文件复制、任意地位读写、缓存等技巧都是咱们必须要把握的。在这篇文章中,我将给你们介绍 10 个实用的文件操作技巧。 应用 try-with-resources 语句解决文件 IO 流,确保在应用结束后主动敞开流。应用 java.nio.file.Files 类来读取、写入和操作文件。它提供了许多便当的办法,如 copy、move、delete、create 等。应用 java.io.File 类操作文件和目录,如创立、删除、重命名、判断是否存在等。应用 File.separator 来代替硬编码的文件门路分隔符,以保障在不同的操作系统上文件门路的正确性。应用 FileInputStream 和 FileOutputStream 类来读写二进制文件,应用 BufferedReader 和 BufferedWriter 类来读写文本文件。在读取大型文件时,应用 BufferedReader.readLine()办法逐行读取,而不是一次性读取整个文件到内存中。应用 FileChannel 类进行文件的疾速复制和传输,它能够在不应用缓冲区的状况下间接将数据从一个通道传输到另一个通道。应用 FileReader 和 FileWriter 类读写文本文件时,指定字符编码方式,以避免出现乱码问题。在解决大型文件时,应用 RandomAccessFile 类,能够实现对文件的任意地位读写操作。对于频繁读取的文件,能够应用缓存技术,将文件数据缓存到内存中,以进步读取效率。能够应用 java.io.BufferedInputStream 和 java.io.BufferedOutputStream 类实现缓存操作。示例1. 应用 try-with-resources 语句解决文件 IO 流,确保在应用结束后主动敞开流。import java.io.*;public class Example1 { public static void main(String[] args) { try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) { String line; while ((line = br.readLine()) != null) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } }}2. 应用 java.nio.file.Files 类来读取、写入和操作文件。它提供了许多便当的办法,如 copy、move、delete、create 等。import java.nio.file.Files;import java.nio.file.Path;import java.nio.file.Paths;import java.io.IOException;public class Example2 { public static void main(String[] args) { Path source = Paths.get("file.txt"); Path target = Paths.get("file_copy.txt"); try { Files.copy(source, target); } catch (IOException e) { e.printStackTrace(); } }}3. 应用 java.io.File 类操作文件和目录,如创立、删除、重命名、判断是否存在等。import java.io.File;public class Example3 { public static void main(String[] args) { File file = new File("file.txt"); if (file.exists()) { System.out.println("File exists!"); } else { System.out.println("File does not exist."); } }}4. 应用 File.separator 来代替硬编码的文件门路分隔符,以保障在不同的操作系统上文件门路的正确性。import java.io.File;public class Example4 { public static void main(String[] args) { String path = "C:" + File.separator + "path" + File.separator + "file.txt"; File file = new File(path); System.out.println(file.getAbsolutePath()); }}5. 应用 FileInputStream 和 FileOutputStream 类来读写二进制文件,应用 BufferedReader 和 BufferedWriter 类来读写文本文件。import java.io.*;public class Example5 { public static void main(String[] args) { try (FileInputStream fis = new FileInputStream("file.bin"); FileOutputStream fos = new FileOutputStream("file_copy.bin"); BufferedInputStream bis = new BufferedInputStream(fis); BufferedOutputStream bos = new BufferedOutputStream(fos)) { byte[] buffer = new byte[1024]; int length; while ((length = bis.read(buffer)) != -1) { bos.write(buffer, 0, length); } } catch (IOException e) { e.printStackTrace(); } }}6. 在读取大型文件时,应用 BufferedReader.readLine()办法逐行读取,而不是一次性读取整个文件到内存中。import java.io.*;public class Example6 { public static void main(String[] args) { try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) { String line; while ((line = br.readLine()) != null) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } }}7. 应用 FileChannel 类进行文件的疾速复制和传输,它能够在不应用缓冲区的状况下间接将数据从一个通道传输到另一个通道。import java.io.*;import java.nio.channels.FileChannel;public class Example7 { public static void main(String[] args) { try (FileInputStream fis = new FileInputStream("file.txt"); FileOutputStream fos = new FileOutputStream("file_copy.txt")) { FileChannel inChannel = fis.getChannel(); FileChannel outChannel = fos.getChannel(); inChannel.transferTo(0, inChannel.size(), outChannel); } catch (IOException e) { e.printStackTrace(); } }}8. 应用 FileReader 和 FileWriter 类读写文本文件时,指定字符编码方式,以避免出现乱码问题。import java.io.*;public class Example8 { public static void main(String[] args) { try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("file.txt"), "UTF-8")); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("file_copy.txt"), "UTF-8"))) { String line; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); } } catch (IOException e) { e.printStackTrace(); } }}9. 在解决大型文件时,应用 RandomAccessFile 类,能够实现对文件的任意地位读写操作。import java.io.*;public class Example9 { public static void main(String[] args) { try (RandomAccessFile raf = new RandomAccessFile("file.txt", "rw")) { raf.seek(raf.length()); raf.writeBytes("This is a new line."); } catch (IOException e) { e.printStackTrace(); } }}10. 对于频繁读取的文件,能够应用缓存技术,将文件数据缓存到内存中,以进步读取效率。能够应用 java.io.BufferedInputStream 和 java.io.BufferedOutputStream 类实现缓存操作。import java.io.*;public class Example10 { public static void main(String[] args) { try (FileInputStream fis = new FileInputStream("file.txt"); BufferedInputStream bis = new BufferedInputStream(fis)) { byte[] buffer = new byte[1024]; int length; while ((length = bis.read(buffer)) != -1) { // process the data } } catch (IOException e) { e.printStackTrace(); } }}结尾 如果感觉对你有帮忙,能够多多评论,多多点赞哦,也能够到我的主页看看,说不定有你喜爱的文章,也能够顺手点个关注哦,谢谢。 ...

April 24, 2023 · 3 min · jiezi

关于java:连接池-Druid-二-连接回收-DestroyConnectionThread-DestroyTask

接上一篇文章,钻研Druid连接池的连贯回收线程DestroyThread,通过调用destroyTask.run->DruidDataSourcek.shrink实现过期连贯的回收。 DruidDataSourcek.shrink了解DruidDataSourcek的连贯回收办法shrink有一个必要前提:Druid的getConnection办法总是从connectoins的尾部获取连贯,所以闲置连贯最有可能呈现在connections数组的头部,闲置超期须要被回收的连贯也应该处于connections的头部(数组下标较小的对象)。 在这个根底上,咱们开始剖析代码。 public void shrink(boolean checkTime, boolean keepAlive) { try { lock.lockInterruptibly(); } catch (InterruptedException e) { return; } boolean needFill = false; int evictCount = 0; int keepAliveCount = 0; int fatalErrorIncrement = fatalErrorCount - fatalErrorCountLastShrink; fatalErrorCountLastShrink = fatalErrorCount;首先获取锁资源,并初始化控制变量。 try { if (!inited) { return; } final int checkCount = poolingCount - minIdle; final long currentTimeMillis = System.currentTimeMillis(); for (int i = 0; i < poolingCount; ++i) { DruidConnectionHolder connection = connections[i]; if ((onFatalError || fatalErrorIncrement > 0) && (lastFatalErrorTimeMillis > connection.connectTimeMillis)) { keepAliveConnections[keepAliveCount++] = connection; continue; }如果初始化尚未实现,则不能开始做清理动作,间接返回。 ...

April 24, 2023 · 3 min · jiezi

关于java:基础巩固探寻Java装箱和拆箱的奥妙

前言 明天在逛某出名论坛的时候,看到一篇"请不要应用包装类型,防止造成性能损失"的文章。一下子就吸引了我的留神。粗心就是,能用根本数据类型就尽量用根本数据类型,因为包装类型主动拆箱、装箱会带来性能损失尤其是循环应用时会大量创建对象。所以明天聊一下,Java的装箱和拆箱。 什么是装箱和拆箱? Java中的装箱(boxing)和拆箱(unboxing)是指将根本数据类型与其对应的包装类之间进行转换的过程。 装箱 装箱是将根本数据类型转换为其对应的包装类,例如将int类型转换为Integer类型。这个过程是通过主动装箱(autoboxing)或手动装箱(manual boxing)实现的。主动装箱是指Java编译器主动将根本类型转换为包装类型,而手动装箱则是通过调用包装类的构造函数来实现。 拆箱 拆箱是将包装类转换为其对应的根本数据类型,例如将Integer类型转换为int类型。这个过程也是通过主动拆箱(autounboxing)或手动拆箱(manual unboxing)实现的。主动拆箱是指Java编译器主动将包装类型转换为根本类型,而手动拆箱则是通过调用包装类的xxxValue()办法来实现。 为啥要包装类型? 在日常应用的时候,感觉用根本类型还是包装类型貌似没啥区别。如果去掉包装类型,那就不须要装箱和拆箱了。也就防止了性能损失。貌似很完满呀。 如果你是这样想就,大错特错了。java中有根本数据类型和包装类型,这是为了满足不同需要的设计。java大佬们必定不会做这样的无用功的,应用包装类型的次要起因包含: 对象援用:根本类型不是对象,无奈应用对象援用,而包装类型是对象,能够通过援用来操作。泛型:泛型不能应用根本数据类型,必须应用包装类型。如,应用List<Integer>来存储一组整数,而不是应用List<int>。null值:根本类型不能为null,而包装类型能够为null。类型转换:包装类型提供了许多类型转换办法,例如将字符串转换为整数或浮点数等,这些办法十分不便。办法重载:办法重载能够应用不同的参数类型,然而根本数据类型和其对应的包装类型在办法中被认为是雷同的类型,因而能够在办法中应用它们进行重载。 只管根本数据类型在Java中十分有用,然而包装类型提供了更多的性能和灵活性,因而在某些状况下应用包装类型会更为不便和实用。所以就有了装箱和拆箱的过程。 装箱和拆箱的进化过程 在Java 1.4及之前,装箱和拆箱须要手动进行,即须要应用对应的构造函数或办法来实现。例如,要将int类型的变量x装箱成Integer类型,能够应用以下代码: int x = 10;Integer y = new Integer(x); // 手动装箱要将Integer类型的变量y拆箱成int类型,能够应用以下代码: Integer y = new Integer(10);int x = y.intValue(); // 手动拆箱 这种形式比拟繁琐,也容易出错。因而,从Java 5开始,引入了主动装箱和拆箱机制。这意味着咱们能够间接将根本数据类型赋值给对应的封装类型变量,或将封装类型变量赋值给对应的根本数据类型变量,编译器会主动实现转换。例如: int x = 10;Integer y = x; // 主动装箱int z = y; // 主动拆箱 从Java 9开始,引入了一种新的装箱形式,称为“紧凑型装箱”(Compact Number Box),它可能更高效地存储小范畴内的数值。紧凑型装箱只实用于byte、short和int类型的封装类型,这些类型的取值范畴都比拟小(如byte的取值范畴为-128到127),因而能够应用一个byte或short类型的变量来存储它们的值,而无需应用一个整型变量。 到底该如何抉择呢? 根本类型是Java语言的根底,能够间接存储在栈中,具备较高的执行效率和较小的内存占用。包装类型是把根本类型封装起来的对象,能够在堆中存储,能够进行各种面向对象的操作。所以在抉择应用根本类型还是包装类型时,能够思考以下几个因素: 执行效率:根本类型具备较高的执行效率,尤其是在大量计算的状况下,应用根本类型能够明显提高程序的执行速度。内存占用:根本类型在栈中存储,占用的内存较小,而包装类型在堆中存储,占用的内存较大。对象操作:如果须要进行面向对象的操作,例如通过equals()办法比拟两个变量的值,或者将根本类型转换为字符串,就须要应用包装类型。空值解决:根本类型不能示意空值,而包装类型能够用null示意空值,因而在须要解决空值的状况下,须要应用包装类型。总结 Java中的装箱和拆箱技术经验了从手动到主动、从一般到紧凑型的演进,这使得咱们可能更不便地操作根本数据类型和封装类型之间的转换。尽管更加不便, 然而在理论开发中,还是须要依据具体的应用场景进行抉择。 结尾 如果感觉对你有帮忙,能够多多评论,多多点赞哦,也能够到我的主页看看,说不定有你喜爱的文章,也能够顺手点个关注哦,谢谢。 ...

April 24, 2023 · 1 min · jiezi

关于java:生成符合格式的自增编码

Date date = new Date(); int number = 1;for (X x :x ) { String no = String.format("ZG" + sf.format(date) + "%05d", number); number++; System.out.println("生成的编号:" + no);//ZG2023042400001}

April 24, 2023 · 1 min · jiezi

关于java:Redis-Plus-来了性能炸裂

起源:https://developer.aliyun.com/article/705239 1 什么是KeyDB?KeyDB是Redis的高性能分支,专一于多线程,内存效率和高吞吐量。除了多线程之外,KeyDB还具备仅在Redis Enterprise中可用的性能,例如Active Replication,FLASH存储反对以及一些基本不可用的性能,例如间接备份到AWS S3。 KeyDB与Redis协定,模块和脚本放弃齐全兼容性。这包含脚本和事务的原子性保障。因为KeyDB与Redis开发放弃同步,因而KeyDB是Redis性能的超集,从而使KeyDB取代了现有Redis部署。 在雷同的硬件上,KeyDB每秒能够执行的查问数量是Redis的两倍,而提早却升高了60%。Active-Replication简化了热备用故障转移,使您能够轻松地在正本上调配写操作并应用基于TCP的简略负载平衡/故障转移。KeyDB的高性能可让您在更少的硬件上做更多的事件,从而升高了经营老本和复杂性。 在此处查看残缺的基准测试后果和设置信息: https://docs.keydb.dev/blog/2019/10/07/blog-post/2 走进KeyDBKeyDB我的项目是从redis fork进去的分支。家喻户晓redis是一个单线程的kv内存存储系统,而KeyDB在100%兼容redis API的状况下将redis革新成多线程。 我的项目git地址: https://github.com/JohnSully/KeyDB网上公开的技术细节比拟少,本文根本是通过浏览源码总结进去的,如有错漏之处欢送斧正。 举荐一个开源收费的 Spring Boot 最全教程: https://github.com/javastacks/spring-boot-best-practice多线程架构线程模型KeyDB将redis原来的主线程拆分成了主线程和worker线程。每个worker线程都是io线程,负责监听端口,accept申请,读取数据和解析协定。如图所示: KeyDB应用了SO_REUSEPORT个性,多个线程能够绑定监听同个端口。 每个worker线程做了cpu绑核,读取数据也应用了SO_INCOMING_CPU个性,指定cpu接收数据。 解析协定之后每个线程都会去操作内存中的数据,由一把全局锁来管制多线程拜访内存数据。 主线程其实也是一个worker线程,包含了worker线程的工作内容,同时也包含只有主线程才能够实现的工作内容。在worker线程数组中下标为0的就是主线程。 主线程的次要工作在实现serverCron,包含: 解决统计客户端链接治理db数据的resize和reshard解决aofreplication主备同步cluster模式下的工作链接治理在redis中所有链接治理都是在一个线程中实现的。在KeyDB的设计中,每个worker线程负责一组链接,所有的链接插入到本线程的链接列表中保护。链接的产生、工作、销毁必须在同个线程中。每个链接新增一个字段 int iel; /* the event loop index we're registered with */用来示意链接属于哪个线程接管。 KeyDB保护了三个要害的数据结构做链接治理: clients_pending_write:线程专属的链表,保护同步给客户链接发送数据的队列clients_pending_asyncwrite:线程专属的链表,保护异步给客户链接发送数据的队列clients_to_close:全局链表,保护须要异步敞开的客户链接分成同步和异步两个队列,是因为redis有些联动api,比方pub/sub,pub之后须要给sub的客户端发送音讯,pub执行的线程和sub的客户端所在线程不是同一个线程,为了解决这种状况,KeyDB将须要给非本线程的客户端发送数据保护在异步队列中。 同步发送的逻辑比较简单,都是在本线程中实现,以下图来阐明如何同步给客户端发送数据 如上文所提到的,一个链接的创立、接收数据、发送数据、开释链接都必须在同个线程执行。异步发送波及到两个线程之间的交互。KeyDB通过管道在两个线程中传递音讯: int fdCmdWrite; //写管道int fdCmdRead; //读管道本地线程须要异步发送数据时,先查看client是否属于本地线程,非本地线程获取到client专属的线程ID,之后给专属的线程管到发送AE_ASYNC_OP::CreateFileEvent的操作,要求增加写socket事件。专属线程在解决管道音讯时将对应的申请增加到写事件中,如图所示: redis有些敞开客户端的申请并非齐全是在链接所在的线程执行敞开,所以在这里保护了一个全局的异步敞开链表。 锁机制KeyDB实现了一套相似spinlock的锁机制,称之为fastlock。 fastlock的次要数据结构有: struct ticket{ uint16_t m_active; //解锁+1 uint16_t m_avail; //加锁+1};struct fastlock{ volatile struct ticket m_ticket; volatile int m_pidOwner; //以后解锁的线程id volatile int m_depth; //以后线程反复加锁的次数};应用原子操作__atomic_load_2,__atomic_fetch_add,__atomic_compare_exchange来通过比拟m_active=m_avail判断是否能够获取锁。fastlock提供了两种获取锁的形式: ...

April 24, 2023 · 1 min · jiezi

关于java:Java通过实现接口的方式实现线程如何用代码进行表示呀

Java通过实现接口的形式实现线程用代码示意为 public class HelloRunnable implements Runnable {public void run() {System.out.println("Hello from a thread!");public static void main(string args[]) {(new Thread(new HelloRunnable())).start();残缺内容请点击下方链接查看: https://developer.aliyun.com/ask/463626 版权申明:本文内容由阿里云实名注册用户自发奉献,版权归原作者所有,阿里云开发者社区不领有其著作权,亦不承当相应法律责任。具体规定请查看《阿里云开发者社区用户服务协定》和《阿里云开发者社区知识产权爱护指引》。如果您发现本社区中有涉嫌剽窃的内容,填写侵权投诉表单进行举报,一经查实,本社区将立即删除涉嫌侵权内容。

April 23, 2023 · 1 min · jiezi

关于java:Java最佳实践

计算机编程中,最佳实际是许多开发人员遵循的一组非正式规定,以进步软件品质、可读性和可维护性。在应用程序长时间放弃应用的状况下,最佳实际尤其无益,这样它最后是由一个团队开发的,而后由不同的人组成的保护团队进行保护。 本教程将提供Java最佳实际的概述,以及每个条目标解释,包含Java编程的顶级最佳实际列表中的每一项。 Java编程最佳实际概览尽管Java最佳实际的残缺列表可能很长,但对于那些正在努力提高代码品质的编码人员来说,有几个被认为是一个很好的终点,包含应用适当的命名标准、使类成员私有化、防止应用空的catch块、防止内存透露以及正确地正文代码块: 应用适当的命名标准将类成员设置为公有在长数字文字中应用下划线防止空的catch块应用StringBuilder或StringBuffer进行字符串连贯防止冗余初始化应用增强型for循环代替带计数器的for循环正确处理空指针异样Float或Double:哪一个是正确的抉择?应用单引号和双引号防止内存透露返回空集合而不是返回Null元素高效应用字符串防止创立不必要的对象正确正文代码Java中的类成员应该是公有的在Java中,类的成员越不可拜访,越好!第一步是应用private拜访修饰符。指标是促成现实的封装,这是面向对象编程(OOP)的基本概念之一。太多时候,新的开发人员没有正确地为类调配拜访修饰符,或者偏向于将它们设置为public以使事件更容易。 思考以下字段被设置为public的类: public class BandMember { public String name; public String instrument;}在这里,类的封装性被毁坏了,因为任何人都能够间接更改这些值,如下所示: BandMember billy = new BandMember();billy.name = "George";billy.instrument = "drums";应用private拜访修饰符与类成员一起能够将字段暗藏起来,避免用户通过setter办法之外的形式更改数据: public class BandMember { private String name; private String instrument; public void setName(String name) { this.name = name; } public void setInstrument(String instrument) this.instrument = instrument; }}在setter办法中也是搁置验证代码和/或治理工作(如减少计数器)的现实地位。 在长数字文字中应用下划线得益于Java 7的更新,开发人员当初能够在长数字字面量中应用下划线(_),以进步可读性。以下是在容许下划线之前一些长数字字面量的示例: int minUploadSize = 05437326;long debitBalance = 5000000000000000L;float pi = 3.141592653589F;我想您会批准下划线使值更易读: int minUploadSize = 05_437_326;long debitBalance = 5_000_000_000_000_000L;float pi = 3.141_592_653_589F;防止空的Catch块在Java中,把catch块留空是十分不好的习惯,有两个起因:一是它可能导致程序默默地失败,二是程序可能会持续运行而不会产生任何异样。这两种后果都会使调试变得十分艰难 ...

April 23, 2023 · 3 min · jiezi

关于java:memcahed-的-java-sdk-如何批量获取memcahed的key

Memcached Java客户端SDK提供了 BulkGetFuture 接口用于获取多个key对应的value,对应的办法是asyncGetBulk。 如果要获取多个key,能够将key以List的模式传入 asyncGetBulk 办法中,返回 BulkGetFuture 对象,再通过 BulkGetFuture.get() 办法获取到后果。 以下是一个简略的示例代码: //创立连接池MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("localhost:11211"));MemcachedClient client = builder.build();//须要获取的key列表List<String> keys = Arrays.asList("key1", "key2", "key3");//异步获取多个key对应的valueBulkGetFuture<Map<String, Object>> future = client.asyncGetBulk(keys);try { //获取后果 Map<String, Object> resultMap = future.get(); for (String key : keys) { //获取对应的value Object value = resultMap.get(key); System.out.println(key + " : " + value); }} catch (Exception e) { e.printStackTrace();} finally { client.shutdown();}上述代码中应用了XMemcached客户端,它是一个反对Memcached协定的Java客户端SDK,能够通过Maven依赖引入,例如: <dependency> <groupId>com.googlecode.xmemcached</groupId> <artifactId>xmemcached</artifactId> <version>2.4.6</version></dependency>

April 23, 2023 · 1 min · jiezi

关于java:连接池-Druid-一-初始化过程

HikariPool之后,明天钻研另一支流连接池Druid。 对于数据库连接池的根本认知先对数据库连接池的根本工作原理做个理解,不论是HikariPool、还是druid,所有的数据库连接池应该都是依照这个基本原理工作和实现的,带着这个思路去学习数据库连接池,防止盲人摸象。 数据库连接池肯定会蕴含以下根本逻辑: 创立连贯并池化:初始化的时候创立、或者是在利用获取连贯的过程中创立,连贯创立好之后放在连接池(内存中的容器,比方List)中保留。获取数据库连贯:接管了获取数据库连贯的办法,从连接池中获取、而不是创立连贯。敞开数据库连贯:接管了敞开数据库连贯的办法,将连贯偿还到连接池、而不是真正的敞开数据库连贯。连接池保护:连接池容量、连贯超时清理等工作。带着这个思路钻研HikariPool的源码,会有事倍功半的效用。 意识Druid的构造包含以下几个局部: DruidConnectionHolderConnectionsevictConnectionskeepAliveConnectionsdestroyConnectionThread/destroySchedulerFuturecreateConnectionThread/createSchedulerFutureDruidConnectionHolderHikariPool通过poolEntry持有数据库连贯,Druid通过DruidConnectionHolder持有数据库连贯。 DruidConnectionHolder持有物理数据库连贯Connectin对象,以及该连贯的相干属性,比方connectTimeMillis、lastActiveTimeMillis、lastExecTimeMillis,以及underlyingReadOnly、underlyingAutoCommit、underlyingTransactionIsolation等等。连接池能够依据这些属性以及相干参数执行相应的houseKeep。 ConnectionsConnections是DruidConnectionHolder组成的数组,是Druid连接池中惟一存储可用连贯的中央,看起来会比HikariPool简略许多(HikariPool有三个存储连贯的中央),然而这可能也是Druid在性能上稍逊于HikariPool的起因之一。 evictConnections存储须要被回收的连贯的数组,在连接池进行清理的时候用来存储须要被敞开的连贯。 keepAliveConnections存储放弃流动的连贯的数组。 createConnectionThread/createSchedulerFutureDruid依据配置,能够通过createConnectionThread线程、或者createSchedulerFuture线程工作创立数据库连贯并退出连接池。 Druid兴许并没有默认的createSchedulerFuture的实现,如果要启用createSchedulerFuture,须要配置createSchedulerFuture的实现类。 createConnectionThread是Druid默认的创立连贯的线程,负责获取物理连贯、组装物理连贯为DruidConnectionHolder并退出到connections数组中。 destroyConnectionThread/destroySchedulerFuture与创立连贯的形式相似,Druid提供两种不同的形式销毁(或者敞开)过期的数据库连贯。默认实现是destroyConnectionThread。 好了,Druid的根底构造理解完了,咱们采纳和HikariPool齐全一样的剖析套路,接下来要进入源码剖析了,次要包含: Druid连接池的初始化获取数据库连贯 - getConnection办法敞开数据库连贯 - close办法Druid的初始化Druid的初始化过程貌似和HikariPool稍有不同,因为HikariPool默认的在获取连贯之前的HikariPool实例化过程中就实现了连接池的初始化。 所谓实现连接池的初始化,指的是依照参数的设定,实现了数据库连贯的创立和池化,也就是说连接池曾经筹备好了,利用在通过getConnecton办法获取连贯的时候,间接从连接池中borrow就能够了。 Druid貌似不这样。咱们看一下DruidDataSource的实例化办法: public DruidDataSource(){ this(false); } public DruidDataSource(boolean fairLock){ super(fairLock); configFromPropety(System.getProperties()); }super指的是DruidAbstractDataSource,他的构造方法: public DruidAbstractDataSource(boolean lockFair){ lock = new ReentrantLock(lockFair); notEmpty = lock.newCondition(); empty = lock.newCondition(); }只初始化了ReentrantLock,以及他的两个Condition:empty和notEmpty。 而configFromPropety只是负责把参数从配置文件中读入,不做其余的事件。 所以,连接池的初始化过程没有放在DruidDataSource的创立过程中。 既然构造方法中没有实现连接池的初始化,咱们自然而然的就想到去看看getConnection办法,不做初始化、怎么能获取到数据库连贯? 果然,getConnectin办法的第一行代码就是:调用init()办法。 init的办法很长,不过有很多代码都是查看参数合理性的,这部分代码咱们间接跳过: ...疏忽n多行代码 connections = new DruidConnectionHolder[maxActive]; evictConnections = new DruidConnectionHolder[maxActive]; keepAliveConnections = new DruidConnectionHolder[maxActive];创立了咱们后面说过的存储数据库连贯的connections(其实就是池),以及另外两个辅助数组evictConnections和keepAliveConnections,连接池的大小初始化为maxActive。 接下来: if (createScheduler != null && asyncInit) { for (int i = 0; i < initialSize; ++i) { submitCreateTask(true); } } else if (!asyncInit) { // init connections while (poolingCount < initialSize) { try { PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection(); DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo); connections[poolingCount++] = holder; } catch (SQLException ex) { LOG.error("init datasource error, url: " + this.getUrl(), ex); if (initExceptionThrow) { connectError = ex; break; } else { Thread.sleep(3000); } } } if (poolingCount > 0) { poolingPeak = poolingCount; poolingPeakTime = System.currentTimeMillis(); } }查看createScheduler不空、并且参数设置为asyncInit(异步初始化)的话,则提交initialSize个连贯创立工作。咱们说过createScheduler并非Druid的默认实现,所以咱们临时不论这部分代码。 ...

April 22, 2023 · 3 min · jiezi

关于java:CASAQS

CASCompare and Swap先比拟再替换,CAS是一种无锁优化算法,也能够说是乐观锁,它基于共享数据不会被批改的假如,CAS有3个操作数,内存值V,旧的预期值A,要批改的新值B,当且仅当预期值A和内存值V雷同时,将内存值批改为B,否则什么都不做。 CAS的实现原理1.在java层面,提供CAS的办法是在Unsafe类中应用native润饰的:2.native是间接调用本地依赖库C++中的办法3.在CAS底层,如果是多核的操作系统,须要追加一个lock指令,单核不须要加,因为cmpxchg是一行指令,不能再被拆分了cmpxchg ,是汇编的指令,CPU硬件底层就反对 比拟和替换 (cmpxchg),cmpxchg并不保障原子性的。(cmpxchg的操作是不能再拆分的指令) 所以才会呈现判断CPU是否是多核,如果是多核就追加lock指令。 lock指令你能够了解为是CPU层面的锁,个别锁的粒度就是 缓存行 级别的锁,当然也有 总线锁 ,然而老本太高,CPU会依据状况抉择。 CAS中存在的问题1.ABA对于一般数据类型ABA不肯定是问题,因为他不会影像线程执行的最终后果。对于援用数据类型,能够指定版本号来解决,Java中的atomic包里提供了一个类AtomicStampedReference就是这样的。2.自旋次数过长如果CAS始终不胜利,自旋次数很大,会给CPU带来十分大的开销,能够限度自旋次数,比方synchronized,CAS失败肯定次数后,就会将线程挂起,防止占用过多的CPU资源。

April 22, 2023 · 1 min · jiezi

关于java:spring-security放行所有请求通过配置多个WebSecurityConfigurerAdapter实现

我的项目场景:笔者最近有一个我的项目,依赖关系如下图: main-project 是主我的项目auth-project 是公司封装好的认证核心我的项目,打包成了jar包供援用,基于spring security开发需要形容当初笔者须要在main-project上做开发,即在Controller中增加一个新接口,然而因为援用了auth-project,所以所有接口必须要先登录获取token之后能力拜访。为了便于开发,笔者想临时跳过spring security的认证过程。 解决方案:办法一:在main-project的config包下编写一个类,继承WebSecurityConfigurerAdapter,并重写configure(HttpSecurity http)办法,放行所有申请,代码如下: @EnableWebSecurity@Order(1)public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeHttpRequests() .anyRequest().permitAll() .and() .csrf().disable(); }这里有2点须要留神: @Order(1)是为了配置执行程序的优先级,因为auth-project里曾经有了一个WebSecurity的配置类,所以这里配置@Order(1)是为了让咱们写的配置类先加载,数字越小优先级越高;须要禁用csrf进攻,否则除get申请办法以外,其它类型的申请会返回403响应。办法二:在main-project的pom.xml文件中,正文掉auth-project的dependency。 参考文章:浅谈Spring @Order注解的应用Spring Boot申请403 Forbidden谬误 本文如有侵权,请分割我删除,谢谢!

April 21, 2023 · 1 min · jiezi

关于java:通过Java实现Excel文档转换PNGHTMLCSVXPSXML

在理论工作解决中,进行文档转换是再常见不过的事。能够依据具体的应用需要进而抉择不同的文档格局。这篇文章中整顿了对于应用Java程序将Excel转换为局部文档格局的办法,包含图像,HTML,CSV,XPS,XML。操作简略,转换速度快,后果稳固。具体的办法和代码已在上面给出。 程序环境IntelliJ IDEA 2018 (jdk 1.8.0)在进行操作之前先导入JAR包,请参考以下两种导入形式:办法一:如果您应用的是 maven,先创立maven我的项目。而后通过增加以下代码来配置pom.xml 文件,再点击Import Changes将JAR文件导入到应用程序中。 <repositories> <repository> <id>com.e-iceblue</id> <name>e-iceblue</name> <url>https://repo.e-iceblue.cn/nexus/content/groups/public/</url> </repository></repositories><dependencies> <dependency> <groupId>e-iceblue</groupId> <artifactId>spire.xls.free</artifactId> <version>5.1.0</version> </dependency></dependencies>办法二:如果您没有应用 maven,则能够从此链接下载Free Spire.XLS for Java,找到lib文件夹下的Spire.XLS.jar并进行解压;而后在IDEA中创立一个新我的项目,顺次点击“文件”(File),“我的项目构造”(Project Structure),“组件”(Modules),“依赖项”(Dependencies),再点击右方绿色“+”下的第一个选项“JAR文件或门路”(JARs or Directories),找到解压后的Spire.XLS.jar 文件,点击确认,将其导入到我的项目中。 Excel转换为图像具体方法: 创立 Workbook 对象。通过 Workbook.loadFromFile() 办法加载示例工作簿。通过 Workbook.getWorksheets().get() 办法获取指定工作表。调用Workbook.saveToImage() 办法将文档转换为图片(也能够指定工作表的转换范畴)。示例代码: import com.spire.xls.*;public class ExcelToPNG { public static void main(String[] args) { //加载Excel工作表 Workbook wb = new Workbook(); wb.loadFromFile("sample.xlsx"); //获取工作表 Worksheet sheet = wb.getWorksheets().get(0); //调用办法将Excel工作表保留为图片 sheet.saveToImage("ToPNG(1).png"); //调用办法,将指定Excel单元格数据范畴保留为图片 //sheet.saveToImage("ToPNG(2).png",1,1,5,7); }}同样的,调用相似的办法,能够将Excel转换为其余文件格式,办法如下: Excel转HTML: //调用办法将Excel保留为HTML sheet.saveToHtml("ToHTML.html");Excel转XPS: //调用办法将Excel保留为XPS sheet.saveToFile("ToXPS.xps", String.valueOf(FileFormat.XPS));Excel转CSV: //调用办法将Excel保留为CSV sheet.saveToFile("ToCSV.csv", String.valueOf(FileFormat.CSV));Excel转XML: //调用办法将Excel保留为XML sheet.saveToFile("ToXML.xml", String.valueOf(FileFormat.XML));转换效果图:以Excel转换为PNG为例: ...

April 21, 2023 · 1 min · jiezi

关于java:重新学习Java线程原语

Synchronized已经是一个革命性的技术,在以后依然有重要的用处。然而,当初是时候转向更新的Java线程原语,同时重新考虑咱们的外围逻辑。自从Java第一个测试版以来,我就始终在应用它。从那时起,线程就是我最喜爱的个性之一。Java是第一种在编程语言自身中引入线程反对的语言。那是一个具备争议的决定。在过来的十年中,每种编程语言都竞相引入async/await,甚至Java也有一些第三方反对......然而Java抉择了引入更优越的虚构线程(Loom我的项目)。本文并不探讨这个问题。 我感觉这很好,证实了Java的外围实力。Java不仅仅是一种语言,还是一种文化。这种文化重视三思而行的改革,而不是自觉追随时尚潮流。 在本文中,我想从新探讨Java中的线程编程旧办法。我习惯应用synchronized、wait、notify等技术。然而, “然而,这些办法曾经不再是Java中线程解决的最佳形式。 我也是问题的一部分。我还是习惯于应用这些技术,发现很难适应自Java 5以来就存在的一些API。这是一种习惯的力量。 尽管能够探讨许多解决线程的杰出API,但我想在这里专一探讨锁,因为它们是根底但极为重要的。 Synchronized 与 ReentrantLock我犹豫放弃应用 synchronized 的起因是,并没有更好的代替计划。当初弃用 synchronized 的次要起因是,它可能会在 Loom 中触发线程固定,这并不现实。JDK 21 可能会修复这个问题(当 Loom 正式公布时),但还有一些理由弃用它。 synchronized 的间接替代品是 ReentrantLock。可怜的是,ReentrantLock 相比 synchronized 很少有劣势,因而迁徙的益处最多是存疑的。事实上,它有一个次要的毛病。为了理解这一点,让咱们看一个例子。上面是咱们如何应用 synchronized: synchronized(LOCK) { // safe code}LOCK.lock();try { // safe code} finally { LOCK.unlock();}ReentrantLock 的第一个毛病是简短。咱们须要try块,因为如果在块外部产生异样,锁将放弃。而 synchronized 则会主动解决异样。 有些人会应用 AutoClosable 对锁进行封装,大略是这样的: public class ClosableLock implements AutoCloseable { private final ReentrantLock lock; public ClosableLock() { this.lock = new ReentrantLock(); } public ClosableLock(boolean fair) { this.lock = new ReentrantLock(fair); } @Override public void close() throws Exception { lock.unlock(); } public ClosableLock lock() { lock.lock(); return this; } public ClosableLock lockInterruptibly() throws InterruptedException { lock.lock(); return this; } public void unlock() { lock.unlock(); }}留神,我没有实现 Lock 接口,这原本是最现实的。这是因为 lock 办法返回了可主动敞开的实现,而不是 void。 ...

April 21, 2023 · 2 min · jiezi

关于java:Java-Web-文章管理系统

拜访【WRITE-BUG数字空间】_[内附残缺源码和文档] 本示例是应用JavaWeb技术实现一个简略的文章管理系统(新闻管理系统)其中次要性能如下:1.用户和管理员登录;2.用户公布新文章、文章详情查看、文章批改、文章删除与复原;3.用户查看别人对本人受权的文章及其文章信息;4.用户将本人的文章对别人进行受权;5.管理员对普通用户新公布的文章进行审核和删除;6.管理员查看普通用户公布的所有文章及其详情;7.管理员公布新文章。 本示例是应用JavaWeb技术实现一个简略的文章管理系统(新闻管理系统)其中次要性能如下: 用户和管理员登录用户公布新文章、文章详情查看、文章批改、文章删除与复原用户查看别人对本人受权的文章及其文章信息用户将本人的文章对别人进行受权管理员对普通用户新公布的文章进行审核和删除管理员查看普通用户公布的所有文章及其详情管理员公布新文章应用的次要技术有: JavaEE、JDBC、AJAX、JSP、JavaBean 本我的项目开发环境为: Intellij IDEA 2016.3Tomcat 8JDK 1.8MySQL 5.5数据库配置文件能够下载Mynews.sql文件,间接导入配置就好

April 21, 2023 · 1 min · jiezi

关于java:基于Jsp和MySQL实现的云音乐平台

拜访【WRITE-BUG数字空间】_[内附残缺源码和文档] 一、零碎应用阐明1.1 配置开发环境:Eclipse Java EE IDE for Web Developers 4.3.2开发语言:Java 1.8MVC框架:Spring 4.3.3web服务器:Tomcat开发零碎:OSX/LINUX数据库:MySQL1.2 相干门路controller:CloudMusic/src/controllermodel:CloudMusic/src/modelview:CloudMusic/WebContent/WEB-INF/view1.3 运行办法须要提前配置好mysql数据库,sql文件位于根目录 (DDL.sql DML.sql)设置数据库参数 配置文件位于apache-tomcat/webapps/CloudMusic/WEB-INF/classes/dbConfig.properties终端启动服务器cd apache-tomcat ./bin/catalina.sh run运行胜利后,浏览器关上 http://localhost:8080/CloudMusic 可拜访1.4 其余阐明若运行.sh文件 报permisison denied, 则在终端运行 chmod +x catalina.sh 批改权限。

April 21, 2023 · 1 min · jiezi

关于java:进击的-Java

编者按:近几年,随着云原生时代的到来,Java 蒙受了诸多质疑。国际形势和行业格局的变动,大家肯定充沛感触到了云原生这个话题的热度,难道 Java 真的已过巅峰期间,要走向末路了吗?龙蜥社区 Java 语言和虚拟机 SIG 成员、龙蜥社区 RISC-V SIG 成员李三红就这个问题发表了他的认识。本文转自 OSCHINA,以下为原文,欢送浏览: (图 / 李三红) 唱衰 Java 的声音由来已久。 尤其是近几年,云原生时代的到来,软件的交付形式产生了根本性变动,Java 蒙受了诸多质疑。 传统畛域的 Java 开发,交付的是 Java 利用自身,具体体现在以 “jar”“war” 的模式交付,而云原生最佳实际,则是基于微服务模式,以容器为根本交付单位,并在 K8S 中编排。云原生利用要求更疾速地启动、强调资源按量生产,弹性扩大,以及可观测性等。就这一层面而言,在云原生时代,Java 的缺点的确是人造存在的。 “事实上,Java 技术在云原生时代也在不停地进化。” 面对唱衰 Java 的诸多论调,龙蜥社区 Java 语言和虚拟机 SIG 成员、龙蜥社区 RISC-V SIG 成员、阿里云程序语言与编译器团队负责人、Java Champion 李三红抉择感性对待。 “比方更多反对容器部署的个性曾经退出到 OpenJDK 版本。始终被人诟病的 Java 启动慢的问题,目前基于 OpenJDK 技术的几个翻新我的项目正在多个不同方向摸索,包含 CRaC (Coordinated Restore at Checkpoint) 、Leyden,以及由阿里和 Google 作为次要贡献者的 Fast Startup Incubator 等我的项目。在资源弹性应用方面,由 Alibaba Dragonwell 提供的 ElasticHeap 性能,次要目标是解决云计算环境下 Java 内存资源应用的弹性问题。最初,OpenJDK 的 JFR 以及 JFR Streaming 技术为构建 Java 云原生可观测工具提供了重要的技术撑持。” ...

April 21, 2023 · 2 min · jiezi

关于java:基于SSM框架开发的小型英语学习网站

拜访【WRITE-BUG数字空间】_[内附残缺源码和文档]英语曾经越来越凸显其重要性。大学生个别都须要考CET-4或者CET-6,对于程序员,如果没有扎实的英语根底,看有些API文档也比拟费劲。生存中处处存在英语,也越来越体现英语的重要性,如何高效学习英语成了要害的探讨问题。本站意在收集无关英语的经典视频和文档,包含TED、VOA和NEWS,使得用户能够方便快捷得取得学习英语的多个通道。一、需要剖析英语曾经越来越凸显其重要性。大学生个别都须要考CET-4或者CET-6,对于程序员,如果没有扎实的英语根底,看有些API文档也比拟费劲。生存中处处存在英语,也越来越体现英语的重要性,如何高效学习英语成了要害的探讨问题。本站意在收集无关英语的经典视频和文档,包含TED、VOA和NEWS,使得用户能够方便快捷得取得学习英语的多个通道。二、总体设计技术栈前端:HTML、CSS、JavaScript、jQuery、BootStrap。后端:JSP、Spring、SpringMVC、MyBatis。总体性能导图。三、数据库设计构造阐明:数据库设计了三个表,user表中存储用户相干信息,tlike中存储用户珍藏的信息,ted中存储了无关ted视频的相干数据。Tlike表中的uid是user表中的外键,同理tid是ted表的外键,uid和ted独特形成了tlike表的主键。nlike与vlike设计模式与上相似,不在反复设计。相干代码:CREATE TABLE user( uid INT NOT NULL PRIMARY KEY AUTO_INCREMENT,username VARCHAR(50) NOT NULL UNIQUE,password VARCHAR(50) NOT NULL,email CHAR(30) NOT NULL,![](/img/bVc7yFC)![](/img/bVc7wQ6)![](/img/bVc7yFD)![](/img/bVc7yFE)status CHAR(1),uuid VARCHAR(50) UNIQUE,CONSTRAINT user_ck_status CHECK (status = 'Y' or status = 'N'))CREATE TABLE tlike( uid INT NOT NULL,tid INT NOT NULL,date DATE NOT NULL,PRIMARY KEY (uid, tid));CREATE TABLE ted( tid INT NOT NULL AUTO_INCREMENT PRIMARY KEY,title VARCHAR(300),discription VARCHAR(500),mp4ShdUrl VARCHAR(100)); ALTER TABLE tlike ADD CONSTRAINT FK_user_tlike FOREIGN KEY ( uid ) REFERENCES USER ( uid ) ON DELETE RESTRICT ON UPDATE RESTRICT;ALTER TABLE tlike ADD CONSTRAINT FK_ted_tlike FOREIGN KEY ( tid ) REFERENCES ted ( tid ) ON DELETE RESTRICT ON UPDATE RESTRICT; ...

April 21, 2023 · 1 min · jiezi

关于java:IntelliJ-上-Azure-Event-Hubs-全新支持来了

大家好,欢送来到 Java on Azure Tooling 的3月更新。在这次更新中,咱们将介绍 Azure Event Hubs 反对、Azure Functions 的模板加强,以及在 IntelliJ IDEA 中部署 Azure Spring Apps 时的日志流改良。要应用这些新性能,请下载并装置用于 IntelliJ IDEA 的 Azure 工具包。让咱们开始吧。 Azure Toolkit for IntelliJ 晋升▌Azure Event Hubs 反对Azure Event Hubs 是一个齐全受治理的实时数据处理服务,每秒可流转数百万个事件。你能够应用 Azure Event Hubs 来构建动态数据管道,并从数据中取得实时洞察力。更多细节,请查看此文档。 在最新的版本中,咱们为 Azure Explorer 中的 Azure Event Hubs 提供了治理反对。在创立 Event Hubs 实例后,你能够间接在工具包中设置它的状态。你还能够轻松地从 Azure Event Hubs 客户端发送和接管事件。咱们还提供一些反对,帮忙你在右键菜单选项中复制 Connection String。请观看这个简短的演示。 ▌Azure Functions 模板体验加强Azure Functions 是一种无服务器计算服务,它使你可能按需运行代码,而不须要明确配置或治理基础设施。为了帮忙 Java 开发者疾速启动他们的 Functions 我的项目,咱们在最新版本中减少了更多基于 Azure Functions 的 Trigger 模板。咱们还简化了对这些模板的 Azure 存储账户和 Azure Cosmos 数据库账户的 Connection String 设置。 ...

April 20, 2023 · 1 min · jiezi

关于java:基于SSM框架和MySQL的仿天猫商城

拜访【WRITE-BUG数字空间】_[内附残缺源码和文档] 1.我的项目简介天猫商城是一个基于SSM框架的综合性B2C电商平台,需要设计次要参考天猫商城的购物流程:用户从注册开始,到实现登录,浏览商品,退出购物车,进行下单,确认收货,评估等一系列操作。 作为模仿天猫商城零碎的外围组成部分之一,采纳SSM框架的天猫数据管理后盾蕴含商品治理,订单治理,类别治理,用户治理和交易额统计等模块,实现了对整个商城的一站式治理和保护。 后端页面兼容IE10及以上古代浏览器,Chrome,Edge,Firebox等浏览器体现成果最佳。 2.开发环境1.我的项目应用IntelliJ IDEA开发2.我的项目数据库为MySQL 5.7版本,在resources下的sql文件夹中下载sql,并导入到数据库中3.应用IDEA关上我的项目后,在maven面板刷新我的项目,下载依赖包4.在IDEA中启动springboot我的项目即可(run形式或debug形式都行)5.账户名和明码详见附件中的sql文件或者在resources下的sql文件夹中的sql文件(sql运行之后在admin表里有账户名和明码)注意事项:后盾治理界面的订单图表没有数据为失常景象,该图表显示的为近7天的交易额。 后盾界面(局部) 拜访地址:http://localhost:8082/tmall/admin/login (账户名和明码在admin表里)

April 20, 2023 · 1 min · jiezi

关于java:一文搞定接口幂等性架构设计方案

幂等性介绍现如今很多零碎都会基于分布式或微服务思维实现对系统的架构设计。那么在这一个零碎中,就会存在若干个微服务,而且服务间也会产生互相通信调用。那么既然产生了服务调用,就必然会存在服务调用提早或失败的问题。当呈现这种问题,服务端会进行重试等操作或客户端有可能会进行屡次点击提交。如果这样申请屡次的话,那最终解决的数据后果就肯定要保障对立,如领取场景。此时就须要通过保障业务幂等性计划来实现。 什么是幂等性幂等是一个数学与计算机学概念,即f(n) = 1^n,无论n为多少,f(n)的值永远为1,在数学中某一元运算为幂等时,其作用在任一元素两次后会和其作用一次的后果雷同。在编程开发中,对于幂等的定义为:无论对某一个资源操作了多少次,其影响都应是雷同的。 换句话说就是:在接口反复调用的状况下,对系统产生的影响是一样的,然而返回值容许不同,如查问。 幂等函数或幂等办法是指能够应用雷同参数反复执行,并能取得雷同后果的函数。这些函数不会影响零碎状态,也不必放心反复执行会对系统造成扭转。 幂等性不仅仅只是一次或屡次操作对资源没有产生影响,还包含第一次操作产生影响后,当前屡次操作不会再产生影响。并且幂等关注的是是否对资源产生影响,而不关注后果。 幂等性维度幂等性设计次要从两个维度进行思考:空间、工夫。空间:定义了幂等的范畴,如生成订单的话,不容许呈现反复下单。工夫:定义幂等的有效期。有些业务须要永久性保障幂等,如下单、领取等。而局部业务只有保障一段时间幂等即可。同时对于幂等的应用个别都会随同着呈现锁的概念,用于解决并发平安问题。 以SQL为例select * from table where id=1。此SQL无论执行多少次,尽管后果有可能呈现不同,都不会对数据产生扭转,具备幂等性。insert into table(id,name) values(1,'heima')。此SQL如果id或name有唯一性束缚,屡次操作只容许插入一条记录,则具备幂等性。如果不是,则不具备幂等性,屡次操作会产生多条数据。update table set score=100 where id = 1。此SQL无论执行多少次,对数据产生的影响都是雷同的。具备幂等性。update table set score=50+score where id = 1。此SQL波及到了计算,每次操作对数据都会产生影响。不具备幂等性。delete from table where id = 1。此SQL屡次操作,产生的后果雷同,具备幂等性。什么是接口幂等性在HTTP/1.1中,对幂等性进行了定义。它形容了一次和屡次申请某一个资源对于资源自身应该具备同样的后果(网络超时等问题除外),即第一次申请的时候对资源产生了副作用,然而当前的屡次申请都不会再对资源产生副作用。 这里的副作用是不会对后果产生毁坏或者产生不可意料的后果。也就是说,其任意屡次执行对资源自身所产生的影响均与一次执行的影响雷同。 为什么须要实现幂等性应用幂等性最大的劣势在于使接口保障任何幂等性操作,免去因重试等造成零碎产生的未知的问题。 在接口调用时个别状况下都能失常返回信息不会反复提交,不过在遇见以下状况时能够就会呈现问题: 前端反复提交表单在填写一些表格时候,用户填写实现提交,很多时候会因网络稳定没有及时对用户做出提交胜利响应,以致用户认为没有胜利提交,而后始终点提交按钮,这时就会产生反复提交表单申请。 用户歹意进行刷单例如在实现用户投票这种性能时,如果用户针对一个用户进行反复提交投票,这样会导致接口接管到用户反复提交的投票信息,这样会使投票后果与事实重大不符。 接口超时反复提交很多时候 HTTP 客户端工具都默认开启超时重试的机制,尤其是第三方调用接口时候,为了防止网络稳定超时等造成的申请失败,都会增加重试机制,导致一个申请提交屡次。 音讯进行反复生产当应用 MQ 消息中间件时候,如果产生消息中间件呈现谬误未及时提交生产信息,导致产生反复生产。 引入幂等性后对系统有什么影响幂等性是为了简化客户端逻辑解决,能搁置反复提交等操作,但却减少了服务端的逻辑复杂性和老本,其次要是:把并行执行的性能改为串行执行,升高了执行效率。减少了额定管制幂等的业务逻辑,复杂化了业务性能; 所以在应用时候须要思考是否引入幂等性的必要性,依据理论业务场景具体分析,除了业务上的特殊要求外,个别状况下不须要引入的接口幂等性。 Restful API 接口幂等当初风行的 Restful 举荐的几种 HTTP 接口办法中,别离存在幂等行与不能保障幂等的办法,如下:HTTP协定语义幂等性HTTP协定有两种形式:RESTFUL、SOA。当初对于WEB API,更多的会应用RESTFUL格调定义。为了更好的实现接口语义定义,HTTP对于罕用的四种申请形式也定义了幂等性的语义。GET:用于获取资源,屡次操作不会对数据产生影响,具备幂等性。留神不是后果。POST:用于新增资源,对同一个URI进行两次POST操作会在服务端创立两个资源,不具备幂等性。PUT:用于批改资源,对同一个URI进行屡次PUT操作,产生的影响和第一次雷同,具备幂等性。DELETE:用于删除资源,对同一个URI进行屡次DELETE操作,产生的影响和第一次雷同,具备幂等性综上所述,这些仅仅只是HTTP协定倡议在基于RESTFUL格调定义WEB API时的语义,并非强制性。同时对于幂等性的实现,必定是通过前端或服务端实现。 业务问题抛出在业务开发与分布式系统设计中,幂等性是一个十分重要的概念,有十分多的场景须要思考幂等性的问题,尤其对于当初的分布式系统,经常性的思考重试、重发等操作,一旦产生这些操作,则必须要思考幂等性问题。以交易系统、领取零碎等尤其显著,如:当用户购物进行下单操作,用户操作屡次,但订单零碎对于本次操作只能产生一个订单。当用户对订单进行付款,领取零碎不论呈现什么问题,应该只对用户扣一次款。当领取胜利对库存扣减时,库存系统对订单中商品的库存数量也只能扣减一次。当对商品进行发货时,也需保障物流零碎有且只能发一次货。在电商零碎中还有十分多的场景须要保障幂等性。然而一旦思考幂等后,服务逻辑务必会变的更加简单。因而是否要思考幂等,须要依据具体业务场景具体分析。而且在实现幂等时,还会把并行执行的性能改为串行化,升高了执行效率。 此处以下单减库存为例,当用户生成订单胜利后,会对订单中商品进行扣减库存。 订单服务会调用库存服务进行库存扣减。库存服务会实现具体扣减实现。 当初对于性能调用的设计,有可能呈现调用超时,因为呈现如网络抖动,尽管库存服务执行胜利了,但后果并没有在超时工夫内返回,则订单服务也会进行重试。那就会呈现问题,stock对于之前的执行曾经胜利了,只是后果没有按时返回。而订单服务又从新发动申请对商品进行库存扣减。 此时呈现库存扣减两次的问题。 对于这种问题,就须要通过幂等性进行后果。 解决方案对于幂等的思考,次要解决两点前后端交互与服务间交互。这两点有时都要思考幂等性的实现。从前端的思路解决的话,次要有三种:前端防重、PRG模式、Token机制。前端防重通过前端防重保障幂等是最简略的实现形式,前端相干属性和JS代码即可实现设置。可靠性并不好,有教训的人员能够通过工具跳过页面仍能反复提交。次要实用于表单反复提交或按钮反复点击。 PRG模式PRG模式即POST-REDIRECT-GET。当用户进行表单提交时,会重定向到另外一个提交胜利页面,而不是停留在原先的表单页面。这样就防止了用户刷新导致反复提交。同时避免了通过浏览器按钮后退/后退导致表单反复提交。是一种比拟常见的前端防重策略。 ...

April 20, 2023 · 3 min · jiezi

关于java:Java异常Exception详解

疾速入门将可能出现异常的代码块选中->快捷键 ctrl + alt + t -> 选中 try-catch package com.hspedu.exception_;public class Exception01 { public static void main(String[] args) { int num1 = 10; int num2 = 0;//Scanner(); //2. 当执行到 num1 / num2 因为 num2 = 0, 程序就会呈现(抛出)异样 ArithmeticException //3. 当抛出异样后,程序就退出,解体了, 上面的代码就不再执行 //4. 不应该呈现了一个不算致命的问题就导致整个零碎解体 //5. java 设计者,提供了一个叫异样解决机制来解决该问题 //如果程序员,认为一段代码可能出现异常/问题,能够应用try-catch异样解决机制来解决,从而保障程序的健壮性 //将该代码块->选中->快捷键 ctrl + alt + t -> 选中 try-catch //6. 如果进行异样解决,那么即便呈现了异样,程序能够继续执行 try { int res = num1 / num2; } catch (Exception e) { //e.printStackTrace(); System.out.println("出现异常的起因=" + e.getMessage());//输入异样信息 } System.out.println("程序持续运行...."); }}残缺内容请点击下方链接查看: ...

April 20, 2023 · 1 min · jiezi

关于java:基于SSM和MySQL实现的疫情数据统计分析系统

拜访【WRITE-BUG数字空间】_[内附残缺源码和文档]1.我的项目简介疫情数据统计分析零碎是一个基于SSM框架的网页端系统,我的项目中实现的性能如下:用户拜访网站能够浏览全国疫情的图表信息,管理员登录后盾管理系统,能够进行数据录入、数据查问、图表展现、用户录入和用户编辑。性能形容图表展现:到目前为止,全国疫情分布图、扇形图、柱状图和表格数据录入:录入各个省份的确诊人数、疑似人数、隔离人数、治愈人数和死亡人数数据查问:展现录入疫情数据的各个省份的确诊人数、疑似人数、隔离人数、治愈人数和死亡人数,以及查问输出省份的疫情数据用户录入:录入用户信息,包含账号、用户名和明码,应用账号和明码能够登录后盾进行数据管理和系统管理用户编辑:查问和批改用户的信息。。。。。。 @Configuration@MapperScan(basePackages = "com.liu.epidemic.mapper")public class MybatisConfig { @Beanpublic BasicDataSource basicDataSource(){ BasicDataSource dataSource=new BasicDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/epidemic?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8"); dataSource.setUsername("root"); dataSource.setPassword("123456"); //配置连接池的相干参数 dataSource.setInitialSize(3); //连接池的初始容量 dataSource.setMaxActive(10); //连贯最大流动数量 dataSource.setMaxIdle(1); //连贯最大的闲暇数量 dataSource.setMaxWait(4000); //连贯的超时工夫 dataSource.setDefaultAutoCommit(false); //勾销SQL语句的主动提交 return dataSource;}@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource){ SqlSessionFactoryBean factoryBean=new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); //给实体类起别名 com.liu.epidemic.bean.UserInfo==UserInfo factoryBean.setTypeAliasesPackage("com.liu.epidemic.bean"); //创立一个配置对象,该对象能够设置实体类中的成员变量名与数据库的对应关系 org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); //将数据库表中的字段名中的下划线勾销,紧跟前面的第一个字符变大写,造成实体类中的成员变量名 //user_id --- userId province_name --- provinceName configuration.setMapUnderscoreToCamelCase(true); factoryBean.setConfiguration(configuration); SqlSessionFactory sessionFactory = null; try { sessionFactory = factoryBean.getObject(); } catch (Exception e) { e.printStackTrace(); } return sessionFactory;}} ...

April 19, 2023 · 1 min · jiezi

关于java:手把手带你接入小程序订阅消息

更新完微信服务号的模板音讯之后,我又连忙把微信小程序的订阅音讯给实现了!之前我始终认为微信小程序也是要企业能力申请,没想到小程序集体就能申请。 音讯推送平台推送下发【邮件】【短信】【微信服务号】【微信小程序】【企业微信】【钉钉】等音讯类型。 https://gitee.com/zhongfucheng/austin/https://github.com/ZhongFuCheng3y/austin本想着跟微信服务号的模板音讯一样,我去申请一个「测试号」,就能下发微信小程序的订阅音讯了。但微信小程序的订阅音讯始终不反对「测试号」下发。 于是,我就注册了一个微信小程序,拿到我的小程序appId和secret,并在微信后盾创立了几个订阅音讯的模板。 紧接着,这个把小程序的账号就接入到音讯推送平台的账号管理体系下: 写几行代码失去方才创立的模板,顺便跟前端来个简略的交互: 改几行代码,把具体调用微信的逻辑给补上,有SDK的加持下这种代码就是10min就实现了,非常简单。 实现小程序的订阅音讯推送,我花的工夫最长就花在这上面啦: 1、领有本人的小程序(领有调试根底) 2、让本人的登录到这个小程序里(失去openId) 3、小程序弹窗让我能受权给微信发送订阅音讯(失去推送权限) 小程序的账号我曾经创立好了,然而小程序是没有任何内容的。于是我就在小程序的后盾点点点,顺便看看小程序个别是怎么开发的。于是,我就看到了微信小程序的开发者工具: https://developers.weixin.qq.com/miniprogram/dev/devtools/dow... 下载了之后,这个小工具给我举荐「云开发」,只有花点钱我就能够调用云函数了。尽管要19.9块,有点肉疼。但省工夫的事,我咬咬牙就上了。 完了当前,我在小程序工具箱里翻了好几个模板,看看有没有我想要的性能:登录小程序获取openId、弹窗让我受权模板发送音讯。你别说,真给我翻到一个: 我是没做过小程序的,天然就不会小程序开发,于是就只能摸石头过河了。花了一天多,发现我在这个demo我的项目里获取的openId就是死活的调不通小程序(报错就说不非法openId)。 通过漫长的调试,我忍不了了,再这样耗上来是不行的了。我间接去GitHub看看有没有现成的demo,轻易一搜,还真的有。 https://github.com/zhangkaizhao/wxapp-subscribe-message-demo 间接将「wxapp」文件下导入到小程序的开发工具里,一看,还真能用,代码又少。回看同步openId的代码,原来是要调接口申请微信做鉴权的呀。 于是我在音讯推送平台里也长期写了接口进行鉴权,在小程序调用登录的时候改下入参就完事咯。 通过登录凭证校验之后,咱们就能拿到openId,把订阅音讯的权限界面给唤起,点击容许,就能在音讯推送平台下发送一条小程序的订阅音讯啦。 代码方面我就不细说啦,感兴趣的同学能够把我的项目搞下来玩玩,源码都是有的。这几天还在疯狂更新中,看看目前的音讯渠道接入的状况吧? 如果想学Java我的项目的,强烈推荐我的开源我的项目音讯推送平台Austin(8K stars) ,能够用作毕业设计,能够用作校招,能够看看生产环境是怎么推送音讯的。开源我的项目音讯推送平台austin仓库地址: 音讯推送平台推送下发【邮件】【短信】【微信服务号】【微信小程序】【企业微信】【钉钉】等音讯类型。 https://gitee.com/zhongfucheng/austin/https://github.com/ZhongFuCheng3y/austin

April 19, 2023 · 1 min · jiezi

关于java:字节面试10亿个key中怎么判断某个key是否存在

问题形容:10亿个key中,怎么判断某个key是否存在?key的类型是不超过30个字符的字符串。 要求: 内存空间不能占用太大QPS在500w看到这个问题,必定有很多人第一反馈想到Map,因为Map人造反对key-value的数据结构,并且可能疾速的查找某个key。然而,如果你抉择应用Hashmap,那必定会占用很大的内存,这不符合要求。那必定有人说应用布隆过滤器,也就是应用BloomFilter。那咱们一起来看一下,应用布隆过滤器会不会有什么问题? 一、布隆过滤器布隆过滤器是一种概率型数据结构,能够用来测验一个元素是否属于一个汇合。它通过构建多个哈希函数,将每个关键字映射到多个位数组的地位上,若所有相应的位都为1,则阐明该元素可能被蕴含在该汇合中,如果有一个或多个对应的位为0,则必定不在汇合中。因而,布隆过滤器具备空间效率高和查问工夫快等特点。在Redis中,能够应用BitMap类型来实现布隆过滤器;而在Java语言中,能够应用Guava中的BloomFilter来实现。 示例代码: import com.google.common.hash.BloomFilter;import com.google.common.hash.Funnels;public class BloomFilterDemo { public static void main(String[] args) { // 创立布隆过滤器,预计插入1000000000个元素 BloomFilter<CharSequence> bloomFilter = BloomFilter.create( Funnels.stringFunnel(), 1000000000L, 0.01); // 插入10000个元素 for (int i = 1; i <= 10000; i++) { bloomFilter.put("key_" + i); } // 判断key_9999是否存在 if (bloomFilter.mightContain("key_9999")) { System.out.println("key_9999 存在"); } else { System.out.println("key_9999 不存在"); } // 判断key_10000是否存在 if (bloomFilter.mightContain("key_10000")) { System.out.println("key_10000 存在"); } else { System.out.println("key_10000 不存在"); } }}输入: ...

April 19, 2023 · 2 min · jiezi

关于java:简单介绍十几款常用的画架构图流程图的软件

简略介绍十几款罕用的画架构图流程图的软件draw.iodraw.io是开源收费的在线画图工具,还提供桌面版本。 个性: 实时合作;反对在线离线版本;存储反对多种形式:Google Drive, OneDrive, GitHub, GitLab, Dropbox等;许多丰盛的图标库。 ProccessOnProccessOn是一款优良的国产在线合作画图软件,十分好用,也十分丑陋。 ExcalidrawExcalidraw是收费的在线合作画图工具。 手写格调的草图十分难看;丑陋的图库。 OmniGraffleOmniGraffle是一款在苹果零碎上的画图软件。反对Mac/iPad/iPhone,功能丰富。 GliffyGliffy整合在Confluence十分不便,还是很好用的,就是太贵了。 PlantUMLPlantUML开源的画图工具,能够用代码来画图。 Mermaid跟PlantUML一样, Mermaid也能够用代码的形式来画图。 Visio微软出品,以前还是非常罕用的。 LucidChartLucidChart如图: 倒也是挺难看的,就是也要免费: TldrawTldraw收费开源的在线合作工具。 MiroMiro是一款在线合作工具,如下: CloudSkewCloudSkew专门用于画云计算架构图: 价格如下: WhimsicalWhimsical也是在线的工具: 价格如下: Zen FlowchartZen Flowchart 对于画流程图比拟不便: 价格:

April 19, 2023 · 1 min · jiezi

关于java:jvm添加自定义dns

有一个常见的场景, 咱们在开发过程中,要配置很多的本地host,以实现测试环境一些资源的拜访,那么其它人参加进来开发的话,也得在本人电脑上配置,这样既麻烦,又容易出错。那么能不能把这些配置,写到project中去实现呢, 这样每个人本地的/etc/hosts文件中会很洁净,能够随时clone启动调试而不需任何配置。答案是必定的,那么接下来咱们就应用java反射的形式来实现。咱们次要操作的对象就是java.net.InetAddress,能够从源码中看到, 该类中有两个外围的变量cache和expirySet。 // mapping from host name to Addresses - either NameServiceAddresses (while // still being looked-up by NameService(s)) or CachedAddresses when cached private static final ConcurrentMap<String, Addresses> cache = new ConcurrentHashMap<>(); // CachedAddresses that have to expire are kept ordered in this NavigableSet // which is scanned on each access private static final NavigableSet<CachedAddresses> expirySet = new ConcurrentSkipListSet<>();这里会有个疑难, 为啥还有个expirySet,这个问题也能够通过源码失去解决,即为了删除生效的条目。具体含意能够通过浏览正文进行了解。 // remove expired addresses from cache - expirySet keeps them ordered // by expiry time so we only need to iterate the prefix of the NavigableSet... long now = System.nanoTime(); for (CachedAddresses caddrs : expirySet) { // compare difference of time instants rather than // time instants directly, to avoid possible overflow. // (see System.nanoTime() recommendations...) if ((caddrs.expiryTime - now) < 0L) { // ConcurrentSkipListSet uses weakly consistent iterator, // so removing while iterating is OK... if (expirySet.remove(caddrs)) { // ... remove from cache cache.remove(caddrs.host, caddrs); } } else { // we encountered 1st element that expires in future break; } }接下来就是如何来实现反射增加dns条目了,本例中基于java17实现,其它版本会有相应的变动。 ...

April 19, 2023 · 1 min · jiezi

关于java:终于把-Spring-Boot-30-写成书了

大家好,我是R哥。 我的新书《Spring Boot 3 核心技术与最佳实战》打磨一年多,明天终于上市了,定价 158 元,明天刚上市搞 5 折促销,80 元不到上车,这可能是全网最便宜的时候了,机会难得,想拥抱 Spring Boot 3.0 的不要错过。 文章还没发,曾经有老铁粉丝上车了,真爱啊。。。 为什么要学 Spring Boot?Spring 作为 Java 开发界的万能框架,已经和 Struts2、Hibernate 框架组成 SSH,成为 Java Web 开发的三驾马车。大略在 2013 年左右,又和 Spring MVC、MyBatis 框架组成 SSM,成为新一代的 Web 开发框架全家桶,始终风行连续至今。 而为了简化 Spring 框架的上手难度,Spring Boot 框架于 2014 年诞生,能够帮忙开发者更加轻松、快捷地应用 Spring 的组件,它是 Spring、Spring MVC 等框架更上一层的框架,它须要依赖于 Spring、Spring MVC 等原生框架,而不能独立存在。 学会 Spring Boot,能够简化应用 Spring 根底组件的难度,还是学习 Spring Cloud 微服务框架的根底,因为 Spring Cloud 的根底就是 Spring Boot。 Vaadin 最近公布了一份无关企业中 Java 现状的钻研报告: ...

April 19, 2023 · 3 min · jiezi

关于java:RASP技术进阶系列二东西向Web流量智能检测防御

当今网络安全攻防较量已进入深水区,纵深进攻体系曾经成为根底。在HW场景下,对于利用破绽攻打响应和歹意流量溯源剖析的平安工作始终被视作重点,然而在理论事件处理过程中仍存在微小的技术挑战。RASP技术作为新一代突破性的应用层踊跃进攻技术,可在东西向Web流量自动化检测进攻中起到关键作用。 1  技术挑战剖析1.1 HW场景 HW场景工作阶段可分为HW前、HW中、HW后。防守方在后期次要发展已有资产盘点、破绽危险查看、被动加固、环境隔离等工作;在中期次要进行检测进攻、监控告警、事件响应、问题修复、同步情报等;在前期次要进行HW工作总结、问题处理、体系优化等。 实战中,攻打方次要会集中寻找突破点,往往聚焦在利用破绽、0day破绽的筹备上。而对于防守方,存在技术挑战之处除了在于如何疾速进行破绽攻打响应外,也包含预先进行攻打门路溯源、事中进行攻打流量精准剖析和进攻。 1.2 纵深进攻  平安建设是一场动态化、长久化的静止,纵深进攻体系将平安建设变得更加系统化,旨在变被动为被动,基于木桶实践,从物理层到应用层提供层层防御机制。惯例纵深进攻有以下三处短板:1)利用破绽止步于漏扫,不足破绽及时修复和0day破绽免疫的机制; 2)东西向流量检测交由主机EDR和容器平安产品的微隔离技术,对于微服务间RPC协定及加密流量内容无奈监测,并且无奈追踪其在程序内的实在行为; 3)云原生环境下,内外网边界逐步含糊,会裸露更多的API利用且微服务之间的拜访调用关系更加简单。 2  RASP东西向流量 检测进攻实际利用对于流量拜访的模式,南北向流量通常指内部客户端对外部服务器的拜访流量,东西向流量通常指外部环境下不同服务器间的拜访流量。 图1 主机间的南北向流量和东西向流量阐明 在以往传统纵深进攻体系里,通过EDR主机微隔离技术来跟踪描述主机之间的流量拜访关系如下图所示: 图2 主机东西向流量监控 随着容器化架构的遍及,南北向流量和东西向流量新增了利用容器间的拜访场景,如下: 图3 容器架构下南北向流量和东西向流量阐明 但从入侵危险真实性判断和执行操作完整性审查的角度看,主机和容器间的流量监控还无奈剖析到具体应用程序解析申请后的实在执行状况。RASP技术则很好地补充了对利用容器外部程序间的流量拜访和程序外部上下文执行过程的监控信息。 2.1 攻打流量内容可见 RASP基于其技术原理,可从某个Web利用或微服务利用获取到申请并剖析其数据内容和函数执行过程。绝对于网络边界设施对于加密流量无奈审计的问题,RASP可从应用程序外部获取到残缺解密后的申请数据。  图4 HTTP申请数据内容并且,传统安全设备次要以HTTP/HTTPS流量剖析为主,随着云原生技术的倒退,微服务架构遍及,以及各类API接口逐步转为各类RPC接口协议(如Dubbo、二开RPC框架协定等)。传统安全设备简直不对这类流量进行监控和解析,通过RASP技术则可模仿企业RPC协定解析过程进行适配,解决该类型流量内容无奈可视的问题。 图5 Dubbo框架协定申请数据内容2.2 程序外部上下文剖析 审计东西向流量的目标是为了察看利用间是否存在歹意操作。因为传统平安技术的限度,当应用程序接管到申请后无奈审计外部具体函数操作。而RASP可跟踪程序执行上下文信息,延展了东西向流量到应用程序外部的行为可视能力。  图6 东西向流量及代码执行过程跟踪从内部入侵攻打的后果看,最终执行并达到攻打目标的地位,往往是程序对资源拜访过程中存在缺点的地位,如数据库拜访、命令执行、网络申请、文件操作等。通过对程序外部函数堆栈及函数变量进行审查,可为平安人员补充利用层面的平安经营数据及可供审计的内容,包含具体拜访的webshell后门、歹意命令、SQL注入语句、敏感数据内容及存储形式等。 2.3 微服务调用链路追踪 随着分布式、微服务架构、多语言、前后端拆散模式的利用遍及,溯源微服务间的攻打入侵门路存在难度。通过追踪机制,则可便捷地关联入侵申请所通过的微服务利用,提供精准的溯源数据。 图7 微服务链路追踪过程用户通过浏览器收回HTTP申请,HTTP利用外部解决申请时由Dubbo服务生产方调用服务提供方触发一次Dubbo RPC调用,再逐层返回给用户层。在这次操作过程中,若在Dubbo服务提供方检测到破绽信息,心愿能够向上溯源,找到触发本次Dubbo调用破绽的源头调用信息,如:HTTP GET/user/xmirror。从而溯源绘制调用链路门路,如下成果: 图8 溯源链路门路2.4 攻打入侵响应和防护 纵深进攻体系的最终目标是保障业务自身安全性。RASP技术原理外围在于可对最终利用进行输出流量检测,并在代码函数级别对变量进行查看跟踪、过程污点剖析、歹意执行阻断,在利用破绽和流量行为剖析上具备显著劣势。 常常裸露且危害较大的利用破绽,大多存在于开源软件和第三方组件。黑客能够通过源码剖析发现其中的破绽,进而当发现攻打指标应用该开源软件或第三方组件时,便能够发动0day攻打。常态化平安经营下,对利用破绽的修复或者弥补措施通常会采纳如下三种形式: 1)更新降级有破绽的组件。这是支流举荐的修复形式,但为了不影响业务,这种形式须要有较长评估测试周期。如果是0day破绽,可能官网还未提供更新版本或者补丁;如果是老旧零碎,存在版本曾经进行保护的状况。 2)应用破绽情报中的长期修复计划。这属于非官方修复形式,可能存在某些二次开发利用不实用状况,且如果波及批改源码,须要有同时具备平安和研发能力的人员储备,不适宜小型团队。 3)增加进攻攻打流量的WAF策略。WAF属于边界防护,利用资产覆盖面广,易于操作。但如果规定策略严苛,容易造成无关利用流量的误拦挡,影响失常业务申请。破绽在被发现并被攻击者利用产生危害时,被定义为一次残缺的危险入侵事件。也就是说,如果利用存在破绽,但攻击者无奈利用该破绽进行下一步操作,则该破绽危险就不产生危害。RASP的基本原理是利用HOOK技术,通过替换函数体或在函数前后插入检测代码,实现在危险操作执行时进行阻断等。 图9 对Java中JDBC executeQuery办法插桩成果对于一些常常暴发破绽的开源软件和第三方组件而言,破绽存在地位和利用办法可能不同,然而在进行利用时,执行到利用代码底层,往往都会汇集到一些“敏感”函数上,如反序列化、数据库执行、命令执行、文件操作、响应返回等相干函数。如果通过RASP技术对这些底层“敏感”函数调用做肯定辨认阻断,即可免疫大部分0day攻打。甚至在攻打利用时,如同WAF个别,对攻打申请流量进行过滤,但RASP绝对WAF的劣势在于更加具备颗粒度(可设置对某个利用过程失效)且流量内容通明。因而,从此类实际利用登程,RASP人造可作为破绽热修复和免疫0day破绽的利用平安疫苗。 3  攻防角度下 踊跃进攻体系的协同以EDR技术为例,其作为传统纵深进攻体系下主机平安层面进攻计划,特点是采纳自适应平安体系的架构,笼罩进攻、监控、回溯和预测这四项要害能力,各项平安能力以智能、集成和联动的形式应答各类攻打。  图10 传统EDR部署图 一般而言,EDR次要在主机层面提供资产盘点、危险发现、入侵检测、平安基线、病毒查杀等平安经营能力,对于利用微服务相干以及内存马都有力笼罩,对于反序列化攻打、代码注入等攻打类型也难以进攻。然而在与悬镜云鲨RASP协同联动后,可在利用层面建设踊跃防御能力,并切实反馈东西向流量防护成果。 图11 悬镜云鲨分布式RASP部署图 从攻防角度看,利用层面踊跃进攻的建设可促成纵深进攻体系更加深刻,推动业务利用实质性平安落地,次要蕴含以下方面:1)利用资产治理:借助插桩节点推广笼罩,可梳理微服务利用IP、URL地址、API接口、第三方组件等,并绘制资产关联图谱; 2)运行时入侵检测:基于利用污点剖析和上下文剖析技术,实时检测利用破绽利用、webshell拜访执行、内存马执行等,并实时预警高危组件危险,定位具体利用; 3)攻打威逼疫苗:免疫通用Web利用破绽及第三方组件安全漏洞,并对EDR难以进攻的反序列化、代码执行等攻打进行更加无效的阻断; 4)利用平安基线:合乎性应用层基线配置检测,笼罩中间件、单利用、微服务等; 5)敏感数据审查:基于可获取利用外部数据输出、操作、内容的先天技术劣势,可针对业务侧关注的个人信息、业务数据等敏感信息进行合规审查,必要时进行输入阻断和过滤; 6)利用热补丁:对已上线的重要利用零碎,当呈现重大破绽短时间难以修复时,能够动静下发热补丁进行修复,在不中断业务的同时为利用零碎提供长期防护,为破绽修复争取贵重的工夫; 7)东西向流量剖析:以微服务架构作为根底,除了对利用间流量和东西向流量描述,更能深刻出现应用程序外部具体执行操作,为平安经营提供从网络层到利用外部实在执行过程数据,分析攻击链路。  因此能够认为,主机层EDR技术和应用层RASP技术并不存在利用场景的抵触。RASP技术创造性实现了数字化利用公布的出厂免疫,不仅带来了对于未知入侵危险的间接防御能力晋升,也将东西向流量的智能检测进攻技术进行了延长。EDR和RASP两种技术的联动,可人造作为踊跃进攻体系下相互配合合作的搭档,买通利用防护与治理的最初“一公里”。 悬镜云鲨官网网址:https://rasp.xmirror.cn/

April 19, 2023 · 1 min · jiezi

关于java:idea-拷贝-数组内容

应用Arrays.toString将数组内容变字符串,拷贝即可

April 19, 2023 · 1 min · jiezi

关于java:Java-的-Collection-与-List-集合

1.Collection汇合1.1数组和汇合的区别相同点 都是容器,能够存储多个数据 不同点 数组的长度是不可变的,汇合的长度是可变的数组能够存根本数据类型和援用数据类型 汇合只能存援用数据类型,如果要存根本数据类型,须要存对应的包装类 1.2汇合类体系结构 1.3Collection 汇合概述和应用Collection汇合概述 是单例汇合的顶层接口,它示意一组对象,这些对象也称为Collection的元素JDK 不提供此接口的任何间接实现.它提供更具体的子接口(如Set和List)实现创立Collection汇合的对象 多态的形式具体的实现类ArrayListCollection汇合罕用办法 办法名阐明boolean add(E e)增加元素boolean remove(Object o)从汇合中移除指定的元素boolean removeIf(Object o)依据条件进行移除void clear()清空集合中的元素boolean contains(Object o)判断汇合中是否存在指定的元素boolean isEmpty()判断汇合是否为空int size()汇合的长度,也就是汇合中元素的个数1.4Collection汇合的遍历迭代器介绍 迭代器,汇合的专用遍历形式Iterator<E> iterator(): 返回此汇合中元素的迭代器,通过汇合对象的iterator()办法失去Iterator中的罕用办法 boolean hasNext(): 判断以后地位是否有元素能够被取出 E next(): 获取以后地位的元素,将迭代器对象移向下一个索引地位 Collection汇合的遍历 public class IteratorDemo1 { public static void main(String[] args) { //创立汇合对象 Collection<String> c = new ArrayList<>(); //增加元素 c.add("hello"); c.add("world"); c.add("java"); c.add("javaee"); //Iterator<E> iterator():返回此汇合中元素的迭代器,通过汇合的iterator()办法失去 Iterator<String> it = c.iterator(); //用while循环改良元素的判断和获取 while (it.hasNext()) { String s = it.next(); System.out.println(s); } }}迭代器中删除的办法 ...

April 19, 2023 · 2 min · jiezi

关于java:项目讲解之常见安全漏洞

本文是从开源我的项目 RuoYi 的提交记录文字描述中依据关键字破绽|平安|阻止筛选而来。旨在为大家介绍日常我的项目开发中须要留神的一些平安问题以及如何解决。 我的项目平安是每个开发人员都须要重点关注的问题。如果我的项目破绽太多,很容易蒙受黑客攻击与用户信息泄露的危险。本文将联合3个典型案例,解释常见的安全漏洞及修复计划,帮忙大家在我的项目开发中进一步提高安全意识。RuoYi我的项目地址:https://gitee.com/y_project/RuoYi博主github地址:https://github.com/wayn111,欢送大家关注一、重置用户明码RuoYi 我的项目中有一个重置用户明码的接口,在提交记录 dd37524b 之前的代码如下: @Log(title = "重置明码", businessType = BusinessType.UPDATE)@PostMapping("/resetPwd")@ResponseBodypublic AjaxResult resetPwd(SysUser user){ user.setSalt(ShiroUtils.randomSalt()); user.setPassword(passwordService.encryptPassword(user.getLoginName(), user.getPassword(), user.getSalt())); int rows = userService.resetUserPwd(user); if (rows > 0) { setSysUser(userService.selectUserById(user.getUserId())); return success(); } return error();}能够看出该接口会读取传入的用户信息,重置完用户明码后,会依据传入的 userId 更新数据库以及缓存。 这里有一个十分重大的平安问题就是自觉置信传入的用户信息,如果攻打人员通过接口结构申请,并且在传入的 user 参数中设置 userId 为其余用户的 userId,那么这个接口就会导致某些用户的明码被重置因此被攻打人员把握。 1.1 攻打流程如果攻打人员把握了其余用户的 userId 以及登录账号名 结构重置明码申请将 userId 设置未其余用户的 userId服务端依据传入的 userId 批改用户明码应用新的用户账号以及重置后的明码进行登录攻打胜利1.2 如何解决在记录 dd37524b 提交之后,代码更新如下: @Log(title = "重置明码", businessType = BusinessType.UPDATE)@PostMapping("/resetPwd")@ResponseBodypublic AjaxResult resetPwd(String oldPassword, String newPassword){ SysUser user = getSysUser(); if (StringUtils.isNotEmpty(newPassword) && passwordService.matches(user, oldPassword)) { user.setSalt(ShiroUtils.randomSalt()); user.setPassword(passwordService.encryptPassword( user.getLoginName(), newPassword, user.getSalt())); if (userService.resetUserPwd(user) > 0) { setSysUser(userService.selectUserById(user.getUserId())); return success(); } return error(); } else { return error("批改明码失败,旧明码谬误"); }}解决办法其实很简略,不要自觉置信用户传入的参数,通过登录状态获取以后登录用户的userId。如上代码通过 getSysUser() 办法获取以后登录用户的 userId 后,再依据 userId 重置明码。 ...

April 18, 2023 · 3 min · jiezi

关于java:java-memory-model

什么是JMM?JMM就是Java内存模型(java memory model)。因为在不同的硬件生产商和不同的操作系统下,内存的拜访有肯定的差别,所以会造成雷同的代码运行在不同的零碎上会呈现各种问题。所以java内存模型(JMM)屏蔽掉各种硬件和操作系统的内存拜访差别,以实现让java程序在各种平台下都能达到统一的并发成果。在计算机硬件层面,CPU在解决指令时,会先从L1,L2,L3缓存中拉取数据,如果没有再从主内存中获取,JMM就是再CPU缓存和主内存之间协调,定义了Java线程根本的工作形式,保障了原子性,可见性,有序性的。Java内存模型规定所有的变量都存储在主内存中,包含实例变量,动态变量,然而不包含局部变量和办法参数。每个线程都有本人的工作内存,线程的工作内存保留了该线程用到的变量和主内存的正本拷贝,线程对变量的操作都在工作内存中进行,而后再刷回主内存,线程不能间接读写主内存中的变量。 JMM8种内存交互方式 lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占的状态。unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,开释后的变量才能够被其余线程锁定。read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作应用。load(载入):作用于工作内存的变量,它把read操作从主内存中失去的变量值放入工作内存的变量正本中。use(应用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个须要应用到变量的值的字节码指令时将会执行这个操作。assign(赋值):作用于工作内存的变量,它把一个从执行引擎接管到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作应用。write(写入):作用于主内存的变量,它把store操作从工作内存中失去的变量的值放入主内存的变量中。 JMM特色原子性JMM只能保障根本的原子性,如果要保障一个代码块的原子性,提供了monitorenter 和 moniterexit 两个字节码指令,也就是 synchronized 关键字。因而在 synchronized 块之间的操作都是原子性的。 可见性可见性是指变量的变动及在线程之间是可见的volatile:当变量被volatile润饰时,这个变量被批改后会立刻刷新到主内存中,当线程读取这个变量时,也会间接从主内存中读取(当volatile润饰的是援用数据类型,只能保障援用的内存地址是可见,不保障对象的外部属性可见)synchronized:在执行完同步代码后,unlock之前必须把共享变量刷新到主内存中。final:常量,线程无奈更改。 有序性volatile:应用内存屏障达到禁止指令重排序,以保障线程操作的有序性。synchronized:一个线程lock之后,必须unlock后,其余线程才能够从新lock,使得被synchronized包住的代码块在多线程之间的操作是串行执行的。

April 18, 2023 · 1 min · jiezi

关于java:Maven-打包跳过测试的-5-种方法应有尽有还有谁不会

平时开发时的工作的话之次要负责写代码就行了,什么公布我的项目啊,好吧不是咱们干的事。在咱们的理解中打包公布我的项目应该不是一个艰难的问题。 对,最简略的办法就行应用间接应用maven插件打包,甚至咱们都不须要晓得他是怎么实现的,插件能帮咱们将我的项目打包为一个jar包,而后应用java -jar xx.jar就能运行咱们的我的项目。 咱们平时应用的在开发中应用的是开发或测试的数据库,和生产下面的个别是隔离的,意味着打包的时候须要激活生产的配置文件,然而咱们不肯定有拜访生产库的权限,此时咱们间接打包就会呈现问题。当咱们间接点击下面的package的时候他会激活单元测试,须要测试通过当前能力打包,然而很显然测是不能通过的,因为我激活了生产的配置然而我并没有拜访上产库的权限,此时就会陷入始终打包却打不完的感觉,这就须要咱们打包时跳过测试。那怎么跳过测试呢? 为什么打包时要执行单元测试呢?【这不是咱们的重点,那咱们就简略讲讲,须要深刻理解的自行查阅相干材料。】此时就波及到 Maven 的生命周期,在 Maven 的 default 生命周期中共有 23 个阶段,每个生命周期中的前面的阶段会依赖于后面的阶段,当执行某个阶段的时候,会先执行其后面的阶段。 在 default 生命周期中 package 阶段不是第一个阶段,因而也须要依赖于后面的阶段的执行,正好 test 阶段【测试:应用适合的单元测试框架运行测试(Juint是其中之一)。】就是其后面阶段之一,此时就会必须先通过 test 阶段才会到 package 阶段【 打包:将编译后的代码打包成可散发格局的文件,比方JAR、WAR…】。 咱们因为没有拜访生产库的权限,此时连 test 阶段都不能通过,那就不会达到 package 这个阶段。那有什么解决办法呢?上面咱们探讨一下此问题的解决办法: 1、命令行形式跳过测试咱们能够通过应用命令将我的项目打包,增加跳过测试的命令就能够了,能够用两种命令来跳过测试: mvn package -DskipTests=true-DskipTests=true,不执行测试用例,但编译测试用例类生成相应的class文件至 target/test-classes 下。mvn package -Dmaven.test.skip=true-Dmaven.test.skip=true,不执行测试用例,也不编译测试用例类。在应用 mvn package 进行编译、打包时,Maven会执行 src/test/java 中的 JUnit 测试用例,有时为了跳过测试,会应用参数 -DskipTests=true 和 -Dmaven.test.skip=true,这两个参数的次要区别是: 应用 -Dmaven.test.skip=true,岂但跳过单元测试的运行,也跳过测试代码的编译;应用 -DskipTests=true 跳过单元测试,然而会持续编译。2、pom.xml中配置跳过测试能够在 pom.xml 中增加如下配置来跳过测试: <build> <plugins> <!-- maven 打包时跳过测试 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <skip>true</skip> </configuration> </plugin> </plugins></build>举荐一个开源收费的 Spring Boot 最全教程: ...

April 18, 2023 · 1 min · jiezi

关于java:基于SpringMVCSpringMyBatis开发的个人博客网站

拜访【WRITE-BUG数字空间】_[内附残缺源码和文档]一、对于我的项目该博客是基于 SSM 实现的集体博客零碎,适宜初学 SSM 和集体博客制作的同学学习。次要技术架构包含 Maven、SpringMVC、Spring、MyBatis、Thymeleaf、Redis 等。前端采纳 Bootstarp 和 Semantic UI。二、应用步骤Fork 我的项目fork 或者下载我的项目到本地(倡议先 fork 到本人仓库,在通过码云导入仓库下载,实测下载速度能够)。残缺我的项目源码,能够应用 IDEA 导入。数据库文件请先创立数据库,而后以运行 SQL 文件形式导入导入数据库新建数据库 blog,导入数据库 blog.sql。留神,数据库的编码和排序规定是 utf-8 和 utf-8_general_ci。数据库默认用户名 root,明码 123456启动 Redis 服务,并在 redis.properties 配置你的 Redis批改我的项目中的数据库连贯信息批改 db.properties 文件,该文件很容易找到,在 src/main/resources 中。外面有 MySQL 数据库连贯信息,请确保已装置和启动 MySQL。留神批改数据库地址、表名、用户名和明码。db.properties 文件中 databasePath 示意每周日进行数据库备份的门路,可自行批改后盾 SQL 文件中管理员账户为 admin,明码为:111111三、应用留神开发工具的抉择请应用 IntelliJ IDEA, 尽量不要用 Eclipse/MyEclipse。后者可能要折腾一会儿确保你装置了 Maven(如果 maven 加载 pom 报错,发现不是本人配置的 maven,请到 setting 中批改成本人的 maven 仓库)本我的项目有应用到 Redis,所以运行我的项目前先启动 Redis 服务,并在 redis.properties 配置你的 Redis请给你的 IDE 装置 Lombok 插件实体类中屡次应用到 @Data 注解,请确保你的 IDE 装置了 Lombok 插件,否则找不到 getter/setter 办法数据库乱码,在 MySQL 装置门路,比方(E:\MySQL\MySQL Server 5.5)下找到 my.ini 文件进入编辑,批改这两处中央为 utf8,默认是拉丁文character-set-server=utf8default-character-set=utf8 ...

April 18, 2023 · 1 min · jiezi

关于java:一个Java线程的线生线生-vs-人生

java线程的应用1. Java多线程概述上面咱们看下Java的多线程作者: 博学谷狂野架构师GitHub:GitHub地址 (有我精心筹备的130本电子书PDF) 只分享干货、不吹水,让咱们一起加油!1.1 java天生就是多线程的一个Java程序从main()办法开始执行,而后依照既定的代码逻辑执行,看似没有其余线程参加,但实际上Java程序天生就是多线程程序,因为执行main()办法的是一个名称为main的线程。1.1.1 代码案例执行上面的代码package chapter01;import java.lang.management.ManagementFactory;import java.lang.management.ThreadInfo;import java.lang.management.ThreadMXBean;public class ThreadDemo { /** * 打印出java中所有的线程 * @param args */ public static void main(String[] args) { ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false); for (ThreadInfo info : threadInfos) { System.out.println("[" + info.getThreadId() + "]" + info.getThreadName()); } System.out.println(Thread.activeCount()); }}执行后咱们会发现打印了如下的线程信息,阐明Java自身就是多线程的[6] Monitor Ctrl-Break //监控Ctrl-Break中断信号的[5] Attach Listener //内存dump,线程dump,类信息统计,获取零碎属性等[4] Signal Dispatcher // 散发解决发送给JVM信号的线程[3] Finalizer // 调用对象finalize办法的线程[2] Reference Handler//革除Reference的线程[1] main //main线程,用户程序入口1.2 线程的生命周期Thread类提供了六种状态 ...

April 18, 2023 · 4 min · jiezi

关于java:教学网站开发

拜访【WRITE-BUG数字空间】_[内附残缺源码和文档]摘要21世纪是信息化的时代,信息化物品一直地涌入咱们的生存。同时,教育行业也产生了重大改革。传统的身心传授的模式,正在被代替。互联网模式的教育开拓了一片新的热土。这算是对教育行业的一次重大挑战。截至目前,泛滥教育行业领头羊,纷纷开拓互联网化的教育模式。作为高校更不能例外。现在,任何一所高校都领有本人的官方网站,以达到宣传教育的目标。算是对互联网教育的一种提高。这次课程设计,次要以山东师范大学历山学院为原型,制作WEB前端开发课程的课程设计网站。次要面向高校学生理解并相熟Web课程,并且能够进行在线学习。同时通过课程设计网站,咱们更能分明理解课程的要害,更理解老师。甚至能够在线提交倡议给老师。促成师生学习默契。一、我的项目简介Web课程重在学习如何进行网页设计,对同学们来说网站并不生疏,所以建设Web课程设计网站可能更易承受。同时,Web设计的老师,也是精通计算机的老师,对于网站的资源的上传和治理,更能承受。本次课程设计次要以山东师范大学历山学院Web开发课程为原型进行设计。综合思考,老师简介、学习资源、课程介绍等几个方面进行重点性能开发。同时建设教学网站也是十分有意义的。二、我的项目功能分析2.1 总体介绍我的项目主页次要包含:搜寻栏目、菜单、工夫及新闻、焦点图、课程次要常识介绍、单干企业及网站底部等几个模块。

April 18, 2023 · 1 min · jiezi