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

news2024/12/24 8:47:23

一. 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的实现方式,维护线程与实例的映射:
方式一:
在这里插入图片描述
方式二:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++11】右值引用与移动语义

一.左值与右值 左值&#xff1a;可以取地址的表示数据的表达式&#xff0c;左值可以出现在赋值符号左边 右值&#xff1a;不能取地址的表示数据的表达式&#xff0c;右值不能出现在赋值符号左边 int fun() {return 0; } int main() {int a 0;//a->左值const int b 1;//b-&…

Java 入门第四篇 集合

Java 入门第四篇 集合 一&#xff0c;什么是集合 在Java中&#xff0c;集合&#xff08;Collection&#xff09;是一种用于存储和操作一组对象的容器类。它提供了一系列的方法和功能&#xff0c;用于方便地管理和操作对象的集合。集合框架是Java中非常重要和常用的一部分&…

C++进阶篇9---类型转换

C语言中的类型转换 在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型不匹配&#xff0c;或者返回值类型与 接收返回值类型不一致时&#xff0c;就需要发生类型转化&#xff0c;C语言中总共有两种形式的类型转换&#xff1a;隐式类型 转换和…

参数学习——糖果问题(人工智能期末复习)

之前看了好久都不知道这题咋写&#xff0c;后来看了这篇机器智能-高频问题&#xff1a;糖果问题&#xff0c;大概看明白了&#xff0c;其实主要围绕着这两个公式 光看公式也看不懂&#xff0c;还是要结合题目来 己知有草莓味和酸橙味两种类型的糖果&#xff0c;分别放入5种不同…

深入了解—C++11特性

目录 一、 C11简介 二、初始化列表 2.1 C98中{}的初始化问题 2.2 内置类型的列表初始化 2.3 自定义类型的列表初始化 2.3.1. 标准库支持单个对象的列表初始化 2.3.2. 多个对象的列表初始化 三、变量类型推导 3.1 为什么需要类型推导 3.2 decltype类型推导 3.2.1. 推…

SIM初始化流程

ATR ATR(Answer To Reset)&#xff1a;复位应答信号&#xff0c;有SIM卡传输给终端&#xff0c;包括SIM卡自身的一些信息&#xff0c;比如支持的传输速率&#xff0c;传输模式等。 SIM卡的ATR代表"Answer to Reset"&#xff0c;即复位响应。当SIM卡被插入设备中时…

Java的String类常用方法 |StringBuilder和StringBuffer

文章目录 String类常用方法字符串查找转化字符串替换字符串拆分字符串截取其他操作方法 字符串的不可变性StringBuilder和StringBufferStringBuilder的介绍面试题 String类常用方法 字符串查找 方法功能char charAt(int index)返回index位置上字符&#xff0c;如果index为负数…

深度解读分布式事务Seata入门到实践 -尚马教育

目录 一、事务的回顾1、什么是事务2、事务的特性3、事务的隔离级别4、事务的分类 二、分布式事务1、什么是分布式事务2、分布式事务产生的背景3、分布式事务产生的场景4、分布式事务理论4.1 CAP理论4.2 Base理论 5、分布式事务的解决方案 三、强一致性介绍3.1 基本理解3.2 DTP模…

arm-none-eabi-gcc not find

解决办法&#xff1a;安装&#xff1a;gcc-arm-none-eabi sudo apt install gcc-arm-none-eabi; 如果上边解决问题了就不用管了&#xff0c;如果解决不了&#xff0c;加上下面这句试试运气&#xff1a; $ sudo apt-get install lsb-core看吧方正我是运气还不错&#xff0c;感…

环境搭建及源码运行_java环境搭建_mysql安装

1、介绍 MySQL是一个关系型数据库管理系统&#xff0c;由瑞典MySQL AB 公司开发&#xff0c;属于 Oracle旗下产品。MySQL是最流行的关系型数据库管理系统之一 1、源码中涉及到的表&#xff1a;mysql 表&#xff1a;订单、意见反馈、用户基础信息、商品、购物车等表 2、订单属于…

Qt/C++视频监控安卓版/多通道显示视频画面/录像存储/视频播放安卓版/ffmpeg安卓

一、前言 随着监控行业的发展&#xff0c;越来越多的用户场景是需要在手机上查看监控&#xff0c;而之前主要的监控系统都是在PC端&#xff0c;毕竟PC端屏幕大&#xff0c;能够看到的画面多&#xff0c;解码性能也强劲。早期的手机估计性能弱鸡&#xff0c;而现在的手机性能不…

自动反冲洗过滤器直通式工作原理介绍和附反冲洗原理图动画讲解

​ 1&#xff1a;自动反冲洗过滤器直通式设备介绍 自动反冲洗过滤器是一种先进、高效且易操作的精密设备&#xff0c;广泛应用于冶金、化工、石油、造纸、医药、食品、采矿、电力、城市给水等领域。 在过滤过程中&#xff0c;待过滤的水由进水口进入过滤器机体&#xff0c;流…

Java版工程行业管理系统源码-专业的工程管理软件-提供一站式服务—鸿鹄工程管理系统

鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 项目背景 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部工程管理的提…

Python(七)操作JSON

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

基于Java SSM框架实现个性化影片推荐系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现个性化影片推荐系统演示 摘要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;个性化影片推荐系统当然也不能排除在外。个性化影片推荐系统是以实际运用…

【源码解析】flink sql执行源码概述:flink sql执行过程中有哪些阶段,这些阶段的源码大概位置在哪里

文章目录 一. sql执行流程源码分析1. Sql语句解析成语法树阶段&#xff08;SQL - > SqlNode&#xff09;2. SqlNode 验证&#xff08;SqlNode – >Operation&#xff09;3. 语义分析&#xff08;Operation - > RelNode&#xff09;4. 优化阶段&#xff08;RelNode - &…

uni-app 一些实用的页面模板

时间倒计时 <!-- 时间倒计时 --> <template><view class"container"><view class"flex-row time-box"><view class"time-item">{{ laveTimeList[0] }}</view><text>天</text><view class&qu…

喜讯 | 同立海源生物入选2023年国创中心细胞疗法“揭榜挂帅”技术攻关项目

近日&#xff0c;2023年国家生物药技术创新中心细胞疗法“揭榜挂帅”技术攻关拟立项目名单公示&#xff0c;北京同立海源生物科技有限公司&#xff08;简称“同立海源生物”&#xff09;参评的 “细胞分选激活磁珠研发项目” 凭借公司多年在细胞分选磁珠领域的技术沉淀和创新性…

【期末考复习向】transformer的运作机制

1.transformer的encoder运作 transformer的encoder部分包括了输入和处理2大部分。首先是输入部分inputs&#xff0c;这里初始的inputs是采用独热向量进行表示的&#xff0c;随后经过word2vec等操作把独热向量&#xff08;采用独热向量的好处就是可向量是正交的&#xff0c;可以…

K8S(五)—命名空间与资源配额

目录 命名空间(Namespace)命令计算资源配额创建命名空间绑定一个ResourceQuota资源将命名空间和资源限制对象进行绑定尝试创建第二个 Pod查看ResourceQuota 绑定第二个ResourceQuota为命名空间配置默认的 CPU 、memory请求和限制&#xff08;1&#xff09;Pod 中所有容器都没有…