业务代码-整合框架-存储-缓存常见错误详解一

一. java空指针和异常:

1.什么是空指针异常(java.lang.NullPointException):
在这里插入图片描述
1.1常见的空指针异常案例:

public class WhatIsNpe {

    public static class User {

        private String name;
        private String[] address;

        public void print() {
            System.out.println("This is User Class!");
        }

        public String readBook() {
            System.out.println("User Read Imooc Escape!");
            return null;
        }
    }

    /**
     * <h2>自定义一个运行时异常</h2>
     * */
    public static class CustomException extends RuntimeException {}

    public static void main(String[] args) {

        // 第一种情况: 调用了空对象的实例方法
//        User user = null;
//        user.print();

        // 第二种情况: 访问了空对象的属性
//        User user = null;
//        System.out.println(user.name);

        // 第三种情况: 当数组是一个空对象的时候, 取它的长度
//        User user = new User();
//        System.out.println(user.address.length);

        // 第四种情况: null 当做 Throwable 的值
//        CustomException exception = null;
//        throw exception;

        // 第五种情况: 方法的返回值是 null, 调用方直接去使用
        User user = new User();
        System.out.println(user.readBook().contains("MySQL"));
    }
}

2.赋值时自动拆箱出现空指针:
2.1需要装箱拆箱的类型有:
在这里插入图片描述
在这里插入图片描述
2.2自动拆箱时出现的空指针异常:

public class UnboxingNpe {

    private static int add(int x, int y) {
        return x + y;
    }

    private static boolean compare(long x, long y) {
        return x >= y;
    }

    public static void main(String[] args) {

        // 1. 变量赋值自动拆箱出现的空指针,Long赋值给long会自动拆箱
        // javac UnboxingNpe.java
        // javap -c UnboxingNpe.class
        Long count = null;
        long count_ = count;

        // 2. 方法传参时自动拆箱引发的空指针,add()方法里的参数是基本数据类型,传入的参数是Integer类型,会自动拆箱
//        Integer left = null;
//        Integer right = null;
//        System.out.println(add(left, right));

        // 3. 用于大小比较的场景,compare()方法里的参数是基本数据类型,传入的参数是Integer类型,会自动拆箱
//        Long left = 10L;
//        Long right = null;
//        System.out.println(compare(left, right));
    }
}

2.3规避自动拆箱应发空指针类型:

  • 基本数据类型优于包装器类型,优先考虑使用基本类型
  • 对于不确定的包装器类型,一定要校验是否是null
  • 对于值为null的包装器类型,考虑是否可以赋值为0

3.字符串,数组,集合在使用时出现空指针:

  • 字符串使用equals时报空指针错误
  • 对象数组虽然new出来了,但是如果没有初始化,一样会报空指针错误
  • List对象add null不报错,但是addAll不能添加null,否则会报空指针错误
public class BasicUsageNpe {

    private static boolean stringEquals(String x, String y) {
        return x.equals(y);
    }

    public static class User {
        private String name;
    }

    public static void main(String[] args) {

        // 1. 字符串使用 equals 可能会报空指针错误
//        System.out.println(stringEquals("xyz", null));  // 不会报错,打印false
//        System.out.println(stringEquals(null, "xyz"));  // 会报错,因为equals前面是null,null引用会报错

        // 2. 对象数组 new 出来, 但是元素没有初始化
//        User[] users = new User[10];
//        for (int i = 0; i != 10; ++i) {
//            users[i] = new User();
//            users[i].name = "imooc-" + i;  // 会报错,users[i]元素没有初始化,就是null
//        }

        // 3. List对象ddAll传递null会抛出空指针;add 传递null不会报错
        List<User> users = new ArrayList<User>();
        User user = null;
        List<User> users_ = null;
        users.add(user);        // 不会报错
        users.addAll(users_);   //会报错
    }
}

4.optional是容器类,代表存在与不存在,避免空指针异常

/**
 1. <h1>学会 Optional, 规避空指针异常</h1>
 2. */
@SuppressWarnings("all")
public class OptionalUsage {

    private static void badUsageOptional() {

        Optional<User> optional = Optional.ofNullable(null);  // 创建optional实例
        User user = optional.orElse(null); // good
        user = optional.isPresent() ? optional.get() : null; // bad
    }

    public static class User {
        private String name;

        public String getName() {
            return name;
        }
    }

    private static void isUserEqualNull() {

        User user = null;
        if (user != null) {
            System.out.println("User is not null");
        } else {
            System.out.println("User is null");
        }

        Optional<User> optional = Optional.empty();  // 创建optional实例
        if (optional.isPresent()) {
            System.out.println("User is not null");
        } else {
            System.out.println("User is null");
        }
    }

    private static User anoymos() {
        return new User();
    }

    public static void main(String[] args) {

        // 没有意义的使用方法
        isUserEqualNull();

        User user = null;
        Optional<User> optionalUser = Optional.ofNullable(user);
        // 存在即返回, 空则提供默认值
        optionalUser.orElse(new User());
        // 存在即返回, 空则由函数去产生
        optionalUser.orElseGet(() -> anoymos());
        // 存在即返回, 否则抛出异常
        optionalUser.orElseThrow(RuntimeException::new);

        // 存在才去做相应的处理
        optionalUser.ifPresent(u -> System.out.println(u.getName()));

        // map 可以对 Optional 中的对象执行某种操作, 且会返回一个 Optional 对象
        optionalUser.map(u -> u.getName()).orElse("anymos");

        // map 是可以无限级联操作的
        optionalUser.map(u -> u.getName()).map(name -> name.length()).orElse(0);
    }
}

5.try catch 处理异常

  1. 使用异常不要只是返回码,应该返回更加详细的内容
  2. 主动捕获检查性异常,并对异常详细打印日志或者短信
  3. 保持代码整洁,一个方法中不要有多个try catch 或者嵌套try catch
  4. 捕获更加具体的异常,而不是通用的Exception
  5. 合理的设计自定义异常类
/**
 * <h1>Java 异常处理</h1>
 * */
