java面试

1.java多线程

1.1 java多线程通信?

多线程并发编程时,难免会遇到线程间的通信问题。线程通信方式的思想大体上来说可以分为两种:共享和传递

共享的实现方式可以是共享变量、共享文件、数据库、网络等。传递的实现方式可以是消息队列、生产者-消费者模型等。

(1)共享变量:

                 使用volatile 关键字定义共享变量  private static volatile Boolean odd = true。

                  多个线程同时监听一个变量,当这个变量发生变化的时候 ,线程能够感知并执行相应的业务。

                  volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。

(2)使用Object类的wait() 和 notify() 方法:

                 wait和 notify必须配合synchronized使用,wait方法释放锁,notify方法不释放锁。

(3)基本LockSupport实现线程间的阻塞和唤醒:

                 LockSupport.park();                  阻塞

                 LockSupport.unpark(threadB); 唤醒

1.2 java多线程实现方式?

(1)继承Thread类创建线程。

(2)实现Runnable接口创建线程。    多,多实现单继承。

(3)java的Executors线程池。减少对象创建, 提供定时执行、定期执行、单线程、并发数控制等功能。

1.3 java什么是线程池?

创建线程需要花费资源和时间,如果等任务来创建线程那么响应时间就变长。

在程序启动时创建线程来响应处理就是线程池。

1.4 java多线程start()与run()方法区别?

start():Thread类start()来启动一个线程,这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行。

             run()称为线程体,它包含了要执行的这个线程的内容。

run(): run()方法当作普通方法的方式调用,程序还是要顺序执行。没有达到多线程目的。

1.5 java多线程中wait()与sleep()区别?

(1)wait是Object方法,sleep是线程方法。

(2)wait会释放锁,sleep不释放锁。

(3)wait需要依赖synchronized关键字,sleep方法不依赖于同步器synchronized。

1.6 synchronized与static synchronized区别?

synchronized锁的是对象

static synchronized锁的是类

2.java基础

2.1 java多态?

不同类的对象对同一消息作出不同的响应就叫做多态。

多态存在的三个条件:

                        (1)有继承关系。

                        (2)子类重写父类方法。

                        (3)父类引用指向子类对象,父类调用方法会调用子类重写后方法。FU fu=new ZI();

2.2 java中final关键字?

(1)final成员变量表示常量,只能被赋值一次,赋值后值不再改变。

(2)final方法不能被子类的方法重写,但可以被继承。

(3)final类不能被继承,没有子类,final类中的方法默认是final的。

 (4)final不能用于修饰构造方法。

2.3 java中static关键字?

(1)静态变量。类加载只为分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问。

(2)静态方法。静态方法可以直接通过类名调用。静态方法不能访问非静态方法。

(3)static代码块。类加载的时候就会依次执行一次静态代码块。

2.4 接口保证幂等性是基本的要求,那么幂等性你们是怎么做的?

接口的幂等性实际上就是接口可重复调用,在调用方多次调用的情况下,接口最终得到的结果是一致的。有些接口可以天然的实现幂等性,比如查询接口,对于查询来说,你查询一次和两次,对于系统来说,没有任何影响,查出的结果也是一样。

除了查询功能具有天然的幂等性之外,增加、更新、删除都要保证幂等性。那么如何来保证幂等性呢?

2.4.1 全局唯一ID

如果使用全局唯一ID,就是根据业务的操作和内容生成一个全局ID,在执行操作前先根据这个全局唯一ID是否存在,来判断这个操作是否已经执行。如果不存在则把全局ID,存储到存储系统中,比如数据库、redis等。如果存在则表示该方法已经执行。

从工程的角度来说,使用全局ID做幂等可以作为一个业务的基础的微服务存在,在很多的微服务中都会用到这样的服务,在每个微服务中都完成这样的功能,会存在工作量重复。另外打造一个高可靠的幂等服务还需要考虑很多问题,比如一台机器虽然把全局ID先写入了存储,但是在写入之后挂了,这就需要引入全局ID的超时机制。

使用全局唯一ID是一个通用方案,可以支持插入、更新、删除业务操作。但是这个方案看起来很美但是实现起来比较麻烦,下面的方案适用于特定的场景,但是实现起来比较简单。

