JavaSE

IDEA、JDK、Maven安装

《EffectiveJava》读书笔记

IO流

Java集合

枚举

  • 枚举类是用final修饰的,枚举类不能被继承!
  • 枚举类默认继承了java.lang.Enum枚举类。
  • 枚举类的第一行都是常量,存储都是枚举类的对象。
  • 枚举类的第一行必须是罗列枚举类的实例名称。
    所以:枚举类相当于是多例设计模式。
  • java建议做信息标志和信息分类应该使用枚举实现:最优雅的方式。
    可以实现可读性,而且入参受限制,不能乱输入!!!
修饰符 enum 枚举名称{
第一行都是罗列枚举实例的名称。
}
// 枚举
enum Season {
SPRING , SUMMER , AUTUMN , WINTER;
}
// 枚举类的编译以后源代码:
public final class Season extends java.lang.Enum<Season> {
public static final Season SPRING = new Season();
public static final Season SUMMER = new Season();
public static final Season AUTUMN = new Season();
public static final Season WINTER = new Season();

public static Season[] values();
public static Season valueOf(java.lang.String);
}

API

// 获取当前类的全部枚举实例 : public static Season[] values()
Season[] ss = Season.values();
// 获取枚举对象的索引: ordinal()
Season s2 = Season.AUTUMN;
System.out.println(s2.ordinal()); // 2

enum Season{
SPRING, SUMMER , AUTUMN, WINTER ;
}
// 使用案例
enum Oritation{
UP , DOWN , LEFT , RIGHT ;
}

public static void move(Oritation o){
switch (o){
case UP:
System.out.println("让🐎往👆蹦~~~~");
break;
case DOWN:
System.out.println("让🐎往👇蹦~~~~");
break;
case LEFT:
System.out.println("让🐎往👈蹦~~~~");
break;
case RIGHT:
System.out.println("让🐎往👉蹦~~~~");
break;
}
}

递归

直接递归:自己的方法调用自己。

间接递归:自己的方法调用别的方法,别的方法又调用自己。

已知:f(x) = f(x - 1) + 1  (恒等式)
已知:f(1) = 1
求: f(10) = ?

计算流程:
f(10) = f(9) + 1
f(9) = f(8) + 1
f(8) = f(7) + 1
f(7) = f(6) + 1
f(6) = f(5) + 1
f(5) = f(4) + 1
f(4) = f(3) + 1
f(3) = f(2) + 1
f(2) = f(1) + 1
f(1) = 1

递归的三要素(理论):
1.递归的终结点: f(1) = 1
2.递归的公式:f(x) = f(x - 1) + 1
3.递归的方向:必须走向终结点
public static int f(int x){
if(x == 1) {
return 1;
}else{
return f(x - 1) + 1 ;
}
}
拓展:递归的核心思想-公式转换
已知: f(x) = f(x + 1) + 2
f(1) = 1
求: f(10) = ?

公式转换:
f(n-1) = f(n-1+1)+2
f(n-1) = f(n)+2
f(n) = f(n-1)- 2 ;

递归算法的三要素:
1)递归的公式: f(n) = f(n-1)- 2 ;
2)递归的终结点: f(1) = 1
3)递归的方向:必须走向终结点。
public static int f(int n){
if(n == 1) {
return 1;
}else{
return f(n-1)- 2;
}
}

递归的经典案例。

  • 猴子吃桃:
猴子第一天摘了若干个桃子,当即吃了一半,觉得好不过瘾,然后又多吃了一个。
第二天又吃了前一天剩下的一半,觉得好不过瘾,然后又多吃了一个。
以后每天都是如此
等到第十天再吃的时候发现只有1个桃子,请问猴子第一天总共摘了多少个桃子。

公式:
f(x+1) = f(x) - f(x) / 2 - 1
2f(x+1) = 2f(x) - f(x) - 2
2f(x+1) = f(x) - 2
f(x) = 2f(x+1)+2
递归的三要素:
1)公式:f(x) = 2f(x+1)+2
2)终结点:f(10) = 1
3)递归的方向:走向了终结点
public static int f(int x){
if( x == 10){
return 1 ;
}else{
return 2*f(x+1)+2;
}
}
  • 递归实现1-n的和
