java面试

一 、线程安全方面的集合?
1、 Vector,Vector性能最差,所有的方法都是加了synchronized来同步,从而保证线程安全
2、 SynchronizedList是Collections类的静态内部类,它能把所有容器集合转换成线程安全的,比 Vector 有更好的扩展性和兼容性。
3、 CopyOnWriteArrayList是java1.5以后才加入的新类,从命名可以理解为复制在写入的List。
它的添加时加锁的(ReentrantLock ,非synchronized同步锁),读操作是没有加锁。

二 、 hashmap怎么转为线程安全?
场景:以前的业务实现就是一个hashmap,现在要保证线程安全如何实现?
答:可以使用Collections类,它能把所有的容器转换成线程安全的。

三 、 请问用过ConcurrentHashMap源码吗?知道它用了哪些锁吗?
答:CAS与synchronized来保证线程安全。

四 、 CAS原理,CAS中的一些经典问题?ABA
CAS原理:读取内存值,修改,然后比较内存中的值是否被修改,如果没有就把修改的值更新回内存。如果内存中的值被修改就重新读取内存中的值,进入下一次循环(自旋)

五 、 java怎么实现解决ABA问题?
答:AtomicStampedReference类,
Java中使用AtomicStampedReference来解决CAS中的ABA问题,它不再像compareAndSet方法
中只比较内存中的值也比较当前值是否相等,而且先比较引用是否相等,然后比较值是否相等,这样就避免了ABA问题。
解决 ABA 问题的原子类:AtomicMarkableReference(通过引入一个 boolean
来反映中间有没有变过),AtomicStampedReference(通过引入一个 int 来累
加来反映中间有没有变过)

六 、什么是Redis?
答: Redis是一款内存高速缓存数据库。Redis是一个键值存储系统,支持丰富的数据类型,如:String、list、set、hash。

七、Redis应用场景,能做什么
众多语言都支持Redis,因为Redis交换数据快,在服务器中常用来存储一些需要频繁调取的数据,节省内存开销,也极大的提升了速度。
将一些热点数据存储到Redis中,要用的时候,直接从内存取,极大的提高了速度和节约了服务器的开销。
1、会话缓存(最常用)
2、消息队列(支付)
3、活动排行榜或计数
4、发布,订阅消息(消息通知)
5、商品列表,评论列表

八 、redis持久化方式?rdb与aof?
redis数据持久化,为了解决系统宕机后进行数据恢复。
1、RDB:默认存储方式,Rdb可以手动触发(save,与bgsave),周期性触发,redis启动默认开启Rdb数据持久化。Rdb相当于给内存拍一个快照以dump.rdb文件形式存储在本地。优点:适合做冷备。恢复数据快;
2、aof:把redis写操作指令以appendonly.aof文件的方式保存在本地;优点:备份精确不会出现数据丢失,缺点:恢复数据慢,可能日志文件很大,浪费本地磁盘空间。

九 、 rdb与aof最大的区别?
1、存储方式不同,rdb是以dump.rdb文件存储,而aof是以appendonly.aof形式存储。
2、恢复数据方式不同:rdb恢复数据直接把文件加载到内存,而aof是从新去执行一遍日志记录的指令。

十 、jvm问题?怎么判断一个对象要被回收?
引用计数:每个对象中有一个计数器记录着对象被引用的次数,当为0的时候说明没有被引用,就会被标记回收。
可达性分析:从根节点开始去搜索存在引用的对象,如果在jcRoots中不会被回收,相反会被回收
1、当然判断一个对象是否被回收是引用计数器是否为0,
2、是否被一个可达对象所引用。
3、是否在根集中

十 一 、可达性算法中有一个概念叫GC roots知道吗?
jcRoots就是根集,在根集中的对象不会被回收,

十 二、 哪些对象可以被作为垃圾回收的根节点?
虚拟机栈(栈帧中的本地变量表)中引用的对象
方法区中的常量引用的对象
方法区中的类静态属性引用的对象
本地方法栈中 JNI(Native 方法)的引用对象
活跃线程(已启动且未停止的 Java 线程)

十三、 一个接口有多个实现的情况,怎么去解决注入对象的准确性?
@Primary注解
@Qualifier注解来解决实现冲突的问题。

	@Qualifier("dogPlays" )
    @Autowired
    private PetInterface petInterface;

    @Qualifier("catPlays" )
    @Autowired
    private PetInterface petInterface2;

