1. super和this
类在继承时,会用到this和super,之前也很少用到,以前看过,今天看了一下,只记得以前看过,理解还是不深。
-
this
-
直接引用,相当于指向对象本身
-
型参与成员变量名字相同,用this来区分
class A{ private int number = 15; A(){ System.out.println("number:"+number); } public int getNumber(int number){ this.number = number; //用来区分参数number和A类中的属性number(this.number) return this.number; } } 复制代码
-
引用构造函数
this(参数):调用本类中另一种构造函数(应在构造函数的第一行)
-
-
super
-
直接引用,指向当前对象的父类,可以用super.xx来引用父类的属性
-
子类的变量/方法与父类的变量/方法重名
在子类种调用时,重名的方法/变量,直接使用是子类的,this.xx或this.xx()属于调用父类的。
-
引用构造函数
super(参数):调用父类中某种构造函数(应在构造函数的第一行)
-
2. equals()和hashcode()
这两个方法都是Object中带有的方法,即所有方法都有equals和hashcode方法。
在没有重写的情况下,equals方法即"==",hashcode方法获取哈希码,即确定对象在散列表中的索引位置。
如果不重写equals方法,那么比如User这种对象,一般我们equals比较的是它包含的属性是否相同,不重写的话,依照Object的equals方法,两个对象的内存地址不同,所以返回false,这显然不是我们想要要的。所以要重写,在方法中判断属性是否相同。
如果只重写equals方法,而没有重写hashcode方法,比如User对象放在hashset中,hashset的特性是不允许重复。当我们放入两个数据相同的User时,重写后的equals方法返回true,但是没有重写hashcode方法,依照Object的hashcode方法,两个对象的hash值不同,所以不是同一对象,hashset中都可以存入,但是当我们将User放入hashset中,要求如果是要求数据不同的话,就会无法实现。所以需要重写hashcode方法,使得包含数据相同的两个User对象,返回的hash值相同,而此时,hashset会判断重复,就只能存入一个。
```
而这其中就涉及到hashset判断重复的依据
add(object)会先用Object的equals方法和hashcode方法判断set中,
是否已有重复元素,当然,如果两个元素如果不是指向同一对象,无论如何也不会重复。
所以,当我们需要剔除数据相同的元素,就需要在此基础上重新给元素对应的类型重写hashcode方法。
```
复制代码
3. 线程
对我来说,是急需加强的一个点,因为之前用到的不多,所以也就不求甚解。
Java多线程的两种实现方式:继承Thread类、实现Runnable接口
-
继承Thread类
package cn.wh3t; public class MultiThread extends Thread{ private String name; public MultiThread(String name) { // TODO 自动生成的构造函数存根 this.name = name; } @Override public void run() { // TODO 自动生成的方法存根 for(int i =0;i<5;i++) { System.out.println("Thread-"+name+"----hello"+i); } } public static void main(String[] args) { MultiThread multiThread1 = new MultiThread("A"); MultiThread multiThread2 = new MultiThread("B"); multiThread1.start(); System.out.println("hello1"); System.out.println("hello2"); multiThread2.start(); System.out.println("hello3"); System.out.println("hello4"); } } 复制代码
输出结果://多次运行结果都会不同 hello1 hello2 Thread-A----hello0 Thread-A----hello1 Thread-A----hello2 hello3 Thread-A----hello3 hello4 Thread-A----hello4 Thread-B----hello0 Thread-B----hello1 Thread-B----hello2 Thread-B----hello3 Thread-B----hello4 复制代码
实际启动的时候并不是调用Thread的run方法,而是strat方法。
那么run()和start()有什么区别
在Java中,线程通常都有5种状态:创建、就绪、运行、阻塞、死亡
1. 创建:生成线程对象时,还未调用start方法
2. 就绪:调用了该线程对象的start,线程进入就绪状态,但是并未把该线程设成当前线程。在线程运行之后,从等待或者睡眠中回来,也会进入就绪状态
3. 运行:将该线程设为当前线程,线程进入运行状态,开始运行run()中的代码
4. 阻塞:线程正在运行时,被暂停,通常是为了某个时间发生或者加载某个资源之后再继续运行。sleep、suspend、wait都会造成阻塞
5. 死亡:线程的run()结束,或者调用了stop方法,线程就会死亡。对于死亡的线程,再使用start方法也不会使其进入就绪状态
总之就是说,如果没有start方法,只有run方法。就不是多线程执行了。因为start方法使线程进入就绪状态,并没有立即执行,通过上面代码的运行结果也可以看出,线程1的start要早于hello1、hello2的打印,却比他们输出要晚。如果没有start,只是run(),就会依次执行,回归单线程(当前主线程)执行的状态。
-
实现Runnable接口
package cn.wh3t; public class MultiThreadByRunnable implements Runnable{ private int count = 20; @Override public void run() { // TODO 自动生成的方法存根 for(int i =0;i<40;i++) { if(count>0) { System.out.println(Thread.currentThread().getName()+" "+count--); } } } public static void main(String[] args) { MultiThreadByRunnable runnable = new MultiThreadByRunnable(); Thread thread1 = new Thread(runnable,"A"); Thread thread2 = new Thread(runnable,"B"); thread1.start(); System.out.println("hello1"); System.out.println("hello2"); thread2.start(); System.out.println("hello3"); System.out.println("hello4"); } } 复制代码
输出结果://多次运行结果都会不同 hello1 A 20 hello2 A 19 A 18 A 17 hello3 hello4 A 16 B 15 B 13 B 12 A 14 B 11 B 10 B 9 B 8 B 6 B 5 B 4 B 3 B 2 B 1 A 7 复制代码
两种实现方式不同:相比之下,实现Runnable接口更有优势
1. 适合多个代码相同的程序去处理同一资源;
2. 可以避免Java中的单继承,可以扩展实现更多接口
3. 增加程序的健壮性,代码可以被多个程序共享,代码和数据独立
- wait()和notify()
sleep()、suspend()、yield()等都是Thread类的方法,wait()和notify()确实属于Object的方法,即所有类都可以执行这两个方法。因为这两个方法阻塞时要释放占用的锁,而所有对象都有锁。wait()导致线程阻塞,释放该对象上占用的锁。调用notify()则导致因调用该对象的wait()而阻塞的线程中,随机选择一个解除阻塞(但是要等到真正获得锁以后才能执行)。这两个方法必须在synchronized方法或块中执行,因为synchronized方法/块才能占有锁,有锁才能释放。
关于wait和notify要注意的三个点:
1. 调用notify方法而接触阻塞的线程是从wait方法阻塞的线程中随机选取的,所以无法预料哪个线程被选取,所以要小心,避免不确定性产生的问题
2. 除了notify,notifyAll方法也可以唤醒,解除阻塞的是因wait方法而阻塞的所有线程,当然,只有获得锁的那个线程才能进入可执行状态
3. wait和notify必须成对存在
package cn.wh3t;
public class CountThread extends Thread{
int total;
@Override
public void run() {
// TODO 自动生成的方法存根
synchronized (this) {
for(int i=0;i<100;i++) {
total = total +1 ;
}
this.notify();//唤醒被阻塞的线程
System.out.println("notify"+total);
}
}
}
复制代码
package cn.wh3t;
public class TestWaitAndNotify {
public static void main(String[] args) {
CountThread thread = new CountThread();
thread.start();
synchronized(thread) {
System.out.println("等到线程结束"+thread.total);
try {
thread.wait();
System.out.println("wait"+thread.total);
}catch(Exception e) {
e.printStackTrace();
}
System.out.println("计算的结果是:"+thread.total);
}
}
}
复制代码
(主线程等待技术线程结束后才打印输出)
等到线程结束0
notify100
wait100
计算的结果是:100
复制代码
参考:
java 线程详解
Thread的run()与start()的区别
java thread中的wait()和notify()
转载于:https://juejin.im/post/5d07372f6fb9a07ee30e1948