log

目录

Java Base

public class MyPublicClass {
    public int publicField;       // 公共字段,任何类都可以访问
    protected int protectedField; // 受保护字段,同一包或子类可以访问
    private int privateField;     // 私有字段,只有本类可以访问
    void defaultField() {}        // 默认(包级私有),同一包内的类可以访问
}

用final修饰class可以阻止被继承:
用final修饰method可以阻止被子类覆写:
用final修饰field可以阻止被重新赋值:
用final修饰局部变量可以阻止被重新赋值:

一个.java文件只能包含一个public类,但可以包含多个非public类。如果有public类,文件名必须和public类的名字相同。


整形类型:
byte:  1byte
short: 2bytes
int:   4bytes
long:  8bytes

float: 4bytes2
double:8bytes


Integer:
Integer可以为null, int不能
用于数据库字段,或API参数可返回。因为它们可能为null
数值比较用:a.equals(b), 不能用a==b


自动装箱(Autoboxing)
自动拆箱(unboxing)
Integer a = 10; 相当于 Integer.valueOf(10)
int b = a;      相当于 b = a.intValue()


int[] numbers = {1,2,3,4,5}
String[] names = {}

基础数据类型boolean byte short int long float double char
基础数据类型在栈上,类数据类型在堆上
----------
String str = "hi," + "lao" + "zhang";
str1.equals(str2)
格式化
String str = String.format("my name is %s, my ages is %d", "wang", 20);

String str = "hello";
str = "world";        堆上新增加一个区域保存world
原来的字符串"hello"还在堆上,只是我们无法通过变量str访问它而已。
因此,字符串的不可变是指字符串内容不可变
String str = null;    不指向堆上的区域

String:        是不可变类型,函数传值是一个副本,下面两个是可变类型
StringBuffer:  字符串拼接时synchronized保障线程安全
StringBuilder: 和StringBuffer一样继承自AbstractStringBuilder


函数只是传值
String str = "laowang";
change(str)
System.out.println(str); 输出还是laowang

public static void change(str) {
  str = "xiaowang";
}

StringBuffer sb = new StringBuffer("xiaowang");
change(sb)
System.out.println(sb);

因为不创建副本,StringBuffer性能优于String, 但不如StringBuilder
StringBuffer 线程安全
StringBuilder线程不安全


String类是final类型,不可继承
public final class String {
}

String不可变的好处如下。
·只有当字符串是不可变的,字符串常量池才能实现,字符串池的实现可以在运行时节约很多堆空间,因为不同的字符串变量都指向池中的同一个字符串;
可以避免一些安全漏洞,比如在 Socket 编程中,主机名和端口都是以字符串的形式传入,因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变字符串指向的对象的值,造成安全漏洞;
多线程安全,因为字符串是不可变的,所以同一个字符串实例可以被多个线程共享,保证了多线程的安全性,
适合做缓存的 key,因为字符串是不可变的,所以在它创建的时候哈希值就被缓存了,不需要重新计算速度更快,所以字符串很适合作缓存的中的 key。


int[] intarr = new int[10];
int[] intarr1 = {1, 2, 3, 4, 5};
int len = intarr.length();  数组长度
是不可变数组,要管理可变数组使用ArrayList

java泛型不支持基础类型int, 需要用对象类型Integer
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);

Java中主要的日期类型包括java.util.Date、
java.time包下的新API(如LocalDate、LocalTime、LocalDateTime、Instant等)
以及java.util.Calendar类,分别适用于不同场景的日期时间处理需求。

----------------
异或运算
n = 0 ^ 0; // 0
n = 0 ^ 1; // 1
n = 1 ^ 0; // 1
n = 1 ^ 1; // 0

// 多行字符串
String s = """
           SELECT * FROM
             users
           WHERE id > 100
           ORDER BY name DESC
           """;

--------------
double d = 3.1415926;
System.out.printf("%.2f\n", d); // 显示两位小数3.14
System.out.printf("%.4f\n", d); // 显示4位小数3.1416

Scanner scanner = new Scanner(System.in);
System.out.print("Input your name: ");
String name = scanner.nextLine();
System.out.print("Input your age: ");
int age = scanner.nextInt();          // 读取一行输入并获取整数

数组和ArrayList都可以用枚举
int[] ns = { 1, 4, 9, 16, 25 };
for (int n : ns) { 
    System.out.println(n);
}

int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
Arrays.sort(ns);
System.out.println(Arrays.toString(ns));

