Java杂项记录

“==”和equals()的区别

==比较基本数据类型,比较的是值是否相等,比较引用数据类型时是地址值是否相等,地址值相等代表引用同一个对象,返回true。
equals(),在Object类中比较的是地址值是否相等,而在其他类重写后,根据重写的equals()方法进行判断。

重载

  • 方法名相同
  • 返回值,参数类型,参数个数不完全相同
  • 重载方法可以改变修饰符
  • 构造方法能不能重写或者重载

重写

  • 参数列表与被重写方法相同
  • 返回类型可以不同,但必须是父类返回类型的子类
  • 访问权限不能比被重写方法低
  • 父类的成员方法只能被子类重写
  • final修饰的方法不能被重写
  • static修饰的方法不能被重写,但是能再次声明
  • 子类和父类在同一个包中,子类可以重写父类除了final和private修饰的所有方法
  • 子类和父类不在同一个包中,子类只能重写父类pubilc、protected和非final方法

基本数据类型

类型字节位数取值范围默认值
byte18-128~1270
short216-2^15 ~ 2^15-10
int432-2^31 ~ 2^31-10
long864-2^63 ~ 2^63-10
float4322^-126 ~ (2-2^-23) * 2^1270.0F
double8642^-1024 ~ (2-2^-52) * 2^10230.0D
char2160 ~ (2^16)-1‘\u0000’
boolean18true或者falsefalse

匿名内部类能被继承?

Integer和int的区别?

Integer是引用类型,int是基本类型,int默认值是0,Integer默认值是null。

String和StringBuffer的区别

String底层为final修饰的char数组,使用String长度是不可变的。
StringBuffer底层为char数组,但不被final修饰,所以长度可变。
StringBuffer的方法用了synchronhize修饰,所以是安全的。
String重写了equals()方法和hashCode()方法,而StringBuffer没有。

List、Map和Set的区别

Set、List集成自collection接口,所以都是单列元素集合。Map继承自其它接口。
Set和List都支持for循环,可以用迭代器迭代。
Set中没有重复元素,放入重复元素会被覆盖,无下标,无序,Set中的顺序是由hashCode决定的,存如Set的元素Object必须重写equals()。HashSet可放null,但只能存在一个null值,HashSet中的元素必须重写HashCode()方法。Set检索效率低,增删效率高,且不会引起元素位置发生变化。
List可重复,有下标,有序。List和数组类似,可以动态增长,检索效率高,增删慢,会引起元素位置发生变化。
Collocation和Map分别为两个不同的接口。Map键值对进行存储,为双列集合,Key不重复,value可以重复。
线程安全区别:LinkedList、ArrayList、HashSet非线程安全,Vector线程安全。HashMap非线程安全,HashTable线程安全(StringBuilder非线程安全,StringBuffer线程安全)。

List Hash 数组的区别

  • 存储对象:list只能存储对象,数组能存储基本数据类型和对象,hash存储键值对。
    注意:
//这种情况不会报错,看上去是添加的基本数据类型,但是添加的时候自动转换成了包装类型。
ArrayList array = new ArrayList();
array.add(100);
  • 存储顺序:List中存储的数据有序,是按照添加数据的顺序存储的。数组有序。hash没有顺序,输出的数据顺序不是存入的顺序。
  • 数据是否可重复:List中的数据可重复,数组中的数据可重复,hash中的数据不可重复,因为在HashMap中是通过键值对来存取的数据,当key重复的时候,会将原来的value替换新的value。
  • 获取元素个数:List通过size()方法,数组通过length属性,HashMap通过size()方法。

链表和数组的区别(LinkedList和ArrayList)

数组是线性结构,可以直接索引,即需要对第i个元素进行操作时,a[i]即可。
链表也是线性结构,需要对第i个元素进行操作时,需要指针往后遍历i次。
即链表和数组在查询操作时,数组效率较高。在增删操作时,数组效率较低。
**初始化:**数组无需初始化,因为数组的元素在内存的栈区,系统自动申请空间,而链表节点在内存的堆区,每个元素必须手动申请空间,即数组是静态分配内存,而链表是动态分配内存。
上面的都是废话,下面是总结:

  • 数组静态分配内存,链表动态分配内存
  • 数组在内存中连续,链表不连续
  • 数组元素在栈区,链表元素在堆区
  • 数组利用下标定位,时间复杂度为O(1),链表定位元素时间复杂度为O(n),我也说不清楚什么时时间复杂度
  • 数组插入元素,删除元素的时间复杂度为O(n),链表为O(1)
  • 数组查询快,增删慢,链表相反
  • 其他:数组需要预留控件,在使用前先申请需要的内存大小,可能浪费控件,链表在内存中可以存在任何地方,不要求连续,不能指定大小,方便拓展。
  • 查询效率:数组查询效率较高(因为可通过下标查询),链表查询效率较低。所以查询较多的情况下使用数组。
  • 增删效率:数组增删效率较低(因为需要移动增加或删除位置后面所有的数据),链表增删效率较高。所以增删操作频繁的情况下使用链表。
  • 内存空间:数组的内存空间连续,固定。链表动态分配。
  • 扩展性:数组扩展性差,链表扩展性强。因为数组创建完成时,就固定了内存大小,当内存满了的时候就无法扩展,只能重新创建。而链表是添加一个数据,分配一点内容空间。
  • 随机读取率:数组随机读取率高,链表随机读取率低。因为数组的内存空间是连续的,而链表是散乱的。
  • 位置:数组元素在栈区,链表元素在堆区

