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抽象类 ↵
对于需要实现接口的具体类而言,有两个选择:
- 实现接口,自己实现全部方法
- 继承抽象类,然后根据需要重写方法
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方法有更好的可移植性。