---------------------
class Student extends Person {}
class Action inplements ActionInterface {}


final class 禁止继承
sealed指定三个类可继承
public sealed class Shape permits Rect, Circle, Triangle {
}

强制转型
Object obj = "hello";
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.toUpperCase());
}
也可以这样写:
if (obj instanceof String s) {
    System.out.println(s.toUpperCase());
}

抽象类
class Person {
    public abstract void run();
}

如果一个抽象类没有字段,所有方法全部都是抽象方法:
就可以把该抽象类改写为接口:interface。
interface Person {
    void run();
    String getName();
}

类只能继承自一个抽象类
类可以继承自多个接口

一个interface可以继承自另一个interface。interface继承自interface使用extends
在接口中,可以定义default方法
interface Person {
    String getName();
    default void run() {
        System.out.println(getName() + " run");
    }
}

interface是可以有静态字段的,并且静态字段必须为final类型:
public interface Person {
    public static final int MALE = 1;
    public static final int FEMALE = 2;
}

匿名内部类,实现了Runable接口并实例化
class Outer {
    private String name;

    Outer(String name) {
        this.name = name;
    }

    void asyncHello() {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello, " + Outer.this.name);
            }
        };
        new Thread(r).start();
    }
}

-------------------
cp 就是classpath
java -classpath .;C:\work\project1\bin;C:\shared abc.xyz.Hello
java -cp .;C:\work\project1\bin;C:\shared abc.xyz.Hello
在linux中,路径用:分开


自带“依赖关系”的class容器就是模块module
src/module-info.java 表明本模块依赖
module hello.world {
	requires java.base; // 可不写,任何模块都会自动引入java.base
	requires java.xml;
}

从Java 9开始,原有的Java标准库已经由一个单一巨大的rt.jar分拆成了几十个模块,这些模块以.jmod扩展名标识,可以在$JAVA_HOME/jmods目录下找到它们:
    java.base.jmod
    java.compiler.jmod
    java.datatransfer.jmod
    java.desktop.jmod

---------------
枚举和枚举类
enum Weekday {
    SUN, MON, TUE, WED, THU, FRI, SAT;
}

public class Weekday {
    public static final int SUN = 0;
    public static final int MON = 1;
    public static final int TUE = 2;
    public static final int WED = 3;
    public static final int THU = 4;
    public static final int FRI = 5;
    public static final int SAT = 6;
}


java14开始:
记录类record:
定义class时使用final,无法派生子类;
每个字段使用final,保证创建实例后无法修改任何字段。

record Point(int x, int y) {}
相当于下面代码:
final class Point extends Record {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int x() {
        return this.x;
    }

    public int y() {
        return this.y;
    }

    public String toString() {
        return String.format("Point[x=%s, y=%s]", x, y);
    }

    public boolean equals(Object o) {
        ...
    }
    public int hashCode() {
        ...
    }
}
-------------
Java规定:
1.必须捕获的异常,包括Exception及其子类,但不包括RuntimeException及其子类,这种类型的异常称为Checked Exception。
2.不需要捕获的异常,包括Error及其子类,RuntimeException及其子类。

下面代码编译时会抛出异常:
public class Main {
    public static void main(String[] args) {
        byte[] bs = toGBK("中文");
        System.out.println(Arrays.toString(bs));
    }

    static byte[] toGBK(String s) {
        return s.getBytes("GBK");
    }
}
如下编写可以通过编译:
    static byte[] toGBK(String s) throws UnsupportedEncodingException {
        return s.getBytes("GBK");
    }

可以在main中捕获异常:
public class Main {
    public static void main(String[] args) {
        try {
            byte[] bs = toGBK("中文");
            System.out.println(Arrays.toString(bs));
        } catch (UnsupportedEncodingException e) {
            System.out.println(e);
        }
    }

    static byte[] toGBK(String s) throws UnsupportedEncodingException {
        return s.getBytes("GBK");
    }
}
让main抛出异常
    public static void main(String[] args) throws Exception {
        byte[] bs = toGBK("中文");
        System.out.println(Arrays.toString(bs));
    }
同时处理两个异常:
    try {
        process1();
        process2();
        process3();
    } catch (IOException | NumberFormatException e) {
        // IOException或NumberFormatException
        System.out.println("Bad input");
    } catch (Exception e) {
        System.out.println("Unknown error");
    }
}

通过printStackTrace()可以打印出方法的调用栈
    try {
        process1();
    } catch (Exception e) {
        e.printStackTrace();
    }