f(n) = 1 + 2 + 3 + 4 + 5 + 6 + ...n-1 + n ;
f(n) = f(n-1) + n

流程:
f(5) = return f(4) + 5 = 1 + 2 + 3 + 4 + 5
f(4) = return f(3) + 4 = 1 + 2 + 3 + 4
f(3) = return f(2) + 3 = 1 + 2 + 3
f(2) = return f(1) + 2 = 1 + 2
f(1) = return 1

递归的核心三要素:
(1)递归的终点接: f(1) = 1
(2)递归的公式: f(n) = f(n-1) + n
(3)递归的方向必须走向终结点:
public static int f(int n){
if(n == 1 ) return 1;
return f(n-1) + n;
}
  • n的阶乘
n!= 1*2*3*4*5*6*...*(n-1)*n。
f(n) = 1*2*3*4*5*6*...*(n-1)*n
f(n) = f(n-1)*n

流程:
f(5) = f(4) * 5 ; = 1*2*3*4*5
f(4) = f(3) * 4 ; = 1*2*3*4
f(3) = f(2) * 3 ; = 1*2*3
f(2) = f(1) * 2 ; = 1*2
f(1) = 1

递归的核心三要素:
(1)递归的终点接: f(1) = 1
(2)递归的公式 f(n) = f(n-1)*n
(3)递归的方向必须走向终结点
public static int f(int n){
if(n == 1){
return 1 ;
}else{
return f(n-1)*n;
}
}
  • 递归实现文件搜索(非规律递归)
需求:希望去D:/soft目录寻找出eclipse.exe文件。

