Java基础回顾-02

1.类的扩展

1.1 如何应对继承的双面性

  • 避免使用继承
  • 正确使用继承

避免继承的三种方法:

  • 使用final关键字
  • 优先使用组合而非继承
  • 使用接口

1.1.1 使用final避免继承

final方法不能被重写

final类不能被继承

1.1.2 优先使用组合

使用组合可以抵挡父类变化对子类的影响,从而保护子类,应该优先使用组合。

public class Child {
    private Base base;
    private long sum;
    public Child() {
        base = new Base();
    }
    public void add(int number) {
        base.add(number);
        sum += number;
    }
    public void addAll(int[] numbers) {
        base.addAll(numbers);
        for (int i = 0; i < numbers.length; i++) {
            sum += numbers[i];
        }
    }
    public long getSum() {
        return sum;
    }
}

1.2 接口

接口声明了一组能力,但它自己并没有实现这个能力,它只是一个约定。

接口涉及交互两方对象,一方需要实现这个接口,另一方使用这个接口,但双方对象并不直接互相依赖,它们只是通过接口交互。

定义接口:

public interface MyComparable {
    int compareTo(Object other);
}

注:接口定义里面,声明了一个方法compareTo,但没有定义方法体,在Java8之前,接口内不能实现方法。接口方法不需要加修饰符,加与不加相当与都是public abstract。

实现接口:表示类的对象具有接口所示的能力。

接口中的变量:

public interface Interface1 {
    public static final int a = 0;
}

变量a的修饰符是public static final,但是这个修饰符是可以选的,即使不写,也是public static final。

变量可以通过“接口名.变量名”的方式使用。如Interface1.a。

接口的继承:一个接口可以继承其他接口。

public interface IBase1 {
    void method1();
}

public interface IBase2 {
    void method2();
}
public interface IChild extends IBase1, IBase2 {
}

Java 8和Java 9对接口的增强。

Java 8允许在接口定义两类新方法:静态方法和默认方法,它们有实现体。

1.3 抽象类

抽象类和具体类一样,可以定义具体方法、实例变量等,它和具体类的核心区别是,抽象类不能创建对象(比如,不能使用new Shape()),而具体类可以。

抽象类和接口:接口不能定义实例变量,而抽象类可以,一个类可以实现多个接口,但只能继承一个类。

抽象类可接口是配合而非替代关系,它们经常一起使用,接口声明能力,抽象类提供默认实现,实现全部或部分方法,一个接口经常有一个对应的抽象类。如在Java类库中,有:

  •  Collection接口和对应的AbstractCollection抽象类
  • List接口和对应的AbstractList抽象类
  • Map接口和对应的AbstractMap抽象类  ↵

  对于需要实现接口的具体类而言,有两个选择:

  1. 实现接口,自己实现全部方法
  2. 继承抽象类,然后根据需要重写方法

2 线程基础

2.1 创建线程

线程表示一条单独的执行流,它有自己的程序执行计数器,有自己的栈。创建线程有两种方式:

  • 继承Thread
  • 实现Runnable接口

2.1.1 继承Thread

public class HelloThread extends Thread {
    public void run() {
        System.out.println("hello");
    }

}

run方法的方法签名是固定的,public,没有参数,没有返回值,不能抛出受检异常。

public class TestThread {
    public static void main(String[] args) {
        Thread thread = new HelloThread();
        thread.start();
    }
}

run方法类似于单线程程序中的main方法,线程从run方法的第一条语句开始执行直到结束。

start表示启动该线程,使其成为一条单独的执行流,操作系统会分配线程相关的资源,每条线程会有单独的程序执行器和栈,操作系统会把这个线程作为一个独立的个体进行调度,分配时间片让它执行,执行的起点就是run方法。

如果直接调用run方法,虽然屏幕输出不会发生变化,但并不会启动一条单独的执行流,run方法依然是在main线程中执行的,run方法只是main方法调用的一个普通方法。

public class HelloThread extends Thread {
    public void run() {
        System.out.println("Thread name: " + Thread.currentThread());
        System.out.println("hello");
    }
}

测试代码:

public class TestThread {
    public static void main(String[] args) {
        Thread thread = new HelloThread();
        thread.start();
        System.out.println("Thread name: " + Thread.currentThread());
    }
}

 Thread name: Thread[main,5,main]
Thread name: Thread[Thread-0,5,main]
hello

 查看Thread源码可见,currentThread为本地方法:

public static native Thread currentThread();

返回的是Thread的一个实例,而在Thread中的toString()方法可以的知,对于以上打印出来的信息与一下代码(名称、优先级、组名)一一对应:

public String toString() {
        ThreadGroup group = getThreadGroup();
        if (group != null) {
            return "Thread[" + getName() + "," + getPriority() + "," +
                           group.getName() + "]";
        } else {
            return "Thread[" + getName() + "," + getPriority() + "," +
                            "" + "]";
        }
    }

 2.2.2 实现Runnable接口

因Java只支持单继承,如果类已经有父类了,就不能再继承Thread,这是可以通过实现java.lang.Runnable接口来实现线程。Runnable接口的定义很简单,只有一个run方法:

public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

 使用方法如下:
 

public class HelloRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("hello");
    }

    public static void main(String[] args) {
        Thread helloThread = new Thread(new HelloRunnable());
        helloThread.start();
    }
}

2.2 线程的状态

线程有一些基本属性和方法,包括id、name、优先级了(priority)、状态(State)、是否deamon线程(isDaemon)、sleep方法、yield方法、join方法、interrupt、stop和过时方法。

线程有一个状态的概念,Thread有一个方法用于获取线程的状态:

public State getState()

返回类型为Thread.State,它是一个枚举类型,有如下值:

public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

根据上述,各状态的变化关系如图所示:

 

2.3 Thread内置方法的使用和区别

线程有一个优先级的概念,在Java中,优先级从1到10,默认为5,相关方法为:

public final void setPriority(int newPriority)
public final int getPriority()

sleep方法,调用该方法会让当前线程睡眠指定的时间,单位是毫秒;睡眠期间,线程可以被中断,sleep会抛出InterruptedException。

public static native void sleep(long millis) throws InterruptedException;

yield方法,让出CPU。

public static native void yield();

join方法,让调用join的线程等待该线程结束。

public final synchronized void join(long millis)
    throws InterruptedException

interrupt方法,其作用是中断此线程(此线程不一定是当前线程,而是指调用该方法的Thread实例所代表的线程),但实际上只是给线程设置一个中断标志,线程仍会继续运行。

public void interrupt()

stop方法,停止线程,已过时弃用。

@Deprecated
public final synchronized void stop(Throwable obj) {
    throw new UnsupportedOperationException();
}

sleep()方法和yield()方法的区别如下:

  • sleep方法暂停当前线程后,会给其他所有线程执行机会,不理会线程的优先级;yield方法只会给优先级相同或更高的线程执行机会。
  • sleep方法会将线程转入阻塞状态,直到经过阻塞时间才会转入就绪状态;yield不会将线程转入阻塞状态,只是强制当前线程进入就绪状态。
  • sleep方法声明抛出异常;yield方法没有声明抛出异常;
  • sleep方法比yield方法有更好的可移植性。

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值