多级抛出异常,方便跟踪异常栈
public class Main {
    public static void main(String[] args) {
        try {
            process1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void process1() {
        try {
            process2();
        } catch (NullPointerException e) {
            throw new IllegalArgumentException(e);
        }
    }

    static void process2() {
        throw new NullPointerException();
    }
}

在catch中抛出异常,不会影响finally的执行。JVM会先执行finally,然后抛出异常。


自定义异常:
在一个大型项目中,可以自定义新的异常类型,但是,保持一个合理的异常继承体系是非常重要的。
一个常见的做法是自定义一个BaseException作为“根异常”,然后,派生出各种业务类型的异常。
BaseException需要从一个适合的Exception派生,通常建议从RuntimeException派生:

public class BaseException extends RuntimeException {
}

public class UserNotFoundException extends BaseException {
}
public class LoginFailedException extends BaseException {
}
多个构造方法:
public class BaseException extends RuntimeException {
    public BaseException() {
        super();
    }

    public BaseException(String message, Throwable cause) {
        super(message, cause);
    }

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

    public BaseException(Throwable cause) {
        super(cause);
    }
}

这种增强的NullPointerException详细信息是Java 14新增的功能,但默认是关闭的,我们可以给JVM添加一个-XX:+ShowCodeDetailsInExceptionMessages参数启用它:

java -XX:+ShowCodeDetailsInExceptionMessages Main.java
------------
// assert
public class Main {
    public static void main(String[] args) {
        int x = -1;
        assert x > 0;
        System.out.println(x);
    }
}

JVM默认关闭断言指令,即遇到assert语句就自动忽略了,不执行。
要执行assert语句,必须给Java虚拟机传递-enableassertions(可简写为-ea)参数启用断言
java -ea Main.java

-----------

Reflect反射:
Class cls = new Class(String);

Class类的构造方法是private,只有JVM能创建Class实例,
我们自己的Java程序是无法创建Class实例的。

获取类的三种方法:
Class cls1 = String.class;

String s = "Hello";
Class cls2 = s.getClass();

Class cls3 = Class.forName("java.lang.String");

boolean sameClass = cls1 == cls2; // true


判断类型和子类:
Integer n = new Integer(123);

boolean b1 = n instanceof Integer; // true,因为n是Integer类型
boolean b2 = n instanceof Number; // true,因为n是Number类型的子类


打印类的信息
    public static void main(String[] args) {
        printClassInfo("".getClass());
        printClassInfo(Runnable.class);
        printClassInfo(java.time.Month.class);
        printClassInfo(String[].class);
        printClassInfo(int.class);
    }

    static void printClassInfo(Class cls) {
        System.out.println("Class name: " + cls.getName());
        System.out.println("Simple name: " + cls.getSimpleName());
        if (cls.getPackage() != null) {
            System.out.println("Package name: " + cls.getPackage().getName());
        }
        System.out.println("is interface: " + cls.isInterface());
        System.out.println("is enum: " + cls.isEnum());
        System.out.println("is array: " + cls.isArray());
        System.out.println("is primitive: " + cls.isPrimitive());
    }

访问字段:
        Class stdClass = Student.class;
        // 获取public字段"score":
        System.out.println(stdClass.getField("score"));
        // 获取继承的public字段"name":
        System.out.println(stdClass.getField("name"));
        // 获取private字段"grade":
        System.out.println(stdClass.getDeclaredField("grade"));
调用方法:
        Class stdClass = Student.class;
        // 获取public方法getScore,参数为String:
        System.out.println(stdClass.getMethod("getScore", String.class));
        // 获取继承的public方法getName,无参数:
        System.out.println(stdClass.getMethod("getName"));
        // 获取private方法getGrade,参数为int:
        System.out.println(stdClass.getDeclaredMethod("getGrade", int.class));

----------------
Java语言使用@interface语法来定义注解(Annotation)
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}
元注解
@Target:    定义Annotation能够被应用于源码的哪些位置
@Retention: 定义了Annotation的生命周期
@Repeatable:定义Annotation是否可重复
@Inherited: 定义子类是否可继承父类定义的Annotation

定义:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

---------------

泛型
public interface Comparable<T> {
    int compareTo(T o);
}

class Person implements Comparable<Person> {
    String name;
    int score;
    Person(String name, int score) {
        this.name = name;
        this.score = score;
    }
    public String toString() {
        return this.name + "," + this.score;
    }
    public int compareTo(Person other) {
        return this.name.compareTo(other.name);
    }
}
比较接口:
        Person[] ps = new Person[] {
            new Person("Bob", 61),
            new Person("Alice", 88),
            new Person("Lily", 75),
        };
        Arrays.sort(ps);
        System.out.println(Arrays.toString(ps));


泛型中静态函数返回值要加一个<T>
public class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() { ... }
    public T getLast() { ... }

