Java笔记

<font color='red’线程的同步

  1. 整型:byte,short,int,long
  2. 浮点型:float,double
  3. 逻辑型:boolean
  4. 字符型:char

七、面向对象

0)Java面向对象的三大特性

1)static:单例设计模式(会手写)

​ 修饰属性、方法、代码块、内部类

image-20220705172110589

静态属性:

非静态属性:

说明:

image-20220705172204227

​ 5)单例设计模式

2)final的使用场景

​ 修饰类:此类不能被其他类继承

​ 比如:String类,System类,StringBuffer类

​ 修饰方法(不常用,你不想重写就不重写好了):此方法不可被重写

​ 比如:Object类中getClass()

​ 修饰变量(包括属性和局部变量):此时的“变量”就是一个常量了,别人不能改值

​ final 修饰属性可以考虑的位置:显示初始化、代码块中初始化、构造器中初始化

image-20220705172228588

​ final 修饰局部变量:尤其是final修饰形参时,表明此形参是一个常量。当我们调用此方法,给常量形参赋予一个实参,一旦赋值以后,就只能在方法体内使用此形参,但不能重新进行赋值。

public void show(final int num){
    num = 20;
    System.out.println(num)
}

​ static final 用来修饰属性:全局常量

public static final String LOGIN_USER = "loginUser";

3)abstract:模板方法

​ 特性:不能实例化,单继承,有构造器,可以有抽象方法,多实现(一个类implements多个接口)

​ 修饰类:不能实例化,提供子类(让子类实例化)

​ 修饰方法:没有方法体,抽象方法只定义功能的标准,具体的执行需要子类去实现

​ 注意点:1. abstract不能修饰:属性、构造器等结构

​ 2. abstract不能修饰private方法、static方法、final方法、final类

image-20220705172251588

4)interface:工厂,代理

​ 特性:不能实例化,多继承(implements aa,bb,cc),无构造器,可以有抽象方法(规范)

image-20220705172402788

image-20220705172424939

​ 代理模式

image-20220705172436989

​ 工厂模式

image-20220705172457965

5)内部类

6)面试题

image-20220705172514825

7)异常

7.1 分类

java.lang.Throwable

​ java.lang.Error:一般不编写针对性的代码进行处理

​ java.lang.Exception:可以进行异常的处理

​ 编译时异常(checked)

​ IOException

​ FileNotFoundException

​ ClassNotFoundException

​ 运行时异常(unchecked,RuntimeException)

​ NullPointerException

​ ArrayIndexOutOfBoundsException

​ ClassCastException 类型转换异常(强转)

​ NumberFormatException 数值格式异常:类型不同

​ InputMismatchException 输入不匹配(让你输入int,你输入string)

​ ArithmeticException 算数异常(除数为0)

7.2 throws和throw的区别

​ throws:

​ 跟在方法声明后面,后面跟的时异常类名

​ 后面可跟多个异常类,用逗号隔开

​ 抛出后由方法调用者处理

​ throw:

​ 方法体内,后面跟异常对象名

​ 只能抛出一个异常对象

​ 抛出后由方法体内的语句处理

image-20220705172531600

8)overloading和overriding

1)、定义不同:

​ 重载:方法名相同、参数不同。 重写:子类重写父类的方法

2)、范围不同:

​ 重载:在一个类中。 重写:子类与父类之间的

3)、多态不同:

​ 重载:编译时的多态性。 重写:运行时的多态性

4)、参数不同:

​ 重载:参数的个数、类型、顺序可以不同。 重写:父类子方法参数必须相同

5)、修饰不同:

​ 重载:修饰范围没有要求。 重写:重写方法的修饰范围大于被重写方法的修饰范围

多态是一个类需要表现出多种形态,子类重写父类的方法,使子类具有不同的方法实现

八、Java常用类

1)字符串相关的类

image-20220705170950789

1.String类

image-20220705171017465

image-20220705171048852

        String s1 = "javaEE";
        String s2 = "javaEE";

        String s3 = new String("javaEE");
        String s4 = new String("javaEE");

        Person p1 = new Person("Tom", 12);
        Person p2 = new Person("Tom", 12);

        System.out.println(s1 == s2); //true
        System.out.println(s1 == s3); //false
        System.out.println(s1 == s4); //false
        System.out.println(s3 == s4); //false
        System.out.println("************************************");
        System.out.println(p1.getName() == p2.getName()); //true
        System.out.println(p1.getName().equals(p2.getName()));//true

image-20220705171128788

可以通过反射改变String

String s = 'Hello Word';
//首先通过反射获取到String中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");
//改变value的访问权限
valueFieldOfString.setAccessible(true);
//获取到s对象上value属性的值
char[] value = (char[]) valueFieldOfString.get(s);
//改变value所引用的数组中的第5个字符(索引,角标)
value[5]='_'; //Hello_Word

2.StringBuffer

image-20220705171154692

3.StringBuilder

