Java网络编程复习
Java网络编程
考试内容
- 选择题 40(20题)
- 程序分析 30(3题)
- 编写程序 30(3题)
chap3 Java语言基础
标识符:
-
由字母、 数字、下划线、$组成。不能由数字开头
-
不能是Java中的保留字(关键字)。
-
大小写敏感,长度无限制
变量
-
局部变量:局部变量在使用前必须给定初值,否则,将编译出错,而类成员变量无此要求。
class test{ int x; void method(){ int y = 8; //若换成int y;则会报错 System.out.println(x) System.out.println(y);} public static void main(String[] args){ test t=new test(); t.method();} } -
类成员变量
常量
-
符号常量:使用修饰符“final”可以定义符号常量
-
定义形式:final 类型 常量名=值;
final int a = 8;//值一经确定不可更改
实型
- 实型包括两种:float和double
- 一个实型常量在机器中默认以double类型存储。如实型常量2.5在机器中以double类型存储,占据8个字节空间。
- 实型常量后加后缀F或f在机器中以float类型存储。如12.3F占据4个字节空间。
其他类型
-
枚举类型enum
语法格式:enum 枚举名{常量列表}
1、 声明名字为Season的枚举类型
enum Season{ spring, summer, autumn, winter}2、 定义一个枚举变量
枚举变量x只能取枚举类型中的常量, 通过使用枚举名和“.”运算符获得枚举类型中的常量
enum Season{春季,夏季, 秋季, 冬季} public class Example { Season x=Season.春季; } Season x[]= Season.values();//则 x[0]~x[3]分别是春季,夏季,秋季,冬季 -
动态类型var
var相当于一种动态类型,是Java10新特性,用它来定义局部变量,申明时必须初始化,且不能用作方法参数。
使用var 定义变量的语法: var 变量名 = 初始值;
算数运算符
-
自增++自减——运算符
class test { public static void main(String args[]) { int a=5; System.out.println(++a); System.out.println(--a); System.out.println(a++); System.out.println(a--); } } 输出结果: 6 5 5 6 -
取模运算符%
5.2%2.2=0.8
10%-4=2-10%-4=-2
取模运算去掉符号运算,最后结果符号与被除数相同
注意:
- 整数 int a=10/0 编译无错,运行报错,异常
int a=10%0 运行报错,异常 - 浮点数
double a=10.0/0 Infinity(正无穷大)
double a=-10.0/0 -Infinity(负无穷大)
double a=0.0/0 NaN
double a=0.0%0 NaN
- 整数 int a=10/0 编译无错,运行报错,异常
-
逻辑运算符
短路与 op1 && op2 (1)如果op1和op2都为真,则该逻辑表达式结果为真,否则结果为假。
(2)如果op1为假,则不计算op2,该表达式结果为假。短路或 op1 || op2 (1)op1和op2中,只要有一个为真,则该逻辑表达式结果为真,否则结果为假。
(2)如果op1为真,则不计算op2,该表达式结果为真。 -
位运算符
& op1&op2 返回****op1和op2的二进制表示的按位与操作的结果 | op1 | op2 返回op1和op2的二进制表示的按位或操作的结果 ^ op1^op2 返回op1和op2的二进制表示的按位异或操作的结果 ~ ~op1 返回op1的二进制表示进行按位取反操作的结果 >> op1 >> op2 将op1的二进制表示向右移op2位,左边填充符号位 << op1 << op2 将op1的二进制表示向左移op2位,右边填充0 >>> op1 >>> op2 将op1的二进制表示向右移op2位,左边填充0 注意:
(1)右移n位后的结果与除以2的n次方效果相同
(2)左移n位后的结果与乘以2的n次方效果相同
(3)无符号右移要慎重
数据类型转换
-
自动类型转化
运算过程中,Java自动把精度较低的类型转换为另一种精度较高的类型。
注意:如果byte、short、char在一起运算时,会先将这些值转换为int型。再进行运算,结果为int型。
下列代码会编译出错,提示c = (a + b)有精度损失
class test { public static void main(String args[]) { byte a = 1; byte b = 1; byte c = (a + b);//a+b自动转化为int类型运算,赋值给比byte类型有精度损失 } } -
手动强制类型转换
形式为:(类型名)表达式
class test { public static void main(String args[]) { byte a = 1; byte b = 1; byte c = (byte)(a + b); } }
标准输入
-
System.in是字节流,作用是从标准输入读一个字节,常用的方法如下:
int read() 从流中读取一个字节并将该字节作为整数返回,若没有数据则返回-1 ;
int read(byte b[]) 从流中读取多个字节放到b中, 返回实际读取到的字节数 ;
int read(byte b[],int off,int len) 从流中读取最多len字节的数据, 放到数组b的下标off开始的单元中,返回读取到的字节数;//从键盘读入一串字符 import java.io.*; public class ReadString { public static void main(String args[]) { char c; try{ do { c=(char)System.in.read(); System.out.print(c); }while(c!='\n'); }catch(IOException e){ } } } -
借助java.util.Scanner类完成输入
从键盘(标准输入设备)输入数据,可以用Scanner类的对象及其中的方法实现输入。
import java.util.Scanner; void GuessNumber() { Scanner scanner = new Scanner(System.in); int number = (int)(Math.random()*10); int guess; do { System.out.println("猜数字(0~9):"); guess = scanner.nextInt(); }while (guess != number); System.out.println("猜中了!"); }
数组
一维数组:
-
创建规则:
int list[ ]=new int[3];
int a[]={1,2,3,4};
-
数组的长度:list.length;
二维数组:
-
创建规则:
直接为每一维分配空间,如:int a[ ] [ ]=new int[2] [3];
chap4 Java面向对象程序设计
类首说明
[修饰符] class 类名 [extends父类名] [implements接口名列表]
- 类的修饰符:public、abstract、final
- 访问权限修饰符:缺省或public
abstract:抽象类
final:最终类,它所修饰的类不能被继承,即没有子类
成员变量的说明
[修饰符] 成员变量类型 成员变量名列表
-
访问权限修饰符:public protected private (缺省) public >protected >缺省>private
-
访问修饰符缺省时,成员变量只能被同一包(package) 中的所有类访问,所以也称为包(package)变量。
-
static:static修饰的成员变量称为类变量(静态变量),类变量为该类的所有对象所共享,它的值不因类的对象不同而不同。可以通过类来访问静态成员变量,也可以通过该类的对象访问静态成员变量。
public class test { public static int a=10; public static void main(String args[]) { System.out.println(test.a); test.a=12; test t1=new test(); test t2=new test(); System.out.println(t1.a); System.out.println(t2.a); } } -
final:final定义的成员变量叫最终变量——常量,常量在说明以后就不能改变其值,**final修饰符和static修饰符并不冲突 **
-
注意:一定要在定义时就给定初始值
例如:
static final x=5; √
static final x; x=5; × -
transient和volatile
成员方法
[方法修饰符] 返回值类型 方法名([形参列表])[throws异常列表]
访问修饰符: 缺省、public、protected、private
非访问修饰符: static、abstract、final、native、synchronized
成员方法的调用
- 内部调用:直接调用
- 外部调用
- 类方法:类名.类方法名([实型参数列表])
对象名.类方法名([实型参数列表]) - 对象方法:对象名.对象方法名([实型参数列表])
- 类方法:类名.类方法名([实型参数列表])
使用类方法(静态方法)注意:
(1) 在类方法中不能直接引用对象变量。
(2) 在类方法中不能使用super、this关键字。
(3) 类方法不能直接调用类中的对象方法。
class FamilyMember
{static private String surname=“李”; //定义一个类变量, 代表姓
private String givenname; //定义一个对象变量, 代表名
FamilyMember(String givenname) //带参数的构造方法
{this.givenname=givenname; } //对于对象变量, 可以使用this关键字
FamilyMember(); //不带参数的构造方法
{ givenname=″小刚; }
static String getSurname() //类方法,获得变量surname的值
{ return surname; }
static void changeSurname(String surname) //类方法,改变姓
{ FamilyMember.surname=surname; }//此处不能使用这样的语句:this.surname=surname;只能使用带类名的限定名称(因为类方法中不能使用super、this关键字)
public String whatIsYourName()
{ return(surname+givenname); }
}
方法的重载
-
规则:多个方法有相同的名字,但是这些方法的参数列表必须不同
或者参数个数不同,
或者是参数类型不同,
或者参数类型的顺序不同注意:
在这里参数类型是关键,仅仅参数的变量名不同是不行的;方法重载时,返回值的类型可以相同,也可以不同;除了同一个类,方法的重载也可以在父类和子类之间
构造方法
-
如何给对象的成员变量初始化?
- 自动初始化
public class Person { private int age; private int height; public static void main(String args[]) { Person p1=new Person(); } }- 通过一个成员方法显示地初始化为其他值
public class Person { private int age; private int height; public void initialize(int a;int h) { age=a;height=h; } public static void main(String args[]) { Person p1=new Person(); p1.initialize(20,175); } }- 定义构造方法时初始化
public class Person { private int age; private int height; public Person(int a,int h) { age=a;height=h; } public static void main(String args[]) { Person p1=new Person(20,175); } }
构造方法是一种特殊的、与类同名的方法,专门用于创建对象,完成初始化工作。对象变量都是引用型变量
public class Person
{
private int age=5;
private int height=10;
public Person(int a,int h)
{ age=a;height=h; }
public static void main(String args[])
{ Person p1=new Person(20,175);}
}
-
构造方法的重载
(1)缺省的构造方法
如果没有定义构造方法,则Java自动提供了一个缺省的构造方法,如下:
public Person(){};(2)带参数的构造方法
public Person(String n){Name=n;}
只要类中显式定义了一个或多个构造方法,而且所有显式定义的构造方法都带参数,那么将失去缺省构造方法。
this的使用
Java语言规范中,每个类均有这样3个变量值:null,this,super
- this的含义:在方法中,关键词this用来本类对象。在普通非静态方法中,this表示调用这个方法的对象;在构造方法中,this表示新创建的对象。
Person(String n,int a){
name=n;
age=a;
}
|
v
Person(String name,int age)
{this.name=name;
this.age=age;
}
继承
类继承语法形式如下:class SubClassName extends SupperClassName
-
属性的继承
- 子类可以继承父类的所有属性(只要该属性没有private修饰)
- 通过属性的继承,子类不需要把父类属性的定义部分重复定义一遍,这样做的好处是减少程序维护的工作量。
-
属性的隐藏
子类重新定义一个与父类那里继承来的成员变量完全相同的变量,就称作属性的隐藏。
class living { public int age; } class human extends living { public int age; public static void main(String args[]) { human h1=new human(); h1.age=20;} } -
属性的添加
在定义子类时,加上的新的属性变量,就可以使子类比父类多一些属性
例如:ColorPoint类比父类Point多了一个属性,颜色(color)
-
方法的继承
父类的非私有方法也可以被子类自动继承
-
方法的添加
子类可以新加一些方法,以针对子类实现相应的功能
-
方法的重载
重载的效果:实际是相当于在子类中新加了一个方法
某类的静态成员为该类及该类的所有子类所共有 ,如果子类中新定义的静态成员变量与父类中的某个静态成员变量同名,则这两个静态成员变量相互独立。
-
方法的覆盖
在子类中定义的方法和父类中的方法的首部是一样的,包括方法名、参数列表、返回类型和异常抛出。但方法体的实现改变了。
在覆盖时要注意以下几点:
- 覆盖的方法的首部必须要和被覆盖的方法的首部完全匹配,才能达到覆盖的效果;
- 覆盖的方法的返回值类型必须和被覆盖的方法的返回值类型一致;
- 覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
- 被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
super的使用
super:代表父类对象,在继承中有重要的作用
-
使用情况:
-
子类隐藏了超类中的变量或方法,而在程序中又要使用超类中被隐藏的变量或方法时
格式:super.变量 ; super.方法([参数表])
-
在子类的构造方法中引用超类的构造方法时
格式:super([参数表])
-
-
注意事项:
-
由于它指的是父类对象,所以super不能在static环境中使用,包括类变量、类方法和static语句块。
-
在构造方法中使用super时,super语句必须放在第一句
class Point{ int x=0; Point(int x){ this.x=x; } } class MapPoint extends Point{ MapPoint(){ super(3);//调用父类构造方法,x = 3 System.out.println("MapPoint()"); } public static void main(String[] args){ new MapPoint(); } } -
在子类的构造方法中,super可以不明确使用,也可以明确使用。
子类中没有明确使用super,系统会自动在构造方法中加上super(),来调用直接父类的不带参数的构造方法,由于Point类中没有定义不带参数的构造方法,所以编译通不过。
解决方法:
- 在MapPoint的构造方法中,明确使用super来调用父类已有的构造方法,如super(3);
- 在Point中加入一个不带参数的构造方法,如Point{} ;
- 去掉Point中全部的构造方法,则编译器会自动加入一个不带参数的构造方法,称为缺省构造方法。
建议:在子类的构造方法中最好能明确使用super调用父类的构造方法给从父类继承来的变量初始化
-
抽象类与抽象方法
- abstract关键字修饰的类和方法
- 抽象类不能创建任何对象,抽象类必须产生其子类,由子类创建对象。
- 抽象类中可以包含抽象方法,也可以不包含抽象方法,但如果类中的某一方法是抽象的,整个类就必须被说明成抽象的。
- 抽象方法在子类中必须被实现,否则子类仍是抽象的。
抽象类举例
abstract class Shape
{ abstract double area();
abstract void draw(); }
//上述方法对一般图形无法定义其实现
class Rectangle extends Shape {
int width;
int length;
public double area() {
//矩型类实现了Shape类的抽象方法area()
return width*length;
}
public void draw () { … … }
final类和方法和final方法
如果一个类被final修饰符所修饰和限定,说明这个类不能被继承,即不可能有子类——最终类
final修饰符所修饰的方法,是不能被子类所覆盖的方法。
接口
[修饰符] interface 接口名[extends] [接口列表]
修饰符:或者不使用修饰符(同包访问),或者只能使用public(任意访问)
extends: 定义父接口,支持多重继承
public interface Cookable extends Foodable,Printable
-
接口和类的区别:
- 类只能单继承,而接口可以多继承。
- 类中的方法可以是具体的,也可以抽象的。 接口中的方法都是抽象的。
- 接口中的方法要用类来实现,一个类可以实现多个接口。
-
接口体:定义常量和抽象方法
接口的成员:成员变量和方法
-
接口中的成员变量:含public、static、final的——静态最终变量(都是隐常量)
//两者在接口中定义完全相同 int STEP=5; public static final int STEP=5; -
接口中的方法:接口中说明的方法都是抽象方法,所有方法隐含public和abstract的。
例如 int increment(int x);
注意:接口中的方法不能使用下面的修饰符:static,native或synchronized,final
-
-
接口的使用
声明接口的形式:[类修饰符] class类名 [extends子句] [ implements 子句]
实例:
interface Runner{ public void run();}
interface Swimmer{ public void swim();}
abstract class Animal {abstract public void eat();}
class Person extends Animal implements Runner,Swimmer {
public void run() {System.out.println("run");}
public void swim(){System.out.println("swim");}
public void eat(){System.out.println("eat");}
}
包
包是用于组织类的一种方式,可以对类进行分组,一个包中可以包含任意数量的类和接口,本身是一种命名机制,具体的表现就是一个文件夹
- 包的创建
- 无名包:系统为每个没有明确指明所属包的.java文件默认创建的包。无名包中的类无法被引用和复用
- 有名包:
- 说明格式 package 包名;
package SubClass;
package MyClass.SubClass; - **说明语句必须放在整个.java文件的第一行 **
- 说明格式 package 包名;
内部类
-
内部类的定义: 将类的定义置入一个用于封装它的类(外部类)里
注意:内部类不能与外部类同名(否则,编译器无法区分内部类与外部类),如果内部类还有内部类,内部类的内部类不能与它的任何一层外部类同名。
-
内部类对象的定义和使用
内部类对象的创建:创建非静态内部类的对象时一定要确保已经有一个外部类对象
-
利用外部类的方法创建并返回,因为方法是由外部类对象调用的,那创建该内部类对象时,一定已经拥有了所属的外部类对象了
Outer out = new Outer(); Outer.Inner.in = out.new Inner(); -
创建内部类还可以在除外部类中的其它类中,但是要确保该类具有访问内部类的权限,并且已经创建了一个外部类对象。格式如下:
outerObject=new outerClass(Constructor Parameters); outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters);
public class Outer{ public Outer(){ System.out.println("OuterClass Object!"); } private class Inner1 { private Inner1(String s){ System.out.println(s);} } static class Inner2 { Inner2(String s){ System.out.println(s);} } public static void main(String[] args){ Outer ob= new Outer(); Outer.Inner1 ib1 = ob.new Inner1("InnerClass1 Object!"); Inner2 ib2=new Inner2("InnerClass2 Object!");//实例化static内部类时,在new前面不需要用对象变量 } } /*输出: OuterClass Object! InnerClass1 Object! InnerClass2 Object!*/ -
-
静态内部类
和普通的非静态内部类有较大的不同,使用时要遵循如下原则:
- 实例化static内部类时,在new前面不需要用对象变量;
- static内部类中不能访问其外部类的非static属性及方法,即只能访问static成员;
- static方法中不能访问非static的属性及方法,也不能不带前缀地new一个非static的内部类。
-
内部类访问外部类的成员
-
内部类中是可以直接访问外部类的其他属性与方法的,即使它们是private的。
-
如果内部类中有与外部类同名的属性与方法,可以使用下面的格式来表达外部类的引用,从而区分外部类和内部类的同名的属性与方法:outerClass.this
-
匿名内部类
-
匿名类: 类或方法中定义的一种没有类名的特殊内部类。
语法规则如下:new interfacename(){……};或new superclassname(){……};
注意:
- 这种类不取名字,而直接用其父类的名字或者它所实现的接口的名字;
- 类的定义与创建该类的一个对象同时进行,即类的定义前面有一个new,没有类的首部,对象的创建和类体共同构成一个匿名类表达式,后面以“;”结束;
- 类中不能定义构造方法,因为它没有名字。
interface Contents{
int value();
}
public class Goods4 {
public Contents cont(){
//返回匿名类对象,该匿名类继承了Contents接口
return new Contents(){ //此处new contents()即为匿名类
private int i = 11;
public int value() {
return i;
}
};
}
public static void main(String[] args)
{
Goods4 g=new Goods4();
Contents c=g.cont();
}
}
chap5 异常处理
运行时异常和非运行时异常
运行时异常
运行时异常:RuntimeException类及其所有子类。运行时异常是程序员编写程序不正确所导致的异常,理论上,程序员经过检查和测试可以查出这类错误。
编译器并不强制要求程序捕获处理运行时异常,如果程序没有捕获处理,java运行时系统会做,即把生成的运行时异常对象交给默认的异常处理,也即在标准输出设备上显示异常的内容以及发生异常的位置。
RuntimeExceptionDemo1.java
class RuntimeExceptionDemo1{
public static void main(String args[]){
int i=0;
System.out.println(2/0);
}}
该程序能编译通过,而在运行时,出现如下提示:
javac RuntimeExceptionDemo1.java
java RuntimeExceptionDemo1
Exception in thread "main" java.lang.ArithmeticException: / by zero at RuntimeExceptionDemo1.main(RuntimeExceptionDemo1.java:4)
非运行时异常(一般异常)
非运行时异常:指可以由编译器在编译时检测到的、可能会发生在方法执行过程中的异常,如找不到指定的文件等,这不是程序本身的错误,如果这些异常情况没有发生,程序本身仍然是完好的。
编译器强制要求Java程序必须捕获或声明抛出所有非运行时异常,但对运行时异常不作要求
NonRuntimeExceptionDemo1.java
import java.io.*;
class NonRuntimeExceptionDemo1
{
public static void main(String args[]){
FileInputStream in=new FileInputStream("text.txt");
int s;
while((s=in.read())!=-1) System.out.print(s);
in.close();
}
}
编译,会出现如下的错误提示:
javac NonRuntimeExceptionDemo1.java
NonRuntimeExceptionDemo1.java:5: unreported exception >java.io.FileNotFoundException; must be caught or declared to be thrown
FileInputStream in=new FileInputStream("text.txt");
对于非运行时异常,Java编译器对程序进行编译的时候,便指出用户需要①捕获该类异常(try-catch-finally)或者②声明抛出(throws)。即对于非运行时异常,用户需要在程序中进行处理,否则编译时无法通过。
try-catch-finally异常处理
- 用户处理异常的三种方法:
- 用户可以用try-catch-finally语句进行捕获和处理;
- 如果不想捕获和处理异常,可以通过throws语句声明要抛出的异常
- 用户可以定义自己的异常类,并用throw语句来抛出。
例:访问文本文件text.txt,并将其在屏幕上打印出来。
import java.io.*;
class TryCatchFinally
{
public static void main(String args[]){
try{
FileInputStream in=new FileInputStream("text.txt");
int s;
while((s=in.read())!=-1) System.out.print(s);
in.close();
}
catch(FileNotFoundException e){
System.out.println("捕获异常:"+e);
}
catch(IOException e){
System.out.println("捕获异常:"+e);
}
finally{
System.out.println("finally块总是执行!");
} }
}
运行结果:
捕获异常:java.io.FileNotFoundException: text.txt (系统找不到指定的文件。)
finally块总是执行!
无论在try块中是否产生异常,也不管产生的异常是否会被捕获,finally中的语句最终都会被执行。
throws抛出异常
声明抛出异常:不捕获异常,而是将异常交由上一层处理,在其他地方捕获异常。如果使用后者,那么应该(在某些情况下)向编译器表明:此方法可能会抛出异常,但方法本身不会捕获它。可以在方法头中用throws子句来实现此功能。
例1:调用的方法抛出了异常
class Test {……
public String getInput() throws IOException
{
System.in.read();
}
}
//也可抛出多个异常
class Animation
{
public Image loadImage(String s) throws EOFException, MalformURLException
{
……
}
}
例2:检测到了错误并使用throw语句抛出异常
import java.io.*;
class Test {
public String getInput() throws IOException
{
IOException ae =new IOException("buffer is full");
throw ae;
}
static String getInput() throws IOException{
char[] buffer =new char[20];
int counter = 0;
boolean flag =true;
while(flag) {
buffer[counter] =(char)System.in.read();
if(buffer[counter]=='\n') flag = false;
counter++;
if(counter>=20){
IOException ae = new IOException("buffer is full");
throw ae; }
}
return new String(buffer);
}
chap6 Java的基本类库
java.lang
String类:不可改变的静态字符串
- String类可创建一个对象,用于代表一个字符串(不变的字符串),并定义了类似查找,比较和连接字符的操作。
- 所有字符串常量都是String对象,存储在String Pool(字符串池)中,字符串池是常量池的一部分。
- String类对象一旦创建,其内容不可更改:String类的所有方法都不会改变String类对象 内容,要改变String类对象的值就必须创建一个新的String对象。
- String是类,在比较字符串内容时,不能用==,而应该用equals方法。String类覆盖了Object类的equals方法。
String a="hellojava";
String b="java";
String c="hello"+b;
if(a==c)
{ System.out.println("相等");
}
else
{ System.out.println("不等");
}
//由于java是在编译时将StringPool中所有相同的字符串合并,所以对于本程序,并未将a指向的字符串与c指向的字符串合并,导致a和c指向的不是同一个对象
-
求子串(substring函数)
String a="hello"; String b=a.substring(0,4);//得到“hell String c=a.substring(2,3);//得到“l” -
求字符串中字符的个数(length函数)
String a="hello"; int b=a.length();//得到5 String c="hello你好"; int d=c.length(); //得到7 -
得到字符串中的某个字符(charAt函数)
String a=”hello”; char b; b=a.charAt(0);//得到下标为0的字符 //得到字符h -
字符数组转换为String
char [] a={'a','b','c','d'}; String b=new String(a); -
大小写转换
String s1="Hello"; String s2=s1.toUpperCase();//得到“HELLO” String s3=s1.toLowerCase();//得到“hello” -
其他数据类型->字符串
String out2 = String.valueOf(100) ; String out3 = “”+100; String out1 = new Integer(100).toString(); //(同 Byte Short Long Float Double)String对象作为参数传递和基本的数据类型效果一样,因为它是不可改变的字符串。
StringBuffer类
StringBuffer类实现一种动态可变的字符串。
-
StringBuffer类提供三种创建方法(构造方法):
- public StringBuffer();
- public StringBuffer(int length);
- public StringBuffer(String str);
-
StringBuffer类的更新方法
添加:append
插入: insert
修改: setCharAt
删除: deleteStringBuffer s=new StringBuffer("hello"); s.append(“java”);//s变为“hellojava” s.insert(5,"sun"); // s变为“hellosunjava” s.setCharAt(0,'H'); // s变为“Hellosunjava” s.delete(5,8);// s变为“Hellojava” -
StringBuffer对象和String对象的转换
StringBuffer s=new StringBuffer(“hello”); String a=s.toString();例:
import java.io.*; public class StringBufferToString { public static void main(String args[]) { char ch; try{ int length =20; StringBuffer strb=new StringBuffer(length); while ((ch=(char)System.in.read())!='\n') { strb.append(ch) } String str=strb.toString(); System.out.println(str); } catch (IOException e) { } } }
数据类型类
| 数据类型类 | 基本数据类型类 |
|---|---|
| Boolean | boolean |
| Character | char |
| Double | double |
| Float | float |
| Integer | integer |
| Long | long |
常用的方法举例(Integer类):
//将字符串转换为int型
String s="1234";
int i=Integer.parseInt(s);
//将int类转化为String类
int i=1234;
String s=Integer.toString(i);
MATH类
double c=Math.random();//得到一个[0,1)之间的随机数
//使用随机函数获得20~80的随机整数
int x = (int)( Math.random()*60+20);
int x = (int)( Math.random()*60)+20;
java.util
vector类
-
向量和数组的异同
相似点:都是类,均可保存列表。
不同点:- 数组一旦定义,其空间长度不可变,而向量的空间能够在运行时动态的扩充或缩减。
- 数组中可以存放基本数据类型,也能存放对象。向量中只能存储对象,不能存储基本数据类型。如果想在向量中存储基本数据类型,可通过基本数据类(如Integer)。
-
向量类的构造方法: Vector<元素类型> 向量名称 = new Vector<元素类型>(容量);这里的元素类型不能是基本数据类型
Vector<String> vector1=new Vector<String>(5); -
在向量中添加元素
vector1.add(”姚明”); vector1.add(”韦德”); vector1.add(“纳什”); -
在向量中获取某个元素
E elementAt(int index); vector1.elementAt(0);//获取下标为0的元素,实际上是一个String对象 vector1.elementAt(0).length();//调用该String对象的成员函数 -
向量的容量和元素个数
//向量的容量:向量中可存放的元素个数 System.out.println(vector1.capacity()); //向量中的元素个数:向量中实际存放的元素个数 System.out.println(vector1.size()); //往向量添加的元素个数超过向量的容量时,向量会自动扩充自己的容量。一般扩充为原来的2倍(默认容量增量为0时) -
在向量中插入、修改和删除元素
//往向量中插入元素 void insertElementAt(E obj, int index); vector1.insertElementAt(“小孙”,1); //在下标1位置插入元素 //修改向量中的元素 void setElementAt(E obj, int index); vector1.setElementAt(“小孙”,0); //删除向量中的元素 vector1. removeElementAt(1);//删除下标为1的元素 //清空向量 vector1.clear(); //向量中的所有元素都被删除 //判断向量中是否有与某对象等值的对象 boolean contains(Object o) ; vector1.contains(“张三”); //查找特定元素在向量中的下标位置 int indexOf(Object o); System.out.println(vector1.indexOf(“张三”)); //得到0 //注意:如果有多个相同值的对象,则返回第一个找到的对象的位置如果没找到则返回-1
泛型
- 泛型是JDK 1.5以上版本的新特性,泛型的本质是参数化类型,编写的代码可以被很多不同类型的对象或数据所重用,也就是说所操作的数据类型被指定为一个参数。
- 类似于C++的模板,经常用在java的集合中,能让集合记住其元素的数据类型。
- 这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。
Object对象数组与泛型对比
//Object对象数组
ArrayList files=new ArrayList();
files.add(new String(“text.txt”))
String filename=(String) files.get(0);
files.add(new file(“ …”))
//泛型
ArrayList<String> files=new ArrayList<String>();
files.add(new String(“text.txt”))
String filename=files.get(0); //不需要强制类型转换
files.add(new file(“ …”))
//未使用泛化的代码
ArrayList a=new ArrayList();
a.add(new Integer(1));
a.add(new Integer(2));
for(Iterator i=a.iterator();i.hasNext();)
{
int i1 = ((Integer)i.next()).intValue();
}
//使用泛化的代码
ArrayList<Integer> a=new ArrayList<Integer>();
a.add(new Integer(1));
a.add(new Integer(2));
for(Iterator<Integer> i=a.iterator();i.hasNext();)
{
int i1 = (i.next()).intValue();
}
chap7 图形用户界面
图形用户界面概述(swing)
所在的包: javax.swing
-
容器组件
- 顶层容器,如JFrame,JApplet,JDialog和JWindow
- 中间容器,如JPanel,JScrollPane,JSplitPane,JToolBar
-
基本组件
- 基本控制组件,如JButton, JComboBox, JList, JMenu, JSlider, JTextField
- 不可编辑的信息显示组件,如JLabel, JProgressBar, JToolTip
- 可编辑的信息显示组件,如JColorChooser, JFileChooser, JTable, JTextArea
容器组件
分类:
- 顶层容器,如JFrame,JApplet,JDialog和JWindow
- 其它容器,如JPanel,JScrollPane,JSplitPane,JToolBar
顶层容器概述
- 建立一个窗口系统,首先需要创建一个图形用户界面,这就需要使用顶级容器类JWindow、JFrame、JDialog 等。
- JWindow类提供了用于窗口操作的基本功能,但通常使用的是JWindow类的子类JFrame(框架)类和JDialog(对话框)类。
- 建立窗口是Java图形用户界面程序设计的第一步,在swing包中,javax.swing.JFrame
- JFrame类用于创建带有菜单条的全功能窗口对象,为窗口、面板等组件提供框架,它可以包含窗口标题、最大化、最小化和关闭窗口等按钮,通常是GUI应用程序窗口的顶层容器组件。
- JFrame类的对象开始是不可见的,要调用show()方法 (或setVisible(true))才能显示出来,也可以调用hide()方法将其隐藏。框架对象被创建后就可使用add()方法将其它组件加入到框架中。
JFrame
-
建立窗口是Java图形用户界面程序设计的第一步,在swing包中,一般由JFrame类来完成这一功能。 javax.swing.JFrame
-
JFrame 类用于创建带有菜单条的全功能窗口对象,为窗口、面板等组件提供框架,它可以包含窗口标题、最大化、最小化和关闭窗口等按钮,通常是GUI应用程序窗口的顶层容器组件。
-
Frame类的对象开始是不可见的,要调用show()方法 (或setVisible(true)方法) 才能显示出来,框架对象被创建后就可使用add()方法将其它组件加入到框架中。
-
JFrame类的构造方法:
- Jframe()创建一个不带标题的框架
- Jframe(String)创建一个带标题的框架
注意:JFrame和JDialog是JWindow的子类,它们都是窗口类,默认的布局管理器都是 BorderLayout
-
JFrame类的常用方法:
import javax.swing.Jframe JFrame J = new JFrame("My First Window"); JButton btn = new JButton("按钮"); J.show();//显示框架 J.setVisible(boolean b);//使框架可见/不可见(true/false) J.hide();//隐藏框架 J.setTitle("My First Window");//设置框架的标题 setSize(int w, int h);//调整框架的尺寸(宽/高为w/h) J.setSize(100,20); setBounds(int x, int y, int w,int h);//调整框架的位置及尺寸(左上角为(x,y), 宽、高为w、h) add(Component ob);//将其它组件ob加入到框架的中心位置 add(String p, Component ob);//将组件ob加入到框架的p位置 (框架默认的布局方式是BorderLayout, 它将容器划分为东西南北中) J.add("south",btn);
JDialog
-
对话框类JDialog的对象是有标题条而无菜单条和最小化按钮图标的容器组件,它必须依附在某个窗口上(如JFrame),一旦它所依附的窗口关闭了, 对话框也自动关闭。
-
对话框默认的布局是BorderLayout。同框架类一样, 要调用show()方法显示才可见, 调用hide()方法可将其隐藏。
-
对话框通常用于在应用程序中弹出一个窗口, 用于提示输入数据、保存文件等。
-
有两种模式的对话框:
- 响应模式: 对话框出现期间,所依附窗口不接收任何操作
- 非响应模式: 对话框出现时, 与所依附窗口都可同时接收操作
-
JDialog的构造方法:
-
JDialog(JFrame) //创建依附于Frame的无模式对话框
-
JDialog(JFrame,boolean)//创建对话框,并由布尔值的真假决定此对话框有无模式
-
JDialog(JFrame,String) //创建无模式对话框,并给定对话框的标题
-
JDialog(JFrame, String, boolean) //创建对话框,指出是否有模式,并给定对话框的标题
import javax.swing.* public class JDialogDemo{ public static void main(String[] args){ JFrame frm = new JFrame(); JDialog dig = new JDialog(frm,"hello"); dig.show(); } }
-
JApplet
-
面板JPanel是能在屏幕上实际显示的组件,提供了容纳其他组件的功能,但本身必须放在Window,Frame,Dialog等容器中才能使用
-
所有面板的默认的布局管理器是BorderLayout,即按照从左至右、从上到下的方式布局
import javax.swing.JApplet;
常用组件
-
Jbutton
import java.swing.*; public class ButtonDemo extends JFrame {public static void main(String args[]) { ButtonDemo frm = new ButtonDemo("按钮的创建");//创建框架 JButton btn1 = new JButton(); //生成一个没有标记的按钮 JButton btn2 = new JButton("quit"); //生成一个带标记label的按钮 frm.add(btn1); frm.add(btn2); btn1.setLabel("quit") //设置按钮标记 btn2.getLabel( ) //获取按钮标记 btn1.addActionListener(ActionListener l) //将l指定为按钮的监听者 btn1.removeActionListener(ActionListener l) //将l从按钮监听者中去掉 frm.show(); } -
JLabel
标签(Label)是一种只能用来显示单行文本的组件。
标签在容器中的对齐方式有三种:左对齐、居中和右对齐, 用Label.LEFT、Label.CENTER、Label.RIGHT三个静态常量表示,在程序中可以设置其对齐方式。标签的构造方法:
-
Label( ) //生成一个空标签
-
Label(String text)//生成一个带有指定文本的标签
-
Label(Strlng text, int alignment) //生成一个带有指定文本和在容器中的对齐方式的标签
import java.swing.*; public class LabelDemo {public static void main(String args[]) {JFrame frm=new JFrame("标签的创建"); JLabel la1, la2, la3; //定义三个标签 //实例化标签对象 la1=new JLabel(); la2=new JLabel("Label2"); la3=new JLabel("Label3", Label.RIGHT); frm.setLayout(new GridLayout(3,9)); //将标签加入框架中 frm.add(la1); frm.add(la2); frm.add(la3); la1.setText("Label1"); la2.setAlignment(Label.CENTER); frm.show(); } }
-
-
JTextField
文本框(JTextField)和多行文本区域(JTextArea)是用来显示和输入文本的控件,它们都是TextComponent的子类。
文本框由TextField类来创建,其构造方法有:
- JTextField( ) //创建一个空的文本框
- TextField(String text) //创建一个带有初始文本的文本框
- TextField(int Columns) //创建一个指定列数的文本框
- TextField(String text, int colulmns) //创建一个指定列数和带有初始文本的文本框
文本框的常用方法:
- addActionListener(ActionListener l) 将l指定为文本框的ActionEvent事件监听者
- removeActionListener(ActionListener l) 将l从文本框的监听者中去掉
- setText(String s) 设置文本框中的字符串
- getText( ) 获取文本框中的字符串
组件在容器中的布局
AWT提供了五种布局管理器:**BorderLayout,FlowLayout,GridLayout,CardLayout,GridBagLayout **
-
顺序布局(FlowLayout)
顺序布局是最基本的一种布局,是面板Panel和它的子类Applet的默认布局方式。
顺序布局是指将组件从左到右依次排列,一行排满就转到下一行继续排列,直到所有的组件都排列完毕。PANEL和APPLET的默认布局管理器是FlowLayout
JFrame f=new JFrame(); f.setLayout(new FlowLayout());构造方法:
- public FlowLayout() //创建一个新的FlowLayout,其默认值是居中对齐,默认组件彼此有5单位的水平与垂直间距。
- public FlowLayout(int align) //创建一个新的FlowLayout,此FlowLayout可以设置对齐(align)方式,对齐必须是LEFT、CENTER或RIGHT之一。默认组件彼此有5单位的水平与垂直间距。
- public FlowLayout(int align,int hgap,int vgap) //创建一个新的FlowLayout,可以自己设置对齐方式、水平间隔和垂直间隔。
-
边界布局(BorderLayout)
BorderLayout是容器JFrame和JApplet的默认布局方式将容器分成五个区域,south,north,east,west,center。
构造方法:
-
BorderLayout() //构造一个组件之间没有间距的新边界布局。
-
BorderLayout(int hgap, int vgap) //用指定的组件之间的水平间距构造一个边界布局。
//将组件添加到BorderLayout布局的容器中的方法 add(new Button("South"), BorderLayout.SOUTH); add(new Button("South"),"South"); add("South",new Button("South"));
-
-
网格布局(GridLayout)
网格布局是把容器区域分为若干个网格,每个网格可以放置一个组件,这种布局方式非常适合数量庞大的组件。网格布局比顺序布局多了行和列的设置,也就是说要先设置网格布局共有几行几列。然后加进去的组件会先填完第一行格子,然后再从第二行开始填,依此类推,就像是一个个的格子一般。而且网格布局会将填进去的组件大小设为一样。
构造方法:
- public GridLayout()
- public GridLayout(int rows,int cols)
- public GridLayout(int rows,int cols,int hgap,int vgap)
事件处理
两种实现方法
- 编写一个实现WindowListener接口的类,在该类中实现这个接口中的所有方法,然后把该类的对象传给addWindowListener函数;
//例:加法器
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class jiafaqi extends JFrame {
JTextField jiashu1;
JTextField jiashu2;
JTextField he;
JButton jiafa;
public jiafaqi(){
setLayout(new FlowLayout());
jiashu1=new JTextField(5);
jiashu2=new JTextField(5);
he=new JTextField(5);
jiafa=new JButton("=");
add(jiashu1);
add(jiashu2);
add(jiafa);
add(he);
setLocation(300,300);
setSize(500,100);
setTitle("加法器");
jiafa.addActionListener(new MyActionListener(jiashu1,jiashu2,he)) ;
show();
}
public static void main(String[] args){
jiafaqi jiaf = new jiafaqi();
}
}
class MyActionListener implements ActionListener{
JTextField jia1;
JTextField jia2;
JTextField he;
public MyActionListener(JTextField jia1,JTextField jia2,JTextField he){
this.jia1=jia1;
this.jia2=jia2;
this.he=he;
}
public void actionPerformed(ActionEvent e){
int x=Integer.parseInt(jia1.getText());
int y=Integer.parseInt(jia2.getText());
int z=x+y;
he.setText(Integer.toString(z));
}
}
-
Java提供了一个抽象类WindowAdapter,该类实现了WindowListener接口,我们可以定义WindowAdapter类的一个子类MyWindowAdapter ,将该子类的对象传递给addWindowListener函数
//方法一 import java.awt.*; import java.awt.event.*; public class test { public static void main(String []args) { Frame f=new Frame("我的第一个窗口"); f.setSize(400,300);//设置窗口大小 f.setLayout(null);//设置布局模式为空 f.addWindowListener(new MyWindowAdapter()); //使得Frame能够监听窗口事件 f.setVisible(true); } } class MyWindowAdapter extends WindowAdapter { public void windowClosing(WindowEvent e) {System.exit(0); } //方法二 import java.awt.*; import java.awt.event.*; public class test { public static void main(String []args) { Frame f=new Frame("我的第一个窗口"); f.setSize(400,300);//设置窗口大小 f.setLayout(null);//设置布局模式为空 f.addWindowListener(new WindowAdapter() {//创建了一个匿名内部类对象该匿名内部类的父类是WindowAdapter该匿名内部类覆盖了父类的windowClosing方法 public void windowClosing(WindowEvent e) { System.exit(0); } }); f.setVisible(true); } } -
外观组件控制
- setBounds(int x, int y, int width, int height);
- setLocation(int x, int y);
- setSize(int width, int height);
- setBackground(Color.blue);
- setForeground(new Color(b, g, r));
- setFont();
图形绘制
Graphics类:包括绘制直线、矩形和椭圆等方法,但是绘制图形的操作能力非常有限。例如不能改变线的粗细,不能旋转这些图形。
-
画图的准备工作
-
要绘制图形,必须具备两个要素:画布和画笔
-
Swing中任何JComponent类的子类都可以充当画布的角色,任何java.awt.Component类的子类都可以作为画布
-
所有swing控件都有一个paint方法,负责在需要的时候对控件进行绘制
public void paint (Graphics g) -
paint方法中的参数g就是画笔,paint方法是自动调用的,当第一次显示组件或改变组件的大小需要重新画组件的界面时,该方法都会由系统自动调用
-
实际开发中,通常都是采用继承JComponent或JPanel类并重写paint的方式来获得画布和画笔的,然后加到顶层容器中;也可以直接重写窗口Frame类的paint方法
class MyComponent extends JComponent { public void paint (Graphics g) { code for drawing } } MyComponent m=new MyComponet(); add(m); -
坐标系统:绘制图形采用的是笛卡尔坐标系统,该坐标都是以像素为单位。画布上左上角为该坐标的原点(0,0)位置,x轴向右延伸,y轴向下延伸
-
在一个画布上定位某个图形时,是通过图形的最左上册点的定位进行的
import iava.awt.* import javax.swing.* public class DrawRec extends JFrame{ public void paint(Graphics g){ g.setColor(Color.blue); g.drawRec(100,20,40,20); g.fillRec(200,20,40,20) } public static void main(String[] args){ DrawRec R = new DrawRec(); R.setTitle("This is a rectangle"); f.pack(); f.show(); } }
-
动画
使用画布和画笔不但可以实现绘制静态的图形,配合线程或时钟类Timer还可以开发出灵活的动画效果。
- 开发动画的方法
- 让程序根据一定的规则不断地对画布进行重新绘制,即将绘制的规则编写到paint方法中,定时让paint方法重新调用实现重画,但是,要实现重绘必须调用repaint方法间接调用paint,不能直接调用paint方法重绘制。
- 开发动画需要定时执行指定的任务,可以自己开发一个线程,也可以用javax.swing.Timer类
//用线程绘制椭圆与长方形的转换
import javax.swing.*;
import java.awt.*
public class cartoon extends JFrame{
static boolean b = true;
public cartoon(){
this.setBounds(100,200,300,200);
this.show();
}
public static paint(Graphics g){
super.paint(g);
g.setColor(Color.red);
if(b)
g.drawRect(40, 50, 100, 40);
else
g.drawOval(40,50,150,100);
b = !b;
try{
Thread.sleep(1000);
}
catch(InterruptedException e){}
repaint();
}
puclic static void main(String[] args){
cartoon c = new cartoon();
}
}
//用按钮控制笑脸和哭脸的转换
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Buttonchange extends JFrame{
public static boolean b = true;
public Buttonchange(){
this.setBounds(400, 300, 500, 300);
this.show();
this.setLayout(new FlowLayout());
}
public void paint(Graphics g){
super.paint(g);
g.setColor(Color.blue);
if(b)
{
g.drawOval(100, 100, 100, 100);
g.fillOval(170, 125, 20, 10);
g.fillOval(120, 125, 20, 10);
g.drawArc(125, 125, 50, 50, 210, 120);
}
else
{
g.drawOval(100, 100, 100, 100);
g.fillOval(170, 125, 20, 10);
g.fillOval(120, 125, 20, 10);
g.drawArc(125, 175, 50, 50, 30, 120);
}
b = !b;
}
public static void main(String[] args){
Buttonchange win = new Buttonchange();
JButton change = new JButton("change");
win.add(change);
change.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
win.repaint();
}
});
}
}
//用线程控制笑脸哭脸的转换
import java.awt.*;
import javax.swing.*;
public class Buttonchange extends JFrame{
public static boolean b = true;
public Buttonchange(){
this.setBounds(400, 300, 500, 300);
this.show();
this.setLayout(new FlowLayout());
}
public void paint(Graphics g){
super.paint(g);
g.setColor(Color.blue);
if(b)
{
g.drawOval(100, 100, 100, 100);
g.fillOval(170, 125, 20, 10);
g.fillOval(120, 125, 20, 10);
g.drawArc(125, 125, 50, 50, 210, 120);
}
else
{
g.drawOval(100, 100, 100, 100);
g.fillOval(170, 125, 20, 10);
g.fillOval(120, 125, 20, 10);
g.drawArc(125, 175, 50, 50, 30, 120);
}
b = !b;
try {
Thread.sleep(1000);
}
catch (InterruptedException e){}
repaint();
}
public static void main(String[] args){
Buttonchange win = new Buttonchange();
}
}
chap9 多线程
线程类概述
线程与进程的区别
- 每个进程有一段专有内存空间。进程各自占有不同空间,内存消耗很大,会造成系统在不同程序之间切换时开销很大,进程之间通信速度很慢。
- 同一进程的各线程之间共享相同内存空间,利用共享内存来实现数据交换、实时通信及必要的同步工作。线程之间通信速度快,相互切换所占系统资源也小。
多线程实现机制
Thread方法实现进程的创建
//主要方法:声明一个 Thread 类的子类,并覆盖 run() 方法
class mythread extends Thread {
public void run( ){
/* 覆盖该方法*/ }
}
//声明一个实现 Runnable 接口的类,并实现 run() 方法
class mythread implements Runnable{
public void run( ){
/* 实现该方法*/ }
}
//示例1:编写一个简单的程序,要求它按两个不同的时间间隔(1秒和3秒)在屏幕上连续显示当前时间。
import java.util.*
public class TimePrinter extends Thread{
int pausetime;
String name;
public TimePrinter(int x, String y){
x = pausetime;
y = name;
}
public void run(){
while(true){
try{
System.out.printlin(name+":" + new Date(System.currentTimeMillis()));
Thread.sleep(pausetime);
}
catch(InterruptedException e){
System.out.println(e);
}
}
}
public static void main(String[] args){
TimePrinter p1 = new TimePrinter(3000, "fast");
TimePrinter p2 = new TimePrinter(1000, "slow");
p1.start();
p2.start();
}
}
//示例2:编写一个线程,该线程可以控制界面上标签的移动,每过1秒,向右移动10个像素。主界面上设置一个按钮,可以用来启动上述线程。
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class MyThread extends Thread{
JLabel jl;
public MyThread(JLabel jl){
this.jl=jl;}
public void run(){
for(int i=0;i<20;i++){
try {
sleep(1000);
jl.setLocation(jl.getX() + 10, jl.getY());
}
catch(InterruptedException ie){}
}
}
public static void main(String[] args){
JButton btn = new JButton("move");
JFrame frm = new JFrame();
frm.setBounds(200,100,400,200);
frm.setLayout(new FlowLayout());
frm.show();
frm.add(btn);
JLabel JLabel1 = new JLabel("move");
frm.add(JLabel1);
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
MyThread thread1 = new MyThread(JLabel1);
thread1.start();
}
});
}
}
总结:
- 定义一个Thread类的扩展类
- 覆盖public void run( )方法 //run( )是告知系统如何执行线程
- 创建对象(用构造方法)
- 调用该对象的start()方法,将该线程启动(注意不能直接调用run()方法), start()方法引起run的调用
Runnable接口实现进程
//主要方法:声明一个实现 Runnable 接口的类,并实现 run() 方法
class mythread implements Runnable{
public void run( )
{/* 实现该方法*/ }
}
import java.util.*
public class TimePrinter implements Runnable{
int pausetime;
String name;
public TimePrinter(int x, String y){
x = pausetime;
y = name;
}
public void run(){
while(true){
try{
System.out.printlin(name+":" + new Date(System.currentTimeMillis()));
Thread.sleep(pausetime);
}
catch(InterruptedException e){
System.out.println(e);
}
}
public static void main(String[] args){
Thread t1 = new Thread(new TimePrinter(1000,"fast guy"));
Thread t2 = new Thread(new TimePrinter(3000,"slow guy"));
t1.start();
t2.start();
}
}
总结:
- 定义一个实现Runnable接口的类
- 实现public void run()方法(必须实现)
- 将该类的对象作为Thread类构造方法的参数,创建一个线程实例
- 调用该对象的start()方法启动线程
两种实现方法的比较
-
实现Runnable接口的优势:
- 符合OO设计的思想
- 便于用extends继承其它类
-
采用继承Thread类方法的优点:程序代码更简单
-
通过Thread实例的start(),一个Thread的实例只能产生一个线程
-
Runnable的实例是可运行的,但它自己并不能直接运行,它需要被Thread对象来包装才行运行 ,但同一实例(Runnable实例)可产生多个线程
Thread类
控制线程的主要方法
-
start() :启动线程,引起run()方法的调用(创建线程 不自启动)
-
sleep(Long) :将线程暂停参数指定的毫秒数(不释放对象锁)
-
Wait(Long) :将线程挂起制定的毫秒数(释放对象锁)
Wait( ):将线程挂起,直到当调用notify()或notifyAll()时
(这两个方法只能在Synchronized函数中或同步块中使用)
-
notify() :唤起一个线程
-
yield() :只希望让其他正在等待的同优先级的线程有机会执行,且在没有其他线程等待时立即重新执行
-
join():如果一个线程需要等待另一个线程消亡后再继续运行,则可调用希望其消亡的那个线程的join()方法
-
isAlive():判断线程是否处于运行状态
-
setPriority():设置线程的优先级
-
getPriority():获得线程的优先级
-
stop():线程完成运行并结束后,将不能再运行
多线程互斥同步
为了防止出现多线程访问共享资源的冲突,只要给该资源加一个锁,当一个线程在使用这个资源时该资源就被锁住,其他线程就不能使用,除非解锁
实现方法
Java中只需要把一个方法声明为synchronized,便可有效地防止冲突
- wait:导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法,该方法是会释放锁的。
- notify:唤醒正在等待对象监视器的单个线程。 如果任何线程正在等待这个对象,其中一个被选择被唤醒。
- wait和notify(notifyAll)必须配合synchronized使用,而且wait必须在notify前用,wait后就会进入notify所在的线程,notify后唤醒wait所在的线程,但是wait所在的线程仍然没有获取锁,需要等待notify所在的线程释放锁。
//对于共享资源栈Stack的访问
//例子:设计两个线程,其中一个线程IncThread,工作流程为循环10次,每次对共享变量J增加1(函数为inc()),另外一个线程DecThread,工作流程为循环10次,每次对共享变量J减少1(函数为dec()),在主程序中启动两个线程进行并发执行。在改写数据过程中利用同步机制保障数据的一致性。
class data{
int j=0;
public synchronized void inc(){ j++; }
public synchronized void dec(){ j--; }
}
class IncThread extends Thread{
data d;
public IncThread(data dtemp){ d=dtemp;}
public void run(){
for(int i=0; i<10;i++) d.inc();}
}
class DecThread extends Thread{
data d;
public DecThread(data dtemp){d=dtemp;}
public void run(){
for(int i=0;i<10;i++) d.dec();
}
}
public class test{
public static void main(String arg[]){
data d=new data();
IncThread In=new IncThread(d);
DecThread Dec=new DecThread(d);
In.start();
Dec.start();
}
浙公网安备 33010602011771号