    // 可以编译通过:
    public static <T> Pair<T> create(T first, T last) {
        return new Pair<T>(first, last);
    }
}

Java的泛型是由编译器在编译时实行的,编译器内部永远把所有类型T视为Object处理,但是,在需要转型的时候,编译器会根据T的类型自动为我们实行安全地强制转型。

局限一:<T>不能是基本类型,例如int,因为实际类型是Object,Object类型无法持有基本类型:

局限二:无法取得带泛型的Class
所有泛型实例,无论T的类型是什么,getClass()返回同一个Class实例,因为编译后它们全部都是Pair<Object>

Pair<Integer>不是Pair<Number>的子类

<? extends Number>的泛型定义称之为上界通配符(Upper Bounds Wildcards),即把泛型类型T的上界限定在Number了。
<? extends Number> getFirst();

即返回值是Number或Number的子类,因此,可以安全赋值给Number类型的变量:
Number x = p.getFirst();


int sumOfList(List<? extends Integer> list) {
    int sum = 0;
    for (int i=0; i<list.size(); i++) {
        Integer n = list.get(i);
        sum = sum + n;
    }
    return sum;
}
这是一个对参数List<? extends Integer>进行只读的方法


void set(Pair<? super Integer> p, Integer first, Integer last) {
    p.setFirst(first);
    p.setLast(last);
}
Pair<? super Integer>表示,方法参数接受所有泛型类型为Integer或Integer父类的Pair类型。

1.<? extends T>允许调用读方法T get()获取T的引用,但不允许调用写方法set(T)传入T的引用(传入null除外);
2.<? super T>允许调用写方法set(T)传入T的引用,但不允许调用读方法T get()获取T的引用(获取Object除外)。
一个是允许读不允许写,另一个是允许写不允许读。


PECS原则
何时使用extends,何时使用super?为了便于记忆,我们可以用PECS原则:Producer Extends Consumer Super。

即:如果需要返回T,它是生产者(Producer),要使用extends通配符;如果需要写入T,它是消费者(Consumer),要使用super通配符。

无限定通配符
void sample(Pair<?> p) {
}

因为<?>通配符既没有extends,也没有super,因此:

不允许调用set(T)方法并传入引用(null除外);
不允许调用T get()方法并获取T引用(只能获取Object引用)。
换句话说,既不能读,也不能写,那只能做一些null判断:

static boolean isNull(Pair<?> p) {
    return p.getFirst() == null || p.getLast() == null;
}

Pair<?>是所有Pair<T>的超类:

--------------

List<Integer> list = List.of(1, 2, 5);
但是List.of()方法不接受null值,如果传入null,会抛出NullPointerException异常。

转数组:
List<String> list = List.of("apple", "pear", "banana");
Object[] array = list.toArray();
for (Object s : array) {
      System.out.println(s);
}
自动转为类型
List<Integer> list = List.of(12, 34, 56);
Integer[] array = list.toArray(new Integer[3]);

List定义方法:
class Person {
  public boolean equals(Object o) {
    if (o instanceof Person p) {
        return this.name.equals(p.name) && this.age == p.age;
    }
    return false;
  }
}

ArrayList<>()
Map<String, Student> map = new HashMap<>();

Map<DayOfWeek, String> map = new EnumMap<>(DayOfWeek.class);
        map.put(DayOfWeek.MONDAY, "星期一");
        map.put(DayOfWeek.TUESDAY, "星期二");


Map不但需要正确覆写equals()方法,还要正确覆写hashCode()方法。

----------------------------------------

读取配置文件
String f = "setting.properties";
Properties props = new Properties();
props.load(new java.io.FileInputStream(f));

String filepath = props.getProperty("last_open_file");
String interval = props.getProperty("auto_save_interval", "120");

写入配置文件
Properties props = new Properties();
props.setProperty("url", "http://www.liaoxuefeng.com");
props.setProperty("language", "Java");
props.store(new FileOutputStream("C:\\conf\\setting.properties"), "这是写入的properties注释");

