代码拉取完成,页面将自动刷新
1. Java中&&和&的区别
答:&运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。
2.Java中输出不同编码的字符
答:tempStr = new String(str.getBytes(“ISO-8859-1″), “GBK”);
3.String、StringBuffer、StringBuilder
答: String(字符串常量);StringBuffer(字符串变量/线程安全);StringBuilder(字符串变量/非线程安全)
String每次修改都会新生成一个String对象;而StringBuffer和StringBuilder将会维护一个char缓冲区,不会新建对象。
StringBuilder晚于StringBuffer并且兼容其API,用于非线程安全场景,速度更加占优。
4.String拼接
答:Strng的拼接在不同的情况下不一样。
e.g.
// java源代码
String s1 = "s1";
String s2 = "s2";
String s3 = s1 + s2;
String s4 = "s1" + "s2";
// class字节码
0 ldc #2 <s1> # 从常量池中拿"s1"
2 astore_1 # 放入本地变量表1号位置,下同
3 ldc #3 <s2>
5 astore_2
6 new #4 <java/lang/StringBuilder> # 初始化String变量 s3
9 dup # 复制一份引用(因为下面的构造方法会消耗一个引用,这个是JVM的通常操作,即new+dup+<init>)
10 invokespecial #5 <java/lang/StringBuilder.<init>>
13 aload_1 # 载入本地变量1号(即s1)
14 invokevirtual #6 <java/lang/StringBuilder.append> # append方法,这里可以看到String的拼接调用的是StringBuilder.append
17 aload_2
18 invokevirtual #6 <java/lang/StringBuilder.append>
21 invokevirtual #7 <java/lang/StringBuilder.toString>
24 astore_3 # 将临时建立的StringBuilder中的值赋予本地变量3号
25 ldc #8 <s1s2> # s4的赋值直接计算出来了
27 astore 4
【注】: System.out.println(null+"233"); // 打印的是"null233",因为AbstractStringbuilder.appendNull()
【注】:Java没有运算符重载,String的+拼接是语法糖,编译器自动变成了新建StringBuilder并且append
5. String比较
答:和C语言类似,在Java代码中出现的字符串字面量将会放入常量池。在编译期间,String类型被赋值时能确定其值的将会直接将该引用赋给常量池中对应的字符串字面量。
如 String a = "233";String b = "233"; 则a == b,a == "233"(即做==时,参与运算的均为指向常量池中"233"的指针,故相等)
final修饰的变量是基本类型或String时,若编译期间能得到其确切值,则将其替换为常量使用:
e.g.
String a = "hello2";
final String b = "hello";
String d = "hello";
String c = b + 2;
String e = d + 2;
System.out.println((a == c)); # true,c直接指向常量池,和a一样
System.out.println((a == e)); # false,d+2不是常量,所以生成了新的StringBuilder引用
6. final关键字
答:需要分类讨论,即修饰引用、类、方法
修饰引用 基本数据类型,则该引用为常量,只能初始化一次且不能被修改
修饰引用 引用数据类型,则该引用内部数据可以修改,但是引用本身不能被修改
修饰引用 类的成员变量,则必须初始化值(直接赋值或者构造方法中赋值)
修饰引用 函数参数,防止修改(区别同上)
【拓展】:Java的假闭包就需要final修饰闭包访问的外部变量
修饰类 该类无法被继承
修饰方法 该方法无法被重写,但是可以被继承
7. static关键字
答:
修饰方法 static方法即静态方法,不依赖于对象实例就可以访问
修饰变量 static变量为所有类对象共享,并且在内存中仅有一个副本,当且仅当类在加载时初始化
修饰代码块 static块可以在类中的任何地方,当类被初次加载时按照static块的顺序来执行,并且仅执行一次
【注】:static代码块执行时机
e.g.
// Test.java
public class Test {
Person person = new Person("Test");
static{
System.out.println("test static");
}
public Test() {
System.out.println("test constructor");
}
public static void main(String[] args) {
new MyClass();
}
}
class Person{
static{
System.out.println("person static");
}
public Person(String str) {
System.out.println("person "+str);
}
}
class MyClass extends Test {
Person person = new Person("MyClass");
static{
System.out.println("myclass static");
}
public MyClass() {
System.out.println("myclass constructor");
}
}
// output
test static # 执行Test的main要先加载Test,则实行Test的static块
myclass static # main函数中new一个myClass,则加载MyClass,父类Test已经被加载故不再执行Test的static块
person static # 构造MyClass前,先构造父类Test,先初始化Test成员变量person,故加载Person,执行Person的static块
person Test # 构造Test的成员变量person
test constructor # 接下来执行Test的构造方法Test()
person MyClass # 进入MyClass的构造,先初始化MyClass的成员变量person
myclass constructor # 执行MyClass的构造方法MyClass()
8. 方法的重载和重写
重写(Override)在子类对父类允许的方法中进行内容的重写实现,但是返回值、参数都不能改变
重载(Overload)方法名字相同而参数和返回值都可以不同,常见于构造方法
【注】:父类final修饰的方法,当继承的子类可见时,子类无法重写该方法
9. inner class(内部类)
成员内部类:外部类成员位置的类,可以访问外部类中的所有成员变量和方法
局部内部类(方法内部类):定义在一个方法或者作用域中的类,其访问权限仅仅限于本方法/作用域中
匿名内部类:局部内部类的更进一步,本质上是继承该类或者实现接口的子类匿名对象。
e.g.
// com.pkg1
public class Test1{
protect void foo(){}
}
// com.pkg2
public class Test2{
public void foo2(){
new Test1(){
// 本质上是extend的语法糖,所以可以重写
@Override
protected void foo() {}
// 可以使用super得到父类
void callFoo(){
super.foo();
}
}.callFoo();
}
}
从字节码上看,匿名内部类会被编译器自动取名字(外部类名字后面加一个$1)并且声称class文件
【注】:匿名内部类中引用的外部函数中的对象,需要使用final修饰 https://zhuanlan.zhihu.com/p/29245059
静态内部类:static修饰的内部类可以等同于重新写一个.java文件,和非静态的内部类比丧失了对外部类变量和方法的引用
10. Java位运算
& 按位与。
| 按位或。
~ 按位非。
^ 按位异或。
<< 左位移运算符。
>> 右位移运算符。
<<< 无符号右移运算符。
>>> 无符号右移。无论是正数还是负数,高位通通补0。
15. 抽象类、抽象方法
抽象类无法实例化,不能final,必须被继承才能使用,其他的功能和普通的类一样(成员变量、构造方法、main方法等)。
继承抽象类的子类在构造方法中默认隐式加入父类的构造方法。
抽象方法必须在抽象类中,并且没有{},直接;结束,非抽象子类必须重写抽象父类的抽象方法。
非抽象类中不能存在抽象方法,抽象类中可以存在非抽象方法。有抽象方法的类必然是抽象类。
构造函数和静态方法不能是抽象方法。
16. 访问修饰符
修饰符 当前类 同一包内 子孙类(同一包) 子孙类(不同包) 其他包
public Y Y Y Y Y
protected Y Y Y Y/N N
default Y Y Y N N
private Y N N N N
【注】:protected、private不能修饰类和接口及接口内部成员与方法
【注】:protected是为了保护给子类的方法,使得不同包的子类可以访问父类的protected
22.CAS和synchronized
synchronized是悲观锁。在对象内部有monitor,通过计数器来保证同步。
修饰普通同步方法,锁是当前实例对象
修饰静态同步方法,锁是当前类的class对象
修饰同步方法块,锁是括号里面的对象
多个线程访问同一个synchronized对象时,先读取monitor中计数器的值,若是0则+1、给monitor上锁,否则等待。synchronized方法则使用关键字
CAS(Compare and Swap):乐观锁,只有修改期间才会上锁。如AtomInteger的自增,A、B线程同时调用,则A、B一直读
// var1是AtomInteger对象,var2是值地址
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
// native方法,从内存里面读
var5 = this.getIntVolatile(var1, var2);
} while(
// native方法,比较
!this.compareAndSwapInt(var1, var2, var5, var5 + var4)
);
return var5;
}
CAS在底层的硬件级别给你保证一定是原子的,同一时间只有一个线程可以执行CAS,先比较再设置,其他的线程的CAS同时间去执行此时会失败。
CAS的bug是会出现的问题 ABA 空循环。如果要解决ABA 问题,可以使用AtomicStampedReference类, 它内部用类似创建版本号的方式来解决 ABA 问题。
【拓展】:为什么synchronized、wait、notify和while要一起出现?
一般情况下,至少存在两个线程,一个wait等待另一个notify。wait的线程中
'''
while(!condition){
wait();
}
'''
如果这段代码不用synchronized修饰,则可能出现while判断了condition去执行wait的间隙中,另一个线程notify;则notify扑空了,被wait的线程无法被唤醒
所以需要保证condition的检查和wait与notify是互斥的
23. 多线程锁升级(即优化synchronized)
https://zhuanlan.zhihu.com/p/143479902
JVM中对象在内存中分为 对象头、实例数据、对齐填充;synchronized存在于对象头的MarkWord中。
适应性自旋锁:先不放弃CPU,在一定次数后没有抢到锁再挂起
锁消除:逃逸分析判断是否只有一个线程访问锁对象,是在消除
锁粗化:小锁变大锁,用于小锁上锁频率过高在直接用外部大锁代替
锁的状态:无锁、偏向锁、轻量级锁、重量级锁
(1)偏向锁:降低线程获取锁的代价。当线程访问了加锁的代码块时,首先将当前线程ID放入对象头中,下次就不用检查了
其他的线程发现加锁对象头中存在线程ID且不是自己的时候,检查那个ID的线程是否存活,若存活则上升到轻量级锁,否则变为自己的线程ID
(2)轻量级锁:检测到另一个线程存活时,本线程将CAS自旋来抢夺锁
(3)重量级锁:CAS自旋抢不到,只能挂起自己了,这是上升为重量级锁
24.ThreadLocal
用于存储线程内部的资源。
每一个线程都有一个ThreadLocalMap的引用,这个map是一个全局的。ThreadLocal作为一个key从这个map里面取值
所以ThreadLocal使用了线程自身和其他线程不同的唯一标识来隔离数据
26. JVM内存模型(JMM)
一种定义规范,屏蔽硬件与操作系统底层差异,是JVM的实现规范。
JVM运行时数据区:方法区(包括常量池)、堆、虚拟机栈、本地方法栈、程序计数器
虚拟机栈帧中会存有局部变量表、操作数栈、动态链接、方法出口等
局部变量表中的引用类型将会维护一个地址,指向堆区
堆区为全部的线程共有,存实例数据
27. 逃逸分析
引用类型会占用堆的内存,当JVM打开逃逸分析后,对如下的对象进行优化
锁消除:仅仅有一个线程会访问的对象,解除其锁
标量替换:将可以用常量或基本类型的类成员进行替换
栈上分配:临时使用栈空间存储临时对象(因为对象无法在退栈后就无人使用了,可以视为一次性的)
28. GC
引用计数:对象被其他对象引用了,计数器+1,消除引用则-1,;计数器为零时对象被清除
无法对A.b和B.a进行有效清除
GC ROOT引用链:从几个对象作为root出发,遍历引用对象,可达到的不背
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。