2)JDK8之前的日期时间API

1.System静态方法

2.Date类

3.Calendar类

4.SimpleDateFormat类

3)JDK8中新时间日期API

1.LocalDate、LocalTime、LocalDateTime

2.Instant

3.DateTimeFormat

4.其他类

4)Java比较器

1.Comparable接口

2.Comparator接口

5)System类

6)Math类

7)BigInteger于BigDecimal

8) java.lang.Object类

	1. java.lang.Object类是Java语言中的根类,即所有类的父类。

	2. 如果一个类没有用 extends 关键字指定父类, 那么默认则继承自 java.lang.Object 类。

	3. Object类中的功能(属性,方法)就具有通用性

		属性:无
		方法:equals() / toString()/ getClass() / hashCode() / clone()	/finalize()	/wait()	/ notify()/notifyAll()

	4. Object 类只声明了一个空参的构造器

8.1 toString()

image-20220705171943153

方法摘要

public String toString():返回该对象的字符串表示。

toString方法返回该对象的字符串表示,其实该字符串内容就是对象的类型+@+内存地址值。

由于toString方法返回的结果是内存地址,而在开发中,经常需要按照对象的属性得到相应的字符串表现形式,因此也需要重写它。

image-20220705171958638

8.2 hashCode()

# 1.为什么重写hashcode
	Object类中定义的hashcode方法:
			生成的哈希码跟对象的本身属性值是无关的
	重写之后:
			我们可以自定义哈希码的生成规则,可以通过对象的属性值计算出哈希码
# 2.其他意义
	HashMap中,借助equals和hashcode方法来完成数据的存储
	将根据对象的内容查询转换为根据索引查询

8.3 equals()

# 1.== 和 equals 的区别
	1. == 是运算符,equals来自于Object类定义的一个方法
 	2. == 可以用于基本数据类型和引用类型,equals只能用于引用类型
# 2.为什么重写equals
	Object类中定义的equals
			比较引用数据类型时,比较的是地址  ,其实就是 ==
	重写之后:
			比较引用数据类型时,比较的是属性值

Object 类中 equals()的定义

public boolean equals(Object obj){
    return (this == obj);
}

说明:Object类中定义的equals()和 == 的作用是相同的:比较两个对象的地址值是否相等,即两个引用是否指向同一个对象地址

image-20220705171924389

// String 的 equals() 源码
public boolean equals(Object anObject) {
        if (this == anObject) {		// 比较对象的地址
            return true;
        }
        if (anObject instanceof String) {		 // 比较数据类型
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {		// 比较内容
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

8.4 clone()

8.5 finalize()

8.6 wait()

# 面试题:sleep()和wait() 的异同?
1. 相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态
2. 不同点:
   1. 两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()
   2. 调用的要求不同:sleep()可以在任何需要的场景下调用;wait()必须使用在同步代码块或同步方法中
   3. 是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁

8.7 notify()

image-20220705171534466

8.8 notifyAll()

8.9 getClass()

九、多线程

1)基本概念

  1. 程序(program):一段静态代码
  2. 进程(process):运行起来的程序,作为资源分配的单位
  3. 线程(thread):一个程序内部的执行路径,多个线程共享堆空间

2)线程的创建和使用★

方式一:继承于Thread类

  1. 定义Thread类的子类
  2. 重写该类的 run() ,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
  3. 创建Thread子类的实例,即创建了线程对象。
  4. 调用线程对象的 star() 来启动该线程。
public class Test extends Thread {

    private static int ticket = 100; // 需用static修饰
    @Override
    public void run() {
        while (true){
            if(ticket>0){
                System.out.println(getName() + ":买票,票号为:"+ ticket);
                ticket --;
            }else {
                break;
            }
        }
    }

    public static void main(String[] args) {
        Test thread1= new Test();
        Test thread2 = new Test();
        Test thread3 = new Test();

        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");

        thread1.start();
        thread2.start();
        thread3.start();

    }

}
//上述代码中Thread.currentThread()方法返回当前正在执行的线程对象。
//getName()方法返回调用该方法的线程的名字。
//static 修饰 ticket,这样三个对象共享 ticket 属性。但还是会出现线程安全问题(有三个100号票)

image-20220705171229942

方式二:实现Runnable接口★

  1. 定义 runnable 接口的实现类
  2. 实现该接口的 run() 方法,该run()方法的方法体同样是该线程的线程执行体。
  3. 创建 Runnable 实现类的实例(对象)
  4. 依此实例作为参数传递给Thread类的构造器,该Thread对象才是真正的线程对象
  5. 调用线程对象的 start() 方法来启动该线程。
public class Test implements Runnable {
    private  int ticket = 100;    // 无需用static修饰
    @Override
    public void run() {
        while (true){
            if(ticket>0){
                System.out.println(Thread.currentThread().getName() + ":买票,票号为:"+ ticket);
                ticket --;
            }else {
                break;
            }
        }
    }
    public static void main(String[] args) {
        Test test = new Test();

        Thread thread1 = new Thread(test);
        Thread thread2 = new Thread(test);
        Thread thread3 = new Thread(test);

        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}
// 还是会有线程安全问题(3个100号)

3)线程的生命周期

  1. 新建:创建线程对象

  2. 就绪:.star() 后,什么都有了,就差CPU资源

  3. 运行:获取到CPU资源,执行.run() 方法

  4. 阻塞:让出CPU并临时终止自己的运行

  5. 死亡:完成全部工作、线程被提前强制终止、出现异常

    image-20220705171301139

4)线程的同步★