InputStream和Reader的区别是一个是字节流,一个是字符流。字符流在内存中已经以char类型表示了,不涉及编码问题。
Properties props = new Properties();
props.load(new FileReader("settings.properties", StandardCharsets.UTF_8));


Set接口并不保证有序,而SortedSet接口则保证元素是有序的:
HashSet是无序的,因为它实现了Set接口,并没有实现SortedSet接口;
TreeSet是有序的,因为它实现了SortedSet接口。


Queue实际上是实现了一个先进先出(FIFO:First In First Out)的有序表

放入PriorityQueue的元素,必须实现Comparable接口,PriorityQueue会根据元素的排序顺序决定出队的优先级。



Deque来实现一个双端队列,它的功能是:

既可以添加到队尾,也可以添加到队首;
既可以从队首获取,又可以从队尾获取。


不可变集合
Collections还提供了一组方法把可变集合封装成不可变集合:

封装成不可变List:List<T> unmodifiableList(List<? extends T> list)
封装成不可变Set:Set<T> unmodifiableSet(Set<? extends T> set)
封装成不可变Map:Map<K, V> unmodifiableMap(Map<? extends K, ? extends V> m)
这种封装实际上是通过创建一个代理对象,拦截掉所有修改方法实现的


线程安全集合
Collections还提供了一组方法,可以把线程不安全的集合变为线程安全的集合:

变为线程安全的List:List<T> synchronizedList(List<T> list)
变为线程安全的Set:Set<T> synchronizedSet(Set<T> s)
变为线程安全的Map:Map<K,V> synchronizedMap(Map<K,V> m)

--------

InputStream / OutputStream
IO流以byte(字节)为最小单位,因此也称为字节流。

Reader / Writer
字符流传输的最小数据单位是char

包java.io提供了同步IO,而java.nio则是异步IO


FileInputStream、FileOutputStream、
FileReader和FileWriter。

序列化
public interface Serializable {
}

反序列化
和ObjectOutputStream相反,ObjectInputStream负责从一个字节流读取Java对象:


测试:
public class CalculatorTest {

    Calculator calculator;

    @BeforeEach
    public void setUp() {
        this.calculator = new Calculator();
    }

    @AfterEach
    public void tearDown() {
        this.calculator = null;
    }

    @Test
    void testAdd() {
        assertEquals(100, this.calculator.add(100));
        assertEquals(150, this.calculator.add(50));
        assertEquals(130, this.calculator.add(-20));
    }
}

JUnit提供assertThrows()来期望捕获一个指定的异常

条件测试:
使用config.getConfigFile()
@Test
void testWindows() {
    assertEquals("C:\\test.ini", config.getConfigFile("test.ini"));
}

@Test
void testLinuxAndMac() {
    assertEquals("/usr/local/test.cfg", config.getConfigFile("test.cfg"));
}


@Test
@DisabledOnOs(OS.WINDOWS)
void testOnNonWindowsOs() {
    // TODO: this test is disabled on windows
}

@Test
@DisabledOnJre(JRE.JAVA_8)
void testOnJava9OrAbove() {
    // TODO: this test is disabled on java 8
}

@Test
@EnabledIfEnvironmentVariable(named = "DEBUG", matches = "true")
void testOnlyOnDebugMode() {
    // TODO: this test is only run on DEBUG=true
}


参数化测试
@ParameterizedTest
@ValueSource(ints = { 0, 1, 5, 100 })
void testAbs(int x) {
    assertEquals(x, Math.abs(x));
}

@ParameterizedTest
@ValueSource(ints = { -1, -5, -100 })
void testAbsNegative(int x) {
    assertEquals(-x, Math.abs(x));
}


@MethodSource注解,它允许我们编写一个同名的静态方法来提供测试参数:
@ParameterizedTest
@MethodSource
void testCapitalize(String input, String result) {
    assertEquals(result, StringUtils.capitalize(input));
}

static List<Arguments> testCapitalize() {
    return List.of( // arguments:
            Arguments.of("abc", "Abc"), //
            Arguments.of("APPLE", "Apple"), //
            Arguments.of("gooD", "Good"));
}

使用@CsvSource,它的每一个字符串表示一行,一行包含的若干参数用,分隔,因此,上述测试又可以改写如下:

@ParameterizedTest
@CsvSource({ "abc, Abc", "APPLE, Apple", "gooD, Good" })
void testCapitalize(String input, String result) {
    assertEquals(result, StringUtils.capitalize(input));
}

