一、JUC
1.1 ForkJoin框架
ForkJoin框架是JDK1.7之后提出的一个多线程并发处理框架,本质上是对线程池的一种补充,它的核心思想是将一个大型任务拆分成多个小任务,分别执行,最终将小任务的结果进行汇总,形成最终的结果。
多个线程+多个任务
- ForkJoinTask 表示任务
- ForkJoinPool 表示线程(线程池的一种扩展)
1.2 递归 (面试常问)
什么是递归?
定义:编程语言中,函数直接或间接调用函数本身,则该函数称为递归,即一个方法自己调自己。
电影院求自己是第几排的问题,递推公式:
java
f(n) = f(n-1)+1,f(1) = 1
递归代码:
java
int f(int n){
if(n==1) return 1;
return f(n-1)+1;
}
递归需要满足3要素:
1.一个父问题可以拆分为若干个子问题,并且若干个子问题的结果汇总起来就是父问题的结果;
2.父问题和子问题,解题思路完全一致,只是数据规模不同。
3.存在终止条件
假如有n个台阶,每次你可以跨1个或者2个台阶,请问n个台阶一共有多少种走法?
可以根据第一步的走法将所有走法分为两类:
第一步走了1个台阶
第一步走了2个台阶
n个台阶的走法等于先走1个台阶后,n-1个台阶的走法+先走2个台阶,n-2个台阶的走法
java
f(1) = 1
f(2) = 2
f(n) = f(n-1)+f(n-2)
终止条件
f(1) = 1
f(2) = 2
递归代码:
java
public class DgTest {
public static void main(String[] args) {
int f = f(10);
System.out.println(f);
}
public static void test(){
test();
}
public static int f(int n){
if(n==1) return 1;
if(n == 2) return 2;
return f(n-1) + f(n-2);
}
}
1.创建一个ForkJoinTask任务,ForkJoinTask是一个抽象,需要创建一个类来继承ForkJoinTask的子类RecursiveTask,实现抽象方法compute,拆分的逻辑就写在compute方法中。
2.任务要通过ForkJoinPool来执行,将任务直接放入ForkJoinPool中,直接获取结果即可。
计算0-20亿数字相加之和。
java
import java.util.concurrent.RecursiveTask;
public class ForkJoinDemo extends RecursiveTask<Long> {
private Long start;
private Long end;
private Long temp = 100_0000L;
public ForkJoinDemo(Long start,Long end){
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if((end - start) < temp){
Long sum = 0L;
for (Long i = start;i<=end;i++){
sum += i;
}
return sum;
}else {
Long avg = (start+end)/2;
ForkJoinDemo task1 = new ForkJoinDemo(start,avg);
task1.fork();
ForkJoinDemo task2 = new ForkJoinDemo(avg+1,end);
task2.fork();
return task1.join() + task2.join();
}
}
}
java
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
public class ForkJoinTest {
public static void main(String[] args) {
Long startTime = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinDemo(0L,20_0000_0000L);
forkJoinPool.execute(task);
Long sum = 0L;
try {
sum = task.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
Long endTime = System.currentTimeMillis();
System.out.println(sum + "耗时" + (endTime - startTime) + "毫秒");
}
}
1.3 volatile关键字
java
public class SingletonDemo {
private volatile static SingletonDemo instance;
public SingletonDemo(){
System.out.println("创建了一个SingletonDemo对象");
}
public static SingletonDemo getInstance(){
if(instance == null){
synchronized (SingletonDemo.class){
if(instance == null){
instance = new SingletonDemo();
}
}
}
return instance;
}
}
volatile关键字的作用是可以使内存中的数据对线程可见。
Java内存模型:Java Memory Model
一个线程访问内存数据的时候,其实不是拿到数据本身,而是将数据复制保存到工作内存中,相当于使用的是个副本,对工作内存中的数据进行修改,修改完成之后再保存到主内存中,主内存对线程不可见。
java
import java.util.concurrent.TimeUnit;
public class Test4 {
private static int num = 0;
public static void main(String[] args){
new Thread(()->{
while(num == 0){
System.out.println("=======Thread=========");
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e){
e.printStackTrace();
}
num = 1;
System.out.println(num);
}
}
结果输出1,但是循环没有停止,因为主内存对线程不可见,子线程从主内存中取出num=0放入到工作内存中,主线程也从主内存中取出num=0放入工作内存,执行num=1,然后将num=1还回到主内存中,但是此时,子线程的工作内存中num=0,所以线程不会结束。
解决办法:属性加上volatile
java
private static volatile int num = 0;
二、集合框架
2.1 什么是集合?
多个对象,个数未知,类型未知。
集合可以简单理解为一个长度可以改变,可以保存任意数据类型的动态数组。
在Java中,集合不是一个类来完成的,而是由一组接口和类共同构成了一个框架体系,大致可以分为三层,最上层是一组接口,继而是接口的实现类,接下来是对集合各种操作的工具类。
|--------------|--------------------------------------------------|
| 接口 | 描述 |
| Collection | 集合框架最基础的接口,一个Collection存储一组无序的、不唯一的对象,一般不直接使用该接口 |
| List | Collection的子接口,存储一组有序、不唯一的对象,开发中常用的接口之一 |
| Set | Collection的子接口,存储一组无序,唯一的对象 |
| Map | 独立于Collection的另外一个接口,存储一组键值对象,提供键到值的映射 |
| Iterator | 专用来输出集合得接口,一般适用于无序集合,从前向后单向输出元素 |
| ListIterator | Iterator的子接口,可以双向输出集合中的元素 |
| Enumeration | 传统的输出接口,已经被Iterator取代 |
| SortedSet | Set的子接口,可以对集合中的元素进行排序 |
| SortedMap | Map的子接口,可以对集合中的键值元素进行排序 |
| Queue | 队列接口,此接口的实现类可以实现队列操作 |
| Map.Entry | Map的内部接口,描述Map中的一个键值对元素 |
三、Spring
Spring框架是Java开发的行业标准。第三方框架。
Spring全家桶:Spring MVC、Spring Boot 、SpringCloud、Spring Data JPA、Spring Security、Spring AI
项目开发基于Spring框架,由Spring框架搭建项目的基础环境,在此环境上再添加其他的业务框架。
pom.xml
java
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.2.18</version>
</dependency>
Spring不是业务层框架,它是构建业务层框架(SpringMVC、SpringBoot等)的框架
Spring提供的是底层的容器,该容器来构建业务层框架。
业务框架本质上也是由各个对象组成的,Spring提供的容器就是管理这些对象的。
Spring的两大核心组件:IOC(控制饭庄) + AOP(面向切面)
3.1 IOC
将对象的创建方式进行反转,由原来的手动new变成现在的Spring框架自动创建。
lombok 自动创建get、set、toString等方法的工具。
3.1.1 如何使用lombok?
1.pom.xml中添加依赖
java
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>
2.设置-插件-市场,下载lombok插件

3.代码中使用@Data
java
import lombok.Data;
@Data
public class User {
private int id;
private String name;
}
3.1.2 IOC创建对象的方式
IOC创建对象,只需要在配置文件(spring.xml)中设置要创建的对象即可。
方式一:在XML文件中配置Bean
1.pom.xml文件中添加依赖
java
<!--版本要对应jdk-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.31</version>
</dependency>
2.引入spring.xml文件
java
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- Bean 定义、组件扫描、数据源等配置放在这里 -->
<bean id="user" class="User"> <!--相当于new了个User,赋值给user-->
<!--为属性赋值-->
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
</bean>
</beans>
3.使用IOC方式获取值
java
import lombok.Data;
@Data
public class User {
private int id;
private String name;
}
java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
//IOC
//调用spring.xml配置
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
//获取id和name
System.out.println(applicationContext.getBean("user"));
}
}

注意:类必须使用@Data注解或者生成getter和setter方法,否则在xml文件中赋值会报错。
方式二:通过注解进行配置
java
package com.myspring;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Data
@Component
public class User {
@Value("1")
private int id;
@Value("张三")
private String name;
@Autowired //自动封装,获取Address类中address的值
private Address address;
}
java
package com.myspring;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Data
@Component
public class Address {
@Value("11") //用于bean注入,不要选择导入lombok包,否则会报错
private int id;
@Value("软件园")
private String address;
}
java
package com.myspring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
public static void main(String[] args) {
//Spring 需要扫描指定包路径下的类
ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.myspring");//类需要放在包下,否则会报错误:找不到具体包名
System.out.println(applicationContext.getBean("user"));
}
}

