本文主要是收录一些人们可能经常会忽略,但又挺重要的Java方面的小知识。有推荐的也可以在下方留言,大家一起进步呀!

目录:
1.a=a+b和a+=b的区别
2.String str1 = new String("a"); String str2 = "a"+"b"; 这两行代码分别创建了几个对象,是什么
3.启动线程的方式以及如何更好地结束一个线程?
4.Java 中 == 和 equals 和 hashCode的区别

2019-7-7

1.a=a+b和a+=b的区别

(1)从效率上看
a+=b要略高于a=a+b
(2)从运算符上看
a+=b的执行过程是先计算出a的值,然后用一个temp对象存储,之后和b进行相加,然后将值赋给a引用。
a=a+b的执行过程是先计算a+b,然后再赋值给a引用,赋值给a引用的时候如果引用a有计算过程,则会再次计算。
(3)从类型自动转换上看
当a和b的数据类型不一样时,a+=b会自动进行类型转换;而a=a+b不会,需要手动进行类型转换
比如:

byte a = 1;
int b = 4;
a = a + b;//此时编译会报错,因为a为byte,b为int,
//a+b的时候默认会向大类型即int转换,则a+b的类型为int,而a为byte,所以报错

a += b;//此时编译不会报错,因为+=会自动进行类型转换,并且向低转换
2.String str1 = new String("a"); String str2 = "a"+"b"; 这两行代码分别创建了几个对象,是什么

(1)首先第一行代码中创建了两个对象(因为字符串池中没有a字符串),一个是new String创建出来的str1对象,一个是在字符串池中创建的a对象。
(2)然后因为a字符串已经在常量池中存在了,所以在第二行代码中就不会再创建a对象了。接着创建的就是b对象,最后是str2对象,值为ab。
总结:要先看字符串池中有没有该字符串,有的话会直接拿过来,没有再重新创建。

3.启动线程的方式以及如何更好地结束一个线程?

(1)启动线程的方式

  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口通过FutureTask包装器来创建Thread线程
  • 匿名内部类
图片.png

(2)如何更好地结束一个线程

线程中调用interrupt()方法仅仅是在当前线程中打了个停止的标记,并不是真的停止线程。
如何判断线程的状态是否是停止,主要包含以下两种方法:
1、this.interrupted():测试当前线程是否已经是中断状态,执行后具有将状态标志置清除为false的功能。
2、this.isInterrupted():测试线程thread对象是否已经是中断状态,但不具备清除状态标志。
注:使用stop()方法停止线程是非常暴力的,该方法已经作废,不考虑。

注意:官方文档中说明If this thread is blocked in an invocation of the [wait()], [wait(long)], or [wait(long, int)] methods of the [Object] class, or of the [join()], [join(long)], [join(long, int)], [sleep(long)], or [sleep(long, int)], methods of this class, then its interrupt status will be cleared and it will receive an [InterruptedException].
也就是说,在run方法里面调用Object类的wait或线程的join/sleep方法之后,这个标记会被清空并抛出InterruptedException异常。解决方法:可在调用的方法的catch中再this.interrupt一次。(不是下文例子中的catch)

① 抛异常法

public static void main(String[] args) throws InterruptedException {
        MyThread t1 = new MyThread();
        t1.start();
        Thread.sleep(100);
        t1.interrupt();
    }

    public static class MyThread extends Thread {
        @Override
        public void run() {
            try {
                for (int i = 0; i < 5000000; i++) {
                    if (this.isInterrupted()) {
                        System.out.println("Thread Stop!");
                        throw new InterruptedException();
                    }
                    System.out.println("i: " + i);
                }
                System.out.println("我在for下面");
            } catch (InterruptedException e) {
                System.out.println("进入到catch了!");
            }
        }
    }

运行结果:
...
i: 11759
i: 11760
i: 11761
i: 11762
Thread Stop!
进入到catch了!

② 使用return

public static void main(String[] args) throws InterruptedException {
        MyThread t1 = new MyThread();
        t1.start();
        Thread.sleep(100);
        t1.interrupt();
    }

    public static class MyThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 5000000; i++) {
                if (this.isInterrupted()) {
                    System.out.println("Thread Stop!");
                    return;
                }
                System.out.println("i: " + i);
            }
            System.out.println("我在for下面");
        }
    }

运行结果:
...
i: 12301
i: 12302
i: 12303
i: 12304
i: 12305
Thread Stop!

③ 使用退出标志
和上面两种方法差不多,只不过是用一个布尔变量代替this.isInterrupted(),然后要想停止线程,赋值该布尔变量为false即可。

2019-8-2

4.Java 中 == 和 equals 和 hashCode的区别

(1)==
对于八种基本数据类型,比较的是他们的值
对于引用数据类型(类,接口和数组),比较的是他们在内存中的地址,所以,除非是同一个new出来的对象,他们比较之后的结果为true,否则为false。eg:

Integer a = new Integer(127);
Integer b = 127;
Integer c = 127;
//a==b为false,b==c为true
Integer d = 128;
Integer e = 128;
//d==e为false
//这里为什么b==c为true而d==e为false呢?
//Integer b = 127在编译时会被翻译成Integer.valueOf(127),该方法对于[-128,127]之间
//的数会进行缓存,如果已经有的话会直接从缓存中拿出,不会再new
//因为128不在该区间,那么d和e就只是new出来的两个不同的对象了,所以就为false

Java中堆和栈的区别
最主要的区别就是:new出来的对象和数组是存放在堆中的,基本数据类型的变量和对象的引用变量是存放在栈中的。

Java的内存机制:
栈:当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当该变量超过它的作用域后,Java会自动释放掉为该变量所分配的内存空间。
堆:在堆中分配的内存,由GC来管理。在堆中产生了一个数组或对象之后,还可以在栈中定义一个特殊的变量,让栈中这个变量的值等于该数组或者对象在堆内存中的首地址,栈中的这个变量就成了数组或者对象的引用变量,以后可以在程序中使用栈中的引用变量来访问数组或者对象。

(2)equals
默认情况下的equals方法是没有被覆盖的,调用的都是Object类的equals方法,这种情况下的equals方法和==是等效的。

public boolean equals(Object obj) {
   return (this == obj);
   }
如果equals方法被覆盖,则按照新的方法来判断,覆盖后一般
都是通过对象的内容是否相等来判断对象是否相等。比如String类的equals方法:

public boolean equals(Object anObject) {
   if (this == anObject) {
       return true;
   }
   if (anObject instanceof String) {
       String anotherString = (String)anObject;
       int n = count;
       if (n == anotherString.count) {
       char v1[] = value;
       char v2[] = anotherString.value;
       int i = offset;
       int j = anotherString.offset;
       while (n-- != 0) {
           if (v1[i++] != v2[j++])
           return false;
       }
       return true;
       }
   }
   return false;
}

String类的equals方法总结为:
首先看是不是同一个对象,是的话返回true
然后看对比对象是不是String类型,
接着看长度是否一致
最后比较逐个字符是否相同

实现高质量的equals方法的技巧如下:
首先比较两个是否==,
然后检查对比对象是否是该类型,
接着比较他们的域是否相同
最后检查对称性,传递性,一致性

(3)hashCode
hashCode()方法也是Object类的一个方法,返回的是一个int型的整数。在集合类操作中使用,主要是为了提高查询速度。
两者的hashCode相同,他们不一定equals。
两者的hashCode不相同,他们一定不equals。
他们equals,hashCode一定相同。
他们不equals,hashCode有可能相同。