-----------

// 多线程同步
public class Main {
    public static void main(String[] args) throws Exception {
        var add = new AddThread();
        var dec = new DecThread();
        add.start();
        dec.start();
        add.join();
        dec.join();
        System.out.println(Counter.count);
    }
}

class Counter {
    public static int count = 0;
}

class AddThread extends Thread {
    public void run() {
        for (int i=0; i<10000; i++) { Counter.count += 1; }
    }
}

class DecThread extends Thread {
    public void run() {
        for (int i=0; i<10000; i++) { Counter.count -= 1; }
    }
}


public class Counter {
    public void add(int n) {
        synchronized(this) {
            count += n;
        }
    }
    ...
}
写法二:

public synchronized void add(int n) { // 锁住this
    count += n;
} // 解锁
因此,用synchronized修饰的方法就是同步方法,它表示整个方法都必须用this实例加锁。


Java标准库提供了ExecutorService接口表示线程池,它的典型用法如下:

// 创建固定大小的线程池:
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交任务:
executor.submit(task1);
executor.submit(task2);
executor.submit(task3);
executor.submit(task4);
executor.submit(task5);

因为ExecutorService只是接口,Java标准库提供的几个常用实现类有:

FixedThreadPool:线程数固定的线程池;
CachedThreadPool:线程数根据任务动态调整的线程池;
SingleThreadExecutor:仅单线程执行的线程池。


Runnable VS Callable
Runnable接口有个问题,它的方法没有返回值。如果任务需要一个返回结果,那么只能保存到变量,还要提供额外的方法读取,非常不便。所以,Java标准库还提供了一个Callable接口,和Runnable接口比,它多了一个返回值:

class Task implements Callable<String> {
    public String call() throws Exception {
        return longTimeCalculation(); 
    }
}


进程与线程有什么区别
进程是操作系统分配资源的最小单位,线程是CPU调度的最小单位;
一个进程中可以包含多个线程;
进程与进程之间是相对独立的,进程中的线程之间并不完全独立,可以共享进程中的堆内存、方法区内存、系统资源等;
进程上下文的切换要比线程的上下文切换慢很多;
某个进程发生异常,不会对其它进程造成影响,但,某个线程发生异常,可能会对此进程中的其它线程造成影响;


线程池
避免了频繁创建和销毁线程所带来的性能开销
public ThreadPoolExecutor(
          int corePoolSize,
          int maximumPoolSize,
          long keepAliveTime,
          TimeUnit unit,
          BlockingQueue<Runnable> workQueue,
          ThreadFactory threadFactory,
          RejectedExecutionHandler handler)

submit()和execute()方法都是用来提交任务到线程池中执行的,但它们之间存在一些区别:

①  参数类型,execute()方法只能接收实现了Runnable接口类型的任务,而submit()方法可以接收Runnable类型的任务和Callable接口的实现类。

② 返回类型,execute()方法没有返回值,submit()方法则返回一个Future对象,通过这个对象可以获取任务的执行结果或者取消任务执行。

③ 异常处理,execute()方法在执行任务出现异常时会直接抛出异常,而submit()方法则会捕获异常并封装到Future对象中,可以通过调用Future对象的get()方法来获取执行过程中的异常。

④ 对线程池的影响,当线程池已满时,execute()方法会直接抛出RejectedExecutionException异常,而submit()方法会将任务放入阻塞队列中,等待有空闲的线程时再执行。


Runtime.getRuntime().availableProcessors()获取的是CPU核心线程数,也就是计算资源。

CPU密集型,线程池大小设置为N,也就是和cpu的线程数相同,可以尽可能地避免线程间上下文切换,但在实际开发中,一般会设置为N+1,为了防止意外情况出现线程阻塞,如果出现阻塞,多出来的线程会继续执行任务,保证CPU的利用效率。

IO密集型,线程池大小设置为2N,这个数是根据业务压测出来的,如果不涉及业务就使用推荐。


只要有一个用户线程在运行,守护线程就会一直运行。只有所有的用户线程都结束的时候,守护线程才会退出。

编写代码时,也可以通过thread.setDaemon(true)指定线程为守护线程。

守护线程和用户线程的创建方式相同,只是在用户线程的基础上将setDaemon设置为true即可。


yield的主要作用是让当前正在执行的线程从运行状态变为就绪状态。