注意:
- 实体类必须添加注解
- 构建IOC的时候包必须覆盖实体类
- 一个类只能构造一个bean
方式三:基于配置类
将基于XML和注解进行整合,创建一个配置类来替代XML文件
java
package com.myspring;
import lombok.Data;
@Data
public class User {
private int id;
private String name;
private Address address;
}
java
package com.myspring;
import lombok.Data;
@Data
public class Address {
private int id;
private String address;
}
java
package com.myspring.configuration;
import com.myspring.Address;
import com.myspring.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 配置类相当于XML
*/
@Configuration
public class BeanConfiguration {
@Bean
public User user(){
User user = new User();
user.setId(1);
user.setName("李四");
Address address = new Address();
address.setId(11);
address.setAddress("软件园");
user.setAddress(address);
return user;//必须放最后,放在new Address()会报错
}
}
java
package com.myspring;
import com.myspring.configuration.BeanConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
public static void main(String[] args) {
//如果使用包名的话,配置类必须添加@Configuration,否则会报错。获取配置类,该注解可以省略
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfiguration.class);
// ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.myspring.configuration");
System.out.println(applicationContext.getBean("user"));
}
}

四、Maven
Maven帮助工程进行jar包管理以及父子工程构建的服务
jar包自动管理。
由Maven自动给程序导入jar
pom.xml中配置工程所需的jar
Maven有一个远程仓库,包含了所有的jar,本地只需要连接到远程仓库,就可以自动将相应的jar包下载到本地,再自动导入到工程中。
4.1 创建maven工程
1.File-新建-项目

2.选择maven生成器,内部项目:选择maven-....-webapp

