JAVA编程试题集合2

1举例说明程序设计中的几种锁

1.悲观锁和乐观锁—悲观锁(Pessimistic Lock), 很悲观,每次拿数据都会上锁,这样别人想拿这个数据就会block直到获取锁。传统关系型数据库用到很多这种锁,比如行锁,表锁等,读锁,写锁等,都是在操作前先上锁。
乐观锁(Optimistic Lock), 很乐观,每次拿数据时都认为别人不会修改,所以不上锁,但更新时会判断在此期间别人有没有更新这个数据,可使用版本号等机制。
两种锁各有优缺点,不可认为一种好于另一种,乐观锁适用于读多写少的情况,冲突很少发生,这样可省去锁开销,加大系统吞吐量。但如果经常冲突(并发量大),上层应用会不断重试(判断经常不通过),这样反倒降低性能,所以用悲观锁就比较合适(高并发下修改数据库的同一行数据)。

2.读写锁:允许多个读操作同时进行,但每次只允许一个写操作。读写锁是一种性能优化策略。读数据时使用读锁,写数据时使用写锁,如果没有写锁时,读是无阻塞的,能提高程序执行效率。

3.自旋锁:自旋锁使线程在没有取得锁时不挂起,而去执行空循环(自旋),若干个空循环后如果可以获得锁则继续执行,若依然不能获得才被挂起。使用自旋锁后线程被挂起几率相对减少,线程执行连贯性得到加强。对于锁竞争激烈、锁占用时间很短的并发线程,具有积极意义,但对于锁竞争激烈,单线程锁占用很长时间的并发程序,自旋锁在自旋等待后还是无法获得锁,不仅仅浪费CPU时间,最终还被挂起,反而浪费系统资源。

2 解释一下RPC

  • RPC(Remote Procedure Call Protocol)就是远程调用,本质上是一种通信方式。核心思想是将不同进程间的通讯抽象为函数调用,基本过程是调用端通过将参数序列化到流中并发送给服务端,服务端从流中反序列化出参数并完成实际处理,然后将结果序列化后返回给调用端。通常RPC通过接口来实现,接口定义服务名,接口方法定义每个请求的入参和结果。RPC内部的序列化、网络通讯等细节则由框架完成,对用户完全透明。

3.索引在数据库中有什么作用?

  • 加快数据查询(以空间换时间)
  • 数据库中查询最频繁。索引是对数据库表中一个或多个列的值进行排序的结构。与搜索所有行相比,索引用指针指向存储(在表中指定列)的数据,然后根据指定次序排列这些指针,有助于更快获取信息。通常经常查询索引列时才需要在表上创建索引。索引将占用磁盘空间,影响数据更新速度。但多数情况下索引带来的速度优势大大超过不足。
  • 主键、二级索引、覆盖索引
  • 创建索引
ALTER TABLE koushao.`T_activitySign` ADD INDEX idx_SignerUnionId_SignStatus (`SignerUnionId`,`SignStatus`) ;

4.面向对象的特征有哪些方面?

  • 抽象:将一类对象的共同特征总结出来构造成类,包括数据抽象和行为抽象。抽象只关注对象有哪些属性和行为,不关注行为细节。
  • 继承:从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让程序有一定的延续性。
  • 封装:封装是把数据和操作数据的方法绑定起来,对数据访问只通过已有接口。面向对象的本质就是将现实世界描绘成一系列对象。在类中编写方法就是对实现细节的封装;编写一个类就是对数据和数据操作的封装。封装就是隐藏一切可隐藏的,只向外界提供最简单的接口(可以想想普通洗衣机和全自动洗衣机的差别,明显全自动洗衣机封装更好操作更简单;智能手机也是封装得足够好,因为几个按键(接口)就搞定所有事情)。
  • 多态性:多态性是指允许不同子类型的对象对同一消息作出不同响应。简单说是用同样对象引用调用同样方法但是做了不同事情。多态性分为编译时多态性和运行时多态性。方法重载(overload)实现了编译时多态性(也称为前绑定),而方法重写(override)实现了运行时多态性(也称为后绑定)。运行时多态是面向对象最精髓的东西,要实现多态需要做两件事:1). 方法重写(子类继承父类并重写父类中已有的或抽象的方法);2). 对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样方法就会根据子类对象的不同而表现出不同行为)。