调用 yield 方法并不会释放线程持有的任何监视器锁,这与 wait 方法不同,后者会释放锁并导致线程进入等待状态。

wait() 必须在同步方法或同步代码块中使用,因为它需要释放锁;yield() 则可以在任何地方使用。


java.util.concurrent.locks包提供了更灵活的锁机制,如ReentrantLock。与synchronized相比,Lock提供了更多的功能,如可中断的获取锁、尝试获取锁以及定时获取锁等。


/**
 * 线程不安全的单例模式
 */
public class SingleInstance {

    private static SingleInstance instance;

    public static SingleInstance getInstance(){
        if(instance == null){
            synchronized (SingleInstance.class){
                if(instance == null){
                    instance = new SingleInstance();
                }
            }
        }
        return instance;
    }
}
对于上面的代码包含三个步骤:
① 分配内存空间 ② 初始化对象 ③ 将instance引用指向内存空间
正常执行的CPU指令顺序为①②③,CPU对程序进行重排序后的执行顺序是①③②,此时就会出现问题。
private static volatile Singleton instance; // 关键:volatile修饰

volatile的作用?:
禁止指令重排序:确保写操作前的所有操作不会重排到写操作之后
保证内存可见性:修改立即对其他线程可见

当一个共享变量被 volatile 修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。

volatile 关键字在 Java 中确保了变量的更新操作会立即被其他线程看到,但它并不能保证原子性。

每次线程访问volatile变量时,它都必须从主内存中读取该变量的最新值,而不是使用线程本地缓存中的值。

对于复合操作(如 i++)并不能保证其原子性。在这种情况下,应该使用其他同步机制(如 synchronized 或原子类)来确保操作的原子性。

在多核处理器的系统中,由于缓存一致性问题,即使在一个线程中对 volatile 变量的写入操作可能会被其他线程延迟看到,这是因为不同核心的缓存需要时间来同步。


37、Java中提供了哪些类解决多线程特性问题?
在Java中解决原子性问题的方案包括synchronized、Lock、ReentranLock、ReadWriteLock、CAS操作、Java中提供的原子类等。

解决可见性和有序性问题,可以禁用CPU缓存和编译器优化。

JVM提供了禁用缓存和编译优化的方法,包括volatile关键字、synchronized、final关键字以及Java内存模型中的Happens-Before原则。


@Async的作用就是异步处理任务。

在方法上添加@Async,表示此方法是异步方法;
在类上添加@Async,表示类中的所有方法都是异步方法;
使用此注解的类,必须是Spring管理的类;
需要在启动类或配置类中加入@EnableAsync注解,@Async才会生效;
在使用@Async时,如果不指定线程池的名称,也就是不自定义线程池,@Async是有默认线程池的,使用的是Spring默认的线程池SimpleAsyncTaskExecutor。


为了提高程序的执行性能,编译器和CPU会对程序的指令进行重排序,可以分为编译器重排序和CPU重排序,CPU重排序又可以分为指令级重排序和内存系统重排序。


48、as-if-serial原则是什么?
编译器和CPU对程序代码的重排序必须遵循as-if-serial原则,as-if-serial原则规定编译器和CPU无论对程序代码如何重排序,都必须保证程序在单线程环境下运行的正确性。


ThreadLocal 是一个本地线程副本变量工具类,在每个线程中都创建了一个 ThreadLocalMap 对象,简单说 ThreadLocal 就是一种以空间换时间的做法,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。通过这种方式,避免资源在多线程间共享。


Semaphore(信号量)在Java中是一个非常重要的并发工具,它主要用于限制可以同时访问某些资源的线程数量。


Callable接口的call()方法允许有返回值,而Runnable接口的run()方法则不返回任何结果。

Callable的call()方法允许抛出异常,而Runnable的run()方法则不能抛出任何被检查的异常。

Future是一个代表异步计算结果的接口。它提供了检查计算是否完成的方法以及获取计算结果的方法。通过Future,我们可以了解Callable任务是否已经完成,如果完成了,还可以获取它的返回值。


线程安全和并发安全的数据结构
数据结构选择,选择适合并发环境的数据结构,
如ConcurrentHashMap、CopyOnWriteArrayList等,它们内部已经实现了必要的同步机制。


Thread dump可以帮助开发人员诊断和解决多线程应用程序中的问题,例如死锁、资源竞争等。通过分析Thread dump,可以了解每个线程的状态、调用栈、锁信息等,从而找出问题的根源。


