- Boolean赋值问题
- 下面的程序段是正确的,程序的执行流程是将 flag 赋值为 true 再去看 if 后的括号中的结果是true 还是 false, 在经过赋值之后括号中就是 true 了,所以the flag is true会被打印出来。
public static void main(String []args){
Boolean flag = false;
if(flag = true ){
System.out.println(“the flag is true”);
}
}
- Math. ceil 和 Math.floor 方法
- 在官方文档中,Math.ceil()方法返回的是大于等于当前数字的最小整数,实例如下
double i = -0.5;
System.out.println(Math.ceil( i ));
//输出的是0,因为大于等于 -0.5 的最小整数是 0
i = -1;
System.out.println(Math.ceil(i));
//输出的是 -1 ,因为 -1 满足了 大于等于 -1 的最小整数。
b) floor方法和ceil 相反,返回的是小于或者等于当前数字的最大整数
3、COW(Copy-on-write)写时复制,是指在并发下,线程对容器的对该不应该直接修改容器本身,而是应该赋值一份副本,在修改后将副本重写回容器。,下边这个例子就是没有使用写时赋值带来了并发问题,问题的关键在于,当访问最后一个容器执行到 int lastIndex = list.size() – 1,然后就进入阻塞队列,然后删除最后一个元素的线程在这个时候把最后一个线程删除,那么等到获取最后一个元素的线程重新获得cpu ,它就按刚刚计算得到的 list.size() – 1 得到的值去访问元素,这样的话就会报异常了,因为这个时候后一个元素已经被删除掉了。
public class ProblemOnVisitVector {
public static void main(String[] args) {
// 初始化Vector
Vector<String> vector = new Vector();
vector.add("关注公众号");
vector.add("Java3y");
vector.add("买Linux可到我下面的链接,享受最低价");
vector.add("给3y加鸡腿");
new Thread(() -> getLast(vector)).start();
new Thread(() -> deleteLast(vector)).start();
new Thread(() -> getLast(vector)).start();
new Thread(() -> deleteLast(vector)).start();
}
// 得到Vector最后一个元素
private static Object getLast(Vector list) {
int lastIndex = list.size() - 1;
try {
TimeUnit.MICROSECONDS.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
return list.get(lastIndex);
}
// 删除Vector最后一个元素
private static void deleteLast(Vector list) {
int lastIndex = list.size() - 1;
list.remove(lastIndex);
}
}
4、String.hashCode() 方法的原理是,s 数组就是保存这String 的值的数组,那么就会出现两个不同的String 的hashCode 一样的情况(成为哈希碰撞)
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] ( i * 31 = i * 21 – i;)
下边是示例两个不同的String 的hashCode 相同造成 equals 方法返回 true
String str1 = "通话";
String str2 = new String("重地") ;
System.out.println(String.format("str1:%d | str2:%d", str1.hashCode(),str2.hashCode()));
System.out.println(str1.equals(str2));
5、Integer 分配内存的问题,
a、问题的根源在于Integer.valueOf(int i); 这个方法会先去看一个叫做 IntegerCache的东西,这里存放这实例化Integer 的时候如果值在 [-128, 127] 的话,就把他们存进去,以后实例化Integer 对象的时候,就先去看 IntegerCache 中是否有这个数,有的话直接返回一个引用,没有的话再去new 一个 Integer对象
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
b、但是 Integer a = 1000; Integer b = 1000; a == b 返回值就是 false 了,因为 1000 已经无法放在缓冲区里了,在这样会造成在堆中开辟两份内存存放这两个 1000 ,所以返回的就是 false 了
public static void main(String[] args) {
Integer a = 128;
Integer b = 128;
System.out.println(a == b); // 这里返回的是 false
a = 127;
b = 127;
System.out.println(a == b); // 这里返回的是 true
String aa = "hello"; String bb = "hello";
System.out.println(aa == bb);
}
6、Thread.join() 方法,这个方法的作用是等待当前线程执行完毕后再往下执行。
a、Thread.join()方法说到底就是下边这几行代码,有三个重点
while (isAlive()) {
wait(0);
}
- join方法会先去判断线程是否是或者的,所以使用join的时候一定要先用start启动线程
- join方法会释放锁,因为wait方法会释放锁。
- join方法的本质是等待0秒钟,就是说他会释放锁之后马上又获得cpu,又拿到锁。
7、Array.remove 方法,查看源码可以看到这个方法是有重载的,分别是
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
和
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
也就是说,当入参是int 类型的时候,是根据下标去删除元素的,所以在下边这个题目中第一次会把元素 1 (是元素为 1, 不是下标为 1)给删除,然后当遍历到元素 4 的时候,就会因为下标越界的问题而报错了。
public static void main(String[] args) {
List<Integer> NumberList = new ArrayList<>();
NumberList.add(2);
NumberList.add(4);
NumberList.add(1);
NumberList.add(3);
NumberList.add(5);
for (int i = 0; i < NumberList.size(); i++) {
int v = NumberList.get(i);
if(v % 2 == 0){
NumberList.remove(v);
}
}
System.out.println(NumberList);
}
8、Math.round方法,这是四舍五入方法。本质是 floor( x + 0.5) floor 就是返回小于等于这个数的最大整数(也就是向下取整)
9、当类有String类型的成员变量的时候,实例化对象的时候和就会为这个String 赋值,这个赋值和普通的 String a = “ABC”; 是一模一样的,同样回去判断常量池中是否存在值为“ABC”的String 对象,如果有的话就取得它的一个引用,没有的话就实例化一个。下边的这个实例中,test和testB 在实例化的时候都会为name 赋值,在testB 实例化的时候发现常量池已经有了“ABC”这个String 对象了,所以就只是简单返回了一个引用而已,所以test.name == testB.name 得到的是 true
public static void main(String[] args) {
Test test = new Test();
Test testB = new Test();
String result = test.equals(testB) + ",";
result += test.name == testB.name;
System.out.println(result);
}
10、super() 和 this() ,没有参数也没有点什么什么的,super()表示的是父类的无参构造方法,也可以是指向父类对象的一个引用,因为super().xxx 可以访问具备足够权限的成员变量和方法, this()表示当前类的无参构造器,也可以引用当前的这个对象
a、在子类的构造方法中,默认会在构造方法的第一行调用父类的无参构造器(意思就是说如果在子类如果没有调用父类的无参构造器的话,那么父类的无参构造器是要存在的,不然子类就调用不到了)
b、在子类的有参构造器中,如果写this()的话,就是调用了当前类的无参构造器了,注意,这个时候有参构造器是没有直接调用父类的无参构造器的,这个是在子类的无参构造器中调用的。
c、super()和this()使用的时候都需要放在方法的第一行,否则会报错,这样一来的话这两个就不可以放在同一个方法中了。
d、在静态方法和静态代码块中不能使用this() 和 super()
e、在子类的构造方法中可以不用在第一行显式调用super()
11、当父类的成员变量生命为private 的时候,子类是可以继承到这个成员变量的,但是在使用的时候会受到private 的约束而不可使用,下边这个例子中,可以通过反射得知子类Test是有name 这个成员变量的,但是name的作用域仅仅在TestFather中,所以在子类中是无法访问到了