分析:
(1)定义一个方法用于做搜索。
(2)进入方法中进行业务搜索分析。
小结:
非规律化递归应该按照业务流程开发!
/**
* 去某个目录下搜索某个文件
* @param dir 搜索文件的目录。
* @param fileName 搜索文件的名称。
*/
public static void searchFiles(File dir , String fileName){
// 1.判断是否存在该路径,是否是文件夹
if(dir.exists() && dir.isDirectory()){
// 2.提取当前目录下的全部一级文件对象
File[] files = dir.listFiles(); // null/[]
// 3.判断是否存在一级文件对象(判断是否不为空目录)
if(files!=null && files.length > 0){
// 4.判断一级文件对象
for (File f : files) {
// 5.判断file是文件还是文件夹
if(f.isFile()){
// 6.判断该文件是否为我要找的文件对象
if(f.getName().contains(fileName)){
System.out.println(f.getAbsolutePath());
try {
// 启动它(拓展)
Runtime r = Runtime.getRuntime();
r.exec(f.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}else{
// 7.该文件是文件夹,文件夹要递归进入继续寻找
searchFiles(f ,fileName);
}
}
}
}
}

日志级别

日志级别 描述
OFF 关闭:最高级别,不打印日志。
FATAL 致命:指明非常严重的可能会导致应用终止执行错误事件。 灾难信息,合并计入ERROR
ERROR 错误:指明错误事件,但应用可能还能继续运行。 记录错误堆栈信息
WARN 警告:指明可能潜在的危险状况。 记录运维过程报警数据
INFO 信息:指明描述信息,从粗粒度上描述了应用运行过程。 记录运维过程数据
DEBUG 调试:指明细致的事件信息,对调试应用最有用。 程序员调试代码使用
TRACE 跟踪:指明程序运行轨迹,比DEBUG级别的粒度更细。 运行堆栈信息,使用率低
ALL 所有:所有日志级别,包括定制级别

ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF

设置日志组,控制指定包对应的日志输出级别,也可以直接控制指定包对应的日志输出级别

# 开启debug模式,输出调试信息,常用于检查系统运行状况
debug: true

# 设置日志级别,root表示根节点,即整体应用日志级别
logging:
level:
root: debug

# 关闭运行日志图表(banner)
spring:
main:
banner-mode: off

日志文件

logging:
file:
name: server.log
logback:
rollingpolicy:
max-file-size: 3KB
file-name-pattern: server.%d{yyyy-MM-dd}.%i.log

日志输出格式控制

logging:
pattern:
console: "%d %clr(%p) --- [%16t] %clr(%-40.40c){cyan} : %m %n"

时间复杂度

在上图中,我们可以看到当 n 很小时,函数之间不易区分,很难说谁处于主导地位,但是当 n 增大时,我们就能看到很明显的区别,谁是老大一目了然:

O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n)

出处:https://blog.csdn.net/chenguanghan123/article/details/83478259

如果ax=N(a>0,且a≠1),那么数x叫做以a为底N的对数,记作x=logaN,读作以a为底N的对数,其中a叫做对数的底数,N叫做真数。

出处:https://blog.csdn.net/ted_cs/article/details/82881831

Java正则表达式

https://www.runoob.com/regexp/regexp-syntax.html

字符类
[abc] abc(简单类)
[^abc] 任何字符,除了 abc(否定)
[a-zA-Z] azAZ,两头的字母包括在内(范围)
[a-d[m-p]] admp[a-dm-p](并集)
[a-z&&[def23]] def(交集)
[a-z&&[^bc]] az,除了 bc[ad-z](减去)
[a-z&&[^m-p]] az,而非 mp[a-lq-z](减去)

预定义字符类
. 任何字符
\d 数字:[0-9]
\D 非数字: [^0-9]
\s 空白字符:[ \t\n\x0B\f\r]
\S 非空白字符:[^\s]
\w 单词字符:[a-zA-Z_0-9]
\W 非单词字符:[^\w]

以上正则匹配只能校验单个字符。

Greedy 数量词
X? X,一次或一次也没有
X* X,零次或多次
X+ X,一次或多次
X{n} X,恰好 n
X{n,} X,至少 n
X{n,m} X,至少 n 次,但是不超过 m
public String[] split(String regex);// 按照正则表达式匹配的内容进行分割字符串,反回一个字符串数组。
public String replaceAll(String regex,String newStr);// 按照正则表达式匹配的内容进行替换

正则表达式爬取信息中的内容

public class RegexDemo05 {
public static void main(String[] args) {
String rs = "来黑马程序学习java,电话020-43422424,或者联系邮箱" +
"itcast@itcast.cn,电话18762832633,0203232323" +
"邮箱bozai@itcast.cn,400-100-3233 ,4001003232";
// 需求:从上面的内容中爬取出 电话号码和邮箱。
// 1.定义爬取规则
String regex = "(\\w{1,}@\\w{2,10}(\\.\\w{2,10}){1,2})|(1[3-9]\\d{9})|(0\\d{2,5}-?\\d{5,15})|400-?\\d{3,8}-?\\d{3,8}";
// 2.编译正则表达式成为一个匹配规则对象
Pattern pattern = Pattern.compile(regex);
// 3.通过匹配规则对象得到一个匹配数据内容的匹配器对象
Matcher matcher = pattern.matcher(rs);
// 4.通过匹配器去内容中爬取出信息
while(matcher.find()){
System.out.println(matcher.group());
}
}
}

Java定时任务

// TimerTask  单线程
private static void timerTask() throws InterruptedException {
Timer timer = new Timer();

TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("hi, 欢迎关注:java技术栈");
}
};

// 第一次任务延迟时间
long delay = 2000;

// 任务执行频率
long period = 3 * 1000;

// 开始调度
timer.schedule(timerTask, delay, period);

// 指定首次运行时间
// timer.schedule(timerTask, DateUtils.addSeconds(new Date(), 5), period);

Thread.sleep(20000);

// 终止并移除任务
timer.cancel();
timer.purge();
}

并发编程

多线程