2.4.2 防重复提交策略

上述的保证幂等方案是分成两步的,第②步依赖第①步的查询结果,无法保证原子性的。在高并发下就会出现下面的情况:第二次请求在第一次请求第②步订单状态还没有修改为‘已支付状态’的情况下到来。

既然得出了这个结论,余下的问题也就变得简单:把查询和变更状态操作加锁,将并行操作改为串行操作。

2.4.3 防重表

这种方法适用于在业务中有唯一标的插入场景中,比如在以上的支付场景中,如果一个订单只会支付一次,所以订单ID可以作为唯一标识。这时,我们就可以建一张去重表,并且把唯一标识作为唯一索引,
在我们实现时,把创建支付单据和写入去去重表,放在一个事务中,如果重复创建,数据库会抛出唯一约束异常,操作就会回滚。

2.4.4多版本控制

这种方法适合在更新的场景中,比如我们要更新商品的名字,这时我们就可以在更新的接口中增加一个版本号,来做幂等

boolean updateGoodsName(int id,String newName,int version);

2.4.5状态机控制

这种方法适合在有状态机流转的情况下,比如就会订单的创建和付款,订单的付款肯定是在之前,这时我们可以通过在设计状态字段时,使用int类型,并且通过值类型的大小来做幂等,比如订单的创建为0,付款成功为100。付款失败为99

在做状态机更新时,我们就这可以这样控制

update `order` set status=#{status} where id=#{id} and status<#{status}

2.5 @Transactional来控制事务,那么能不能说出一些事务不生效的场景?

 2.5.1数据库引擎不支持事务

 2.5.2没有被spring管理

我们会把事务注解加到service层,如果没有@Service注解,这个类就不会被加载成一个Bean,那这个类就不会被spring管理,事务自然就失效了。。

 2.5.3 就是在@Transactional方法内部捕获了异常,没有在catch代码块里面重新抛出异常,事务也不会回滚。