image-20220705171332952

4.1 同步代码块

image-20220705171348853

public class Test implements Runnable {
    private  int ticket = 100;	// 继承Thread方式:private static int ticket = 100;
    Object obj = new Object();  // 继承Thread方式:static Object obj = new Object();
    @Override
    public void run() {
        while (true){ // whrile(true) 不能被包含进去,不然会变成全是一个窗口在卖票
            //synchronized (obj){
            synchronized (this){ // 继承Thread方式:不能用这种,不是同一个对象
                if(ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":买票,票号为:"+ ticket);
                    ticket --;
                }else {
                    break;
                }
            }

        }
    }

    public static void main(String[] args) {
        Test test = new Test();

        Thread thread1 = new Thread(test);
        Thread thread2 = new Thread(test);
        Thread thread3 = new Thread(test);

        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

4.2 同步方法

public class Test implements Runnable {
    private  int ticket = 100;
    @Override
    public void run() {
        while (true){
            show();
        }
    }
    // private static synchronized void show(){  // 同步监视器:Test.class, 继承Thread方法需要这么写
    private synchronized void show(){ // 同步监视器:this  继承Thread方法:这么写不行,此时锁:t1,t2,t3
        if(ticket>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":买票,票号为:"+ ticket);
            ticket --;
        }
    }
    public static void main(String[] args) {
        Test test = new Test();

        Thread thread1 = new Thread(test);
        Thread thread2 = new Thread(test);
        Thread thread3 = new Thread(test);

        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

image-20220705171513164

4.3 Lock锁 ------ JDK5.0新增

public class Test implements Runnable {
    private int ticket = 100;
    //1. 实例化ReentrantLock
    private ReentrantLock lock = new ReentrantLock(true);// 参数默认为:false,不公平的锁
    @Override
    public void run() {
        while (true) {
            try{
                //2.上锁
                lock.lock();
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }finally {
                //3.解锁
                lock.unlock();
            }
        }
    }
    public static void main(String[] args) {
        Test test = new Test();

        Thread thread1 = new Thread(test);
        Thread thread2 = new Thread(test);
        Thread thread3 = new Thread(test);

        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

5)线程的通信

image-20220705171534466

面试题:sleep()和wait() 的异同?
  1. 相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态
  2. 不同点:
    1. 两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()
    2. 调用的要求不同:sleep()可以在任何需要的场景下调用;wait()必须使用在同步代码块或同步方法中
    3. 是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁

6)JDK5.0新增线程创建方式

方式三:实现Callable接口

//1.创建Callable接口的实现类
public class Test implements Callable {
    //2.重写call()方法
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i=0; i <=100 ;i++){
            if (i % 2 == 0){
                System.out.println(Thread.currentThread().getName()+" : " + i);
                sum += i;
            }
        }
        return sum;
    }
    public static void main(String[] args) {
        //3.创建Callable接口的实现类的对象
        Test test = new Test();
        //4.将Callable接口的实现类的对象作为参数,传递到FutureTask构造器中,创建FutureTask对象
        FutureTask futureTask = new FutureTask(test);
        //5.将FutureTask对象作为参数,传递到Thread类构造器中,创建Thread对象,并start()
        new Thread(futureTask).start();
        try {
            //6.获取Callable中call()方法的返回值
            // get()返回值即为Callable实现类重写的call()的返回值
            Object sum = futureTask.get();     // Thread-0 : 2,4....100
            System.out.println("总和为:"+ sum);// 总和为:2550
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

方式四:使用线程池★

image-20220705171555589

public class Test {
    public static void main(String[] args) {
        //1.创建线程池
        ExecutorService service = Executors.newFixedThreadPool(10);// ExecutorService是接口,不能改属性
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;// ThreadPoolExecutor是类,能改
        //设置指定属性
        service1.setCorePoolSize(20);
        //2.执行指定的线程操作
        service.execute(new Test1());//适用于Runnable
        service.execute(new Test2());//适用于Runnable
//        service.submit(Callable callable);//适用于Callable
        //3.关闭线程池
        service.shutdown();

    }
}
class Test1 implements Runnable{
    @Override
    public void run() {
        int sum = 0;
        for (int i=0; i <=100 ;i++){
            if (i % 2 == 0){
                System.out.println(Thread.currentThread().getName()+" : " + i);
            }
        }
    }
}
class Test2 implements Runnable{
    @Override
    public void run() {
        int sum = 0;
        for (int i=0; i <=100 ;i++){
            if (i % 2 != 0){
                System.out.println(Thread.currentThread().getName()+" : " + i);
            }
        }
    }
}

3)通过Callable和Future创建线程

(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。

(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。注释:FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。

(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。

(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

package com.nf147.Constroller;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableThreadTest implements Callable<Integer> {


    public static void main(String[] args) {
        CallableThreadTest ctt = new CallableThreadTest();
        FutureTask<Integer> ft = new FutureTask<>(ctt);
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " 的循环变量i的值" + i);
            if (i == 20) {
                new Thread(ft, "有返回值的线程").start();
            }
        }
        try {
            System.out.println("子线程的返回值:" + ft.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }

    @Override
    public Integer call() throws Exception {
        int i = 0;
        for (; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
        return i;
    }
}

4)创建线程的三种方式的对比

1.采用实现Runnable、Callable接口的方式创见多线程时,优势是:

(1)线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。

(2)在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

劣势是:

(1)编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。

2.使用继承Thread类的方式创建多线程时优势是:

(1)编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。

劣势是:

(1)线程类已经继承了Thread类,所以不能再继承其他父类。

https://blog.csdn.net/weixin_44602460/article/details/119862617

7) 线程池,7个参数

7.1 为什么要用线程池

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建、销毁线程造成的消耗。
  2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控

7.2 ThreadPoolExecutor线程池类参数详解

image-20220705171702340

当线程池任务处理不过来的时候(什么时候认为处理不过来后面描述),可以通过handler指定的策略进行处理,ThreadPoolExecutor提供了四种策略:

  1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常;也是默认的处理方式。
  2. ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
  3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
  4. ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

可以通过实现RejectedExecutionHandler接口自定义处理方式。

7.3 线程池任务执行

① 添加执行任务
  • submit() 该方法返回一个Future对象,可执行带返回值的线程;或者执行想随时可以取消的线程。Future对象的get()方法获取返回值。Future对象的cancel(true/false)取消任务,未开始或已完成返回false,参数表示是否中断执行中的线程
  • execute() 没有返回值。
② 线程池任务提交过程

一个线程提交到线程池的处理流程如下图

image-20220705171716904

  1. 线程池中的数量 < corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
  2. 线程池中的数量 == corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。
  3. 线程池中的数量 >= corePoolSize,缓冲队列workQueue满,并且线程池中的数量 < maximumPoolSize,建新的线程来处理被添加的任务。
  4. 线程池中的数量 > corePoolSize,缓冲队列workQueue满,并且线程池中的数量 == maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
  5. 线程池中的数量 > corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

总结即:优先级为 : 核心线程corePoolSize > 任务队列workQueue > 最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

注意:

  1. 当workQueue使用的是无界限队列时,maximumPoolSize参数就变的无意义了,比如new LinkedBlockingQueue(),或者new ArrayBlockingQueue(Integer.MAX_VALUE);
  2. 使用SynchronousQueue队列时由于该队列没有容量的特性,所以不会对任务进行排队,如果线程池中没有空闲线程,会立即创建一个新线程来接收这个任务。maximumPoolSize要设置大一点。
  3. 核心线程和最大线程数量相等时keepAliveTime无作用.
③ 线程池关闭
  1. shutdown() 不接收新任务,会处理已添加任务
  2. shutdownNow() 不接受新任务,不处理已添加任务,中断正在处理的任务

7.4 常用队列介绍

  1. ArrayBlockingQueue: 这是一个由数组实现的容量固定的有界阻塞队列.
  2. SynchronousQueue: 没有容量,不能缓存数据;每个put必须等待一个take; offer()的时候如果没有另一个线程在poll()或者take()的话返回false。
  3. LinkedBlockingQueue: 这是一个由单链表实现的默认无界的阻塞队列。LinkedBlockingQueue提供了一个可选有界的构造函数,而在未指明容量时,容量默认为Integer.MAX_VALUE。

队列操作:

image-20220705171737627

7.5 Executors线程工厂类

  1. Executors.newCachedThreadPool();
    说明: 可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.
    内部实现:new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new SynchronousQueue());
  2. Executors.newFixedThreadPool(int);
    说明: 定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    内部实现:new ThreadPoolExecutor(nThreads, nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue());
  3. Executors.newSingleThreadExecutor();
    说明: 单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照顺序执行。
    内部实现:new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue())
  4. Executors.newScheduledThreadPool(int);
    说明: 定长线程池,支持定时及周期性任务执行。
    内部实现:new ScheduledThreadPoolExecutor(corePoolSize)

【附】阿里巴巴Java开发手册中对线程池的使用规范

  1. 【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
    正例:

    public class TimerTaskThread extends Thread {
        public TimerTaskThread(){
            super.setName("TimerTaskThread"); 
            ...
        }
    }
    
    1. 【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
      说明: 使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资
      源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者
      “过度切换”的问题。

    2. 【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样
      的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
      说明: Executors 返回的线程池对象的弊端如下:
      1) FixedThreadPool 和 SingleThreadPool:
      允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
      2) CachedThreadPool 和 ScheduledThreadPool:
      允许的创建线程数量为 Integer.MAX_VALUE, 可能会创建大量的线程,从而导致 OOM。

7.6 线程池的执行流程

7.7 总结

ThreadPoolExecutor通过几个核心参数来定义不同类型的线程池,适用于不同的使用场景;其中在任务提交时,会依次判断corePoolSize, workQueque, 及maximumPoolSize,不同的状态不同的处理。技术领域水太深,如果不是日常使用,基本一段时间后某些知识点就忘的差不多了,因此阶段性地回顾与总结,对夯实自己的技术基础很有必要。

线程池源码:https://blog.csdn.net/Anenan/article/details/115603481

十、枚举类&注解

十一、集合

1)概述