@SuppressWarnings("all")  //屏蔽编译器警告
public class ExceptionProcess {

    private static class User {}

    /**
     * <h2>Java 异常本质 -- 抛出异常</h2>
     * */
    private void throwException() {
        User user = null;
        // 对user进行业务处理
        if (null == user) {
            // 抛出异常
            throw new NullPointerException();
        }
    }

    /**
     * <h2>不能捕获空指针异常</h2>
     * */
    private void canNotCatchNpeException() {
        try {
            throwException(); //抛出异常
        } catch (ClassCastException cce) { // 类型转换异常 ClassCastException
            System.out.println(cce.getMessage()); // 打印异常类信息
            System.out.println(cce.getClass().getName()); // 打印异常类名称
        }
    }

    /**
     * <h2>能够捕获空指针异常</h2>
     * */
    private void canCatchNpeException() {
        try {
            throwException(); //抛出异常
        } catch (ClassCastException cce) { // 类型转换异常 ClassCastException
            System.out.println(cce.getMessage()); // 打印异常类信息
            System.out.println(cce.getClass().getName()); // 打印异常类名称
        } catch (NullPointerException npe) { // 空指针异常 NullPointerException
            System.out.println(npe.getMessage());
            System.out.println(npe.getClass().getName());
        }
    }

    public static void main(String[] args) {
        ExceptionProcess process = new ExceptionProcess();
        // 能够捕获空指针异常
        process.canCatchNpeException();
        // 不能够捕获空指针异常
        process.canNotCatchNpeException();
    }
}

结果如下:
在这里插入图片描述
6.编码中常见的异常(并发修改,类型转换,枚举查找):

  1. 可迭代对象在遍历的同时做修改,则会报并发修改异常
  2. 类型转换不符合java的继承关系,则会报类型转换异常
  3. 枚举在查找时,如果枚举值不存在,不会返回空,而是直接抛出异常

枚举类:

package com.imooc.java.escape;

/**
 * <h1>员工类型枚举类</h1>
 * */
public enum StaffType {

    RD,
    QA,
    PM,
    OP;
}

package com.imooc.java.escape;

import com.google.common.base.Enums;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * <h1>编码中的常见的异常</h1>
 * */
@SuppressWarnings("all")
public class GeneralException {

    /**
     * 定义user类型
     */
    public static class User {

        private String name;

        /**
         * 构造函数
         */
        public User() {}

        public User(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

    /**
     * 定义继承User类的Manager类
     */
    public static class Manager extends User {}

    /**
     * 定义继承User类的Worker类
     */
    public static class Worker extends User {}

    /**
     * 定义typeIndex常量
     */
    private static final Map<String, StaffType> typeIndex = new HashMap<>(
            StaffType.values().length
    );

    /**
     * 静态代码块,对枚举进行一次遍历,并把遍历结果放在常量typeIndex上
     */
    static {
        for (StaffType value : StaffType.values()) {
            typeIndex.put(value.name(), value);
        }
    }

    /**
     * 并发修改异常
     * @param users
     */
    private static void concurrentModificationException(ArrayList<User> users) {

        // 直接使用 for 循环会触发并发修改异常。在遍历的同时删除对象会触发异常
//        for (User user : users) {
//            if (user.getName().equals("imooc")) {
//                users.remove(user);
//            }
//        }

        // 使用迭代器则没有问题。使用迭代器删除对象不会触发异常
        Iterator<User> iter = users.iterator();
        while (iter.hasNext()) {
            User user = iter.next();
            if (user.getName().equals("imooc")) {
                iter.remove();
            }
        }
    }

    /**
     * 根据类型值获取枚举类对象
     * @param type
     * @return
     */
    private static StaffType enumFind(String type) {

        //根据类型值获取枚举类对象
//        return StaffType.valueOf(type);

        // 1. 最普通、最简单的实现
//        try {
//            return StaffType.valueOf(type);
//        } catch (IllegalArgumentException ex) { // IllegalArgumentException参数异常
//            return null;
//        }

        // 2. 改进的实现, 但是效率不高
//        for (StaffType value : StaffType.values()) {
//            if (value.name().equals(type)) {
//                return value;
//            }
//        }
//        return null;

        // 3. 静态 Map 索引, 只有一次循环枚举的过程
//        return typeIndex.get(type);

        // 4. 使用 Google Guava Enums, 需要相关的依赖
        return Enums.getIfPresent(StaffType.class, type).orNull();
    }

    public static void main(String[] args) {

        // 1. 并发修改异常
//        ArrayList<User> users = new ArrayList<User>(
//                Arrays.asList(new User("qinyi"), new User("imooc"))
//        );
//        concurrentModificationException(users);

        // 2. 类型转换异常
//        User user1 = new Manager();
//        User user2 = new Worker();

//        Manager m1 = (Manager) user1; //不会产生类型转换异常
//        Manager m2 = (Manager) user2; //会产生类型转换异常,因为user2是Worker类,Worker类与Manager类无联系

//        System.out.println(user2.getClass().getName()); //查看user2对象的类名称
//        System.out.println(user2 instanceof Manager); //instanceof确定user2是否是Manager类

        // 3. 枚举查找异常
        System.out.println(enumFind("RD"));
        System.out.println(enumFind("abc"));
    }
}

7.解决使用try finally的资源没有关闭隐患
资源释放:打开了资源,使用完之后手动释放(关闭)
资源泄露:打开了资源,使用之后由于某种原因忘记或出现异常等等没有手动释放
try finally的问题与改进方案:

  • 对单个资源的操作基本不会有问题
  • 当同时操作多个资源时,代码沉余,且存在资源泄露的风险
  • try-with-resources不仅比try-finally方便,而且不容易出错,可以用在单个资源,多个资源中
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.Buffer;

/**
 * <h1>解决使用 try finally 的资源泄露隐患</h1>
 * */
public class Main {

