面试题汇总

106 阅读13分钟

一.手写spring框架:

手写一个完整的Spring框架是一个非常复杂和耗时的过程,需要对Java编程语言、面向对象编程、设计模式、IoC、AOP、Web开发等多个方面有深入的理解和经验。在这里,我简单介绍一下手写Spring框架的一些基本步骤和流程,以供参考:

1.  创建一个项目:使用Maven或Gradle创建一个Java项目,并添加必要的依赖,如Spring的核心依赖、JUnit测试框架等。

2.  设计框架的基本结构:Spring框架主要由IoC容器、AOP框架、Web框架等几个部分组成。因此,我们需要设计一个合适的基本结构,包括类、接口、注解等。

3.  实现IoC容器:IoC容器是Spring框架的核心,它负责管理Bean对象的创建、依赖注入、生命周期管理等任务。我们需要设计一个BeanDefinition接口,用于描述Bean对象的信息,如Bean的类型、依赖关系、生命周期等。然后实现一个BeanFactory接口,用于管理BeanDefinition对象的注册、获取、销毁等操作。

4.  实现AOP框架:AOP框架负责实现横向切面的功能,如日志、事务、安全等。我们需要设计一个AOP的注解,如@Aspect、@Pointcut、@Before等,用于标识切面的位置和执行顺序。然后实现一个AOP框架,用于解析注解、生成代理对象等操作。

5.  实现Web框架:Web框架负责处理HTTP请求和响应,将请求路由到正确的Controller中,并返回响应。我们需要设计一个@Controller注解,用于标识Controller类,一个@RequestMapping注解,用于标识请求的路径和方法,以及一个DispatcherServlet类,用于接收HTTP请求、解析路由、调用Controller方法并返回响应。

6.  实现其它功能:Spring框架还包括很多其它功能,如数据访问、缓存、消息队列等,根据需求实现相应的功能。

7.  编写测试用例:为了确保Spring框架的正确性和稳定性,需要编写充分的测试用例,覆盖所有的功能点和边界条件。

需要注意的是,手写一个完整的Spring框架是一项非常庞大和复杂的任务,需要深厚的Java编程经验和对框架设计的深刻理解。如果您想学习Spring框架,建议从Spring官方文档入手,逐步深入学习各个功能点和核心思想,并结合实际项目进行练习和实践。

Demo的话代码比较多 问题不常见目前不列举了。

二.数据库索引原理

1.  数据库索引是一种数据结构,用于提高数据库查询的效率。它通过在数据库中存储某些列的值和指向相应行的指针,使得对于这些列的查询变得更加快速。

2.  数据库索引的原理是将某些列的值和相应行的物理地址存储在一个数据结构中。当进行查询时,数据库系统会首先在索引中查找匹配条件的行,然后通过指针访问实际的数据行。

3.  常见的索引结构包括B-tree、Hash索引等。B-tree是一种多路平衡查找树,每个节点可以存储多个关键字,并且根据关键字进行分割。这样,通过在树上进行搜索,可以快速找到匹配条件的行。Hash索引则是将列值的哈希码作为索引,在查找时只需要进行哈希运算即可。

4.  索引的使用可以显著提高数据库的查询性能,但也有一些缺点。首先,索引需要额外的空间来存储索引数据结构,这会增加数据库的存储空间需求。其次,当对表进行更新时,索引也需要进行相应的更新操作,这会导致额外的性能开销。

5.  因此,在设计数据库时,需要仔细考虑是否需要使用索引,以及使用哪种类型的索引,并且需要根据具体的查询需求和数据更新频率来做出相应的决策。

三.mq的使用+demo

消息队列(Message Queue,简称MQ)是一种解耦系统组件之间通信的方法,它允许不同的组件之间异步地传递消息,从而提高系统的可扩展性、灵活性和可靠性。

下面是使用MQ的一般步骤:

1.  安装和配置:首先需要选择一种MQ软件并进行安装和配置。常见的MQ软件包括Apache Kafka、RabbitMQ、ActiveMQ等。

2.  创建队列:在MQ中,消息是存储在队列(Queue)中的,因此需要先创建一个队列。在创建队列时,需要指定队列的名称和一些其他参数,如持久化策略、消息过期时间等。

3.  发送消息:使用消息生产者(Producer)向队列中发送消息。消息可以是文本、二进制数据、JSON等格式的数据,消息生产者会将消息发送到指定的队列中。

4.  接收消息:使用消息消费者(Consumer)从队列中接收消息。消息消费者可以注册一个回调函数,当有新的消息到达队列时,MQ会调用回调函数来处理消息。

5.  确认消息:当消息被成功处理后,消息消费者需要向MQ确认消息已经被处理。这样MQ就可以将消息从队列中删除。

6.  处理消息:消息消费者从队列中接收到消息后,需要对消息进行处理。处理方式可以是将消息存储到数据库、进行业务逻辑处理等。

使用MQ可以带来许多好处,包括:

