1.怎样声明一个类不会被继承,什么场景下会用
答: 使用final来修饰
如果一个类被final修饰,此类不可以有子类,不能被其它类继承,如果一个中的所有方法都没有重写的需要,当前类没有子类也罢,就可以使用final修饰类。
2.Java中==和equals有哪些区别
区别:
- equals 和== 最大的区别是一个是方法一个是运算符。
- == 可以用于基本数据类型也可以用于引用数据类型
- equals()只能用于引用数据类型(若没有进行重写,则比较的是引用类型变量所指向的对象地址)
==:如果比较的对象是 基本数据类型,则比较的是数值是否相等;如果比较的是引用数据类型,则比较的是对象
的地址值是否相等。
equals():用来比较方法两个对象的内容是否相等。
==注意:==equals 方法 不能用于基本数据类型的变量,如果没有对 equals 方法进行重写,则比较的是引用类型的变
量所指向的对象的地址
3.Java异常处理方式
三种处理方式:
- 声明异常
- 抛出异常
- 捕获异常
Java 通过面向对象的方法进行异常处理,一旦方法抛出异常,系统自动根据该异常对象寻找合适异常处理器(Exception Handler)来处理该异常,把各种不同的异常进行分类,并提供了良好的接口。
在 Java 中,每个异常都是一个对象,它是 Throwable 类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行处理。Java 的异常处理是通过 5 个关键词来实现的:try、 catch、throw、throws 和 finally。
在Java应用中,异常的处理机制分为声明异常,抛出异常和捕获异常。
区别 | throw | throws |
---|---|---|
位置不同 | 方法内部 | 方法的签名处,方法的声明处 |
内容不同 | 异常对象(检查异常,运行时异常) | 异常的类型(可以多个类型,用,拼接) |
作用不同 | 异常出现的源头,制造异常 | 在方法的声明处,告诉方法的调用者,这个方法中可能会出现我声明的这些异常。然后调用者对这个异常进行处理:要么自己处理要么再继续向外抛出异常 |
1.throws声明异常
1、如果一个方法可能生成某种异常,但是不能确定如果处理这种异常,则该方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由方法的调用者负责处理,而方法的调用者也可以选择继续处理或者向上抛出
2、在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是他的父类
public void f1() throws FileNotFoundException{
FileInputStream file = new FileInputStream("d://a.text");
//没有这个文件,会抛出异常,直接方法后面throws直接抛出这个异常类型,可以抛出多个或者直接抛出父类Exception
}
注意:
- 对于编译异常,程序中必须处理,比如用try-catch或者throws都可以
- 对于运行异常,程序中没有处理,默认的处理方法就是Throws
- 子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型和父类抛出的异常一致或者父类抛出异常类型的子类型
- 当在方法中调用另一个方法,这个方法会抛出异常,而抛出异常的类型决定是否报错
public static void f1() throws FileNotFoundException{
FileInputStream file = new FileInputStream("d://a.text");
}
public static void f2(){
f1();
//会报错
//FileNotFoundException属于编译异常,在执行f2方法时,f1会抛出异常,而f2没有异常的解决方法
}
public static void f1() throws ArithmeticException{
}
public static void f2(){
f1();
//不会报错
//因为ArithmeticException属于运行异常,并不要求程序员显示处理,因为运行异常有默认的处理方法Throws
}
2.throw抛出异常
throw主要用在的 方法 中,用来抛出自己的异常信息:
如:
switch(a){
case 1:
........;
break;
case 2:
.........;
break;
default:
throw new Exception("没有此方法");
//此处定义了一个异常父类,并抛出自己的异常信息“没有此方法”。
//此处抛出异常即方法介结束了
}
3.try/catch捕获异常
try{
//将可能有异常的代码放入
}catch(Exception e){
//捕获到异常
//当异常发生时,系统将异常封装成Exception对象e,传递给catch
//得到异常对象后,程序员自己处理
}finally{
//不管异常是否发生,都会执行的操作。一般是释放资源的代码
}
- try-catch
- 如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch里面
- 如果异常没有发生,则顺序执行try的代码模块,不会进入到catch
- 如果希望异常不论是否发生,都执行某段代码,就使用finally代码模块
- try-finally
- 这种用法相当于没有捕获异常,因此程序会直接崩掉
- 应用场景执行一段代码,无论是否发生异常,都必须执行某个业务逻辑
4.throw与throws的区别:
1.throw:运用于方法中,用于给调用者一个返回异常信息的对象,可以作为一个方法的结束,而且可以的到一个异常
2.throws;运用于方法的声明自身,用于表示当前方法不适合处理当前异常,而却应该有调用者来处理,throws这种方式将异常抛出
意义 | 位置 | 后面跟的东西 | |
---|---|---|---|
throws | 异常处理的方式 | 方法声明处 | 异常类型 |
throw | 手动生成异常对象的关键字 | 方法体中 | 异常对象 |
若throw创建了异常对象是编译时的异常,那么方法中需要使用throws或try…catch来处理当前
4.自定义异常在生产中如何应用
1、定义:自定义异常类名继承Exception或RuntimeException
2、如果继承Exception,属于编译异常
3、如果继承RuntimeException,属于运行异常
public class CustomException {
public static void main(String[] args) {
int age = 80;
if(!(age>=18 && age <=120)){
throw new AgeException("年龄需要在18—120之间");
}
System.out.println("你的年龄正确!!");
}
}
class AgeException extends RuntimeException{
public AgeException(String message){
super(message);
}
}
5.redis的过期键有哪些删除策略
过期精度
在 Redis 2.4 及以前版本,过期期时间可能不是十分准确,有0-1秒的误差。
从 Redis 2.6 起,过期时间误差缩小到0-1毫秒。
过期和持久
Keys的过期时间使用Unix时间戳存储(从Redis 2.6开始以毫秒为单位)。这意味着即使Redis实例不可用,时间也是一直在流逝的。
要想过期的工作处理好,计算机必须采用稳定的时间。 如果你将RDB文件在两台时钟不同步的电脑间同步,有趣的事会发生(所有的 keys装载时就会过期)。即使正在运行的实例也会检查计算机的时钟,例如如果你设置了一个key的有效期是1000秒,然后设置你的计算机时间为未来2000秒,这时key会立即失效,而不是等1000秒之后。
即过期策略是按照时间点来处理的,这里的时间点通过时间戳来存储
Redis如何淘汰过期的keys
Redis keys过期有两种方式:被动和主动方式。
当一些客户端尝试访问它时,key会被发现并主动的过期。
当然,这样是不够的,因为有些过期的keys,永远不会访问他们。 无论如何,这些keys应该过期,所以定时随机测试设置keys的过期时间。所有这些过期的keys将会从密钥空间删除。
具体就是Redis每秒10次做的事情:
- 测试随机的20个keys进行相关过期检测。
- 删除所有已经过期的keys。
- 如果有多于25%的keys过期,重复步奏1.
这是一个平凡的概率算法,基本上的假设是,我们的样本是这个密钥控件,并且我们不断重复过期检测,直到过期的keys的百分百低于25%,这意味着,在任何给定的时刻,最多会清除1/4的过期keys。
在复制AOF文件时如何处理过期
为了获得正确的行为而不牺牲一致性,当一个key过期,DEL
将会随着AOF文字一起合成到所有附加的slaves。在master实例中,这种方法是集中的,并且不存在一致性错误的机会。
然而,当slaves连接到master时,不会独立过期keys(会等到master执行DEL命令),他们仍然会在数据集里面存在,所以当slave当选为master时淘汰keys会独立执行,然后成为master。
扩展
绝对时间点过期
设置值的到期时间,根据时间的时间戳设置,例如:2021年6月1号上午10点10分10秒到期,就填此时刻的时间戳
// 设置 key 过期时间的时间戳(unix timestamp) 单位:秒时间戳,
// 例如:2021年6月1号上午10点10分10秒到期 timestamp=1622513410
expireat (key timestamp)
// 设置 key 过期时间的时间戳(unix timestamp) 单位:毫秒时间戳
// 例如:2021年6月1号上午10点10分10秒到期 timestamp=1622513410000
pexpireat (key milliseconds-timestamp)
相对时间点过期
设置值的有效时间,例如:有效时间60秒;更新value后过期时间会重新计算,例如:有效时间60秒,第59秒更新了value后,过期时间还是60秒后。
expire (key seconds) // 为给定 key 设置过期时间,以秒计。
pexpire (key milliseconds) // 设置 key 的过期时间以毫秒计。
时钟轮算法(即最近最少未使用算法)
6.redis缓存如何回收
回收策略
noeviction:返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但DEL和几个例外)
allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。
allkeys-random: 回收随机的键使得新添加的数据有空间存放。
volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。
volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键
allkeys-lfu:从所有键中驱逐使用频率最少的键
如果没有键满足回收的前提条件的话,策略volatile-lru, volatile-random以及volatile-ttl就和noeviction 差不多了。
选择正确的回收策略是非常重要的,这取决于你的应用的访问模式,不过你可以在运行时进行相关的策略调整,并且监控缓存命中率和没命中的次数,通过RedisINFO命令输出以便调优。
一般的经验规则:
- 使用allkeys-lru策略:当你希望你的请求符合一个幂定律分布,也就是说,你希望部分的子集元素将比其它其它元素被访问的更多。如果你不确定选择什么,这是个很好的选择。干掉最近最少未被访问的数据
- 使用allkeys-random:如果你是循环访问,所有的键被连续的扫描,或者你希望请求分布正常(所有元素被访问的概率都差不多)。随机无差别的干掉数据
- 使用volatile-ttl:如果你想要通过创建缓存对象时设置TTL值,来决定哪些对象应该被过期。优先干掉设置了过期时间且访问次数最少的数据
allkeys-lru 和 volatile-random策略对于当你想要单一的实例实现缓存及持久化一些键时很有用。不过一般运行两个实例是解决这个问题的更好方法。
为了键设置过期时间也是需要消耗内存的,所以使用allkeys-lru这种策略更加高效,因为没有必要为键取设置过期时间当内存有压力时。
回收进程如何工作
理解回收进程如何工作是非常重要的:
- 一个客户端运行了新的命令,添加了新的数据。
- Redi检查内存使用情况,如果大于maxmemory的限制, 则根据设定好的策略进行回收。
- 一个新的命令被执行,等等。
- 所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断地回收回到边界以下。
如果一个命令的结果导致大量内存被使用(例如很大的集合的交集保存到一个新的键),不用多久内存限制就会被这个内存使用量超越。