    /**
     * <h2>传统的方式实现对资源的关闭</h2>
     * */
    private String traditionalTryCatch() throws IOException {

        // 1. 单一资源的关闭
//        String line = null;
        // 输出流
//        BufferedReader br = new BufferedReader(new FileReader(""));
//        try {
              // 读取一行数据
//            line = br.readLine();
//        } finally {
//            br.close();
//        }
//        return line;

        // 2. 多个资源的关闭
        // 第一个资源,输入流
        InputStream in = new FileInputStream("");
        try {
            // 第二个资源,输出流
            OutputStream out = new FileOutputStream("");
            try {
                byte[] buf = new byte[100];
                int n;
                // 读取文件
                while ((n = in.read(buf)) >= 0)
                    // 输出文件
                    out.write(buf, 0, n);
            } finally {
                out.close();
            }
        } finally {
            in.close();
        }

        return null;
    }

    /**
     * <h2>java7 引入的 try with resources 实现自动的资源关闭</h2>
     * */
    private String newTryWithResources() throws IOException {

        // 1. 单个资源的使用与关闭
//        try (BufferedReader br = new BufferedReader(new FileReader(""))) {
//            return br.readLine();
//        }

        // 2. 多个资源的使用与关闭
        try (FileInputStream in = new FileInputStream("");
             FileOutputStream out = new FileOutputStream("")
        ) {
            byte[] buffer = new byte[100];
            int n = 0;
            while ((n = in.read(buffer)) != -1) {
                out.write(buffer, 0, n);
            }
        }
        return null;
    }

    public static void main(String[] args) throws MyException {

//        AutoClose autoClose = new AutoClose();
//        try {
//            autoClose.work();
//        } finally {
//            autoClose.close();
//        }

        try (AutoClose autoClose = new AutoClose()) {
            autoClose.work();
        }
    }
}

/**
 * 定义AutoClose类
 */
public class AutoClose implements AutoCloseable {

    @Override
    public void close() {
        System.out.println(">>> close()");
        throw new RuntimeException("Exception in close()");
    }

    public void work() throws MyException {
        System.out.println(">>> work()");
        throw new MyException("Exception in work()");
    }
}

/**
 * 自定义异常 MyException
 */
public class MyException extends Exception {

    public MyException() {
        super();
    }

    public MyException(String message) {
        super(message);
    }
}

二,java计算,集合,接口

1.Bigdecimal 用于精确计算的类:


import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * <h1>数值计算</h1>
 */
@SuppressWarnings("all")
public class NumberAndTime {

    /**
     * <h2> scale 需要与小数位匹配 </h2>
     */
    private static void scaleProblem() {
        BigDecimal decimal = new BigDecimal("12.222");
        // 给decimal设置精度为2,因为12.222精度是3,设置为2会丢失精度,下面这样写会报错
        BigDecimal result = decimal.setScale(2);
        // 给decimal设置精度为12,因为12.222精度是3,设置为12不会丢失精度,下面这样不会报错,不够后面会用0补
        BigDecimal result = decimal.setScale(12);
        System.out.println(result);
        // 给decimal设置精度为2,设置BigDecimal.ROUND_HALF_UP舍入方式,四舍五入,不会报错
        BigDecimal result = decimal.setScale(2, BigDecimal.ROUND_HALF_UP);
        System.out.println(result);
    }

    /**
     * <h2> BigDecimal 做除法时出现除不尽的情况 </h2>
     */
    private static void divideProblem() {
        // 30除以7,因为30除不尽7,会报错
        System.out.println(new BigDecimal(30).divide(new BigDecimal(7)));
        // 30除以7,虽然30除不尽7,但是进行了四舍五入,保留2两位小数,不会报错
        System.out.println(
                new BigDecimal(30).divide(new BigDecimal(7), 2, BigDecimal.ROUND_HALF_UP)
        );
    }

    /**
     * <h2> 精度问题导致比较结果和预期的不一致 </h2>
     */
    private static void equalProblem() {
        BigDecimal bd1 = new BigDecimal("0");
        BigDecimal bd2 = new BigDecimal("0.0");
        // equals会比较精度是否一致,再判断数字
        System.out.println(bd1.equals(bd2));  // false
        // compareTo只判断值 大于返回正数,小于返回负数,等于返回0
        System.out.println(bd1.compareTo(bd2) == 0); // true
    }

    public static void main(String[] args) throws Exception {
//        scaleProblem();
//        divideProblem();
//        equalProblem();

    }
}

2.SimpleDateFormat 注意点:


import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * <h1>时间计算</h1>
 */
@SuppressWarnings("all")
public class NumberAndTime {
    /**
     * <h2>
     *     SimpleDateFormat 可以解析大于/等于它定义的时间精度
     *     但是不能解析小于它定义的时间精度
     * </h2>
     */
    private static void formatPrecision() throws Exception {
        // 定义时间SimpleDateFormat
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String time_x = "2020-03-01 10:00:00";
        String time = "2020-03";
        // 可以解析大于/等于它定义的时间精度,把string转换为时间格式
        System.out.println(sdf.parse(time_x));  // Sun Mar 01 0:00:00 CST2020
        // 不能解析小于它定义的时间精度,会报错
        System.out.println(sdf.parse(time)); // 报错
    }

    /**
     * <h2> SimplleDateFormat 存在线程安全问题 </h2>
     */
    private static void threadSafety() {
        // 定义时间SimpleDateFormat
        SimpleDateFormat sdf = new SimpleDateFormat(
                "yyyy-MM-dd HH:mm:ss");
        // 定义线程池
        ThreadPoolExecutor threadPoolExecutor =
                new ThreadPoolExecutor(
                        10, 100, 1, TimeUnit.MINUTES,
                        new LinkedBlockingDeque<>(1000)
                );
        while (true) {
            threadPoolExecutor.execute(() -> {
                String dateString = "2020-03-01 10:00:00";
                try {
                    // 把string类型转换为时间格式
                    Date parseDate = sdf.parse(dateString);
                    // 再把时间格式转换为string类型
                    String dateString2 = sdf.format(parseDate);
                    // 比较最后和开始的string是否一致
                    System.out.println(dateString.equals(dateString2));
                } catch (ParseException ex) {
                    ex.printStackTrace();
                }
            });
        }
    }