image-20220705172546163

Collection接口:单列集合,用来存储一个一个的对象

​ List接口:存储有序的、可重复的数据。 --> “动态”数组

​ ArrayList:作为List接口的主要实现类;线程不安全,效率高;底层使用Object[] elementData 存储

image-20220705172603038

​ LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储

​ Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData 存储

​ Set接口:存储无序的、不可重复的数据 --> 高中讲的“集合”

​ HashSet:作为Set接口的主要实现类;线程不安全;可以存储null值

​ LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历

​ 对于频繁的遍历操作,LinkedHashSet效率高于HashSet

​ TreeSet:可以按照添加对象的指定属性,进行排序

image-20220705172635924

Map接口:双列集合,用来存储一对(key - value)数据 --> 高中函数:y = f(x)

​ HashMap : 作为Map的主要实现类;线程不安全,效率高;存储null的key和value

​ LinkedHashMap: 保证在遍历map元素时,可以按照添加的顺序实现遍历

​ 原因:在HashMap底层结构的基础上,添加了一对指针,指向前一个和后一个元素。

​ 对于频繁的遍历操作,此类执行效率高于HashMap

​ TreeMap :保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序

​ 底层使用红黑树

​ Hashtable :作为古老的实现类;线程安全,效率低;不能存储null的key和value

