回顾Java知识点,面试题汇总Day10(持续更新)
一、JUC
1.1 ForkJoin框架
ForkJoin框架是JDK1.7之后提出的一个多线程并发处理框架,本质上是对线程池的一种补充,它的核心思想是将一个大型任务拆分成多个小任务,分别执行,最终将小任务的结果进行汇总,形成最终的结果。
多个线程+多个任务
- ForkJoinTask 表示任务
- ForkJoinPool 表示线程(线程池的一种扩展)
1.2 递归 (面试常问)
什么是递归?
定义:编程语言中,函数直接或间接调用函数本身,则该函数称为递归,即一个方法自己调自己。
电影院求自己是第几排的问题,递推公式:
f(n) = f(n-1)+1,f(1) = 1递归代码:
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个台阶的走法
f(1) = 1 f(2) = 2 f(n) = f(n-1)+f(n-2)终止条件
f(1) = 1
f(2) = 2
递归代码:
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亿数字相加之和。
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(); } } }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关键字
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
一个线程访问内存数据的时候,其实不是拿到数据本身,而是将数据复制保存到工作内存中,相当于使用的是个副本,对工作内存中的数据进行修改,修改完成之后再保存到主内存中,主内存对线程不可见。
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
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
<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中添加依赖
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.32</version> </dependency>2.设置-插件-市场,下载lombok插件
3.代码中使用@Data
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文件中添加依赖
<!--版本要对应jdk--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.31</version> </dependency>2.引入spring.xml文件
<?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方式获取值
import lombok.Data; @Data public class User { private int id; private String name; }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文件中赋值会报错。
方式二:通过注解进行配置
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; }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; }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文件
package com.myspring; import lombok.Data; @Data public class User { private int id; private String name; private Address address; }package com.myspring; import lombok.Data; @Data public class Address { private int id; private String address; }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()会报错 } }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
