文章目录
190515
01 - 多线程start() 和 run()
高赞解析(via:Magic图)
-
start方法
用start方法来启动线程,是真正实现了多线程, 通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法。但要注意的是,此时无需等待run()方法执行完毕,即可继续执行下面的代码。所以run()方法并没有实现多线程。 -
run方法run()方法只是类的一个普通方法而已,如果直接调用run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码。
02 - 结果输出
高赞解析(via:ど゛低调、)
- 不论有什么运算,小括号的优先级都是最高的,先计算小括号中的运算,得到
x+y +""+25+y - 任何字符与字符串相加都是字符串,但是是有顺序的,字符串前面的按原来的格式相加,字符串后面的都按字符串相加,得到
25+“”+25+5
我的理解:与字符串拼接都是字符串,但识别到是输出字符串之前,仍然按照正常数据类型进行加减,识别到字符串之后再进行拼接。
03 - 多态
高赞解析(via:stevenniu)
public class Father {
public void say(){
System.out.println("father");
}
public static void action(){
System.out.println("爸爸打儿子!");
}
}
public class Son extends Father{
public void say() {
System.out.println("son");
}
public static void action(){
System.out.println("打打!");
}
public static void main(String[] args) {
Father f=new Son();
f.say();
f.action();
}
}
输出: son
爸爸打儿子!
当调用say方法执行的是Son的方法,也就是重写的say方法
而当调用action方法时,执行的是father的方法。
- 普通方法,运用的是动态单分配,是根据new的类型确定对象,从而确定调用的方法;
- 静态方法,运用的是静态多分派,即根据静态类型确定对象,因此不是根据new的类型确定调用的方法。
04 - 数组赋值效率比较
高赞解析(via:云想衣裳花想容春风拂槛露华浓)
- 从速度上看:
System.arraycopy>clone>Arrays.copyOf>for -
for的速度之所以最慢是因为下标表示法每次都从起点开始寻位到指定下标处(现代编译器应该对其有进行优化,改为指针),另外就是它每一次循环都要判断一次是否达到数组最大长度和进行一次额外的记录下标值的加法运算。 - 查看
Arrays.copyOf的源码可以发现,它其实本质上是调用了System.arraycopy。之所以时间差距比较大,是因为很大一部分开销全花在了Math.min函数上了。
190517
01 - JVM基础
高赞解析(via:StrongYoung)
运行时数据区包括:虚拟机栈区,堆区,方法区,本地方法栈,程序计数器
-
虚拟机栈区 :也就是我们常说的栈区,线程私有,存放基本类型,对象的引用和
returnAddress,在编译期间完成分配。 -
堆区 , JAVA 堆,也称
GC堆,所有线程共享,存放对象的实例和数组, JAVA 堆是垃圾收集器管理的主要区域。 - 方法区 :所有线程共享,存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。这个区域的内存回收目标主要是针对常量池的对象的回收和对类型的卸载。
-
本地方法栈: 为虚拟机使用的
Native方法服务,也是线程私有。 - 程序计数器 :线程私有,每个线程都有自己独立的程序计数器,用来指示下一条指令的地址。
-
运行时常量池: 线程共享 ,是方法区的一部分,
Class文件中存放编译期生成的各种字面量和符号引用,类加载后进入方法区的运行时常量池中。
02 - 构造的运用
public class Test {
public static void main(String [] args){
System.out.println(new B().getValue()); //输出为
}
static class A{
protected int value;
public A(int v) {
setValue(v);
}
public void setValue(int value){
this.value = value;
}
public int getValue(){
try{
value++;
return value;
} catch(Exception e){
System.out.println(e.toString());
} finally {
this.setValue(value);
System.out.println(value);
}
return value;
}
}
static class B extends A{
public B() {
super(5);
setValue(getValue() - 3);
}
public void setValue(int value){
super.setValue(2 * value);
}
}
}
高赞解析(via:谷哥的小弟)
http://blog.csdn.net/zhumintao/article/details/53818972
03 - 基本类型转换
高赞解析(via:Pandora)
------------知识点------------
Java表达式转型规则由低到高转换:
1、所有的byte,short,char型的值将被提升为int型;
2、如果有一个操作数是long型,计算结果是long型;
3、如果有一个操作数是float型,计算结果是float型;
4、如果有一个操作数是double型,计算结果是double型;
5、被fianl修饰的变量不会自动改变类型,当2个final修饰相操作时,结果会根据左边变量的类型而转化。
--------------解析--------------
语句1错误:b3=(b1+b2);自动转为int,所以正确写法为b3=(byte)(b1+b2);或者将b3定义为int;
语句2正确:b6=b4+b5;b4、b5为final类型,不会自动提升,所以和的类型视左边变量类型而定,即b6可以是任意数值类型;
语句3错误:b8=(b1+b4);虽然b4不会自动提升,但b1仍会自动提升,所以结果需要强转,b8=(byte)(b1+b4);
语句4错误:b7=(b2+b5); 同上。同时注意b7是final修饰,即只可赋值一次,便不可再改变。
04 - 泛型的级别
高赞解析(via:晓宇大美女~)
- 只看尖括号里边的!!明确点和范围两个概念
- 如果尖括号里的是一个类,那么尖括号里的就是一个点,比如
List<A>,List<B>,List<Object> - 如果尖括号里面带有问号,那么代表一个范围,
<? extends A>代表小于等于A的范围,<? super A>代表大于等于A的范围,<?>代表全部范围 - 尖括号里的所有点之间互相赋值都是错,除非是俩相同的点
- 尖括号小范围赋值给大范围,对;大范围赋值给小范围,错。如果某点包含在某个范围里,那么可以赋值,否则,不能赋值
-
List<?>和List是相等的,都代表最大范围
- 补充:
List既是点也是范围,当表示范围时,表示最大范围
public static void main(String[] args) {
List<A> a;
List list;
list = a; //A对,因为List就是List<?>,代表最大的范围,A只是其中的一个点,肯定被包含在内
List<B> b;
a = b; //B错,点之间不能相互赋值
List<?> qm;
List<Object> o;
qm = o; //C对,List<?>代表最大的范围,List<Object>只是一个点,肯定被包含在内
List<D> d;
List<? extends B> downB;
downB = d; //D对,List<? extends B>代表小于等于B的范围,List<D>是一个点,在其中
List<?extends A> downA;
a = downA; //E错,范围不能赋值给点
a = o; //F错,List<Object>只是一个点
downA = downB; //G对,小于等于A的范围包含小于等于B的范围,因为B本来就比A小,B时A的子类嘛
}