​ Properties:常用来处理配置文件。key和value都是String类型

HashMap底层:数组 + 链表(jdk7 及以前)

​ 数组 + 链表 + 红黑树(jdk8)

2)Collection 的遍历

    @Test
    public void test(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        
        Iterator iterator = collection.iterator();
        
	    while (iterator.hasNext()){
        System.out.println(iterator.next());
    }
    public void test() {

        Person person = new Person();
        person.name = "小明";
        person.age = 18;

        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(person);
        // for(集合元素的类型 局部变量:集合)
        // 内部仍然是Iterator迭代器
        for (Object object : coll) {
            System.out.println(object);
        }
    }
// 方式一:普通for循环,改变数组
for(int i=0; i<arr.length; i++){
    arr[i] = "GG";
}
// 方式二:增强for循环,不改变数组
for(String s : arr){
    s = "GG";
}

3)List

List list = new ArrayLlist();

image-20220705172655024

public void test1(){
    List list = new ArrayList();
    list.add(1);
    list.add(2);
    list.add(3);
    updataList(list);
    System.out.println(list);
}

private void updataList(List list) {
    list.remove(2);    //删除 2 的下标 3
    list.remove(new Integer(2)); //删除 2 的值
}

4)Set

	  Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。

​ 要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals()

​ 要求:重写的hashCode() 和 equals() 尽可能保持一致性:相等的对象必须具有相等的散列码

一、Set:存储无序的、不可重复的数据

以HashSet为例说明

无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。

不可重复性:保证添加的元素按照equals()判断时,不能返回true,即:相同的元素只能添加一个

二、添加元素的过程:以HashSet为例

image-20220705172711924

​ HashSet底层:数组+链表的结构

5)Map

image-20220705172739041

// Map的遍历
@Test
public void test(){
    Map map = new HashMap();
    map.put("AA",123);
    map.put(45,1234);
    map.put("BB",123);
    // 遍历所有的Key集:keySet()
    Set set = map.keySet();
    Iterator iterator = set.iterator();
    while (iterator.hasNext()){
        System.out.println(iterator.next());
    }
    System.out.println();
    // 遍历所有的value集:values()
    Collection values = map.values();
    for(Object obj : values) {
        System.out.println(obj);
    }
    System.out.println();
    // 遍历所有的key-value
    Set entrySet = map.entrySet();
    for(Object obj:entrySet){
        // entrySet中的元素都是entry
        Map.Entry entry = (Map.Entry) obj;
        System.out.println(entry.getKey()+"----->"+entry.getValue());
    }
}

image-20220705172822811

6) Collections 工具类

控制 Collection接口和 Map接口,和Arrays类似

