接口和抽象类的区别
比较点 | 抽象类 | 接口 |
---|---|---|
默认方法 | 抽象类可以有默认的方法实现 | java 8之前,接口中不存在方法的实现 |
实现方式 | 子类使用extends关键字来继承抽象类.如果子类不是抽象类,子类需要提供抽象类中所声明方法的实现 | 子类使用implements来实现接口,需要提供接口中所有声明的实现. |
构造器 | 抽象类中可以有构造器 | 接口中不能 |
和正常类区别 | 抽象类不能被实例化 | 接口则是完全不同的类型 |
访问修饰符 | 抽象方法可以有public,protected和default等修饰 | 接口默认是public,不能使用其他修饰符 |
多继承 | 一个子类只能存在一个父类 | 一个子类可以存在多个接口 |
添加新方法 | 抽象类中添加新方法,可以提供默认的实现,因此可以不修改子类现有的代码 | 如果往接口中添加新方法,则子类中需要实现该方法 |
什么是不可变对象?好处是什么?
不可变对象指对象一旦被创建,状态就不能再改变,任何修改都会创建一个新的对象,如 String、Integer及其它包装类.不可变对象最大的好处是线程安全.
switch中能否使用string做参数?
在JDK 1.7之前,switch只能支持byte,short,char,int或者其对应的包装类以及Enum类型.从JDK 1.7之后switch开始支持String类型.但到目前为止,switch都不支持long类型.
String s = new String("abc")
创建了几个String对象?
2个.一个是字符串字面常数,在字符串常量池中;另一个是new出来的字符串对象,在堆中.
请问s1==
s3是true还是false,s1==
s4是false还是true?s1==
s5呢?
String s1 = "abc";
String s2 = "a";
String s3 = s2 + "bc";
String s4 = "a" + "bc";
String s5 = s3.intern();
s1==
s3返回false,
s1==
s4返回true,
s1==
s5返回true.
“abc"这个字符串常量值会直接方法字符串常量池中,s1是对其的引用.由于s2是个变量,编译器在编译期间无法确定该变量后续会不会改,因此无法直接将s3的值在编译器计算出来,因此s3是堆中"abc"的引用.因此s1!=s3.对于s4而言,其赋值号右边是常量表达式,因此可以在编译阶段直接被优化为"abc”,由于"abc"已经在字符串常量池中存在,因此s4是对其的引用,此时也就意味s1和s4引用了常量池中的同一个"abc".所以s1==
s4.String中的intern()
会首先从字符串常量池中检索是否已经存在字面值为"abc"的对象,如果不存在则先将其添加到字符串常量池中,否则直接返回已存在字符串常量的引用.此处由于"abc"已经存在字符串常量池中了,因此s5和s1引用的是同一个字符串常量.
以下代码中,s5==
s2返回值是什么?
String s1="ab";
String s2="a"+"b";
String s3="a";
String s4="b";
String s5=s3+s4;
返回false.在编译过程中,编译器会将s2直接优化为"ab",将其放置在常量池当中;而s5则是被创建在堆区,相当于s5=new String(“ab”);
你对String对象的intern()熟悉么?
Stirng中的intern()
是个Native方法,它会首先从常量池中查找是否存在该常量值的字符串,若不存在则先在常量池中创建,否则直接返回常量池已经存在的字符串的引用. 比如
String s1="aa";
String s2=s1.intern();
System.out.print(s1==s2);
上述代码将返回true.因为在"aa"会在编译阶段确定下来,并放置字符串常量池中,因此最终s1和s2引用的是同一个字符串常量对象.
String,StringBuffer和StringBuilder区别?
String是字符串常量,final修饰;
StringBuffer字符串变量(线程安全);
StringBuilder 字符串变量(线程不安全).
此外StringBuilder和StringBuffer实现原理一样,都是基于数组扩容来实现的.
String和StringBuffer的区别?
String和StringBuffer主要区别是性能:
String是不可变对象,每次对String类型进行操作都等同于产生了一个新的String对象,然后指向新的String对象.所以尽量不要对String进行大量的拼接操作,否则会产生很多临时对象,导致GC开始工作,影响系统性能.
StringBuffer是对象本身操作,而不是产生新的对象,因此在有大量拼接的情况下,我们建议使用StringBuffer(线程安全).
需要注意现在JVM会对String拼接做一定的优化,比如
String s="This is only "+ "simple" +"test";
以上代码在编译阶段会直接被优化成会`String s=“This is only simple test”.
####### StringBuffer和StringBuilder
StringBuffer和StringBuilder的实现原理一样,其父类都是AbstractStringBuilder.StringBuffer是线程安全的,StringBuilder是JDK 1.5新增的,其功能和StringBuffer类似,但是非线程安全.因此,在没有多线程问题的前提下,使用StringBuilder会取得更好的性能.
java当中使用什么类型表示价格比较好?
如果不是特别关心内存和性能的话,使用BigDecimal.否则使用预定义精度的 double 类型.
在数据库中不使用BigDecimal,而是使用decimal
a=a+b与a+=b有什么区别吗?
+=操作符会进行隐式自动类型转换,此处a+=b隐式的将加操作的结果类型强制转换为持有结果的类型,而a=a+b则不会自动进行类型转换如:
byte a = 127;
byte b = 127;
b = a + b; // 报编译错误:cannot convert from int to byte
b += a;
以下代码是否有错,有的话怎么改?
short s1= 1;s1 = s1 + 1;
有错误.short类型在进行运算时会自动提升为int类型,也就是说s1+1
的运算结果是int类型,而s1是short类型,此时编译器会报错. 强转就能解决.
short i=1;
i = (short) (i+1);
线程池是否有用到,在什么场景下使用的,平常用的那种线程池?
-
有用到, 适合单系统的异步交互,在发送短信,存入日志时
- 说说创建线程池的重要参数
corePoolSize:线程池的大小。线程池创建之后不会立即去创建线程,而是等待线程的到来。当前执行的线程数大于该值时,线程会加入到缓冲队列。
maximumPoolSize:线程池中创建的最大线程数。
keepAliveTime:空闲的线程多久时间后被销毁。默认情况下,该值在线程数大于corePoolSize时,对超出corePoolSize值的这些线程起作用。
unit:TimeUnit枚举类型的值,代表keepAliveTime时间单位。
handler:线程拒绝策略。
- 说说创建线程池的重要参数
-
线程池调优怎么做?
基本思想:
高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换。
并发不高、任务执行时间长的业务要区分开:
IO密集型的任务,因为IO操作并不占用CPU,可以加大线程池中的线程数目,让CPU处理更多的业务
CPU密集型任务,线程池中的线程数设置得少一些,减少线程上下文的切换。
并发高、业务执行时间长,在于整体架构的设计,能否使用中间件对任务进行拆分和解耦。 -
平常用的那种线程池
newSingleThreadExecutor:一个单线程的线程池。如果因异常结束,会再创建一个新的,保证按照提交顺序执行。
newFixedThreadPool:创建固定大小的线程池。根据提交的任务逐个增加线程,直到最大值保持不变。如果因异常结束,会新创建一个线程补充。
newCachedThreadPool:创建一个可缓存的线程池。会根据任务自动新增或回收线程。
newScheduledThreadPool:支持定时以及周期性执行任务的需求。
newWorkStealingPool:JDK8新增,根据所需的并行层次来动态创建和关闭线程,通过使用多个队列减少竞争,底层使用ForkJoinPool来实现。优势在于可以充分利用多CPU,把一个任务拆分成多个“小任务”,放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。
在高并发场景下使用了那些锁的机制?
- 悲观锁(synchronize)
Java 中的重量级锁 synchronize
数据库行锁 - 乐观锁
Java 中的轻量级锁 volatile 和 CAS
数据库版本号 - 分布式锁(Redis锁)
网友评论