如果不想用注解去解决还有其他的实现方式吗?
答:实现ApplicationContextAware接口,可以更方便的拿到ApplicationContext中的所有bean

@Component
public class AppUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext arg0) throws BeansException {
        applicationContext = arg0;
    }

    public static Object getObject(String id) {
        Object object = null;
        object = applicationContext.getBean(id);
        return object;
    }
} 

十四 、微服务项目中用到哪些组件?
1.eureka服务注册中心:用于服务注册以及服务发现
2.config配置中心;结合rabbitMQ实现动态刷新服务配置
3.ribbon负债均衡器:用做服务调用时的负债均衡
4.zuul网关路由:用于管理API网管以及动态路由
5.hystrix熔断机制:通过服务监控,服务降级以及服务熔断用来处理高并发情况下可能出现的问题

十五 、 sping config存在什么问题?
刷新配置需要手动刷新。结合rabbitmq bus
刷新过程,当我们在git上修改了配置,然后通过rabbitmq下发刷新指令,服务收到刷新指令之后去注册中心找到配置中心的地址,在配置中心下载自己的配置。配置中心在git上下载服务配置。

十六 、zuul相对于gateway的缺点有哪些
zuul的局限性,底层使用了servlet,存在阻塞IO的问题,gateway使用了netye。

十七、 RabbitMQ怎么保证消息可靠性?
1、生产者丢失消息:从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息;
2、消息队列丢数据:消息持久化。
处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。
这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。
这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。
那么如何持久化呢?
这里顺便说一下吧,其实也很容易,就下面两步
将queue的持久化标识durable设置为true,则代表是一个持久的队列
发送消息的时候将deliveryMode=2
这样设置以后,即使rabbitMQ挂了,重启后也能恢复数据
3、消费者处理消息成功后,手动回复确认消息。

十七、 RabbitMQ怎么实现手动确认?
手动确认就是消息消费完之后给队列一个回执,消息队列收到回执后确认消息被消费,才会删除消息。否则回进行消息回滚把消息分配给其他消费者`

//定义一个新的队列,名为 task_queue
//第二个参数是持久化参数 durable
ch.queueDeclare(“task_queue”, true, false, false, null);
//第三个参数设置消息持久化
ch.basicPublish("", “task_queue”,
MessageProperties.PERSISTENT_TEXT_PLAIN,
msg.getBytes());

十八、Java 8 方法引用
方法引用通过方法的名字来指向一个方法。
方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
方法引用使用一对冒号 :: 。

class Car {
    @FunctionalInterface
    public interface Supplier<T> {
        T get();
    }
    //Supplier是jdk1.8的接口,这里和lamda一起使用了
    public static Car create(final Supplier<Car> supplier) {
        return supplier.get();
    }
 
    public static void collide(final Car car) {
        System.out.println("Collided " + car.toString());
    }
 
    public void follow(final Car another) {
        System.out.println("Following the " + another.toString());
    }
 
    public void repair() {
        System.out.println("Repaired " + this.toString());
    }
 
    public static void main(String[] args) {
        //构造器引用:它的语法是Class::new,或者更一般的Class< T >::new实例如下:
         Car car  = Car.create(Car::new);
         Car car1 = Car.create(Car::new);
         Car car2 = Car.create(Car::new);
         Car car3 = new Car();
        List<Car> cars = Arrays.asList(car,car1,car2,car3);
        System.out.println("===================构造器引用========================");
        //静态方法引用:它的语法是Class::static_method,实例如下:
        cars.forEach(Car::collide);
        System.out.println("===================静态方法引用========================");
        //特定类的任意对象的方法引用:它的语法是Class::method实例如下:
        cars.forEach(Car::repair);
        System.out.println("==============特定类的任意对象的方法引用================");
        //特定对象的方法引用:它的语法是instance::method实例如下:
        final Car police = Car.create(Car::new);
        cars.forEach(police::follow);
        System.out.println("===================特定对象的方法引用===================");
 
    }
}

十九、Java 8 Lambda 表达式

Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

使用Lambda 表达式可以使代码变的更加简洁紧凑。

1.1 语法

lambda 表达式的语法格式如下:

(parameters) -> expression或(parameters) ->{statements; }

以下是lambda表达式的重要特征:

· 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。

· 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。

· 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。

· 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。*

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值