    public static void main(String[] args) throws Exception {
//        formatPrecision();
        threadSafety();
    }
}

3.for循环与迭代器 :
任何实现Iterable接口的对象,都可以使用for-each循环处理


import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

/**
 * <h1>小小 for 循环, 沾上集合出大问题</h1>
 * */
@SuppressWarnings("all")
public class ForeachOptimize {

    private static Collection<Integer> left =
            Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    private static Collection<Integer> right =
            Arrays.asList(1, 2, 3, 4, 5);

    /**
     * <h2>集合迭代经常犯的错误</h2>
     * */
    private static void wrongIterator() {

//        // 传统方式 - 使用索引(没问题)
//        int[] xyz = new int[]{1, 2, 3, 4, 5};
//        for (int i = 0; i != xyz.length; ++i) {
//            System.out.println(xyz[i]);
//        }
//
//        // 传统方式 - 迭代器(没问题)
//        for (Iterator<Integer> i = left.iterator(); i.hasNext(); ) {
//            System.out.println(i.next());
//        }

        // 嵌套迭代容易出现问题(有问题,l.next()会在循环right时被next,会照成left会被提前next,导致报错
//        for (Iterator<Integer> l = left.iterator(); l.hasNext(); ) {
//            for (Iterator<Integer> r = right.iterator(); r.hasNext(); ) {
//                System.out.println(l.next() * r.next());
//            }
//        }

        // 正确的用法, 嵌套迭代(没问题)
//        for (Iterator<Integer> l = left.iterator(); l.hasNext(); ) {
//            Integer tmp = l.next();
//            for (Iterator<Integer> r = right.iterator(); r.hasNext(); ) {
//                System.out.println(tmp * r.next());
//            }
//        }

        // 直接用foreach更简单如下(没问题)
        for (Integer l : left) {
            for (Integer r : right) {
                System.out.println(l * r);
            }
        }
    }

    private static void square(int value) {
        System.out.println(value * value);
    }

    public static void main(String[] args) {

        wrongIterator();

        // Java8 Iterable.forEach vs for-each(没问题)
        for (Integer l : left) {
            square(l);
        }
        left.forEach(l -> square(l));
        left.forEach(ForeachOptimize::square);
    }
}

4,Lombox常用注解:
在这里插入图片描述
注解解析:编译器使用javac对代码进行编译的过程中会先对源代码进行分析,生成一个抽象语法树,然后去调用实现了lombox程序,抽象语法树进行处理,在使用了@Get,@Set注解的时候,会对抽象语法树进行修改,追加get,set方法,然后用修改后的抽象语法树去生成java字节码。

4.1.iPhone属性命名使用@Data会变成iphone,要注意不能一个字母小写,第二个字母大写的命名

import lombok.Data;

/**
 * <h1>Java Object</h1>
 * */
@Data
public class Personal {

    private String iPhone;
    private String name;
    private String userName;
}


import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * <h1>lombok 工具的使用以及需要避免的坑</h1>
 * */
public class Main {

    /**
     * <h1>lombok 第一个坑</h1>
     * */
    private static void singleAlphabetHump() throws Exception {

        ObjectMapper mapper = new ObjectMapper();

        Personal personal = new Personal();
        personal.setIPhone("8.1");

        // {"name":null,"userName":null,"iphone":"8.1"}
//        System.out.println(mapper.writeValueAsString(personal));

        String json = "{\"name\": \"qinyi\"," +
                "\"userName\": \"qinyi-imooc\",\"iphone\":\"8.1\"}";

        Personal personal1 = mapper.readValue(json, Personal.class);
        System.out.println(personal1);
    }
}

4.2 AppleComputer类的id,name属性是继承父类的,这种要使用equals要在子类AppleComputer中加@EqualsAndHashCode(callSuper = true),表示也比较继承分类的id,name。否则在子类中用equals不会比较父类的属性。


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Computer {

    private Integer id;
    private String name;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AppleComputer extends Computer {

    private long price;
    private String color;

    public AppleComputer(Integer id, String name, long price, String color) {

        super(id, name);
        this.price = price;
        this.color = color;
    }
}
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * <h1>lombok 工具的使用以及需要避免的坑</h1>
 * */
public class Main {
    /**
     * <h2>lombok 的第二个坑</h2>
     * */
    private static void equalsAndHashCodeBug() {

        AppleComputer computer1 = new AppleComputer(
                1, "Mac Pro", 1L, "yellow"
        );
        AppleComputer computer2 = new AppleComputer(
                2, "Mac Air", 1L, "yellow"
        );

        System.out.println(computer1.equals(computer2));
    }

    public static void main(String[] args) throws Exception {

//        singleAlphabetHump();

        equalsAndHashCodeBug();
    }
}

5.抽象类和接口:
抽象类:子类的通用特性,就是子类都有的特性,包含了属性和行为。对类本质的抽象,表达的是is a的关系。
接口:定义行为,并不关心谁去实现,不是所有子类都有的行为。对行为的抽象,表达的是like a 的关系。
**抽象类与接口的相同点:**接口中的方法(java8改变了这一语法)和抽象类中的抽象方法都不能有方法体,并且必须在子类中实现。都可以被继承,但是不能被实例化。
抽象类与接口的不同点:使用的语法不同,抽象类用extends,接口用implements。接口中只能定义常量,不能表达对象状态,抽象类可以。接口中的方法必须是public类型的,抽象类没有限制。类可以同时实现多个接口,但是只能继承一个抽象类

三.泛型,反射,编译优化

1. Serializable接口:一个标记接口,不用实现任何方法,标记当前类对象是可以序列化的
序列化:将对象写入到IO流中
反序列化:从Io流中恢复对象
(1)序列化,反序列化例子:

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.io.Serializable;

/**
 * <h1>Java Object</h1>
 * */
@Setter
@Getter
@ToString
public class People implements Serializable {

    private Long id;

    public People() {}

    public People(Long id) {
        this.id = id;
    }
}

/**
 * <h1>序列化和反序列化</h1>
 * */
@SuppressWarnings("all")
public class Main {

    /**
     * <h1>序列化和反序列化 People 对象</h1>
     * */
    private static void testSerializablePeople() throws Exception {

        //1.序列化的步骤
        // 用于存储序列化的文件
        File file = new File("/tmp/people_10.java_");
        People p = new People(10L);
        // 创建一个输出流
        ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream(file)
        );
        // 输出可序列化对象
        oos.writeObject(p);
        // 关闭输出流
        oos.close();

        // 2.反序列化的步骤
        // 创建一个输入流
        ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream(file)
        );
        // 得到反序列化的对象
        Object newPerson = ois.readObject();
        // 关闭输入流
        ois.close();
        System.out.println(newPerson);
    }
    
public static void main(String[] args) throws Exception {
        testSerializablePeople();
    }

注意:

  • 子类实现序列化接口,父类没有实现,如果父类有无参构造方法,子类可以实现序列化,否则会报错
  • 类中存在引用对象,这个引用对象是可序列化的才能实现序列化
  • 同一个对象多次序列化,只会序列化一次,后面的保存序列化的编号,所以如果在第一次序列化后,在改对象属性值再进行序列化,这样还是保存的是第一次序列化数据。

2.泛型:参数化类型,就是将类型由原来的具体的类型参数化
泛型作用:在不创建新类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型
在这里插入图片描述


import java.util.ArrayList;
import java.util.List;

/**
 * <h1>理解泛型</h1>
 * */
@SuppressWarnings("all")
public class Genericity {

    /**
     * <h2>简单使用泛型</h2>
     * */
    private static void easyUse() throws Exception {

        List<String> left = new ArrayList<>();
        List<Integer> right = new ArrayList<>();

//        System.out.println(left.getClass());
//        System.out.println(left.getClass() == right.getClass());

//        if (left instanceof ArrayList<Double>) {}
//        if (left instanceof ArrayList) {
//
//        }
//
//        if (left instanceof ArrayList<?>) {}

        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.getClass().getMethod("add", Object.class).invoke(list, "abcd");
        list.getClass().getMethod("add", Object.class).invoke(list, 1.2);

        for (int i = 0; i != list.size(); ++i) {
            System.out.println(list.get(i));
        }
    }

    /**
     * <h2>泛型是先检查再编译</h2>
     * */
    private static void checkAndCompile() {

        ArrayList<String> list = new ArrayList<>();
        list.add("1234");
//        list.add(123);
    }

    /**
     * <h2>泛型不支持继承</h2>
     * */
    private static void genericityCanNotExtend() {

        // 第一类错误
//        ArrayList<String> first = new ArrayList<Object>();
//
//        ArrayList<Object> list1 = new ArrayList<>();
//        list1.add(new Object());
//        ArrayList<String> list2 = list1;

        // 第二类错误
//        ArrayList<Object> second = new ArrayList<String>();
//
//        ArrayList<String> list1 = new ArrayList<>();
//        list1.add(new String());
//        ArrayList<Object> list2 = list1;
    }

    /**
     * <h2>泛型类型变量不能是基本数据类型</h2>
     * */
    private static void baseTypeCanNotUseGenericity() {

//        List<int> invalid = new ArrayList<>();
    }

    /**
     * <h2>泛型的类型参数只能是类类型, 不能是简单类型</h2>
     * */
    private static <T> void doSomething(T... values) {
        for (T value : values) {
            System.out.println(value);
        }
    }

    public static void main(String[] args) throws Exception {

//        easyUse();

        Integer[] ints1 = new Integer[]{1, 2, 3};
        int[] ints2 = new int[]{1, 2, 3};

        doSomething(ints1);
        System.out.println("----------------");
        doSomething(ints2);
    }
}

3.不是所有的字符串拼接都使用StringBuilder:
(1)循环里面使用“+“去拼接字符串,会造成空间浪费,每次拼接的结果都需要创建新的不可变类;时间浪费,创建的新不可变类需要初始化,产生大量临时对象,影响young gc,full gc。
(2)StringBuffer和StringBuilder的区别:
线程安全:StringBuffer线程安全和StringBuilder不安全
缓冲区:StringBuffer使用缓冲区和StringBuilder不使用缓冲区
性能:StringBuffer性能远大于StringBuilder

四.java线程安全

多线程操作变量过程:
在这里插入图片描述
1.Synchronized关键字:

  • Synchronized方法不会被继承,需要在子类中重新指定
  • Synchronized可以标注在方法声明,方法体中
  • jdk对Synchronized进行了优化,主要表现在偏向锁,轻量级锁,重量级锁
public class MainActive implements Runnable {

    private int value = 0;

    @Override
    public synchronized void run() {

        String name = Thread.currentThread().getName();

        while (true) {

            if (value < 1000) {
                System.out.println(name + " start : " + value);
                value++;
                System.out.println(name + " done : " + value);
            } else {
                break;
            }
        }
    }
}

2.阻塞队列的核心定义:支持两个附加操作的队列

  • 队列空则等
  • 队列满则等

四种处理方法:
在这里插入图片描述
生产者:


import java.util.concurrent.BlockingQueue;

/**
 * <h1>生产者</h1>
 * */
// 实现Runnable接口,让Producer交给一个独立的线程去执行
public class Producer implements Runnable {

    // 定义阻塞队列
    private final BlockingQueue<Integer> blockingQueue;
    private static int element = 0;