程序是静止的,运行中的程序就是进程。
进程的三个特征:
1.动态性 : 进程是运行中的程序,要动态的占用内存,CPU和网络等资源。
2.独立性 : 进程与进程之间是相互独立的,彼此有自己的独立内存区域。
3.并发性 : 假如CPU是单核,同一个时刻其实内存中只有一个进程在被执行。
CPU会分时轮询切换依次为每个进程服务,因为切换的速度非常
快,给我们的感觉这些进程在同时执行,这就是并发性。
什么是线程?
线程是属于进程的。一个进程可以包含多个线程,这就是多线程。
线程是进程中的一个独立执行单元。
线程创建开销相对于进程来说比较小。
线程也支持“并发性”。

线程的创建方式

a.继承Thread类的方式
-- 1.定义一个线程类继承Thread类。
-- 2.重写run()方法
-- 3.创建一个新的线程对象。
-- 4.调用线程对象的start()方法启动线程。

注意:
1.线程的启动必须调用start()方法。否则当成普通类处理。
-- 如果线程直接调用run()方法,相当于变成了普通类的执行,此时将只有主线程在执行他们!
-- start()方法底层其实是给CPU注册当前线程,并且触发run()方法执行
2.建议线程先创建子线程,主线程的任务放在之后。否则主线程永远是先执行完!
Thread t = new MyThread();
t.start()

class MyThread extends Thread{
@Override
public void run() {
for(int i = 0 ; i < 100 ; i++ ){
System.out.println("子线程输出:"+i);
}
}
}
Thread类的API

1.public void setName(String name):给当前线程取名字。
2.public void getName():获取当前线程的名字。
-- 线程存在默认名称,子线程的默认名称是:Thread-索引。
-- 主线程的默认名称就是:main
3.public static Thread currentThread()
-- 获取当前线程对象,这个代码在哪个线程中,就得到哪个线程对象。
4.public static void sleep(long time): 让当前线程休眠多少毫秒再继续执行。
-- public Thread()
-- public Thread(String name):创建线程对象并取名字。
b.实现Runnable接口的方式。
-- 1.创建一个线程任务类实现Runnable接口。
-- 2.重写run()方法
-- 3.创建一个线程任务对象。
-- 4.把线程任务对象包装成线程对象
-- 5.调用线程对象的start()方法启动线程。
Thread的构造器:
-- public Thread(){}
-- public Thread(String name){}
-- public Thread(Runnable target){}
-- public Thread(Runnable target,String name){}

实现Runnable接口创建线程的优缺点:
缺点:代码复杂一点。
优点:
-- 线程任务类只是实现了Runnable接口,可以继续继承其他类,而且可以继续实现其他接口(避免了单继承的局限性)
-- 同一个线程任务对象可以被包装成多个线程对象
-- 适合多个多个线程去共享同一个资源(后面内容)
-- 实现解耦操作,线程任务代码可以被多个线程共享,线程任务代码和线程独立。
-- 线程池可以放入实现Runable或Callable线程任务对象。(后面了解)
注意:其实Thread类本身也是实现了Runnable接口的。
-- 不能直接得到线程执行的结果!
Runnable target = new MyRunnable();
Thread t = new Thread(target);
t.start();

class MyRunnable implements Runnable{
@Override
public void run() {
for(int i = 0 ; i < 10 ; i++ ){
System.out.println(Thread.currentThread().getName()+"==>"+i);
}
}
}

// 匿名内部类写法
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0 ; i < 10 ; i++ ){
System.out.println(Thread.currentThread().getName()+"==>"+i);
}
}
}).start();
c.线程的创建方式三: 实现Callable接口。
-- 1,定义一个线程任务类实现Callable接口 , 申明线程执行的结果类型。
-- 2,重写线程任务类的call方法,这个方法可以直接返回执行的结果。
-- 3,创建一个Callable的线程任务对象。
-- 4,把Callable的线程任务对象包装成一个未来任务对象。
-- 5.把未来任务对象包装成线程对象。
-- 6.调用线程的start()方法启动线程
优点:全是优点。
-- 线程任务类只是实现了Callable接口,可以继续继承其他类,而且可以继续实现其他接口(避免了单继承的局限性)
-- 同一个线程任务对象可以被包装成多个线程对象
-- 适合多个多个线程去共享同一个资源(后面内容)
-- 实现解耦操作,线程任务代码可以被多个线程共享,线程任务代码和线程独立。
-- 线程池可以放入实现Runable或Callable线程任务对象。(后面了解)
-- 能直接得到线程执行的结果!
缺点:编码复杂。
Callable call = new MyCallable();
// 4.把Callable任务对象包装成一个未来任务对象
// -- public FutureTask(Callable<V> callable)
// 未来任务对象是啥,有啥用?
// -- 未来任务对象其实就是一个Runnable对象:这样就可以被包装成线程对象!
// -- 未来任务对象可以在线程执行完毕之后去得到线程执行的结果。
FutureTask<String> task = new FutureTask<>(call);
// 5.把未来任务对象包装成线程对象
Thread t = new Thread(task);
// 6.启动线程对象
t.start();

