基础知识
imoprt
The import keyword is to bring in an entire library or a member of that one
import java.util.*;
import java.util.Vector;
import static java.lang.Math.*;
compilation unit
Each compilation unit must have a name ending in java, and inside the compilation unit there can be a public class that must have the same name as teh file. There can be only one public class in each compilation unit.
classpath
Place all the .class files for a particular package into a single directory
CLASSPATH contains one or more directories that are used as roots for a search for .class files.
流程控制
public class Test{
public static void main(String[] args)
{
int n = 0, m, j, i;
for(i=3; i<=100; i+=2)
{
if(i>=20)
{
i=100;
continue;
}
m = (int)Math.sqrt((double)i);
for(j=2; j<=m; j++)
{
if((i%j) == 0)
break;
}
if(j > m)
{
System.out.print(i+"\t");
n++;
if(n%5 == 0)
System.out.println();
}
}
}
}
3 5 7 11 13
17 19
带标签的break
P83
public class Test {
public static void main(String[] args){
outer:
for(int i=0; i<5; ++i){
for(int j=0; j<3; ++j){
System.out.println("i : "+i+", j : "+j);
if( j == 1 ){
break outer;
}
}
}
}
}
i : 0, j : 0
i : 0, j : 1
带标签的continue
P85
public class Test {
public static void main(String[] args){
outer:
for(int i=0; i<5; ++i){
for(int j=0; j<3; ++j){
System.out.println("i : "+i+", j : "+j);
if( j == 1 ){
continue outer;
}
}
}
}
}
i : 0, j : 0
i : 0, j : 1
i : 1, j : 0
i : 1, j : 1
i : 2, j : 0
i : 2, j : 1
i : 3, j : 0
i : 3, j : 1
i : 4, j : 0
i : 4, j : 1
类绑定
public class Test {
public static void main(String[] args){
A a = new B();
a.f(a);
}
}
class A{
public void f(A x){
System.out.println("A.f(A)");
}
public void f(B x){
System.out.println("A.f(B)");
}
}
class B extends A{
public void f(A x){
System.out.println("B.f(A)");
}
public void f(B x){
System.out.println("B.f(B)");
}
}
B.f(A)
类的初始化
P159
public class Test {
public static void main(String[] args){
new Leaf();
new Leaf();
}
}
class Root
{
static{
System.out.println("Root's static init block");
}
{
System.out.println("Root's normal init block");
}
public Root()
{
System.out.println("Root's Non-arg Ctor");
}
}
class Mid extends Root
{
static{
System.out.println("Mid's static init block");
}
{
System.out.println("Mid's normal init block");
}
public Mid()
{
System.out.println("Mid's Non-arg Ctor");
}
}
class Leaf extends Mid
{
static{
System.out.println("Leaf's static init block");
}
{
System.out.println("Leaf's normal init block");
}
public Leaf()
{
System.out.println("Leaf's Non-arg Ctor");
}
}
Root's static init block
Mid's static init block
Leaf's static init block
Root's normal init block
Root's Non-arg Ctor
Mid's normal init block
Mid's Non-arg Ctor
Leaf's normal init block
Leaf's Non-arg Ctor
Root's normal init block
Root's Non-arg Ctor
Mid's normal init block
Mid's Non-arg Ctor
Leaf's normal init block
Leaf's Non-arg Ctor
基本数据类型的包装类
P166
public class Test {
public static void main(String[] args){
Integer ina = 2;
Integer inb = 2;
System.out.println(ina == inb);
Integer biga = 128;
Integer bigb = 128;
System.out.println(biga == bigb);
}
}
true
false
== 和 equals方法
当使用== 来判断两个变量是否相等时,如果都是基本变量类型,且都是数值类型,则只要值相等,就返回true;对于两个引用类型变量,必须指向同一个对象,==判断才为true;对于基本数据的包装类,值相等则判断为true
equals是Object类提供的实例方法,可以重写equals方法实现对象的自定义比较。
P169
public class Test {
public static void main(String[] args){
int it = 65;
float fl = 65.0f;
System.out.println(it == fl);
char ch = 'A';
System.out.println(it == ch);
}
}
true
true
public class Test {
public static void main(String[] args){
String s1 = "abcd";
String s2 = "ab";
String s3 = "cd";
String s4 = "ab" + "cd";
String s5 = "a" + "b" + "cd";
String s6 = s2 + s3;
String s7 = new String("abcd");
System.out.println(s1 == s4);
System.out.println(s1 == s5);
System.out.println(s1 == s6);
System.out.println(s1.equals(s6));
System.out.println(s1 == s7);
System.out.println(s1.equals(s7));
}
}
true
true
false
true
false
true
静态类方法
P173
public static void NullAccessStaticTest(){
System.out.println("NullAccessStaticTest");
}
public static void main(String[] args){
Test test = null;
test.NullAccessStaticTest();
}
NullAccessStaticTest
单例类
P174
class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance()
{
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
public class Test {
public static void main(String[] args){
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
}
}
true
final
final修饰的对象只能被赋值一次
在编译时能够确定的值称为“宏变量”,需要调用函数等的则不是
P178
public class Test {
public static void main(String[] args){
final String book = "abcd" + 99.0;
final String book2 = "abcd" + String.valueOf(99.0);
System.out.println(book == "abcd99.0");
System.out.println(book2 == "abcd99.0");
}
}
true
false
抽象类和接口
- 接口只能包含抽象方法;抽象类可以 包含普通方法
- 接口不能定义静态方法;抽象类可以
- 接口只能定义静态常量Field,不能定义普通Field;抽象类都可以
- 接口里不包含构造器;抽象类可以包含
- 接口里不能包含初始化块;抽象类可以
- 一个类最多只能有一个直接父类;但一个类可以直接实现多个接口
内部类
非静态内部类中,内部类访问外部类属性,外部类访问内部类属性
P202
public class Outer {
private String outProp = "outProp";
private class Inner{
private String inProp = "inProp";
private void accessOuterProp()
{
System.out.println(outProp);
}
}
public void accessInnerProp(){
Inner in = new Inner();
in.accessOuterProp();
System.out.println(in.inProp);
}
public static void main(String[] args){
Outer out = new Outer();
out.accessInnerProp();
Inner in = new Outer().new Inner();
in.accessOuterProp();
}
}
outProp
inProp
outProp
外部类访问静态内部类的静态变量和普通成员变量
P204
public class Outer {
static class Inner{
private static int prop1 = 5;
private int prop2 = 9;
}
public void accessInnerProp(){
System.out.println(Inner.prop1);
System.out.println(new Inner().prop2);
}
public static void main(String[] args){
Outer out = new Outer();
out.accessInnerProp();
}
}
5
9
在外部类以外创建非静态内部类实例
P205
public class Test {
public static void main(String[] args){
Out.In in = new Out().new In("Test Message");
}
}
class Out
{
class In
{
public In(String msg)
{
System.out.println(msg);
}
}
}
Test Message
外部类以外继承内部类
P206
public class Test {
public static void main(String[] args){
new Subclass(new Out());
}
}
class Subclass extends Out.In
{
public Subclass(Out out)
{
out.super("hello");
}
}
class Out
{
class In
{
public In(String msg)
{
System.out.println(msg);
}
}
}
hello
在外部类之外使用静态内部类
P207
public class Test {
public static void main(String[] args){
StaticOut.StaticIn in = new StaticOut.StaticIn();
}
}
class StaticOut
{
static class StaticIn
{
public StaticIn(){
System.out.println("Ctor of StaticIn");
}
}
}
Ctor of StaticIn
匿名内部类
构造格式如下
new 父类构造函数(实参列表) | 实现接口()
{
//匿名内部类的类体部分
}
枚举类
- 枚举类可以实现接口,使用enum定义的枚举类继承了java.lang.Enum类,而不是Object类,并且实现了java.lang.Serializable和lava.lang.Comparable两个接口
- 使用enum定义、非抽象的枚举类默认会使用final修饰,因此不能派生子类
- 枚举类的构造器只能使用private访问控制符
- 枚举类的所有实例必须在枚举类的第一行显示列出,且系统会自动添加public static final修饰符
P215
public class Test {
public void judge(SeasonEnum e){
switch (e) {
case SPRING:
System.out.println("Spring");
break;
case SUMMER:
System.out.println("Summer");
case FALL:
System.out.println("Fall");
case WINTER:
System.out.println("Winter");
default:
break;
}
}
public static void main(String[] args){
for(SeasonEnum s:SeasonEnum.values())
{
System.out.println(s);
}
new Test().judge(SeasonEnum.SPRING);
}
}
enum SeasonEnum
{
SPRING, SUMMER, FALL, WINTER;
}
SPRING
SUMMER
FALL
WINTER
Spring
枚举类的Field,方法和构造器
最科学合理的使用方法
P218
public class Test {
public static void main(String[] args){
Gender gender = Enum.valueOf(Gender.class, "FEMALE");
System.out.println(gender.getName());
}
}
enum Gender{
MALE("男"), FEMALE("女");
private final String name;
private Gender(String name){
this.name = name;
}
public String getName()
{
return this.name;
}
}
女
包含接口和抽象方法的枚举类
P219,P220
public enum Operation implements Info{
PLUS
{
public double eval(double x, double y)
{
return x+y;
}
public void info()
{
System.out.println("Plus");
}
},
MINUS
{
public double eval(double x, double y)
{
return x-y;
}
public void info()
{
System.out.println("Minus");
}
},
TIMES
{
public double eval(double x, double y)
{
return x*y;
}
public void info()
{
System.out.println("Times");
}
},
DIVIDE
{
public double eval(double x, double y)
{
return x / y;
}
public void info()
{
System.out.println("Divide");
}
};
public abstract double eval(double x, double y);
public static void main(String[] args) {
System.out.println(Operation.PLUS.eval(3, 4));
System.out.println(Operation.MINUS.eval(3, 4));
System.out.println(Operation.TIMES.eval(3, 4));
System.out.println(Operation.DIVIDE.eval(3, 4));
}
}
interface Info{
void info();
}
7.0
-1.0
12.0
0.75
Set
Set实现了Collection接口。Set不允许包含重复元素,集合多个对象之间没有明显的顺序。Set的实现有
- HashSet 添加进HashSet的Object必须实现了hashCode()方法。元素之间用equals()方法比较是否相同。它有一个子类 LinkedHashSet,用hashCode()决定元素的储存位置,并将元素按插入顺序连接起来。
- TreeSet 可以确保集合中元素处于排序状态。
List
List实现了Collection接口。List代表一个元素有序,可重复的集合,集合中每个元素都有其对应的顺序索引。List允许包含重复元素,可以通过索引来访问指定位置的元素。List的实现有
- ArrayList
- LinkedList
使用 Arrays.asList()方法可以生成一个固定长度的List
Map
Map用于保存具有映射关系的数据,因此Map保存着两组值,key和value。key和value存在单向一对一关系
使用泛型
List<String> strList = new ArrayList<String>();
Map<String, Integer> scores = new HashMap<String, Integer>();
// After Java 7
List<String> strList = new ArrayList<>();
Map<String, Integer> scores = new HashMap<>();
Iterator
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class Test {
public static void main(String[] args){
List<String> strList = new LinkedList<String>();
strList.add("a");
strList.add("b");
strList.add("c");
strList.add("d");
Iterator it = strList.iterator();
while(it.hasNext())
{
String str = (String)it.next();
System.out.println(str);
if(str.equals("c")){
it.remove();
}
str = "test";
}
System.out.println(strList);
}
}
a
b
c
d
[a, b, d]
泛型声明
P296
public class Test {
public static void main(String[] args){
Apple<String> a1 = new Apple<String>("apple");
System.out.println(a1.getInfo());
Apple<Double> a2 = new Apple<Double>(3.14);
System.out.println(a2.getInfo());
}
}
class Apple<T>
{
private T info;
public Apple(){}
public Apple(T info)
{
this.info = info;
}
public void setInfo(T info)
{
this.info = info;
}
public T getInfo()
{
return this.info;
}
}
apple
3.14
从泛型类派生子类
P297
public class Test {
public static void main(String[] args){
A1 a1 = new A1();
a1.setInfo("hello");
System.out.println(a1.getInfo());
A2 a2 = new A2();
a2.setInfo(123);
System.out.println(a2.getInfo());
}
}
class Apple<T>
{
private T info;
public Apple(){}
public Apple(T info)
{
this.info = info;
}
public void setInfo(T info)
{
this.info = info;
}
public T getInfo()
{
return this.info;
}
}
class A1 extends Apple<String>
{
public String getInfo()
{
return "SubClass" + super.getInfo();
}
}
class A2 extends Apple
{
public String getInfo()
{
return super.getInfo().toString();
}
}
SubClasshello
123
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args){
List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());
System.out.println(l1 instanceof ArrayList);
}
}
true
true
由于系统中不会真正生成泛型类,所以 instanceof 运算符后不能使用泛型类
类型通配符
P300
import java.util.Arrays;
import java.util.List;
public class Test {
public void test(List<?> c)
{
for(int i=0; i<c.size(); ++i)
{
System.out.println(c.get(i));
// System.out.println(c.get(i).getClass());
}
}
public static void main(String[] args){
new Test().test(Arrays.asList("abcd", 1, 1.23, 'A'));
}
}
abcd
1
1.23
A
P302
import java.util.List;
import java.util.Arrays;
public class Canvas {
public void drawAll(List<? extends Shape> shapes)
{
for(Shape s : shapes){
s.draw(this);
}
}
public String toString()
{
return "Canvas";
}
public static void main(String[] args)
{
List<Circle> circleList = Arrays.asList(new Circle(), new Circle());
Canvas c = new Canvas();
c.drawAll(circleList);
}
}
abstract class Shape
{
public abstract void draw(Canvas c);
}
class Circle extends Shape
{
public void draw(Canvas c)
{
System.out.println("Draw a circle on " + c);
}
}
Draw a circle on Canvas
Draw a circle on Canvas
public class Apple<T extends Number & java.io.Serializable>
{
...
}
也可以设置通配符/泛型的下限
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Test {
public static<T> T copy(Collection<? super T> dest, Collection<T> src)
{
T last = null;
for (T ele : src){
last = ele;
dest.add(ele);
}
return last;
}
public static void main(String[] args){
List<Number> ln = new ArrayList<Number>();
List<Integer> li = new ArrayList<Integer>();
li.add(5);
Integer last = copy(ln, li);
System.out.println(last);
}
}
5
泛型和数组不能同时使用,比如下面语句就会报错
List<String>[] lsa = new ArrayList<String>[10];
异常处理
- Java把所有非正常情况分成两种 Exception 和 Error, 都继承自 Throwable父类
- try块后最多只有一个catch块会被执行
- try块后的花括号不能省略
- 所有父类异常的catch都必须在子类异常catch后面
- 不管try块中代码是否异常,finally块中代码总会被执行,所以可以用来回收try块中打开的物理资源
- 子类方法中抛出的异常应该是父类方法声明抛出异常的子类或相同,子类方法中不允许抛出比父类方法声明更多的异常。
throws用法
import java.io.FileInputStream;
import java.io.IOException;
public class Test {
public static void test() throws IOException
{
FileInputStream fils = new FileInputStream("a.txt");
}
public static void main(String[] args)
{
try{
test();
}
catch(IOException e)
{
System.out.println("catch the exception");
}
}
}
throw 用法
import java.io.FileInputStream;
import java.io.IOException;
public class Test {
public static void test() throws IOException
{
throw new IOException("haha");
}
public static void main(String[] args)
{
try{
test();
}
catch(IOException e)
{
System.out.println(e.getMessage());
}
}
}
haha
自定义异常类
public class AuctionException extends Exception
{
public AuctionException(){}
public AuctionException(String msg)
{
super(msg);
}
}
Throw All
try
{
...
}
catch (Throwable t)
{
...
}
练习
import java.io.IOException;
import javax.swing.JFrame;
public class Test {
public static void main(String[] args)
{
int i;
for(i=0; i<3; i++)
{
try{
System.out.println("i="+i);
throw new MyException();
}
catch(MyException e)
{
System.out.println(e.Say());
}
}
}
}
class MyException extends Exception
{
static int intNumber = 0;
MyException()
{
intNumber++;
}
String Say()
{
return "No"+intNumber+":MyException!";
}
}
i=0
No1:MyException!
i=1
No2:MyException!
i=2
No3:MyException!
IO
FIle类
代表与平台无关的文件和目录,能操作文件和目录,但是不能访问文件本身。
IO流
字节流和字符流
字节流最小数据单元是8位的字节,字符流是16位的字符
字节流主要由InputStream, OutputStream作为基类,字符流主要由Reader和Writer作为基类,他们都是抽象类
对象序列化
P678,679
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Test {
public static void main(String[] args) throws Exception
{
try{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
Person per = new Person("Tom", 13);
oos.writeObject(per);
oos.close();
}
catch(IOException e)
{
e.printStackTrace();
}
try{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Object.txt"));
Person p = (Person)ois.readObject();
System.out.println("name:"+p.name+",age:"+p.age);
ois.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
class Person implements Serializable
{
public String name;
public int age;
public Person(String name, int age)
{
this.name = name;
this.age = age;
}
}
name:Tom,age:13
GUI
组件、容器、布局管理器的关系
组件Component代表一个能以图形化方式显示出来,并且可以和用户交互的对象
- 容器Container是一种特殊的组件,是可以盛装组件的组件
- 布局管理器是容器管理它里面组件布局的方式
Panel
- AWT中典型的容器,可作为容器来盛装其他组件,为放置提供空间
- 不能单独存在,必须放置到其他容器中
- 默认使用FLowLayout作为其布局管理器
Frame
- Frame代表常见的窗口,是Window类的子类
- Frame对象有标题,允许通过拖拉来改变窗口的位置,大小
- 初始化时不可见,可用setVisible(true)使其显示出来
- 默认使用BorderLayout作为其布局管理器
import java.awt.*;
public class Test extends java.applet.Applet{
Choice testChoice;
List testList;
public void init()
{
testChoice = new Choice();
testList = new List();
for(int i=1; i<=3; i++){
testChoice.addItem(String.valueOf(i));
testList.addItem("Item "+i);
}
setLayout(new FlowLayout());
add(testChoice);
add(testList);
}
public static void main(String[] args)
{
new Test();
}
}
布局管理器
每个容器都有默认的布局管理器,也可以通过如下代码设置
c.setLayout(new XXXLayout());
Window容器可以通过pack()方法把窗口调整到最佳大小
AWT提供了FlowLayout, BorderLayout, GridLayout, GridBagLayout, CardLayout 五种布局,Swing还提供了BoxLayout
- FlowLayout中,组件像水流一样向某方向流动(排列),遇到边界就另起一行重新开始拍列
- BorderLayout将容器分为EAST,SOUTH,WEST,NORTH,CENTER五个位置,普通组件被放在五个位置中的任一位置,因此BorderLayout最多只能放5个组件,向同一位置添加多个组件时,后添加的覆盖先添加的。
- GridLayout将容器分割成纵横线分隔的网格,每个网格占的大小相同,默认从左到右,从上到下添加组件
- GridBagLayout类似GridLayout,但是一个组件可以跨越多个网格,使用GridBagLayoutConstraint控制
- CardLayout以时间而非空间来管理它里面的组件,它将加入容器的所有组件看成一叠卡片,每次只有最上面的容器才可见。
- 绝对定位,首先调用容器方法setLayout(null),然后向容器添加组件时,先调用setBounds()或setSize(),再将组件添加到容器中。
- BoxLayout可以在垂直和水平两个方向上摆放组件,保留了GridBagLayout的很多优点,但是没有那么复杂
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Test extends JApplet implements ActionListener{
JButton bt1 = new JButton("North");
JButton bt2 = new JButton("West");
JButton bt3 = new JButton("East");
JButton bt4 = new JButton("South");
JLabel lb1 = new JLabel("Center");
Container cp = getContentPane();
public void init()
{
cp.setLayout(new BorderLayout());
cp.add("North", bt1);
cp.add("West", bt2);
bt2.addActionListener(this);
cp.add("East", bt3);
cp.add("South", bt4);
bt4.addActionListener(this);
cp.add("Center", lb1);
}
public void actionPerformed(ActionEvent e)
{
lb1.setText("Please Press a Button");
if(e.getSource() == bt2)
lb1.setText("Press West");
if(e.getSource() == bt4)
lb1.setText("Press South");
}
}
AWT基本组件有
P386
- Button
- Canvas
- Checkbox
- CheckboxGroup
- Choice
- Frame
- Label
- List
- Panel
- Scrollbar
- ScrollPane
- TextArea
- TextField
Java事件模型的流程
- Event Source:事件发生的场所,通常是各个组件,例如按钮,窗口,菜单等
- Event:封装了GUI组件上发生的特定事情(通常是一次用户操作),程序员要获得GUI组件上所发生事件的相关信息,都通过Event对象来取得。
- EventListener:负责监听事件源所发生的的事件,并对其作出响应处理
AWT菜单
- MenuBar:菜单条,菜单的容器
- Menu:菜单项的容器,也可以作为菜单项使用
- PopupMenu:上下文菜单组件
- MenuItem:菜单项组件
- CheckboxMenuItem:复选框菜单项组件
- MenuShortcut:菜单快捷键组件
画图
Component类里提供了和绘图有关的三个方法
- paint(Graphics g):绘制组件的外观
- update(Graphics g):调用paint()方法,刷新组件外观
- repaint():调用update()方法,刷新组件外观
Graphics类
Graphics是一个抽象的画笔对象,可以在组件上绘制丰富多彩的几何图形和位图。
Swing
Swing是一种轻量级组件,100%java实现,不再依赖本地平台的图形界面,对跨平台的支持更加出色。
Swing提供了比AWT更多的的图形界面组件
Swing组件都采用了MVC(Modle-View-Controller)的设计模式,可以实现GUI组件的显示逻辑和数据逻辑的分离,提供更多的灵活性。
大部分Swing组件都是JComponent抽象类的子类,JComponent类是AWT里Container类的子类,所以Swing组件都可以作为容器使用。
Swing为除了Canvas之外的所有AWT组件提供了相应的实现
很多Swing组件可以使用图标修饰自己
支持插拔式的的外观风格。每个JComponent对象都有一个相应的ComponentUI对象,为它完成所有的绘画,事件处理,决定尺寸大小等工作。
支持设置边框
JFrame
A Frame is a top-level window with a title and a border
A fraem, implemented as an instance of the JFrame class, is a window that typically has decorations such as a border, a title and buttons for closing and iconifying the window.
它的setDefaultCloseOperation操作可设置用户发起“close”时的默认操作,选项有
- DO_NOTHING_ON_CLOSE
- HIDE_ON_CLOSE
- DISPOSE_ON_CLOSE
- EXIT_ON_CLOSE
默认情况下,被设置为 HIDE_ON_CLOSE
JOptionPane
通过JOptionPane可以非常方便地创建一些简单的对话框,JOptionPane提供了如下4个方法来创建对话框。
- showMessageDialog/showInternalMessageDialog:消息对话框
- showConfirmDialog/showInternalMessageDialog:确认对话框
- showInputDialog/showInternalInputDialog:输入对话框
- showOptionDialog/showInternalOptionDialog:自定义选项对话框
线程
创建线程的方法
- 定义Thread类的子类,并重写run()方法。通过调用start()方法来启动多线程
- 实现Runnable接口创建线程类,并以此实例作为Thread的target来创建Thread对象,调用Thread对象的start()方法来启动该线程。
- 创建Callable接口的实现类,实现call()方法,该方法有返回值,使用FutureTask类来包装Callable对象,使用FutureTask对象作为Thread对象的target创建线程对象,并启动新线程。使用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class Test implements Callable<Integer>{
public Integer call()
{
int i=0;
for(; i<10; i++)
{
System.out.println(Thread.currentThread().getName() + ", i = " + i);
}
return i;
}
public static void main(String[] args)
{
Test t = new Test();
FutureTask<Integer> task = new FutureTask<Integer>(t);
for(int i=0; i<10; i++)
{
System.out.println(Thread.currentThread().getName() + ", i = " + i);
if(i == 2)
{
new Thread(task, "Thread with return value").start();
}
}
try
{
System.out.println("Sub thread's return value: "+task.get());
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
线程的生命周期
- 新建状态,当程序使用new关键字创建了一个线程之后,该线程就出于新建状态。
- 就绪状态,当线程对象调用了start()方法之后,线程就出于就绪状态。至于线程合适开始运行,取决于JVM里线程调度器的调度
- 运行状态,出于就绪状态的线程获得了CPU,开始执行run()方法,就处于运行状态
- 阻塞状态,在采用抢占式调度策略的系统中,当系统给线程的时间片用完之后,系统就会剥夺线程所占的资源,该线程进入阻塞状态,而在小型设备中系统可能采用协作式的调度策略,这时只有当一个线程调用了它的sleep()或yield()方法后才会放弃所占用的资源。
- 死亡状态,线程会以如下3种方式结束,结束后就出于死亡状态。run()或call()方法执行完成,线程抛出一个未捕获的Exception或Error,直接调用线程的stop()方法来结束该线程
join方法
当某个程序执行流中调用其他线程的join方法时,调用线程将被阻塞,直到被join()方法加入的线程执行完为止。
后台线程
后台线程(Deamon Thread)的任务是为其他的线程提供服务,如果所有的前台线程都死亡了,后台线程自动死亡。
调用Thread对象的setDaemon(true)方法可将指定线程设置成后台线程。
yield方法
让线程从运行状态转到就绪状态。
线程同步
使用同步代码块,语法格式如下
synchronized(obj) {
} obj称为同步监视器// Codes need thread safety
同步方法
public synchronized void draw() {
} 同步方法的同步监视器是this// Codes need thread safety
嵌套的同步块是安全的,如
synchronized(a)
{
synchronized(a)
{
// Codes need thread safety
}
}
同步锁
Lock对象
class X
{
private final ReentrantLock lock = new ReentrantLock();
public void m()
{
lock.lock();
try{
// Codes need thread safety
}
finally
{
lock.unlock();
}
}
}
ReentrantLock锁具有可重入性,线程可以对它已加锁的ReentrantLock再次加锁。lock()加锁之后必须显示调用unlock()释放锁,所以一段被锁保护的代码可以调用被相同锁保护的方法。
线程通信
进程调用wait()阻塞自己,知道被notify()或notifyAll()唤醒。