    // 构造函数,给阻塞队列初始化
    public Producer(BlockingQueue<Integer> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

//    @Override
//    public void run() {
//
//        while (element < 100) {
//            System.out.println("Produce: " + element);
              // 给阻塞队列添加element,在队列满时不会等待,会返回false,会导致没有消费所有生成的生产者
//            blockingQueue.offer(element++);
//        }
//
//        System.out.println("Produce Done!");
//    }

    @Override
    public void run() {

        try {
            while (element < 100) {
                System.out.println("Produce: " + element);
                blockingQueue.put(element++);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Produce Done!");
    }
}

消费者:

import java.util.concurrent.BlockingQueue;

/**
 * <h1>消费者</h1>
 * */
// 实现Runnable接口,让Producer交给一个独立的线程去执行
public class Consumer implements Runnable {

    // 定义阻塞队列
    private final BlockingQueue<Integer> blockingQueue;

    // 构造函数,给阻塞队列初始化
    public Consumer(BlockingQueue<Integer> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void run() {

        try {
            while (true) {
                // 在阻塞队列中取元素
                int value = blockingQueue.take();
                System.out.println("Consume: " + value);
                if (value >= 99) {
                    break;
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Consumer Done!");
    }
}

阻塞队列的应用:


import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

/**
 * <h1>阻塞队列的应用</h1>
 * */
public class Main {

    public static void main(String[] args) {

        // 3是容量
        BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(
                3, true
        );

        Producer producer = new Producer(blockingQueue);
        Consumer consumer = new Consumer(blockingQueue);

        // 创建线程
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

3.CopyOnWrite 是一种优化策略,是一种延时懒惰策略,是对读写分离思想的实现
优点:并发读不需要加锁,提高了程序的并发读
缺点:内存占用问题(因为会复制,再写),一致性问题(因为读是读旧容器,写是在新容器,只能保证最终一致性,不能保证实时一致性)
适应场景:适合读多写少的场景
CopyOnWrite 对比 Collections.synchronizedList:

  • 它们都可以实现线程安全的集合(列表)
  • CopyOnWrite 的写操作不仅需要加锁,而且内部对数组进行了 copy,所以,写性能比Collections.synchronizedList 要差
  • Collections.synchronizedList 读操作有 synchronized 关键字修饰,而 CopyOnWrite是直接读,所以,读性能CopyOnWrite 更好
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * <h1>使用 CopyOnWrite 并发读写不会抛出异常</h1>
 * */
public class TaskPoolNoProblem {

    private static final List<String> tasks = new CopyOnWriteArrayList<>();

    public static void main(String[] args) throws Exception {

        for (int i = 0; i != 10; ++i) {
            tasks.add("task-" + i);
        }

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    tasks.add("task-x");
                }
            }
        });
        thread.setDaemon(true);
        thread.start();

        Thread.sleep(1000L);

        for (String task : tasks) {
            System.out.println(task);
        }
    }
}

4.jdk线程池的核心思想:
jdk中线程池的核心实现类是ThreadPoolExecutor
ThreadPoolExecutor 怎么同时维护线程以及执行任务:
在这里插入图片描述
线程池的运行状态:
在这里插入图片描述
线程池的状态转换图:
在这里插入图片描述
jdk线程池的错误用法:

  • 固定大小的线程池会导致内存急剧上升
  • jdk提供的线程池不符合需求,自己定义线程池,但是线程池参数很多,容易出错
    在这里插入图片描述

/**
 * <h1>读书的任务</h1>
 * */
public class Reading implements Runnable {

    private int count;
    private String name;

    public Reading(int count, String name) {
        this.count = count;
        this.name = name;
    }

    @Override
    public void run() {

        while (count > 0) {

            System.out.println(Thread.currentThread().getName() + " reading " + name);

            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }

            --count;
        }
    }
}

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class EasyUseThreadPool {

    /**
     * <h1>简单使用线程池</h1>
     * */
    private static void useFixedThreadPool(int threadCount) {

        ExecutorService executor = Executors.newFixedThreadPool(threadCount);

        Runnable runnable01 = new Reading(3, "Java 编程思想");
        Runnable runnable02 = new Reading(2, "Spring 实战");
        Runnable runnable03 = new Reading(3, "SpringBoot 实战");
        Runnable runnable04 = new Reading(1, "MySQL 权威指南");
        Runnable runnable05 = new Reading(2, "SpringCloud 实战");

        executor.execute(runnable01);
        executor.execute(runnable02);
        executor.execute(runnable03);
        executor.execute(runnable04);
        executor.execute(runnable05);

        executor.shutdown();
    }

    /**
     * <h2>自定义线程池</h2>
     * */
    private static void customThreadPool() {

        // 自定义线程池
        ThreadPoolExecutor custom1 = new ThreadPoolExecutor(
                1, 1, 30, TimeUnit.MINUTES,
                new ArrayBlockingQueue<Runnable>(2)
        );
        // 自定义线程池
        ThreadPoolExecutor custom2 = new ThreadPoolExecutor(
                1, 1, 30, TimeUnit.MINUTES,
                new ArrayBlockingQueue<Runnable>(2),
                // 拒绝策略
                new CustomRejectHandler()
        );

        for (int i = 0; i != 5; ++i) {
            custom2.execute(new Reading(3, "Java 编程思想"));
        }

        custom2.shutdown();
    }

    // 自定义拒绝策略
    private static class CustomRejectHandler implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {

            try {
                // executor.getQueue() 拿到阻塞队列
                executor.getQueue().put(r);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {

//        useFixedThreadPool(3);
        customThreadPool();
    }
}

怎么监控线程池的运行状态:
在这里插入图片描述


import java.util.Date;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <h1>自定义线程池工厂, 带有监控功能</h1>
 * */
public class ExecutorsUtil extends ThreadPoolExecutor {

    // 线程池的关闭,统计已经执行的,正在执行的阻塞的的线程池
    @Override
    public void shutdown() {
        System.out.println(
                String.format(this.poolName +
                                "将会被关闭的. 已经执行的: %d," +
                        "正在执行的: %d, 阻塞的: %d",
                        this.getCompletedTaskCount(),
                        this.getActiveCount(),
                        this.getQueue().size()
                )
        );
        super.shutdown();
    }

    // 线程池即将关闭,统计已经执行的,正在执行的阻塞的的线程池
    @Override
    public List<Runnable> shutdownNow() {
        System.out.println(
                String.format(this.poolName +
                                "将会被关闭的. 已经执行的: %d," +
                                "正在执行的: %d, 阻塞的: %d",
                        this.getCompletedTaskCount(),
                        this.getActiveCount(),
                        this.getQueue().size()
                )
        );
        return super.shutdownNow();
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        // 当前线程的开始时间
        startTimes.put(String.valueOf(r.hashCode()), new Date());
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        // 获取开始时间
        Date startDate = startTimes.remove(String.valueOf(r.hashCode()));
        // 结束的时间
        Date finishDate = new Date();
        // 任务执行的时间
        long diff = finishDate.getTime() - startDate.getTime();
        System.out.println(String.format("任务执行的时间: %d", diff));
    }

    // 固定大小线程池方法
    public static ExecutorService newFixedThreadPool(int nThreads, String poolName) {
        return new ExecutorsUtil(
                nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingDeque<>(), poolName
        );
    }

    // 存储每个线程任务开始执行的时间
    private ConcurrentHashMap<String, Date> startTimes;
    // 线程池的名称
    private String poolName;

    // 编写构造方法
    public ExecutorsUtil(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                         TimeUnit unit, BlockingQueue<Runnable> workQueue,
                         String poolName) {
        // 调用父类的构造函数
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit,
                workQueue, new ExecutorsUtil.EventThreadFactory(poolName));
        this.startTimes = new ConcurrentHashMap<>();
        this.poolName = poolName;
    }

    // 自定义线程池工厂
    static class EventThreadFactory implements ThreadFactory {

        // 原子类AtomicInteger,poolNumber存储线程池的编号
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        // 把线程池的线程放在线程池组里
        private final ThreadGroup group;
        // 线程的编号
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        // 用来给线程池起一个名字
        private final String namePrefix;

        EventThreadFactory(String poolName) {
            SecurityManager s = System.getSecurityManager();
           // 线程池组 s.getThreadGroup()获取线程池组;Thread.currentThread().getThreadGroup()获取当前线程的线程池组
            group = (s != null) ? s.getThreadGroup()
                    : Thread.currentThread().getThreadGroup();
            namePrefix = poolName + "-pool-" + poolNumber.getAndIncrement()
                    + "-thread";
        }

        @Override
        public Thread newThread(Runnable r) {
            // 新增线程
            Thread t = new Thread(
                    // group线程池组 r要执行的任务 namePrefix + threadNumber.getAndIncrement()当前线程的名称
                    group, r, namePrefix + threadNumber.getAndIncrement(),
                    // 0 默认值为0,当前线程指定栈的大小
                    0
            );
            // 判断是否是守护方式
            if (t.isDaemon()) {
                // 设置不是守护方式
                t.setDaemon(false);
            }
            // 检验优先级
            if (t.getPriority() != Thread.NORM_PRIORITY) {
                // 设置为一个正常优先级的线程
                t.setPriority(Thread.NORM_PRIORITY);
            }
            return t;
        }
    }
}

import java.util.concurrent.ExecutorService;

/**
 * <h1>可监控的线程池</h1>
 * */
public class Main {

    public static void main(String[] args) {

        ExecutorService executorService = ExecutorsUtil.newFixedThreadPool(
                10, "imooc-qinyi-"
        );

        Runnable runnable01 = new Reading(3, "Java 编程思想");
        Runnable runnable02 = new Reading(2, "Spring 实战");
        Runnable runnable03 = new Reading(3, "SpringBoot 实战");
        Runnable runnable04 = new Reading(1, "MySQL 权威指南");
        Runnable runnable05 = new Reading(2, "SpringCloud 实战");

        executorService.execute(runnable01);
        executorService.execute(runnable02);
        executorService.execute(runnable03);
        executorService.execute(runnable04);
        executorService.execute(runnable05);

        executorService.shutdown();
    }
}

4.1ThreadLocal:每个线程需要自己独立的实例且该实例需要在多个方法中被使用
ThreadLocal误区:不支持继承,遇到线程池,如果不及时清理现场,会造成数据混乱
ThreadLocal的实现方式,维护线程与实例的映射:
方式一:
在这里插入图片描述
方式二:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/242347.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

有监督学习、无监督学习、半监督学习和强化学习

有监督学习 训练数据有标签 无监督学习 数据是没有标签的 聚类的思想&#xff1a;通过计算空间中的距离来判断是否属于同一类 强化学习 和环境交互&#xff0c;从环境中学习 三者对比 半监督学习 少量有标注&#xff0c;大量无标注 三个假设 1.连续性/平滑性假设:相…

关于对RF射频方面性能要求各有不同

1.1 射频天线性能 对于一个射频设备每个公司对其合格指标要求都不一&#xff0c;有些公司注重于阻抗及电压驻波&#xff0c;有些公司注重与回波损耗及阻抗、有些只关注电压驻波。 1.2 射频的目的 其实射频天线的目的就是在不把无用的杂散放大超标准的前提下&#xff0c;把有用…

Nature 确认:大语言模型只是没有感情的「学人精」

DeepMind、EleutherAI 科学家提出&#xff0c;大模型只是在角色扮演。 ChatGPT 爆火后&#xff0c;大语言模型一跃而至&#xff0c;成为了行业与资本的宠儿。而在人们或是猎奇、或是探究地一次次对话中&#xff0c;大语言模型所表现出的过度拟人化也引起了越来越多的关注。 其实…

使用FluentAvalonia组件库快速完成Avalonia前端开发

前言 工欲善其事必先利其器,前面我们花了几篇文章介绍了Avalonia框架以及如何在Avalonia框架下面使用PrismAvalonia完成MVV模式的开发。今天我们将介绍一款重磅级的Avalonia前端组件库,里面封装了我们开发中常用的组件,这样就不用我们自己再写组件了。专注业务功能开发,提…

SpringBoot类文件具有错误的版本 61.0, 应为 55.0

4.SpringBoot入门案例 每一个从你生命中路过的人&#xff0c;都在教你成长&#xff0c;感恩的心&#xff0c;感谢一程陪伴&#xff0c;感谢曾经有你spring learn https://spring.io/projects/spring-boot#learn Getting Started Developing Your First Spring Boot Applicati…

Tcon基础知识

1、TCON&#xff0c;就是 Timing Controller 的缩写。从主芯片输出的要在 TFT 显示屏上显示的数据&#xff0c;在经过 TCON 模块后可以变换生成 Panel 可以直接利用的 DATA 信号和驱动器&#xff08;包括 source driver 和 gate driver&#xff09;的控制信号。 TV 市场上 TCO…

超声波测距HC-SR04模块的简单应用

文章目录 一、HC-SR04HC-SR04是什么&#xff1f;HC-SR04测距的原理 二、使用步骤1.硬件最远探测距离调节硬件连接 2.软件1.初始化配置代码如下&#xff08;示例&#xff09;&#xff1a;引脚初始化定时器初始化 2.引脚输入输出配置代码如下&#xff08;示例&#xff09;&#x…

OfficeWeb365 SaveDraw 文件上传漏洞复现

0x01 产品简介 OfficeWeb365 是专注于 Office 文档在线预览及PDF文档在线预览云服务,包括 Microsoft Word 文档在线预览、Excel 表格在线预览、Powerpoint 演示文档在线预览,WPS 文字处理、WPS 表格、WPS 演示及 Adobe PDF 文档在线预览。 0x02 漏洞概述 OfficeWeb365 Sav…

基于Leaflet的Webgis经纬网格生成实践

目录 前言 一、Leaflet.Graticule 1、参数说明 二、集成使用 1、新建网页模板 2、初始化地图对象 3、运行效果 三、源码调用分析 1、参数注入 2、经纬网构建 总结 前言 众所周知&#xff0c;在地球仪上或地图上&#xff0c;经线和纬线相互交织&#xff0c;就构成经纬…

揭示 ETL 系统架构中的 OLAP、OLTP 和 HTAP

探索 ETL 系统设计需要了解 OLAP、OLTP 和不断发展的 HTAP。让我们试图剖析这些范式的复杂性。 1. OLAP&#xff08;联机分析处理&#xff09;&#xff1a; OLAP 是商业智能的中流砥柱&#xff0c;通过 OLAP 立方体进行多维数据分析。这些立方体封装了预先聚合、预先计算的数据…

31、应急响应——Windows

文章目录 一、账户排查1.1 登录服务器的途径1.2 弱口令1.3 可疑账号 二、网络排查三、进程排查四、注册表排查五、内存分析 一、账户排查 1.1 登录服务器的途径 3389smb 445httpftp数据库中间件 1.2 弱口令 弱口令途径&#xff1a;3389、smb 445、http、ftp、数据库、中间件…

双指针的运用——双数之和II和三数之和

两数之和 https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/description/ 我们考虑这个排序过的数组&#xff0c;首先一个指针在最左&#xff0c;一个在最右。如果这两个数字比目标数字来的要小&#xff0c;那么如果我们左边指针移动了&#xff0c;移动后一定变…

【LeetCode刷题】-- 163.缺失的区间

163.缺失的区间 class Solution {public List<List<Integer>> findMissingRanges(int[] nums, int lower, int upper) {List<List<Integer>> res new ArrayList<>();for(int num : nums){if(lower < num){res.add(Arrays.asList(lower,num -…

计算机网络:数据链路层(介质访问控制)

欢迎关注大家关注我的公众号&#xff1a;浩泽学编程&#xff1b;联系我&#xff0c;一起交流学习&#xff0c;也可以解答你的迷茫。 目录 前言 一、静态划分信道&#xff08;信道划分介质访问控制&#xff09; 1、频分多路复用FDM 2、时分多路复用TDM 3、波分多路复用WDM …

基于单片机智能自动浇花系统设计

**单片机设计介绍&#xff0c;基于单片机智能自动浇花系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的智能自动浇花系统是一种可以自动感知周围环境&#xff0c;并执行相应动作的系统。通过使用传感器检测土…

微服务--07--Sentienl中使用的限流算法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Sentienl中使用的限流算法1、计数器固定窗口算法2、计数器滑动窗口算法----&#xff08;默认&#xff09;3、漏桶算法----&#xff08;排队等待&#xff09;4、令牌…

如何想成为嵌入式工程师?(这些东西您必须知道)

嵌入式的发展怎么样&#xff1f; 嵌入式系统领域一直在迅速发展&#xff0c;伴随着物联网、智能设备、汽车电子、医疗设备等应用的不断增加&#xff0c;对嵌入式技术的需求也在不断扩大。因此&#xff0c;嵌入式领域仍然是一个充满机会的领域&#xff0c;为专业人士提供…

AGI 营销价值持续释放,Whale 帷幄近期荣誉时刻有哪些?

临近年底&#xff0c;各类奖项、活动、盘点层出不穷&#xff0c;是营销界最活跃也是最丰收的时刻。近期&#xff0c;「Whale 帷幄」凭借着对数字化营销销售场景的独到洞察和扎实的落地应用能力&#xff0c;不仅为客户带来了业绩提升&#xff0c;也斩获了多项权威奖项&#xff0…

AI全栈大模型工程师(二十六)如何选择 GPU 和云服务厂商

&#x1f4a1; 这节课会带给你 如何选择 GPU 和云服务厂商&#xff0c;追求最高性价比 如何部署自己 fine-tune 的模型&#xff0c;向业务提供高可用推理服务 如何控制内容安全&#xff0c;做好算法备案&#xff0c;确保合规 开始上课&#xff01; 硬件选型 当我们为模型训练及…

数字化赋能实体经济,凌雄科技发挥DaaS模式提质增效价值

11月中旬&#xff0c;市场监管总局发布了2023年前三季度经营主体数据。其中&#xff0c;前三季度全国新设民营企业总计706.5万户&#xff0c;截至9月底&#xff0c;全国登记在册的民营企业数量超过5200万户&#xff0c;在企业总量中占比高达92.3%。如何帮助民营企业实现高质量发…