image-20220705172838926

7)面试题

image-20220705172902039

十二、泛型

十三、IO流

image-20220705172932750

image-20220705173006727

image-20220705173021689

序列化:

​ ObjectOutputStream:内存中的对象 —》存储中的文件、通过网络传输出去

反序列化:

​ ObjectInputStream:存储中的文件、通过网络接收过来 —》 内存中的对象

image-20220705173032713

@Test
public void test() throws Exception {
    // 读取配置文件方式一:
    Properties properties = new Properties();
    FileInputStream fis = new FileInputStream("jdbc.properties");
    properties.load(fis);
    // 读取配置文件方式一:使用ClassLoader
    ClassLoader classLoader = GulimallProductApplicationTests.class.getClassLoader();
    classLoader.getResourceAsStream("jdbc.properties");
    properties.load(fis);

    String user = properties.getProperty("user");
    String password = properties.getProperty("password");
    System.out.println("user = " + user+ ",password = " + password);

}

十四、网络编程

十五、反射

1)反射机制概述

image-20220705173046441

2)理解Class类获取Class实例★

image-20220705173055776

获取Class实例的方式:

@Test
public void test() throws ClassNotFoundException {
    // 方式一:调用运行时类的属性:.class
    Class<Person> clazz1 = Person.class;
    System.out.println(clazz1);
    // 方式二:通过运行时类的对象,调用getClass()
    Person person = new Person();
    Class clazz2 = person.getClass();
    System.out.println(clazz2);
    // 方式三★:调用Class的静态方法:forName(String classPath)
    Class clazz3 = Class.forName("com.atguigu.gulimall.product.vo.Person");
    System.out.println(clazz3);
    // 方式四:使用类加载器:ClassLoader
    ClassLoader classLoader = GulimallProductApplicationTests.class.getClassLoader();
    Class clazz4 = classLoader.loadClass("com.atguigu.gulimall.product.vo.Person");
    System.out.println(clazz4);
}

3)类的加载与ClassLoader的理解

image-20220705173118163

4)创建运行时类的对象★

// 创建运行时类对象
@Test
public void test2() throws Exception {

    // 要想此方法正常创建运行时类对象,要求:
    	//1.运行时类必须提供空参构造器
    	//2.空参构造器访问权限得够。通常为public
    //原因:
    	//1.便于通过反射,创建运行时类对象
    	//2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
    Class<Person> clazz1 = Person.class;
    Person person = clazz1.newInstance();
    //clazz1.getFields():获取当前运行时类及其父类中所有public权限的属性
    //clazz1.getDeclaredFields():获取当前运行时类的所有属性(不包含父类中声明的属性)

    System.out.println(person);

}

5)获取运行时类的完整结构

6)调用运行时类的指定结构★

1.指定属性

// 如何操作 运行时类 中的指定属性
@Test
public void test() throws Exception {
    // 获取Class的实例
    Class<Person> clazz = Person.class;
    // 创建运行时类的对象
    Person p = clazz.newInstance();
    //1.getDeclaredField(String fieldName):获取 运行时类 中指定变量名的属性
    Field name = clazz.getDeclaredField("name");

    //2.保证当前属性时可访问的
    name.setAccessible(true);
    //3.获取、设置指定对象的此属性
    name.set(p,"Tom");

    System.out.println(name.get(p));
}

2.指定方法

// 如何操作 运行时类 中的指定方法
@Test
public void test() throws Exception {
    // 获取Class的实例
    Class<Person> clazz = Person.class;
    // 创建运行时类的对象
    Person p = clazz.newInstance();
    //1.getDeclaredMethod():参数1:指明获取方法的名称    参数2:指明获取的方法的形参列表
    Method showNation = clazz.getDeclaredMethod("showNation", String.class);

    //2.保证当前属性时可访问的
    showNation.setAccessible(true);
    //3.invoke(): 参数1:方法调用的对象   参数2:方法调用的实参
    // invoke()返回值即为当前类中调用方法的返回值,如果没有返回值(static 方法),则返回null
    Object o = showNation.invoke(p, "china"); // 我的国籍是:china
                                                    
    System.out.println(o); // china

}

3.指定构造器

// 如何操作 运行时类 中的指定构造器
@Test
public void test() throws Exception {
    // 获取Class的实例
    Class<Person> clazz = Person.class;
    // 创建运行时类的对象
    Person p = clazz.newInstance();
    //1.获取指定构造器getDeclaredConstructor() 参数:指明构造器的参数列表
    Constructor<Person> constructor = clazz.getDeclaredConstructor(String.class);
    //2.保证当前属性时可访问的
    constructor.setAccessible(true);
    //3.调用此构造器创建运行时类的对象
    Person tom = constructor.newInstance("Tom");
    System.out.println(tom);   // Person(name=Tom, age=0, id=0)
}

7)反射的应用:动态代理

1.静态代理

