补全Java基础(一)JavaSE基础

1. super和this

    类在继承时,会用到this和super,以前也不多用到,之前看过,今天看了一下,只记得之前看过,理解仍是不深。html

  • thisjava

    1. 直接引用,至关于指向对象自己算法

    2. 型参与成员变量名字相同,用this来区分bash

      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;
          }
      }
      复制代码
    3. 引用构造函数多线程

      this(参数):调用本类中另外一种构造函数(应在构造函数的第一行)ide

  • super函数

    1. 直接引用,指向当前对象的父类,能够用super.xx来引用父类的属性ui

    2. 子类的变量/方法与父类的变量/方法重名this

      在子类种调用时,重名的方法/变量,直接使用是子类的,this.xx或this.xx()属于调用父类的。spa

    3. 引用构造函数

      super(参数):调用父类中某种构造函数(应在构造函数的第一行)


参考:Java中this和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方法。
```
复制代码

参考:
重写equals方法后重写hashCode方法的必要性
如何重写hashCode算法
HashSet重复元素判断


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要早于hello一、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()

相关文章
相关标签/搜索