java面试题
1.如何来交换两个数字?
第一种方法:使用第三个变量
public class Swap{
public static void main(String[] args) {
int n1 = 10;
int n2 = 20;
System.out.println("交换之前n1:" + n1 + "交换之前n2:" + n2 );
int temp = n1;
n1 = n2;
n2 = temp;
System.out.println("交换之后n1:" + n1 + "交换之后n2:" + n2 );
}
}
第二种方法:不使用第三个变量(使用异或运算符)
运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0;
public class swap{
public static void main(String[] args) {
int n1 = 10;
int n2 = 20;
System.out.println("交换之前n1:" + n1 + "交换之前n2:" + n2 );
n1 = n1 ^ n2;
n2 = n1 ^ n2;
n1 = n1 ^ n2;
System.out.println("交换之后n1:" + n1 + "交换之后n2:" + n2 );
}
}
2.关于double近似存储
double r = (1.4-0.5)/0.9;
System.out.println(r);
这里输出0.9999999999,这是因为java中所有的小数都是采取近似存储的方式。
3.++代码题
int count=10;
for(int i=0;i<10;i++){//执行10次
count=++count;
}
System.out.println(count);
结果输出10。
4.==和equals的区别?
①如果是基本数据类型比较的数据,如果是引用数据类型,比较的是地址
②默认情况下和equals是一样的如果equals重写了则比较内容
5.包装类
我们可以知道输出的是true false true false
原因:第一个:包装类自动拆箱
:第四个:我们可以先用反编译工具xjad把class类反编译成java类找到所对应的代码里面的valueof 按Ctrl点进去我们可以发现valueof里面有一个整数包装类对象数组缓存区 如果存放的数据在-128 – 127之间,直接从这个数组里面找出来,如果不是则需要重新new一个
6.String类
第一个答案:fasle true 第二个答案:true true
使用intern方法如果常量池中没有,则把对象复制一份(或对象引用)放入常量池中,返回常量池中的对象,如果常量池中存在,则直接返回常量地址。
jdk1.7之前是复制一份放入常量池中,jdk1.8(包括1.8)之后则把对象引用放入常量池中
具体的内存解析见下图:
7.异常
try{}、catch{}、finally{]中可以包含return语句吗?如果try{}里面有一个return语句,那么紧跟在这个try后的finally{}中的代码会不会执行?
(1)try{}、catch{}、finally{]中可以包含return语句,finally中不推荐使用return,返回不正确的结果
(2)finally无论是否发生异常都会执行,先执行finally再执行return
8.多线程
1.利用lock和Condition让三个线程交替输入ABC
public class Alternative {
private int num = 1;
//创建锁
Lock lock = new ReentrantLock();
//创建队列
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
//打印A
public void printA(){
lock.lock();
try {
if (num != 1){
conditionA.await();
}
System.out.println("A");
conditionB.signal();
num = 2;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//打印B
public void printB(){
lock.lock();
try {
if (num != 2){
conditionB.await();
}
System.out.println("B");
conditionC.signal();
num = 3;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//打印C
public void printC(){
lock.lock();
try {
if (num != 3){
conditionC.await();
}
System.out.println("C");
conditionA.signal();
num = 1;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
//测试类
public class TestAlternative {
public static void main(String[] args) {
Alternative alternative = new Alternative();
ExecutorService es = Executors.newFixedThreadPool(3);
es.submit(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
alternative.printA();
}
}
});
es.submit(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
alternative.printB();
}
}
});
es.submit(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
alternative.printC();
System.out.println("-----------------");
}
}
});
es.shutdown();
}
}
2.CAS算法Compare And Swap(比较交换算法)
其实现方式是基于硬件平台的汇编指令(compxchg),是靠硬件来实现的,效率高,并且比较和交换的过程是同步的,是一种乐观锁
CAS比较交换算法,修改的方法包含三个核心参数
V:要更新的变量,E预期值,N:新值
只有当V==E时,V=N,否则表示已被更新过,则取消当前的操作,继续尝试执行直到成功
关于CAS算法的内存
下面有关于代码模拟的CAS算法
public class CAS {
private int v;
private int version;
public int getV(){
return this.v;
}
public int getVersion(){
return this.version;
}
public boolean cAndSwap(int e, int n, int version){
if (this.v == e && this.version == version){
v = n;
version++;
return true;
}
return false;
}
}
public class TestCas {
public static void main(String[] args) {
CAS cas = new CAS();
ExecutorService es = Executors.newFixedThreadPool(100);
for (int i = 0; i < 100; i++) {
es.submit(new Runnable() {
@Override
public void run() {
while (true){
int e = cas.getV();
int version = cas.getVersion();
boolean b = cas.cAndSwap(e, new Random().nextInt(100), version);
System.out.println(Thread.currentThread().getName() + "执行了" + b + "版本号:" + version) ;
if (b){
break;
}
}
}
});
}
es.shutdown();
}
}
关于CAS算法中的ABA问题以及解决办法?
ABA问题就是:当进程A准备修改V的值时,进程B抢先修改了V的值,此时进程C又修改V的值为原来的值。
解决办法:如上面代码,在方法中加一个版本号判断此时V值是不是原来的值。
3.i++是不是原子操作?
答案:不是
i++的执行步骤:
①读取i②执行i+1③赋值i
通过代码分析:
在public class TestAtomic {
public static void main(String[] args) {
Num num = new Num();
ExecutorService es = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
es.submit(new Runnable() {//匿名内部类
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num.getNum());
}
});
}
es.shutdown();
}
static class Num{//创建一个num类
static int num= 0;//同时修改一个元素,所以设置成静态的
static int getNum(){
return num++;
}
}
}
上图我们可以看到不是原子操作,因为结果中出现了两个0,就证明他在执行的时候被打断了。那我我们要怎么解决这个问题呢?javaAPI中有专门的原子操作Atomic,代码修改了之后如下面
static class Num{
private static AtomicInteger num = new AtomicInteger();
static int getNum(){
return num.getAndIncrement();
}
}
decrementAndGet() 相当于: - - i
getAndDecrement() 相当于:i - -
getAndIncrement() 相当于:i + +
incrementAndGet() 相当于:+ +
4.多线程的三大特性及其解析:
①原子性:一个或多个操作不能被分割,要么全部执行,要么都不执行
②可见性:多个线程访问同一个变量,一个线程修改了这个变量,别的线程能立即看到修改的值 Volatile 关键字可以保证内存可见性
③有序性:程序执行的顺序按照代码的先后顺序执行,但是处理器为了提高程序运行效率,可能会对输入代码进行优化,他不保证程序中各个语句的执行顺序与编写顺序一致,但是最终结果是一致的
下面解释一下可见性和有序性
可见性:
我们用代码测试一下
public class myThread extends Thread {
public boolean flag = false;
@Override
public void run() {
System.out.println("子线程开始执行了。。。");
while (true){
if (flag){
break;
}
}
System.out.println("子线程结束了。。。");
}
}
public class TestMyThread {
public static void main(String[] args) throws IOException {
myThread thread = new myThread();
thread.start();
System.out.println("输入任意字符结束子线程");
System.in.read();
thread.flag = true;
System.out.println("主线程结束了:" + thread.flag);
}
}
由图我们可以看出子线程并没有执行子线程结束了这句代码,可知子线程中的falg还是false,理由如下
线程在获取堆中的对象时,并不是直接访问的,而是第一次先从堆中访问读取到cpu的缓存区中,然后以后每次就都使用缓存中的数据,如果不使用volatile则子线程看不见flag已经变为true。所以我们应该把flag设为volatile
这样的执行结果如下:
有序性:
5.线程池的七个参数
ThreadPoolExcutor类的参数
corePoolSize:核心线程数
maximumPoolSize:最大线程数
KeepValueTime:非核心线程的存活时间
unit:时间单位
workQueue:工作队列
threadFactory:线程工厂
handler:拒绝策略
AbortPolicy:中断,抛出异常(
核心业务,使用最多)
DiscardPolicy:直接抛弃,不抛出异常(非核心业务)
DiscardOldestPolicy:把旧的抛弃,加入新的(喜新厌旧)
CallerRunsPolicy:线程池创建者执行
9.I/O框架
记事本的编码问题?
(1)记事本编码识别错误问题
联通的GBK编码正好符合UTF-8的编码格式,GBK前面没有特殊的标记
网络编程
1.OSI七层参考模型
mysql
1.事务的特性
原子性(Atomicity):表示一个事务内的所有操作是一个整体,要么全部成功,要么全部失败。
一致性(Consistency):表示一个事务内有一个操作失败时,所有的更改过的数必须回到回滚前的指定状态
隔离性(lsolation):事务查看数据操作所处的状态,要么是另一并发事务修改他之前的状态 要么是另一事物修改他之后的状态,事务不会查看中间状态的数据
持久性(Durability):持久性事务完成之后,他对于系统的影响是永久性的
2.事务的隔离级别
①Read committed(获取未提交的内容):Oracle默认级别
②repeatable read:mysql默认级别 mysql已经解决了幻读问题