回顾Java知识点,面试题汇总Day10(持续更新)

一、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

相关推荐
小明同学013 小时前
C++后端项目:统一大模型接入 SDK(二)
开发语言·c++
Dicky-_-zhang3 小时前
Elasticsearch聚合查询优化实战
java·jvm
淼淼爱喝水3 小时前
【Ansible 入门实战】三种变量详解
java·linux·数据库·ansible·playbook
我不是懒洋洋3 小时前
【C++】类和对象( 类的定义、实例化、 this指针、 C++和C语言实现Stack对比)
c语言·开发语言·数据结构·c++·经验分享·算法·visual studio
Perry 1233 小时前
Java中的多态
java·开发语言
asdfg12589633 小时前
一文理解软件开发中的“设计模式”
java·设计模式·软件开发
2501_930707783 小时前
使用C#代码拆分 PowerPoint 演示文稿
开发语言·c#·powerpoint
hikktn3 小时前
企业级Spring Boot应用管理:从零打造生产级启动脚本
java·spring boot·后端
故事和你913 小时前
洛谷-【图论2-3】最小生成树1
开发语言·数据结构·c++·算法·动态规划·图论