调用线程的interrupt()方法可以设置线程的中断状态。
在线程的运行过程中,可以使用Thread.currentThread().isInterrupted()方法检查中断状态,如果为true,则退出循环,从而停止线程。


乐观锁 自旋


编译器优化,将锁消除,前提是Java必须运行在server模式,同时必须开启逃逸分析;
-server -XX:+DoEscapeAnalysis -XX:+EliminateLocks
 
其中+DoEscapeAnalysis表示开启逃逸分析,+EliminateLocks表示锁消除。


锁粗化就是把多次的锁请求合并成一个请求,扩大锁的范围,降低锁请求、同步、释放带来的性能损耗。


什么是内置锁?
偏向锁


可以通过FileChannel类的lock或者tryLock方法进行加锁解锁。
// 以可写的方式打开一个文件 nezha.txt 的通道
FileChannel channel = FileChannel.open(Paths.get("nezha.txt"), StandardOpenOption.WRITE);
// 阻塞直至获取锁
FileLock lock = channel.lock();
// 会立即返回,要么返回锁,要么返回null
FileLock tryLock = channel.tryLock();


为了重量级锁synchronized提高性能,Java虚拟机(JVM)对synchronized的实现进行了优化,引入了锁升级的概念。

synchronized 锁升级的原理是基于锁状态的变化,从无锁状态开始,逐步升级到偏向锁、轻量级锁,最终到重量级锁。

CountDownLatch和CyclicBarrier在Java并发编程中都是常用的同步工具,但它们在使用场景和特性上存在明显的区别。

Optional

定义Optional对象
import java.util.Optional
@Value("${myValue:#{null}}")
private Optional<String> value;

if (value.isPresent()) {
    // do something cool
}
-------------------------
@Entity
public class Book {
    @Column
    private LocalDate publishingDate;
    ...
    public Optional getPublishingDate() {
        return Optional.ofNullable(publishingDate);
    }
    public void setPublishingDate(LocalDate publishingDate) {
        this.publishingDate = publishingDate;
    }
}
Session session = em.unwrap(Session.class);
Optional<Book> book = session.byId(Book.class).loadOptional(1L);
-------------------------

使用@Entity(name=***)时: Repository中的@Query(***)中,只能写sql语句(当然也不用写nativeQuery=true了!)
使用@Entity+@Table(name=***)时: Repository中的@Query(***)中,可以写hql与sql(而且写sql时候必须加上nativeQuery=true)

nativeQuery = true时 有nativeQuery = true时,是可以执行原生sql语句
nativeQuery = false时
select * from xxx中xxx也不是数据库对应的真正的表名,而是对应的实体名,
并且sql中的字段名也不是数据库中真正的字段名,而是实体的字段名。
Optional.of():传递参数,如果of中的对象是null,就报空指针异常。
Optional.ofNullable():允许ofNullable传递null对象
Optional.empty():返回空的Optional实例
optional.isPresent():判断Optional实例是否为空
optional.orElse():如果optional为空的话返回orElse中的对象
optional.get():获取optional中的T对象
optional.map():如果optional不为null,则执行map方法中的映射函数得到返回值。
                optional.map(Function<? super T,? extends U> mapper)
                
常规写法
User user = .....
if(user != null) {
  String name = user.getUsername();
  if(name != null) {
    return name.toUpperCase();
  } else {
    return null;
  }
} else {
  return null;
}

使用optional
Optional<User> user = ...
return user.map(u -> u.getUsername())
           .map(name -> name.toUpperCase())
           .orElse(null);


常规写法
if (map.get("user")!=null){
    Map<String,Object> user = (Map<String, Object>) map.get("user");
    if (user.get("info")!=null){
        Map<String,Object> info = (Map<String, Object>) user.get("info");
        if (info.get("address")!=null){
            String address = (String) info.get("address");
            System.out.println(address);
        }
    }
}

使用optional
String address=Optional.ofNullable(map)
        .map(m->(Map<String,Object>)m.get("user"))
        .map(user->(Map<String,Object>)user.get("info"))
        .map(info->(String)info.get("address"))
        .orElse(null);


常规写法
if (user!=null){
    UserInfo info = user.getInfo();
    if (info!=null){
        String address = info.getAddress();
    }
}

使用optional
String address = Optional.ofNullable(user)
        .map(u -> u.getInfo())
        .map(info -> info.getAddress())
        .orElse(null);
        
当某个值为空时设置默认值:
User resultUser = Optional.ofNullable(user).orElse(new User());