Http拦截器的作用

  • 登录session的验证
  • 记录系统日志
  • 对请求进行前置或者后置的操作

switch能否作用在byte上,能否作用在long上,能否作用在String上?

在switch(expr1)中,expr1只能是一个整数表达式或者枚举常量(在Java 5 开始引入枚举)。整数表达式可以是int基本类型或者Integer包装类型,由于byte,short,char都可以隐式的转换为int,所以这些类型以及这些类型的包装类型都可以作用域switch。long不能隐式的转换成int,所以long不能,在Java 7 开始可以使用String类型。

char型变量中能不能存一个中文汉字?为什么?

char类型变量是用来存储Unicode编码的字符的,Unicode编码字符集中包含了汉字,所以,char型的变量可以存储一个中文汉字。但是某个特殊的汉字没有包含再Unicode编码字符集中的话,那么这个char型变量就不能存储这个汉字。Unicode编码占用两个字节,所以char类型的变量也占用两个字节。

用最有效率的方法计算2 * 8

答案:2 << 3
将一个数左移n位,就相当于乘以2的n次方,那么,一个数×8,只需要将其左移3位即可。而位运算cpu直接支持,效率最高。

使用final修饰一个变量时,是引用不变还是引用的对象不变

使用final修饰变量时,引用不能变,引用对象中的内容能变。
如:

final StringBuffer a = new StringBuffer("immutable");
//执行如下语句,将会编译错误:
a = new StringBuffer("123");
//但是执行下面的语句,则编译通过:
a.append("broken!");

abstract的方法是否能用static、native、synchronized修饰

abstract方法不可以是static,抽象方法需要被子类实现,而static方法与子类无关。
native方法表示要用另外一种依赖平台的编程语言实现,不存在被子类实现的问题。
synchronized需要作用在一个具体的方法上才有意义,方法上的synchronized同步锁使用的同步对象是this,抽象方法无法确定this。

常见运行时异常

  • NullPointerException
  • ArrayIndexOutOfBoundsException
  • ClassCastException
  • ArithmeticException
  • OutOfMemoryException

ArrayList和Vector的区别

两者都实现了List接口,都是有序集合,都有位置索引,都允许重复。(HashSet不可以按索引检索元素,不允许有重复元素)。
同步性:Vector是线程安全的,也就是方法之间线程同步,ArrayList是线程不安全的。
数据增长:ArrayList与Vectot都有一个初始化的容量大小,当存进去的元素超过元素个数容量就需要增加ArrayList与Vector的存储空间,每次增加时,是增加多个存储单元,Vector默认增长为原来的两倍,也就是增加了一倍而ArrayList从源码看,增长为原来的1.5倍。Arraylist和Vector都可以设置初始的空间大小,Vector还可以设置增长空间的大小,ArrayList没有设置增长空间大小的方法。

List和Map的区别

List是存储单列数据的集合,Map是存储键值对的双列数据集合,List中存储的数据有顺序,并允许重复,Map无序不重复(键不能重复,值可以)。

线程和进程有什么区别

进程是一个独立运行的环境,可以看做一个程序或者一个应用,而线程是进程中执行的一个任务,线程是进程的一个子集。
一个进程可以有很多线程,每个线程并行的执行不同的任务,不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。

多线程编程有什么好处?

在多线程程序中,多个线程并发执行以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态。

如何实现线程?

  1. 继承Thread类,实现接口
  2. 实现Runnable接口

用户线程和守护线程有什么区别?

当在程序中创建一个线程,它就被称为用户线程。一个守护线程是在后台执行并且不会阻止JVM终止的线程。
当没有用户线程在运行时,JVM关闭程序并且退出。
一个守护线程创建的子线程任然是守护线程。