5.什么是DAO模式?

  • DAO(Data Access Object)是一个为数据库提供抽象接口的对象,在不暴露底层存储细节的前提下提供各种数据访问操作。
  • 实际中就是建立一个接口,将所有数据访问操作抽象后封装起来,当需要和数据源交互时使用这个接口,并且编写一个单独的类来实现这个接口,在逻辑上该类对应一个特定的数据存储。
  • DAO模式实际包含两部分,一是解决如何访问数据,二是解决如何用对象封装数据。

6 voliatile修饰符有什么作用?

  • voliatile变量的变更对任何线程都立即可见,可避免并发修改变量导致的数据不一致(机制:插入内存屏障,确保所有访问者从主存储中读取数据,而不是从线程缓存中读取)*
  • 禁止指令重排序(JVM为加快程序执行做的优化,在单线程里可提升效率,多线程时可能导致同步问题)*

7解释Synchronized关键字

  • synchronized是Java关键字,是一种同步锁,它用来修饰一个方法或代码块时,保证在同一时刻最多只有一个线程执行该段代码:
  • 修饰方法,在方法声明中加入 synchronized关键字来实现:每个类实例有一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而避免了类成员变量的访问冲突。
  • 修饰代码块:synchronized 方法有缺陷:大方法声明为synchronized会大大影响效率,Java为我们提供了更好的解决办法,那就是 synchronized 块。(实际就是对尽可能少的必要代码加锁)
  • 不管是修饰方法还是代码块,要取得的都是所在对象的锁,而锁定时间则看加锁代码的执行效率。
  • 优化策略:读多写少的场景适合乐观锁、读写锁(尽量不加锁),高并发写适合synchronized同步锁。

8.什么是线程局部变量?

  • 局限于线程内部的变量,为线程自身所有,不在多线程间共享。
  • Java 提供 ThreadLocal 类来支持线程局部变量,是一种实现线程安全的方式。
  • 风险:需要合理的清理机制,比如web服务器工作线程的生命周期比应用变量的生命周期要长。线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。

9.解释名词:JRE、JDK、JVM 及 JIT

  • JRE 代表Java run-time,是运行Java引用所必须。
  • JDK 代表 Java 程序的开发工具,如 Java 编译器,它也包含 JRE。
  • JVM 代表 Java 虚拟机(Java virtual machine),责任是运行 Java 应用。
  • JIT 代表即时编译(Just In Time compilation),当代码执行次数超过一定阈值时,会将 Java 字节码转换为本地代码(热点编译),有利于提高 Java 应用性能。

10.值传递和引用传递

  • 值传递:方法调用时,实际参数把它的值传递给对应的形式参数,函数接收的是原始值的一个copy,此时内存中存在两个相等的基本类型,即实际参数和形式参数,后面方法中的操作都是对形参这个值的修改,不影响实际参数的值。
  • 引用传递:方法调用时,实际参数的引用(内存地址)被传递给方法中的形式参数,函数接收实际参数的内存地址;形参和实参指向同一块内存,方法执行中对引用的操作将影响到实际对象。
  • 基本数据类型使用传值,对形参的修改不影响实参;引用类型传引用,形参和实参指向同一内存地址,对参数的修改会影响到实际对象;不可变类(String,Integer等)由于自身特性,可以理解为传值,最后操作不会修改实参对象

11.使用索引时的注意点

索引会大大提高数据检索速率,也会降低更新表的速率,因为更新时需要额外更新索引
索引不会包含有NULL的列(数据库设计时不要让字段默认值为NULL)
索引要精短(节省存储,提升效率),对于长的索引字段可使用前缀截取
like操作不推荐,使用的话,"%aaa%"不会使用索引,"aaa%"可以使用索引

12.说明虚拟机是如何处理new关键字的?

