Java易忽略
for 和 forEach
遍历元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static void test_get() {
String[] array = {"1", "2", "3"};
for (String i : array) {
System.out.println(i);
}
ArrayList<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
for (String i : list) {
System.out.println(i);
}
}
遍历后结果如下:
1
2
3
4
5
6
1
2
3
111
222
333
再来看看编译后的源码 build/classed/java/main/包名
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static void test_get() {
String[] array = new String[]{"1", "2", "3"};
String[] var1 = array;
int var2 = array.length;
for(int var3 = 0; var3 < var2; ++var3) {
String i = var1[var3];
System.out.println(i);
}
ArrayList<String> list = new ArrayList();
list.add("111");
list.add("222");
list.add("333");
Iterator var6 = list.iterator();
while(var6.hasNext()) {
String i = (String)var6.next();
System.out.println(i);
}
}
可以看到数组的 forEach 是转换为普通的 for 实现的;List 的 forEach 是通过 Iterator 迭代器实现的
删除元素
普通 for
1
2
3
4
5
6
7
8
9
10
11
private static void test_del_for() {
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
System.out.println("del前list=" + list);
for (int i = 0; i < list.size(); i++) {
list.remove("222");
}
System.out.println("del后list=" + list);
}
输出:
1
2
del前list=[111, 222, 333]
del后list=[111, 333]
Iterator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static void test_del_forIterator() {
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
System.out.println("del前list=" + list);
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
if (next.equals("222")) {
iterator.remove();
}
}
System.out.println("del后list=" + list);
}
输出:
1
2
del前list=[111, 222, 333]
del后list=[111, 333]
forEach (ConcurrentModificationException)
1
2
3
4
5
6
7
8
9
10
11
12
private static void test_del_forEach() {
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
System.out.println("del前list=" + list);
for (String i : list) {
list.remove("222");
}
System.out.println("del后list=" + list);
}
输出:
1
2
del前list=[111, 222, 333]
Exception in thread "main" java.util.ConcurrentModificationException
反编译后:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static void test_del_forEach() {
List<String> list = new ArrayList();
list.add("111");
list.add("222");
list.add("333");
System.out.println("del前list=" + list);
Iterator var1 = list.iterator();
while(var1.hasNext()) {
String i = (String)var1.next();
list.remove("222");
}
System.out.println("del后list=" + list);
}
原因:
1
迭代器内部的每次遍历都会记录List内部的modcount当做预期值,然后在每次循环中用预期值与List的成员变量modCount作比较,但是普通list.remove调用的是List的remove,这时modcount++,但是iterator内记录的预期值=并没有变化,所以会报错。
如果想要删除元素的话需要使用迭代器内部的 remove 方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ArrayList<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
log.info(list.toString());
Iterator<String> it = list.iterator();
while (it.hasNext()){
String next = it.next();
//if外使用list的remove方法还是会报错的
if(next.equals("222")){
it.remove();//这里使用的是迭代器里面的remove()方法,
// 当然如果使用list的remove方法在此删除质地感元素的话是成功的,比如:list.remove("222")
}
}
log.info(list.toString());
修改元素
普通 for(可以修改)
1
2
3
4
5
6
7
8
9
10
11
private static void test_modify_for() {
ArrayList<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
System.out.println("[for]修改前list=" + list);
for (int i = 0; i < list.size(); i++) {
list.set(i, "444");
}
System.out.println("[for]修改后list=" + list);
}
输出:
1
2
[for]修改前list=[111, 222, 333]
[for]修改后list=[444, 444, 444]
forEach(不能修改对象,可以修改对象上的属性)
1
2
3
4
5
6
7
8
9
10
11
private static void test_modify_forEach() {
ArrayList<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
System.out.println("[forEach]修改前list=" + list);
for (String i : list) {
i = "444";
}
System.out.println("[forEach]修改后list=" + list);
}
输出:
1
2
[forEach]修改前list=[111, 222, 333]
[forEach]修改后list=[111, 222, 333]
我们看下反编译源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static void test_modify_forEach() {
ArrayList<String> list = new ArrayList();
list.add("111");
list.add("222");
list.add("333");
System.out.println("[forEach]修改前list=" + list);
String i;
for(Iterator var1 = list.iterator(); var1.hasNext(); i = "444") {
i = (String)var1.next();
}
System.out.println("[forEach]修改后list=" + list);
}
修改的 i 并没有操作的 list,所以 list 不会有任何改变
总结
- for 与 foreach 都可以遍历数组/集合,不过 for 则在较复杂的循环中效率更高
- foreach 不可以删除/修改集合元素,而 for 可以
- foreach 和 for 都可以修改元素里面的属性
- 相比较下来 for 循环更为灵活
Java 长整数坑
长整数造成数据溢出 (Java/Kotlin 都一样)
1
2
3
4
5
6
7
public class LongDivision {
public static void main(String args[]) {
final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000; // 正常是,86400000000,实际是500654080
final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000; // 86400000
System.out.println(MICROS_PER_DAY/MILLIS_PER_DAY); // 5
}
}
整个过程,除数与被除数都是 long 型,很容易保存这两个数,结果一定是 1000,但是结果让你失望了,是 5。
Int 最大/小值是:
1
2
Int.MAX_VALUE=2147483647
Int.MIN_VALUE=-2147483648
- 分析:
24 * 60 * 60 * 1000 * 1000
在计算时是 Int,计算后 Int 溢出了
溢出后,这个值刚好是 500654080
,500654080/86400000=5
- 案例
在 Mashi 项目有个本地时间转换的操作:
1
2
const val DAY = 24 * 60 * 60
const val DAY_MILLIS = 24 * 60 * 60 * 1000
目前是没有什么提示,DAY_MILLIS*25 那就会出错了。
1
25*MILLIS_PER_DAY=-2134967296
- 解决
加L
1
2
3
final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000L; // 正常是,86400000000,实际是500654080
final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000; // 86400000
System.out.println(MICROS_PER_DAY/MILLIS_PER_DAY); // 1000
Java 自加运算符,j = j++,j 的值为什么没有变?
指令
iconst_2 将 2 放到操作数栈顶
istore_1 将栈顶的值放到局部变量 1 中
iload_1 将局部变量 1 的值放到栈顶
iinc 1, 1 将常量 1 加到局部变量 1 中去,但操作数栈顶值不变 (前面一个 1 表示第几个局部变量,第二个表示要加的值)
一个案例
1
2
3
4
public static void main(String[] args) {
int j = 3;
j = j++;
}
反编译
javap -c com.baidu.Test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Compiled from "Test.java"
public class com.baidu.Test {
public com.baidu.Test();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>
()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_3
1: istore_1
2: iload_1
3: iinc 1, 1
6: istore_1
7: return
}
解释
1
2
3
4
5
6
7
8
0: iconst_3 // 将3放到操作数栈顶
1: istore_1 // 将栈顶的值放到局部变量1(即j)中
2: iload_1 // 将局部变量1(即j)的值放到操作数栈顶
3: iinc 1, 1 // 将常量1加到局部变量1中去,但此时栈顶的值并没有变化
6: istore_1 // 将栈顶的值(此时栈顶还是3)放到局部变量1去
7: return
为什么是局部变量1呢?而不是0或者2呢,因为前面有个Sting数组的args,所以不是0,又因为方法是static的,所以没有this引用,所以不是2,如果把static去掉,那么这句就要变成istore_2\. 如果方法中没有参数args,那么就是istore_1。这里局部变量1就是j。
另外一个案例
1
2
3
4
5
6
7
public static void main(String[] args) {
int m = 3;
m = m++;
int n = 5;
n = ++n;
}
结果:
m=3,n=6
反编译
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
D:\workspace\browser_20150803\Test\bin>javap -c com.baidu.Test
Compiled from "Test.java"
public class com.baidu.Test {
public com.baidu.Test();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":
()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_3
1: istore_1
2: iload_1
3: iinc 1, 1
6: istore_1
7: iconst_5
8: istore_2
9: iinc 2, 1
12: iload_2
13: istore_2
14: return
}
解释
1
2
3
4
5
6
7
8
9
10
11
12
0: iconst_3 // 将操作数3放到操作数栈顶
1: istore_1 // 将操作数栈顶的数放到局部变量1(即m)中
2: iload_1 // 将局部变量1(即m)中的数放到操作数栈顶
3: iinc 1, 1 // 将常量1累加到局部变量1(即m)中去,但并没有改变操作数栈顶的值(此时还是栈顶还是3)
6: istore_1 // 将操作数栈顶的数(此时为3)放到局部变量1(即m)中去,此时m为3
7: iconst_5 // 将操作数5放到操作数栈顶,此时栈顶为5
8: istore_2 // 将操作数栈顶的数(即5)放到局部变量2(即n)中
9: iinc 2, 1 // 将常量1累加到局部变量2(即n)中去,但并没有改变操作数栈顶的值(此时还是栈顶还是5)
12: iload_2 // 将局部变量2(即n)的数放到操作数栈顶去(此时栈顶为6)
13: istore_2 // 将操作数栈顶的数放到局部变量2(即n)去,此时n数为(6)
14: return
参考
Java 中 isAssignableFrom() 方法与 instanceof
isAssignableFrom() 方法与 instanceof 关键字的区别总结为以下两个点:
- isAssignableFrom() 方法是从类继承的角度去判断,instanceof 关键字是从实例继承的角度去判断。
- isAssignableFrom() 方法是判断是否为某个类的父类,instanceof 关键字是判断是否某个类的子类。
1
2
3
父类.class.isAssignableFrom(子类.class)
子类实例 instanceof 父类类型
isAssignableFrom() 方法的调用者和参数都是 Class 对象,调用者为父类,参数为本身或者其子类。
instanceof 关键字两个参数,前一个为类的实例,后一个为其本身或者父类的类型。
案例
我们有时候需要为对象字段设置默认值,即在别的处理中生成对象并对对象进行赋值后,有些值需要有默认值,但是又不方便通过构造方法设置的时候,我们可以通过反射配合注解来为其设置默认值而不用调用一堆 set 方法。
下面这个例子即反射配置注解为对象字段设置默认值(包括父类),仅支持 String 和本类型的包装部分包装类(Number 的子类)。
首先定义注解:
1
2
3
4
5
6
7
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface ParamDefaultValue {
String value();
}
定义注解的解析类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class ParamProcessor {
public static void applyDefaultValue(Object o) {
Class sourceClass = o.getClass();
//获取对象所有字段 包括父类
ArrayList<Field> fields = new ArrayList<>();
while (sourceClass != null){
fields.addAll(Arrays.asList(sourceClass.getDeclaredFields()));
sourceClass = sourceClass.getSuperclass();
}
for (Field field : fields) {
field.setAccessible(true);
if (field.isAnnotationPresent(ParamDefaultValue.class)) {
try {
Object val = field.get(o);
if (val != null) {
continue;
}
Class type = field.getType();
if (type.isPrimitive()) {
continue;
}
String defVal = field.getAnnotation(ParamDefaultValue.class).value();
if (String.class.isAssignableFrom(type)) {
field.set(o, defVal);
} else if (Number.class.isAssignableFrom(type)) {
if (Byte.class.isAssignableFrom(type)) {
field.set(o, Byte.valueOf(defVal));
} else if (Float.class.isAssignableFrom(type)) {
field.set(o, Float.valueOf(defVal));
} else if (Short.class.isAssignableFrom(type)) {
field.set(o, Short.valueOf(defVal));
} else if (Integer.class.isAssignableFrom(type)) {
field.set(o, Integer.valueOf(defVal));
} else if (Double.class.isAssignableFrom(type)) {
field.set(o, Double.valueOf(defVal));
} else if (Long.class.isAssignableFrom(type)) {
field.set(o, Long.valueOf(defVal));
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
Java 中 instanceof 和 isInstance 区别
instanceof
obj.instanceof(class)
这个对象 obj 是不是这种 class 类型,obj 是 obj 类本身或者其子类
1.一个对象 obj 是本身类的一个对象 class
2.一个对象 obj 是本身类 class 父类(父类的父类)和接口(接口的接口)的一个对象
3.所有对象 obj 都是 Object
4.凡是 null 有关的都是 false,null.instanceof(class)
isInstance
class.inInstance(obj)
这个对象 obj 能不能被转化为这个类 class,class 是 obj 类本身或 obj 类的父类
1.一个对象 obj 是本身类 class 的一个对象
2.一个对象 obj 能被转化为本身类所继承类 class(父类的父类等)和实现的接口(接口的父接口)强转
3.所有对象 obj 都能被 Object 的强转
4.凡是 null 有关的都是 false,class.inInstance(null)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
private static void testinstanceof() {
Father father = new Son();
Son son = new Son();
Father grandSon = new GrandSon();
boolean b = father instanceof Father;
System.out.println("father instanceof Father?" + b); // true
boolean b1 = son instanceof Father;
System.out.println("son instanceof Father?" + b1); // true
boolean b2 = son instanceof Son;
System.out.println("son instanceof Son?" + b2); // true
boolean b21 = grandSon instanceof Father;
System.out.println("grandSon instanceof Father?" + b); // true
boolean b22 = grandSon instanceof Son;
System.out.println("grandSon instanceof Son?" + b1); // true
boolean b23 = grandSon instanceof GrandSon;
System.out.println("grandSon instanceof GrandSon?" + b2); // true
Object obj = new Son();
System.out.println("Object instanceof Son?" + (obj instanceof Son)); // true
}
public static void testisinstance() {
Father father = new Son();
Son son = new Son();
Father grandSon = new GrandSon();
System.out.println(Father.class.isInstance(father)); // true,father指向son对象,可以转换为Father,Father是Son的父类
System.out.println(Father.class.isInstance(son)); // true,father指向son对象,可以转换为Father,Father是Son的父类
System.out.println(Father.class.isInstance(grandSon)); // true,father指向grandSon对象,可以转换为Father,Father是Son父类,Son是GrandSon的父类
System.out.println(Son.class.isInstance(father)); // true,father指向son对象,可以转换为Son,Son本身
System.out.println(Son.class.isInstance(son)); // true,son指向son对象,可以转换为Son,Son本身
System.out.println(Son.class.isInstance(grandSon)); // true,grandSon指向grandSon对象,可以转换为Son,,Son是GrandSon的父类
System.out.println(GrandSon.class.isInstance(father)); // false,father指向son对象,son不能转为GrandSon
System.out.println(GrandSon.class.isInstance(son)); // false,son指向son对象,son不能转为GrandSon
System.out.println(GrandSon.class.isInstance(grandSon)); // true,grandSon指向GrandSon对象,grandSon本身
System.out.println(Integer.class.isInstance(1)); // true
System.out.println(GrandSon.class.isInstance(null)); // false,null都是false
}
interface Father {
}
static class Son implements Father {
}
static class GrandSon extends Son {
}