所以在阿里巴巴的Java开发者手册里面有明确规定,在 @Transactional的方法里面捕获了异常,必须要手动回滚,
代码如下:

 @Override
    @Transactional
    public void insertOne() {
        try {
            UserEntity userEntity = new UserEntity();
            userEntity.setUsername("Michael_C_2019");
            //插入到数据库
            userMapper.insertSelective(userEntity);
            //手动抛出异常
            throw new IndexOutOfBoundsException();
        } catch (IndexOutOfBoundsException e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }

 2.5.4 同一个类中方法调用,导致@Transactional失效

开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。

那为啥会出现这种情况?其实还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。

 //@Transactional
    @GetMapping("/test")
    private Integer A() throws Exception {
        CityInfoDict cityInfoDict = new CityInfoDict();
        cityInfoDict.setCityName("2");
        /**
         * B 插入字段为 3的数据
         */
        this.insertB();
        /**
         * A 插入字段为 2的数据
         */
        int insert = cityInfoDictMapper.insert(cityInfoDict);
 
        return insert;
    }
 
    @Transactional()
    public Integer insertB() throws Exception {
        CityInfoDict cityInfoDict = new CityInfoDict();
        cityInfoDict.setCityName("3");
        cityInfoDict.setParentCityId(3);
 
        return cityInfoDictMapper.insert(cityInfoDict);
    }

 2.5.5 @Transactional修饰的方法为非public方法

根据spring官网,@Transactional只能用于public的方法上,否则事务不会生效,如果非要用在非public方法上,可以开启AspectJ代理模式。AspectJ使用加载时织入的方式,支持所有的pointcut,因此可以支持内部方法的事务设置。

 2.6 String, StringBulider, StringBuffer区别?

String适用于少量字符串操作。

StringBuilder单线程大量操作,不是线程安全的。

StringBuffer多线程大量操作,线程安全。

String常量,StringBuilder和StringBuffer字符串变量。

运行速度StringBuilder>StringBuffer>String。

 2.7 SpringMVC和Struct2区别?

SpringMVC入口是servlet, Struct2入口是filter

SpringMVC是基于方法, Struct2基于类

2.8 字符流和字节流的区别?

字符流用于处理文本,

字节流处理任何数据类型,计算器中数据以字节存储

2.9 Exception和error区别?

exception需要程序捕捉或者处理掉的异常

error系统级的错误

2.10 JAVA构造函数?

方法名与类名相同,没有返回值,

作用: 对象一建立就自动调用构造函数,

2.11 cookie和session的区别?

cookie数据存储在浏览器上,session在服务器上

cookie不安全,session安全

2.12 get与post请求区别?

get: 请求参数会显示在浏览器地址上,不安全, 长度有限制请求参数1k,get没有请求体

post: 请求参数不会显示在浏览器地址,相对安全,参数无限制

2.13 JAVA动态代理?

给方法添加预处理或者添加后续操作,不干预正常业务,SpringAop原理就是基于动态代理实现的.

实现方式:

1.JDK        实现invacationHandler接口 invoke()方法

2.CGLIB    实现methodInterceptor接口 intercept()方法

区别:  JDK反射机制提供的代理.

          CGLIB是利用asm开源包修改代理对象的字节码来处理.

3.数据库

3.1 mysql explain 查看sql语句执行计划

3.2 mysql查看进程 show processList

3.3 mysql触发器

一张表发生某件事件(插入,删除,更新), 就会自动触发预先编写好的sql语句。create trigger

增加程序复杂度使后期维护变得困难,尽量在代码中处理。

3.4 数据库的隔离级别

3.4.1读取未提交的。

读未提交,顾名思义,就是可以读到未提交的内容。

因此,在这种隔离级别下,查询是不会加锁的,也由于查询的不加锁,所以这种隔离级别的一致性是最差的,可能会产生“脏读”、“不可重复读”、“幻读”。

如无特殊情况,基本是不会使用这种隔离级别的

3.4.2.读取已提交的。

读提交,顾名思义,就是只能读到已经提交了的内容。

这是各种系统中最常用的一种隔离级别,也是SQL Server和Oracle的默认隔离级别。

3.可重复读。

同一事务下,两次相同查询结果可能不一样,而它也是MySql的默认隔离级别。

4.串行化。

这是数据库最高的隔离级别,这种级别下,事务“串行化顺序执行”,也就是一个一个排队执行。

这种级别下,“脏读”、“不可重复读”、“幻读”都可以被避免,但是执行效率奇差,性能开销也最大,所以基本没人会用。

4.注解

5.集合

 5.1 hashcode作用?

 hashcode主要用于查找的快捷性,在散列存储结构中确定对象的存储地址.

5.2 ArrayList原理?

ArrayList底层数据结构是一个数组,数组元素类型为Object类型,对其操作底层都是基于数组的,

线程不安全,可使用synchronied关键字保证代码安全.

 5.3 HashMap原理?

map以key-value键值对进行数据存储. 当传入数据时先将key取出, 利用hash函数转换成hash值, 再用散列算法得到存入下标index.

如果下标有数据用equal比较hash值是否相同, 再比较key值是否相同, 相同覆盖不同则存储.

hashmap默认长度16, 达到上限自动扩容

 5.4 collection与collections区别?

collection  是java.util下接口,集合结构父类

collections是java.util下类,操作集合的静态方法

5.5 ArrayList与LinkedList区别?

 ArrayList基于数组结构,LinkedList基于链表结构

随机访问 ArrayList优于LinkedList

新增删除 LinkedList优于ArrayList

5.6 ArrayList与Vector区别?

vector是线程安全的, Array不是, Vector效率比Array低.

5.7 HashSet与HashMap区别?

Set: 实现set接口, 存储对象, add加元素

Map:实现map接口, 存储键值对 ,put加元素, 更快

5.8 HashMap和HashTable区别?

都实现map接口, hashmap线程不安全但比hashtable快, 单线程用HashMap多线程用HashTable

5.9 数组和链表的区别?

数组::将元素在内存中连续存放,每个元素占用内存相同,可以通过下标迅速访问数组任何元素。增加,刪除就会移动大量元素在内存中腾出空间,适用快速访问。

链表: 链表中的元素在内存中不是顺序存储的,通过元素中指针联系在一起的,适用于增删。

原文地址:https://www.cnblogs.com/chong-zuo3322/p/12684741.html