// 在最后去获取线程执行的结果,如果线程没有结果,让出CPU等线程执行完再来取结果
try {
String rs = task.get(); // 获取call方法返回的结果(正常/异常结果)
System.out.println(rs);
} catch (Exception e) {
e.printStackTrace();
}

// 1.创建一个线程任务类实现Callable接口,申明线程返回的结果类型
class MyCallable implements Callable<String>{
// 2.重写线程任务类的call方法!
@Override
public String call() throws Exception {
// 需求:计算1-10的和返回
int sum = 0 ;
for(int i = 1 ; i <= 10 ; i++ ){
System.out.println(Thread.currentThread().getName()+" => " + i);
sum+=i;
}
return Thread.currentThread().getName()+"执行的结果是:"+sum;
}
}

线程同步_同步代码块

线程同步的方式有三种:
(1)同步代码块。
(2)同步方法。
(3)lock显示锁。

a.同步代码块。
synchronized(锁对象){
// 访问共享资源的核心代码
}
锁对象:理论上可以是任意的“唯一”对象即可。
原则上:锁对象建议使用共享资源。
-- 在实例方法中建议用this作为锁对象。此时this正好是共享资源!必须代码高度面向对象
-- 在静态方法中建议用类名.class字节码作为锁对象。
b.同步方法
方法加上一个修饰符 synchronized.
public synchronized void drawMoney(double money)
同步方法其实底层也是有锁对象的:
如果方法是实例方法:同步方法默认用this作为的锁对象。
如果方法是静态方法:同步方法默认用类名.class作为的锁对象。
c.lock显示锁。
java.util.concurrent.locks.Lock机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,
同步代码块/同步方法具有的功能Lock都有,除此之外更强大

Lock锁也称同步锁,加锁与释放锁方法化了,如下:
- `public void lock() `:加同步锁。
- `public void unlock()`:释放同步锁。
public void drawMoney(double money) {
// 1.先拿到是谁来取钱:取当前线程对象的名称
String name = Thread.currentThread().getName();
lock.lock(); // 上锁~!
try{
// ..
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock(); // 解锁~!
}
}

字符集/编码集

1B = 8b 计算机中的最小单位是字节B.

一个字节8位,2^8 = 256
ASCII编码:ASCII它是一种7位编码,但它存放时必须占全一个字节,也即占用8位。
a 97
b 98
A 65
B 66
0 48
1 49

GBK编码:2个字节
Unicode编码(万国码):
UTF-83个字节

英文和数字在任何编码集中都是一样的,都占1个字节。
英文和数字在任何编码集中可以通用,不会乱码!!
GBK编码中,1个中文字符一般占2个字节。
UTF-8编码中,1个中文字符一般占3个字节。

泛型

泛型接口

修饰符 interface 接口名称<泛型变量>{

}
public interface Data<E> {
void add(E stu);

void delete(E stu);

void update(E stu);

E query(int id);
}

// 操作学生数据
public class StudentData implements Data<Student> {
@Override
public void add(Student stu) {
System.out.println("添加学生!");
}

@Override
public void delete(Student stu) {
System.out.println("删除学生!");
}

@Override
public void update(Student stu) {
}

@Override
public Student query(int id) {
return null;
}
}