1.  解耦系统组件:使用MQ可以将不同的系统组件解耦,从而使得系统更加灵活和可扩展。

2.  异步处理:使用MQ可以异步地处理消息,从而减少响应时间和提高系统的吞吐量。

3.  可靠性:MQ可以提供高可靠性的消息传递,保证消息不会丢失或重复传递。

4.  负载均衡:使用MQ可以进行负载均衡,将消息发送到不同的消费者中,从而实现高效的消息处理。

RabbitMQ 的Demo:

1.  生产者代码示例:

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Connection;

import com.rabbitmq.client.ConnectionFactory;

public class ProducerDemo {

private static final String QUEUE_NAME = "my_queue";

public static void main(String[] args) throws Exception {

// 创建连接工厂

ConnectionFactory factory = new ConnectionFactory();

factory.setHost("localhost");

// 创建连接和通道

try (Connection connection = factory.newConnection();

Channel channel = connection.createChannel()) {

// 声明队列

channel.queueDeclare(QUEUE_NAME, false, false, false, null);

// 发送消息

String message = "Hello RabbitMQ!";

channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));

System.out.println("Sent message: " + message);

}

}

}

2.  消费者代码示例:

import com.rabbitmq.client.*;

import java.io.IOException;

public class ConsumerDemo {

private static final String QUEUE_NAME = "my_queue";

public static void main(String[] args) throws Exception {

// 创建连接工厂

ConnectionFactory factory = new ConnectionFactory();

factory.setHost("localhost");

// 创建连接和通道

Connection connection = factory.newConnection();

Channel channel = connection.createChannel();

// 声明队列

channel.queueDeclare(QUEUE_NAME, false, false, false, null);

// 定义消费者

Consumer consumer = new DefaultConsumer(channel) {

@Override

public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

String message = new String(body, "UTF-8");

System.out.println("Received message: " + message);

}

};

// 开始消费消息

channel.basicConsume(QUEUE_NAME, true, consumer);

}

}

四.集合框架相关:

跳转此网址:www.jianshu.com/p/0fc77e5aa…

五.spring的aop和ioc及其实现原理:

Spring的AOP和IOC是两个核心功能,分别代表着面向切面编程和控制反转。下面简单介绍一下这两个功能的实现原理。

1.  AOP实现原理

AOP(面向切面编程)是一种程序设计思想,它允许将类的业务逻辑与横切关注点(如事务处理、安全检查、日志记录等)分离出来,将这些横切关注点进行独立的模块化处理,从而提高系统的可维护性和可扩展性。

在Spring中,AOP的实现基于动态代理和字节码生成技术。当一个Bean被定义为一个切面时,Spring会使用Java动态代理或CGLib生成一个代理对象,在代理对象中织入横切逻辑。当被代理对象的方法被调用时,代理对象会先执行横切逻辑,然后再调用原始方法。

2.  IOC实现原理

IOC(控制反转)是一种程序设计思想,它通过将对象的创建、依赖关系的管理等工作交给框架来处理,从而实现程序的松耦合和可测试性。

在Spring中,IOC的实现基于Bean工厂和依赖注入技术。当Spring启动时,它会读取配置文件或注解信息,通过Bean工厂创建所有的Bean,并将这些Bean的依赖关系自动注入。当一个Bean需要其他Bean的实例时,它会向Spring容器请求该实例,Spring会在容器中查找并返回该实例。通过这种方式,Spring将对象的创建和依赖关系的管理都交给了框架来处理,实现了程序的松耦合和可测试性。

总之,Spring的AOP和IOC功能都是通过代理技术和依赖注入技术来实现的,它们都是Spring框架的核心功能,为企业级应用的开发提供了非常方便和灵活的支持。

更具体一点的回答:

1.  AOP实现原理

在Spring中,AOP的实现主要依赖于以下两个技术:

1.1. 动态代理:Java中的动态代理机制允许在运行时动态地创建代理类及其对象,从而可以在调用原始对象的方法前后加入额外的逻辑。Spring使用Java动态代理或CGLib代理技术来实现AOP。

在Java中,代理对象必须实现一个接口,所以如果一个Bean需要被AOP代理,就必须实现一个接口。当一个Bean被定义为一个切面时,Spring会创建一个代理对象,并将切面逻辑织入到代理对象中。当被代理对象的方法被调用时,代理对象会先执行切面逻辑,然后再调用原始方法。

1.2. 切点和通知

在Spring中,AOP代理对象通过连接点来切入应用程序的流程,并通过切点来定位到连接点。通知是AOP代理对象在连接点执行前、执行后或抛出异常时执行的逻辑。

2.  IOC实现原理

在Spring中,IOC的实现主要依赖于以下两个技术:

2.1. Bean工厂

Bean工厂是Spring的核心容器,它负责创建、初始化、配置和管理Bean的生命周期。在Spring中,Bean工厂是所有Bean对象的容器,每个Bean在工厂中都有一个唯一的ID。

2.2. 依赖注入