public class Test {
    public static void main(String[] args) {
        Star realStar = new RealStar();
        Star star = new Proxy(realStar);
        star.proSing();           //经纪人做前期准备
        star.sing();			 //明星:唱歌~~~~~~~~
        star.laterSing();      	  //经纪人做后期处理
    }
}
interface Star{
    void proSing();//唱歌前的准备
    void sing(); // 唱歌
    void laterSing();//唱歌后收拾舞台
}
// 被代理类
class RealStar implements Star{
    @Override
    public void proSing() { }
    @Override
    public void sing() { System.out.println("明星:唱歌~~~~~~~~"); }
    @Override
    public void laterSing() { }
}
// 代理类
class Proxy implements Star{
    private Star star;
    public Proxy(Star star){ this.star = star; }
    @Override
    public void proSing() { System.out.println("经纪人做前期准备"); }
    @Override
    public void sing() { star.sing(); }
    @Override
    public void laterSing() { System.out.println("经纪人做后期处理"); }
}

2.动态代理

十六、Java8新特性

1.接口

2.日期相关API

3.注解

4.集合( 饿汉式懒汉式,红黑树)

1.Lambda表达式

2.函数式(Functional)接口

3.方法引用与构造器引用

4.强大的Stream API

5.Optional类

十七、Java9&10&11新特性

十八、设计模式

# 1.设计原则
- 1.1 单一职责原则
- 1.2 开闭原则
- 1.3 里氏代换原则
- 1.4 依赖倒置原则
- 1.5 接口隔离原则
- 1.6 迪米特法则
- 1.7 合成/聚合复用原则
# 2.设计模式概念
- 2.1 设计模式引入 
- 2.2 创建型模式
- 2.3 结构型模式
- 2.4 行为型模式
# 2.2创建型设计模式
- 2.2.1 原型模式 
- 2.2.2 简单工厂模式
- 2.2.3 工厂方法模式
- 2.2.4 单例模式
	- 2.2.4.1 饿汉式
    - 2.2.4.2 懒汉式
    - 2.2.4.3 双重检测式
    - 2.2.4.4 静态内部类
    - 2.2.4.5 枚举式
# 2.3结构型模式
- 2.3.1 外观模式(门面模式)
- 2.3.2 装饰者模式
- 2.3.3 代理模式
	- 2.3.3.1 静态代理
	- 2.3.3.2 Proxy动态代理
	- 2.3.3.2 Cglib动态代理
# 2.4 行为型模式
- 2.4.1 责任链模式
- 2.4.2 观察者模式

1)23中设计模式分类

创建型模式,共五种:工厂方法模式、抽象工厂模式(IOC)、单例模式(spring,饿汉,懒汉,登记)、建造者模式、原型模式。

结构型模式,共七种:适配器模式(Adapter)、装饰器模式、代理模式(AOP)、外观模式、桥接模式、组合模式、享元模式(pool)。

行为型模式,共十一种:策略模式(ribbon)、模板方法模式、观察者模式、迭代子模式、责任链模式(interceptor)、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

2)单例模式(static)

image-20220705173137176

1.饿汉式和懒汉式

//单例设计模式:某个类只存在一个对象实例
public static void main(String[] args) {
        Bank bank1 = Bank.getInstance();
        Bank bank2 = Bank.getInstance();
        System.out.println(bank1==bank2);//true
    
        Order order1 = Order.getInstance();
        Order order2 = Order.getInstance();
        System.out.println(order1==order2);//true
    }
//饿汉式:加载时间长,线程安全
class Bank{
    //1.私有化类的构造器
    private Bank(){}

    //2.内部创建类的对象
    //4.要求此对象也必须声明为静态的
    private static Bank instance = new Bank();
    //3.提供公共的静态方法,返回类的对象
    public static Bank getInstance(){
        return instance;
    }
}

//懒汉式:延时创建对象,线程不安全 ----》 多线程时再修改
class Order{
    //1.私有化类的构造器
    private Order(){}
    //2.声明此类对象没有初始值
    //4.要求此对象也必须声明为静态的
    private static Order instance = null;
    //3.提供公共的静态方法,返回类的对象
    public static Order getInstance(){
        if (instance==null){
            instance = new Order();
        }
        return instance;
    }
}

2.解决懒汉式的线程安全问题

// 懒汉式改成线程安全的
class Order{
    private Order(){}
    private static Order instance = null;
    public static Order getInstance(){
//        // 方式一:效率差
//        synchronized (Order.class){
//            if (instance==null){
//                instance = new Order();
//            }
//            return instance;
//        }
        // 方式二:效率高
        if (instance==null){
            synchronized (Order.class){
                if (instance==null){
                    instance = new Order();
                }       
            }
        }
        return instance;
    }
}
  1. 有序列表1
    1. 有序列表2
      1. 无序列表1
      2. 无序列表2

3)代理模式(interface、AOP)

image-20220705173152302

1.静态代理