对线程优先级的理解

每个线程都有优先级,优先级越高的会有优先权,但是这依赖于线程调度的实现,这个实现和操作系统有关。
可以定义线程的优先级,但并不能保证高优先级一定会比低优先级先执行,线程优先级由1~10,1最低,10最高。

多线程中什么是上下文切换?

上下文切换是存储和恢复CPU状态的过程,它使线程执行能从断点恢复执行,上下文切换是多任务操作系统和多线程环境的基本特征。

线程的sleep()方法和yield()方法有什么区别?

  1. sleep()方法给其他线程运行机会时,不会考虑线程的优先级,因此会给低优先级的线程运行的机会。
  2. yield()方法只会给相同优先级或更高优先级线程的机会。
  3. 执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态。
  4. sleep()方法声明会抛出InterruptedException,而yield()不会抛出任何异常。
  5. sleep()方法比yield()方法具有更好的可移植性(跟操作系统CPU调度有关)。

启动一个线程调用run()还是start()方法?

启动一个线程调用的是start()方法,使线程代表的虚拟机处于可运行状态,这意味着它可以由JVM调度并执行,这并不意味着线程会立即执行。run()方法是线程启动后进行回调的callback方法。

Thread的sleep()方法和对象的wait()方法都可以让线程暂停执行,有什么区别?

sleep()方法是线程类(Thread)的静态方法,意为休眠,调用此方法会让当前线程暂停执行指定的时间,将执行机会让给其他线程,但对象的锁依然保持,因此休眠结束后会自动恢复到就绪状态。
wait()方法是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁,线程暂停执行,进入对象的等待池(wait pool),只有调用对象的notify()方法或者notifyAll()方法,才能唤醒等待池中的线程进入等锁池(lock pool),如果线程中心获得对象的锁就可以进入就绪状态。

Handler是什么?

Handler是用来结合线程消息队列来发送处理“Message”对象和“Runnable”对象的工具,每个Handler实例之后都会关联一个线程和该线程的消息队列。当创建一个Handler的时候,就会自动关联到所在的线程消息队列,然后会陆续把Message,Runnable分发到消息队列,并且在出队的时候处理掉。

Looper是什么?

每个线程只有一个Looper,在初始化Looper后,Looper会维护好该线程的消息队列,用来存放Handler发送的Message,并处理消息队列出队的Message。
Looper和它的线程是绑定的,处理消息也是在Looper所在的线程中处理,所以当主线程创建Handler时,它就会和主线程唯一的Looper绑定,从而我们使用Handler在子线程发送消息时,最终也是在主线程中处理。

MessageQueue是什么?

MessageQueue为一个消息队列,用来存放Handler发送的消息。每个线程最多只有一个MessageQueue,MessageQueue由Looper管理,主线程创建时,会创建一个默认的Looper对象,Looper对象的创建,将自动创建一个MessageQueue。非主线程,不会自动创建Looper。

Message是什么?

Message是MessageQueue中存放的对象,一个MessageQueue可以包含多个Message。当需要发送一个Message时,一般不用new Message()的形式来创建,推荐使用Message.obtain()来获取Message实例。因为在Message中定义了一个消息池,当消息池中存在未使用的消息时,便返回,如果没有,则通过new创建返回。这样可以减少Message产生垃圾回收的问题。

堆和栈的区别

  • 用途:堆内存用于存放new创建的对象或数组,栈内存用于存放方法或局部变量。
  • 顺序:堆先进先出,后进后出。栈先进后出,后进先出。

String,StringBuilder,StringBuffer的区别

  • 运行速度:StringBuilder > StringBuffer > String。String为字符串常亮,而StringBuilder和StringBuffer为字符串变量。即String变量一旦创建后不可更改,而StringBuilder和StringBuffer可更改。
  • 线程安全:StringBuffer线程安全,StringBuilder线程不安全。
    String为final修饰的类,不可继承。
  • 使用情景:字符串不经常变化的情况使用String,在单线程有大量字符串操作的情况下使用StringBuilder,在多线程有大量字符串操作的情况下用StringBuffer。

线程和线程池的区别

  • 线程Thread每次new对象的时候,性能差。而线程池重用存在的线程,减少对象的创建、消亡的开销,性能高。
  • 新建线程缺乏统一管理,可能会出现无限创建线程的情况,相互竞争,最后占用系统资源造成死机或者OOM。而线程池可有效控制最大并发数线程,提高系统资源利用率,避免过多的资源竞争造成系统堵塞。
  • 线程缺乏某些功能,比如定时执行,定期执行,中断线程等。而线程池就提供了这些功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值