1.首先检查这个指令的参数是否能在常量区定位到一个类的引用,并检查该类是否被加载
2.如果1不能满足,先进行类加载操作
3.满足1后,虚拟机将为新生对象分配内存(指针碰撞-空闲列表)
4.分配万内存后,对内存做初始化
5.虚拟机设置对象的信息,hash、GC信息等。—新对象至此完全生成

13.简要说明类加载的过程

将class文件加载到内存,并对数据做校验、解析、初始化,最终转化为虚拟机可以直接使用的java类型,在程序运行过程中完成
触发类加载的时机:1-new关键字,调用/读取类的静态方法/字段;2-使用java.lang.reflect对类做反射调用时;3-初始化类时,先初始化父类
1.加载:1.1-通过类的全限定名获取定义该类的二进制字节流;1.2-将二进制字节流代表的静态存储结构转换为方法区的运行时数据结构;1.3-在内存中生成java.lang.Class对象,作为对该类的访问入口
2.验证:确保class文件的字节流包含信息符合虚拟机的要求
3.准备:为类变量分配内存并设置类变量初始值(不包括实例变量–实例变量随对象一块分配在堆中)
4.解析:将常量池中的符号引用替换为直接引用
5.初始化:类加载的最后一步,开始执行java程序的代码–执行类构造器的()方法

14.简述https建立连接的过程

1.客户端浏览器发出安全请求(https前缀)
*2.服务端发送数字证书(包含服务器的publicKey) *
3.客户端使用预置的CA列表验证证书,如果有问题则提示风险
4.客户端生成随机的对称密钥,用服务器的publicKey加密
5.服务器用自己的privateKey解密,得到对称密钥
6.双方都使用对称密钥加密通信

数字证书的原理:
(1){RSA(hash(message))+message} (message中包含publicKey)(前半部分为数字签名sign)
(2)比对reRSA(sign) == hash(message)
(3)reRSA使用CA的公钥来解密

15.简述虚拟机中实例对象的创建过程

1.从字节码中获取目标对象在常量池的索引
2.根据索引获取常量池中的类数据
3.如果满足类的快速分配条件,则进行快速分配(2.1开始TLAB时尝试在TLAB中分配,2.2无法进行TLAB分配则持续尝试在EDEN区分配,直至分配成功)
4.无法做快速分配,则使用慢速分配(确保类不是抽象类,做类初始化并分配实例)
5.实例分配完成后,设置线程栈顶的对象引用

16.java注解

即元数据:描述数据A(类、方法、变量等)的数据B,对数据A做声明,然后通过反射获取描述信息,并根据描述信息的条件来执行数据A
@Override:在编译阶段生效,编译的时候编译器会检查使用@Override修饰的方法是否和父类方法签名(方法名+参数)相同,如果不同则报错
使用场合:与java源代码写在一块,简单直观,不过注解散布在代码各处,有时不易维护,对于需要集中配置的场合,则推荐使用XML配置(如mybatis的数据源配置)
自定义注解:测试 @Test

@Retention(RetentionPolicy.RUNTIME)
@Target(ElmentType.METHOD)
public @interface Test(){
    boolean ignore() default false;
}

public class AaTest{
    @Test
    public int test1(){
        return 1;
    }
    @Test(ignore=true)
    public int test2(){
        return 2;
    }
}

public static void main(String [] args){
    AaTest tt = new AaTest();
    run(tt);
}

public static void run(Object obj){
    for(Method m : obj.getClass().getMethods()){
        Test t = m.getDeclaredAnnoation(Test.class);
        if(t!=null && !t.ignore()){
            m.invoke(obj);
        }
    }
}

16.cookie—session—token

目的:解决http无连接的问题,提供连接中继;安全性
cookie:缓存http登录信息等,保存在客户端,client与服务端通信时服务端根据cookie解析出数据(有失效期)
session:服务端分配给客户端,客户端通信时加上session,服务端比对自己存储的session,一致则认为是同一请求—session需要集中存储(存储在服务端或者分布式缓存),存储压力大
token:服务端给客户端分配token,接收请求时根据token用密钥生成签名,与token中的签名比对即可—不要专门存储token,每次使用算法计算

猜你喜欢

转载自blog.csdn.net/weixin_40632321/article/details/83588230