public static void main(String[] args) {
    Star s = new Proxy(new RealStar());
    s.confer();              //经纪人面谈
    s.signContract();		//经纪人签合同
    s.bookTicket();			//经纪人订票
    s.sing();			    //明星:唱歌~~~~~~~~
    s.collectMoney();		//经纪人收钱
}
interface Star{
    void confer();//面谈
    void signContract();//签合同
    void bookTicket();//订票
    void sing(); // 唱歌
    void collectMoney();//收钱
}
// 被代理类
class RealStar implements Star{
    @Override
    public void confer() { }
    @Override
    public void signContract() { }
    @Override
    public void bookTicket() { }
    @Override
    public void sing() {System.out.println("明星:唱歌~~~~~~~~");}
    @Override
    public void collectMoney() { }
}
// 代理类
class Proxy implements Star{

    private Star star;
    public Proxy(Star star){this.star = star;}
    @Override
    public void confer() {System.out.println("经纪人面谈");}
    @Override
    public void signContract() {System.out.println("经纪人签合同");}
    @Override
    public void bookTicket() {System.out.println("经纪人订票");}
    @Override
    public void sing() { star.sing();}
    @Override
    public void collectMoney() { System.out.println("经纪人收钱"); }
}

2.动态代理 ( 反射的应用、AOP )

要实现动态代理,需要解决的问题
问题一:如何根据加载到内存中的【被代理类】,动态的创建一个【代理类及其对象】。
问题二:当通过【代理类的对象】调用a方法时,如果动态的去调用【被代理类】中的同名方法a。

public class Test {
    public static void main(String[] args) {
        Star realStar = new RealStar();
        Star proxyInstance = (Star) ProxyFactory.getProxyInstance(realStar);
        proxyInstance.proSing();         //去找invoke()
        proxyInstance.sing("唱山歌");
        proxyInstance.laterSing();

    }
}
interface Star{
    void proSing();//唱歌前的准备
    void sing(String song); // 唱歌
    void laterSing();//唱歌后收拾舞台
}
// 被代理类
class RealStar implements Star{
    @Override
    public void proSing() {System.out.println("经纪人做前期准备"); }
    @Override
    public void sing(String song) { System.out.println("唱什么歌:"+ song);}
    @Override
    public void laterSing() {System.out.println("经纪人做后期处理");  }
}
class ProxyFactory{
    // 调用此方法,返回一个代理类对象,解决问题一。
    public static Object getProxyInstance(Object obj){ // obj:被代理对象
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.bind(obj);
        return java.lang.reflect.Proxy
                .newProxyInstance(obj.getClass().getClassLoader()
                        ,obj.getClass().getInterfaces(),handler);
    }
}
class MyInvocationHandler implements InvocationHandler{
    private Object obj;//需要使用被代理类的对象进行赋值
    public void bind(Object obj){
        this.obj = obj;
    }
    //通过【代理类对象】,调a方法时,会自动调用invoke()方法
    //将【被代理类】要执行的方法a声明在invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //method:【代理类对象】调用的方法,此方法也就作为【被代理类对象】要调用的方法
        //obj:【被代理类对象】
        Object returnValue = method.invoke(obj, args);
        return returnValue;
    }
}

4)工厂模式(interface)

分类:无工厂模式、简单工厂模式、工厂方法模式(23)、抽象工厂模式(23)

ic class Test {
public static void main(String[] args) {
Star realStar = new RealStar();
Star proxyInstance = (Star) ProxyFactory.getProxyInstance(realStar);
proxyInstance.proSing(); //去找invoke()
proxyInstance.sing(“唱山歌”);
proxyInstance.laterSing();

}

}
interface Star{
void proSing();//唱歌前的准备
void sing(String song); // 唱歌
void laterSing();//唱歌后收拾舞台
}
// 被代理类
class RealStar implements Star{
@Override
public void proSing() {System.out.println(“经纪人做前期准备”); }
@Override
public void sing(String song) { System.out.println(“唱什么歌:”+ song);}
@Override
public void laterSing() {System.out.println(“经纪人做后期处理”); }
}
class ProxyFactory{
// 调用此方法,返回一个代理类对象,解决问题一。
public static Object getProxyInstance(Object obj){ // obj:被代理对象
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
return java.lang.reflect.Proxy
.newProxyInstance(obj.getClass().getClassLoader()
,obj.getClass().getInterfaces(),handler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;//需要使用被代理类的对象进行赋值
public void bind(Object obj){
this.obj = obj;
}
//通过【代理类对象】,调a方法时,会自动调用invoke()方法
//将【被代理类】要执行的方法a声明在invoke()中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method:【代理类对象】调用的方法,此方法也就作为【被代理类对象】要调用的方法
//obj:【被代理类对象】
Object returnValue = method.invoke(obj, args);
return returnValue;
}
}


## 4)工厂模式(interface)

分类:无工厂模式、简单工厂模式、工厂方法模式(23)、抽象工厂模式(23)























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值