依赖注入是将一个对象依赖的其他对象通过容器自动注入的过程。在Spring中,容器负责管理所有的Bean,并自动处理Bean之间的依赖关系,当一个Bean需要其他Bean的实例时,它会向Spring容器请求该实例,Spring会在容器中查找并返回该实例。

Spring中有三种依赖注入的方式:

2.2.1. 构造函数注入

在构造函数中传入依赖的Bean的实例,Spring会自动将传入的实例注入到对象中。

2.2.2. Setter方法注入

在对象中定义Setter方法,Spring会自动调用Setter方法并将依赖的Bean实例传入。

2.2.3. 接口注入

在Bean中实现特定的接口,Spring会自动通过接口调用来注入依赖的Bean实例。

总之,Spring的AOP和IOC功能都是通过动态代理和依赖注入来实现的。Spring将Bean的创建和依赖关系的管理都交给了框架来处理,实现了程序的松耦合和可测试性。这样,开发人员就可以将更多的精力放在业务逻辑的实现上,提高了开发效率

聊聊序列化:

一个类的对象要想序列化成功,必须满足两个条件:

该类必须实现 java.io.Serializable 接口。

该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的。

能够实现内存对象的网络传输、硬盘化。对象转成文件。

spring事务传播机制、事务失效的场景

java内存模型、堆内存的划分、垃圾回收机制、内存优化

www.zybuluo.com/TryLoveCatc…

String a=new String("abc"); 一共创建了几个对象?

两个。一个堆中的,一个字符串常量池中的。

spring bean 生命周期

mysql索引数据结构和类型

笔试面试题

1、  spring常用注解含义

@contrller@service@component@Data@value@Dao@RequestBody@param

2、  手写单例


1. 懒汉式单例实现

```java
/**
 * 懒汉式单例
 */
public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {
    }

    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}
```

2. 饿汉式单例实现

```java
/**
 * 饿汉式单例
 */
public class HungrySingleton {
    private static HungrySingleton instance = new HungrySingleton();

    private HungrySingleton() {
    }

    public static HungrySingleton getInstance() {
        return instance;
    }
}
```

3. 双重检查锁定实现

```java
/**
 * 双重检查锁定实现
 */
public class DoubleCheckLockSingleton {
    private volatile static DoubleCheckLockSingleton instance;

    private DoubleCheckLockSingleton() {
    }

    public static DoubleCheckLockSingleton getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckLockSingleton.class) {
                if (instance == null) {
                    instance = new DoubleCheckLockSingleton();
                }
            }
        }
        return instance;
    }
}
```

4. 静态内部类实现

```java
/**
 * 静态内部类实现
 */
public class InnerClassSingleton {
    private InnerClassSingleton() {
    }

    private static class SingletonHolder {
        private static InnerClassSingleton instance = new InnerClassSingleton();
    }

    public static InnerClassSingleton getInstance() {
        return SingletonHolder.instance;
    }
}
```

5. 枚举类单例实现

```java
/**
 * 枚举类单例实现
 */
public enum EnumSingleton {
    INSTANCE;

    public void doSomething() {
        // do something
    }
}
```

6. 容器单例实现

```java
/**
 * 容器单例实现
 */
public class ContainerSingleton {
    private static Map<String, Object> container = new ConcurrentHashMap<String, Object>();

    private ContainerSingleton() {
    }
    public static Object getInstance(String className) {
        Object instance = null;
        if (!container.containsKey(className)) {
            try {
                instance = Class.forName(className).getDeclaredConstructor().newInstance();
                container.put(className, instance);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return instance;
        } else {
            return container.get(className);
        }
    }
}

3、  手写统计给定字符串出现次数最多的字母和次数

给定字符串“abcdefadefadsfs",统计出现次数最多的字母和次数。(注意同样多的字母)

4、  统计成绩都大于80分的学生

studentcoursescore
张三数学80
张三语文90
张三英语90
李四数学70
李四语文80
李四英语88
select student group by student having min(score)>80;

5、  接口和抽象类的区别

1、都不能被实例化。
2、接口的实现类和抽象类的子类只有全部实现了接口或者抽象类中的方法后才可以被实例化。
不同点:
1、接口只能定义抽象方法不能实现方法,抽象类既可以定义抽象方法,也可以实现方法。
2、单继承,多实现。接口可以实现多个,只能继承一个抽象类。
3、接口强调的是功能,抽象类强调的是所属关系。
4、接口中的所有成员变量 为public static final, 静态不可修改,当然必须初始化。接口中的所有方法都是public abstract 公开抽象的。而且不能有构造方法。抽象类就比较自由了,和普通的类差不多,可以有抽象方法也可以没有,可以有正常的方法,也可以没有。

6、  线上系统出现问题如何快速定位和解决?

7、  以下代码输出结果?

public class Test extends Thread{
    public static void main( String[] args ) throws InterruptedException {
        Test t=new Test();
        t.run();
        System.out.printf("def");
    }

    @Override
    public void run() {
        System.out.print("abd");
    }
}