2、解释
&&符号当左边为false的时候就不再执行右边的判断了,因为程序已经可以确定为false了,所以b++并没有执行
||符号当左边为true的时候就不在执行左边的判断了,因为程序已经可以确定为true了,所以d++被执行了
3、这种判断机制的用途
可以解决先后判断问题,例如以下程序
先判断非空再判断字符串长度是没有错的,因为s为空的时候,就不会去执行s.length()==5
2、解释
Integer是int的包装类,Integer引用的是堆内存新建的对象,int引用的是java常量池里的数据
Integer和int会有拆箱装箱机制
int和Integer比较时,Integer会自动拆箱为int
int赋值Integer数据时,Integer会自动拆箱为int
Integer赋值int数据时,int会自动装箱为Integer
int和int用==比较时,比较的是值的大小
int和Integer用==比较时,Integer会自动拆箱为int,比较的是值的大小
Integer和Integer用==比较时,比较的是两个对象是否是同一个的引用
int自动装箱的小细节:对于-128~127的数据,int会判断缓存里有没有相对应值的Integer对象,如果有,直接返回该对象,否则新创建一个对象,并把对象放到缓存中。这样做是为了对于频繁使用的数据只保存一份数据,减少内存的浪费
package com.wu.hello.main;
public class Main {
public static void main(String[] args) {
int a=0;
int b=0;
// true,int和Integer用==比较时,Integer会自动拆箱为int,比较的是值的大小
System.out.println(a==b);
Integer c=new Integer(1);
Integer d=new Integer(1);
// false,Integer和Integer用==比较时,比较的是两个对象是否是同一个的引用
System.out.println(c==d);
Integer e=2;
Integer f=2;
// true,int自动装箱的小细节:对于-128~127的数据,int会判断缓存里有没有相对应值的Integer对象,
// 如果有,直接返回该对象,否则新创建一个对象,并把对象放到缓存中。
System.out.println(e==f);
Integer g=200;
Integer h=200;
// false,int自动装箱的小细节:对于-128~127的数据,int会判断缓存里有没有相对应值的Integer对象,
// 如果有,直接返回该对象,否则新创建一个对象,并把对象放到缓存中。
System.out.println(g==h);
Integer i=new Integer(200);
int j=new Integer(200);
// true,int和Integer用\=\=比较时,Integer会自动拆箱为int,比较的是值的大小
System.out.println(i==j);
}
}
1、区别
String | StringBuffer | StringBuilder | |
---|---|---|---|
内存位置 | java常量池/堆空间 | 堆空间 | 堆空间 |
线程安全问题 | 线程安全 | 线程安全 | 线程不安全 |
是否可变 | 不可变 | 可变 | 可变 |
效率 | 最慢 | 一般 | 最快 |
使用场景 | 不需要更改时使用 | 多线程 | 单线程 |
2、String是不可变的是什么意思?
例如字符串s=“abc”,如果想要s=“adc”,不可以直接把字符 ‘b’ 修改为 ‘d’ ,而要新创建一个对象"adc",让s去引用它
3、String为什么是最慢的?
因为String对象是不可变的,每次修改的时候都需要产生一个新的对象
例如字符串拼接:“abc”+“d”,需要新创建一个字符串对象"abcd",而StringBuffer和StringBuilder可以直接修改当前对象
4、什么线程不安全?
在多线程的情况下会出现错误,例子:多个线程同时修改StringBuilder对象
package com.wu.hello.main;
public class Main {
public static void main(String[] args) throws InterruptedException {
StringBuilder s=new StringBuilder();
for (int i = 0; i < 10; i++){
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 1000; j++){
s.append("a");
}
}
}).start();
}
Thread.sleep(1000);
System.out.println(s.length());
}
}
报了异常,按道理应该输出10000的,因为多线程问题,使得结果发生了错误,这就是线程不安全
5、StringBuilder线程不安全的原因
因为多个线程操作的是同一个StringBuilder对象,而StringBuilder的方法调用时没有加锁,导致多个线程同时进入方法,出现不一致问题,例如线程1进入方法后获取了数据,线程2修改了字符串,那么线程1拿到的就是脏数据
而StringBuffer的每个方法是加了synchronized的,所以多线程是没问题的
6、同样地,HashMap 、HashSet都是非线程安全的,如果在多线程环境下,可以使用 ConcurrentHashMap、ConcurrentHashSet来代替,当然效率也会降低一些
1、笔试题:是否存在数字 i+1
在计算机里面是存在的,Int类型的数占32位,当int是最大值的时候,再加1,那么就会变为最小值
package com.wu.hello.main;
public class Main {
public static void main(String[] args) throws InterruptedException {
int maxValue = Integer.MAX_VALUE;
System.out.println(maxValue);
System.out.println(maxValue+1<maxValue);
}
}
1、先看程序
package com.wu.hello.main;
public class NULL {
public static void haha(){
System.out.println("haha");
}
public static void main(String[] args) throws InterruptedException {
((NULL)null).haha();
}
}
3、解释
其实NULL是类的名字,在java里面,null可以转成任何对象。转了之后也就相当于以下程序:
package com.wu.hello.main;
public class NULL {
public static void haha(){
System.out.println("haha");
}
public static void main(String[] args) throws InterruptedException {
NULL n=null;
n.haha();
}
}
所以,你大概懂了吧!haha()是static方法,null对象同样可以调用!当然的,不是static方法的话就会报空指针异常了
package com.wu.hello.main;
public class Main {
public static void main(String[] args) {
int a=20;
int b=6;
//方法1
System.out.println((int)Math.ceil((double) a/b));
//方法2
System.out.println(a/b+(((a%b)!=0)?1:0));
//方法3
System.out.println((a+b-1)/b);
}
}
最常用的向上取整就是方法3啦
1、先看程序
package com.wu.hello.main;
public class Main {
public static void main(String[] args) {
https://www.baidu.com/;
System.out.println("haha");
}
}
3、解释
实际上的程序是这样的:
package com.wu.hello.main;
public class Main {
public static void main(String[] args) {
https:
System.out.println("haha");
}
}
实际上https: 是goto语句的写法,这种写法已经快被弃用了,原因是goto语句会使程序变得杂乱无章,程序维护困难。
可以看以下程序理解goto用法:
package com.wu.hello.main;
public class Main {
public static void main(String[] args) {
https:
while (true){
break https;
}
System.out.println("haha");
}
}
java对c++的goto语法做了优化,只能接在break、continue后面,跳出循环或者是重新执行循环
1、认真看看以下程序,线程1和线程3都可以证明stop等于true,线程2为什么会停不下来呢?
package com.wu.hello.main;
public class Main {
static boolean stop = false;
public static void main(String[] args) {
// 线程1
new Thread(()->{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
stop=true;
System.out.println("线程1执行完毕,stop="+stop);
}).start();
// 线程2
new Thread(()->{
int i=0;
while (!stop){
i++;
}
System.out.println("线程2执行完毕,i="+i);
}).start();
// 线程3
new Thread(()->{
try {
Thread.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程3执行完毕,stop="+stop);
}).start();
}
}
2、解释
首先我们看百度的解释:
(1)、JIT
即时编译(Just-in-time Compilation,JIT)是一种通过在运行时将字节码翻译为机器码,从而改善字节码编译语言性能的技术。
(2)、热点代码
在Java的编译体系中,一个Java的源代码文件变成计算机可执行的机器指令的过程中,需要经过两段编译,第一段是把.java文件转换成.class文件。第二段编译是把.class转换成机器指令的过程。
当JVM发现某个方法或代码块运行特别频繁的时候,就会认为这是“热点代码”(Hot Spot Code)。JIT会把部分“热点代码”class翻译成本地机器相关的机器码,并进行优化,然后再把翻译后的机器码缓存起来,以备下次使用。
正是因为jit的问题,while循环被多次执行后被jit给优化了,程序已经被修改了
3、证明
在VM中添加-Xint参数,使用解释执行的意思
可以看到,程序执行了22428641次,最终停了下来,解决了这个问题,但是不是用jit会大大减低程序的性能,因为虚拟机不会给我们做优化了,使得被优化不会出错的程序也得不到优化
4、一个好得解决方案
对于多线程的共享资源,可以使用volatile来修饰变量,volatile可以帮我们保证程序的可见性、有序性,但是仍然不能保证原子性
package com.wu.hello.main;
public class Main {
static volatile boolean stop = false;
public static void main(String[] args) {
// 线程1
new Thread(()->{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
stop=true;
System.out.println("线程1执行完毕,stop="+stop);
}).start();
// 线程2
new Thread(()->{
int i=0;
while (!stop){
i++;
}
System.out.println("线程2执行完毕,i="+i);
}).start();
// 线程3
new Thread(()->{
try {
Thread.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程3执行完毕,stop="+stop);
}).start();
}
}
5、详细说说volatile
线程安全要考虑三个方面:可见性、有序性、原子性
可见性: 一个线程对共享变量修改,另一个线程能看到最新的结果
有序性: 一个线程内代码按编写顺序执行
原子性: 一个线程内多行代码以一个整体运行,期间不能有其它线程代码插队
程序的可见性怎么就如本例子,解决了线程2读不到stop最新值的问题
原子性就是,比如以下程序,一个线程对a+=5,另一个线程a-=5,那么a的结果一定为0?答案是否定的,5和-5也有可能出现,因为a+=5虽然是一行代码,但是编译了过后是多条指令,包括了取数据,计算,回存等等,这些操作不能被保证是原子的,线程1取到数据后,线程2把数据给修改了,线程1就读到了脏数据
package com.wu.hello.main;
import java.util.concurrent.CountDownLatch;
public class Main {
static int a=0;
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(2);
// 线程1
new Thread(() -> {
a += 5;
latch.countDown();
}).start();
// 线程2
new Thread(() -> {
a -= 5;
latch.countDown();
}).start();
latch.await();
System.out.println("a=" + a);
}
}
有序性就是,下面的一个程序,显而易见打印的情况可能有3种:
x=0
y=0
x=1
y=1
x=0
y=1
那么,有没可能出现x=1而y=0的情况呢?答案是可能的,因为程序在编译的时候会对程序进行优化,在不改变单个线程结果的情况下,代码的执行顺序是可以改变的,也是说线程1可能会先 执行x=1;再执行y=1;,导致出现x=1而y=0的情况
package com.wu.hello.main;
import java.util.concurrent.CountDownLatch;
public class Main {
static int x=0;
static int y=0;
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(2);
// 线程1
new Thread(() -> {
y=1;
x=1;
}).start();
// 线程2
new Thread(() -> {
System.out.println("x="+x);
System.out.println("y="+y);
}).start();
}
}
volatile修饰变量可以解决这个问题
package com.wu.hello.main;
import java.util.concurrent.CountDownLatch;
public class Main {
static volatile int x=0;
static volatile int y=0;
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(2);
// 线程1
new Thread(() -> {
y=1;
x=1;
}).start();
// 线程2
new Thread(() -> {
System.out.println("x="+x);
System.out.println("y="+y);
}).start();
}
}
所以总结时volatile可以保证可见性、有序性,而原子性需要用锁机制解决
觉得写得不错就点个赞吧!