泛型通配符

通配符:?
?可以用在使用泛型的时候代表一切类型。
E , T , K , V是在定义泛型的时候使用代表一切类型。

泛型的上下限:
? extends Car : 那么?必须是Car或者其子类。(泛型的上限)
? super Car :那么?必须是Car或者其父类。(泛型的下限。不是很常见)
public class GenericDemo {
public static void main(String[] args) {
ArrayList<BMW> bmws = new ArrayList<>();
bmws.add(new BMW());
bmws.add(new BMW());
bmws.add(new BMW());
run(bmws);

ArrayList<BENZ> benzs = new ArrayList<>();
benzs.add(new BENZ());
benzs.add(new BENZ());
benzs.add(new BENZ());
run(benzs);

ArrayList<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
dogs.add(new Dog());
dogs.add(new Dog());
// run(dogs); // 就进不来了!
}

// 定义一个方法,可以让很多汽车一起进入参加比赛
public static void run(ArrayList<? extends Car> cars) {

}
}

class Car {
}

class BMW extends Car {
}

class BENZ extends Car {
}

class Dog {
}

自定义泛型方法

修饰符 <泛型变量> 返回值类型 方法名称(形参列表){

}

class class1 {
public static <T> String arrToString(T[] nums) {
StringBuilder sb = new StringBuilder();
sb.append("[");
if (nums != null && nums.length > 0) {
for (int i = 0; i < nums.length; i++) {
T ele = nums[i];
sb.append(i == nums.length - 1 ? ele : ele + ", ");
}
}
sb.append("]");
return sb.toString();
}
}

自定义泛型类

修饰符 class 类名<泛型变量>{

}
泛型变量建议使用 E , T , K , V
class MyArrayList<E> {
private ArrayList lists = new ArrayList();

public void add(E e) {
lists.add(e);
}

public void remove(E e) {
lists.remove(e);
}

@Override
public String toString() {
return lists.toString();
}
}

Java语言-相关书籍

Java语言-资源


临时笔记

  • 推荐:优先使用不可变的类 LocalDate
  • 不推荐使用:Switch语句 goto语句 char类型 int i, j
  • Character类的 isJavaIdentifierStart isJavaIdentifierPart
  • 15/2 = 7 15%2 = 1 15.0/2 = 7.5
    整数被0除→异常
    浮点数被0除→无穷大或NaN
  • strictfp
  • Math.sprt(x) 平方根
    Math.pow(x,a) 幂运算
    Math.PI π的近似值
    Math.E e的近似值
  • StrictMath类 得到一个完全可预测的结果,速度比Math差
  • int[ ] arrs2= Arrays.copyof(arrs1,arrs1.length)
    int[ ] arrs = Arrays.copyof(arrs,2*arrs.length)
    int[ ] a = new int[4]
    int[ ] a = {1,2,3,4}
    new int[ ] {1,2,3,4}
    new elementType[ ]{ }
    Arrays.equals(arrs1,arrs2) 判断数组对象相等(字段全相等)
  • 依赖 uses-a
    聚合 has-a
    继承 is-a
  • jdeprscan 工具类(检测代码中是否使用用已经废弃的API)
  • Objects.requireNonNullElse(n,”unknow”)
    Objects.requireNonNull(n,”The name cannot be null”)
  • 值传递、引用传递
    Java:对象引用是按照值传递的
  • SELECT DISTINCT vend_id FROM products
  • DESC关键字只应用到直接位于其前面的列名
    SELECT prod_id, prod_prrice, prod_name FROM products ORDER BY prod_price DESC, prod_name;
  • SQL优先处理AND操作符
  • IN操作符一般比OR操作符清单执行更快
  • cat 1.txt 查看文件内容

ResultFul

restful资源设计
RESTFul设计指南
DDD以及软件架构设计
看看ES的RESTful接口
找个技术栈的restful学习

JDK JRE JVM

JDK:java Develpment Kit java 开发工具

JRE:java Runtime Environment java运行时环境

JVM:java Virtual Machine java 虚拟机