Java从入门到精通2
2023-12-19 11:24:06 12 举报
AI智能生成
总结
作者其他创作
大纲/内容
集合类
集合类概述
集合类和数组的不同之处是:数组的长度是固定的,集合的长度是可变的;数组用来存放基本类型的数据,集合用来存放对象的引用。
Collection接口
是层次结构中的跟接口,构成Collection的单位称为元素。Collection接口通常不能直接使用,但该接口提供了添加元素、删除元素、管理数据的方法。由于List接口与Set接口都继承了Collection接口,因此这些方法对List集合与Set集合是通用的。
List集合
ArrayList类
实现了可变的数组,允许保存所有的元素,包括null,并可以根据索引位置对集合进行快速的随机访问。缺点是向指定的索引位置插入对象或删除对象的速度较慢。(更善于查找)
LinkedList类
采用链表结构保存对象。这种结构的优点是便于向集合中插入和删除对象。需要向集合中插入、删除对象时,使用LinkList类实现的List集合的效率较高;但对于随机访问集合中的对象,使用LinkList类实现List集合的效率较低。(更善于添加和删除)
Set集合
HashSet类
实现Set接口,由哈希表(HashMap实例)支持。它不保证Set集合的迭代顺序,特别是它不保证该顺序恒久不变。此类允许使用null元素。
TreeSet类
不仅实现了Set接口,还实现了java.util.SortedSet接口,因此TrreeSet类实现的Set集合在遍历集合时按照自然顺序递增排序,也可以按照指定比较器递增排序,即可以通过比较器对用TreeSet类实现的Set集合中的对象进行排序。
Map集合
HashMap类
基于哈希表的Map接口的实现,此实现提供所有可选的映射操作,并允许使用null值和null键,但必须保证键的唯一性。HashMap类通过哈希表对其内部的映射关系进行快速寻找。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
TreeMap类
实现了Map接口,还实现了java.util.SortedMap接口,因此集合中的映射关系具有一定的顺序。但在添加、删除和定位映射关系时,TreeMap类比HashMap类性能稍差。由于TreeMap类实现的Map集合中的映射关系是根据对象按照一定的顺序排列的,因此不允许键对象是null。
枚举类型与泛型
枚举
使用枚举类型设置常量
使用枚举类型设置常量设置常量时,我们通常将常量放置在接口中,这样在程序中就可以直接使用该常量不能被修改因为在接口中定义常量时,该常量的修饰符为final与static。常规定义常量的代码如下:
- package thirteen;
- public interface Constants {
- public static final int Constants_A=1;//常规定义变量代码
- public static final int Constants_B=2;
- }
变量代码,public static final int constants_B=2;}枚举类型出现后逐渐取代了。上述常量定义方式使用枚举类型定义常量的语法如下:
- public enum Constants{
Constants_A,
Constants_B,
}
其中, enum 是定义枚举类型的关键字,当需要程序中使用该常量时,可以使用Constants和Constants_A来表示。
深入了解枚举类型
枚举类型较传统定义常量的方式,除具有参数类型检测的优势外,还具有其他方面的优势。
用户可以将一个枚举类型看作是一个类,它继承于java.lang.Enum类,当定义一个枚举类型时,每一个枚举类型成员都可以看作是枚举类型的一个实例,这些枚举类型成员都默认被final、public、static修饰,所以当使用枚举类型成员时直接使用枚举类型名称调用枚举类型成员即可。
由于枚举类型对象继承于java.lang.Enum类,所以该类中一些操作枚举类型的方法都可以应用到枚举类型中。
用户可以将一个枚举类型看作是一个类,它继承于java.lang.Enum类,当定义一个枚举类型时,每一个枚举类型成员都可以看作是枚举类型的一个实例,这些枚举类型成员都默认被final、public、static修饰,所以当使用枚举类型成员时直接使用枚举类型名称调用枚举类型成员即可。
由于枚举类型对象继承于java.lang.Enum类,所以该类中一些操作枚举类型的方法都可以应用到枚举类型中。
values()方法
枚举类型实例包含一个values()方法,该方法将枚举中所有的枚举值以数组的形式返回。
valueOf()方法与compareTo()方法
枚举类型中静态方法valueOf()可以将普通字符串转换为枚举类型,而compareTo()方法用于比较两个枚举类型对象定义时的顺序。
ordinal()方法
枚举类型中的ordinal()方法用于获取某个枚举对象的位置索引值。
枚举类型中的构造方法
在枚举类型中,可以添加构造方法,但是规定这个构造方法必须被private修饰符所修饰。
枚举类型声明提供了一种对用户友好的变量定义方法,枚举了某种类型所有可能出现的值。总结枚举类型,它具有以下特点:
1.类型安全
2.紧凑有效的数据定义
3..可以和程序其他部分完美交互
4.运行效率高
1.类型安全
2.紧凑有效的数据定义
3..可以和程序其他部分完美交互
4.运行效率高
泛型
Object类为最上层的父类,很多程序员为了使程序更为通用,设计程序时通常使传入的值与返回的值都以Object类型为主。当需要使用这些实例时,必须正确地将该实例转换为原来的类型,否则在运行时将会发生ClassCastException异常。为了提前预防这种问题,Java提供了泛型机制。其语法如下:
类名<T>
其中,T是泛型的名称,代表某一种类型。开发者在创建该类对象时需要指定T所代表哪种具体的类型。如果不指定具体类型,T则采用Object类型。
定义泛型类时,可以声明多个类型。语法如下:
class MyClass<T1,T2>{ }
其中,T1和T2为可能被定义的类型。
这样,在实例化指定类型的对象时就可以指定多个类型。例如:
这样,在实例化指定类型的对象时就可以指定多个类型。例如:
MyClass <Boolean,Float> m = new MyClass <Boolean,Float>();
泛型的高级用法包括限制泛型可用类型和使用类型通配符等。
泛型的类型参数只能是类类型,不可以是简单类型,如A<int>这种泛型定义就是错误的。
泛型的类型个数可以是多个。
可以使用extends关键字限制泛型的类型。
可以使用通配符限制泛型的类型。
泛型的类型个数可以是多个。
可以使用extends关键字限制泛型的类型。
可以使用通配符限制泛型的类型。
lambda表达式与流处理
lambda表达式
1.lambda表达式可以用非常少的代码实现抽象方法。
2.lambda表达式不能独立执行,因此必须实现函数式接口,并且会返回一个函数式接口的对象。
3.lambdab表达式的语法非常特殊
2.lambda表达式不能独立执行,因此必须实现函数式接口,并且会返回一个函数式接口的对象。
3.lambdab表达式的语法非常特殊
- 语法格式:
- ()-> 结果表达式
- 参数-> 结果表达式
- (参数1,参数2...,参数n)-> 结果表达式
lambda表达式也可以实现复杂方法,将操作符右侧的结果表达式换成代码块即可
- 语法格式如下:
- ()->{代码块)
- 参数->(代码块}
- (参数1,参数2,..参数n)->{代码块)
第1行实现无参方法,方法体是操作符右侧代码块。
第2行实现只有一个参数的方法,方法体是操作符右侧代码块。
第3行实现多参数的方法,方法体是操作符右侧代码块。
第2行实现只有一个参数的方法,方法体是操作符右侧代码块。
第3行实现多参数的方法,方法体是操作符右侧代码块。
lambda表达式的功能归纳总结,语法理解为:
() -> {代码块}
这个方法 按照 这样的代码来实现
这个方法 按照 这样的代码来实现
Iambda表达式实现函数式接口
interface SayHiInterface{
String say();//抽象方法接口
}
public class NoParamterDemo {
public static void main(String[] args) {//利用匿名内部类补全方法体
//lambda表达式实现发招呼接口,返回抽象方法结果
SayHiInterface pi=()->"你好啊,这是lanbda表达式";
System.out.print(pi.say());
}
}
函数式接口
开发者可以常见自定义的函数式接口
开发者可以常见自定义的函数式接口
例如:
interface MyInterface{
void method();
}
interface MyInterface{
void method();
}
如果接口中包含一个以上的抽象方法,则不符合函数式接口的规范,这样的接口不能用Iambda表达式创建匿名对象
Iambda表达式实现无参数抽象方法
interface SayHiInterface{
String say();//抽象方法接口
}
public class NoParamterDemo {
public static void main(String[] args) {//利用匿名内部类补全方法体
//lambda表达式实现发招呼接口,返回抽象方法结果
SayHiInterface pi=()->"你好啊,这是lanbda表达式";
System.out.print(pi.say());
}
}
结果如下
lambda表达式实现有参抽象方法
如果抽象方法中只有一个参数,lambda表达式则可以省略圆括号
lambda表达式中的参数不需要与抽象方法的参数名称相同,但顺序必须相同
如果抽象方法中只有一个参数,lambda表达式则可以省略圆括号
lambda表达式中的参数不需要与抽象方法的参数名称相同,但顺序必须相同
lambda表达式使用代码块
lambda表达式会自动判断返回值类型是否符合抽象方法的定义
lambda表达式会自动判断返回值类型是否符合抽象方法的定义
Iambda表达式调用外部变量
这些外部的变量有些可以被更改,有些则不能。例如,lambda表达式无法更改局部变量的值,但是却可以更改外部类的成员变量(也可以叫做类属性)的值他。
lambda表达式无法更改局部变量
局部变量在lambda表达式中默认被定义为final(静态)的,也就是说,lambda表达式只能调用局部变量,却不能改变其值。
lambda表达式可以更改类成员变量
类成员变量是在lambda表达式中不是被final修饰的,所以lambda表达式可以改变其值
这些外部的变量有些可以被更改,有些则不能。例如,lambda表达式无法更改局部变量的值,但是却可以更改外部类的成员变量(也可以叫做类属性)的值他。
lambda表达式无法更改局部变量
局部变量在lambda表达式中默认被定义为final(静态)的,也就是说,lambda表达式只能调用局部变量,却不能改变其值。
lambda表达式可以更改类成员变量
类成员变量是在lambda表达式中不是被final修饰的,所以lambda表达式可以改变其值
- lambda 表达式可以调用并修改类成员变量的值
- lambda表达式只是描述了抽象方法是如何实现的,在抽象方法没有被调用前,lambda表达式中的代码并没有被执行,所以运行抽象方法之前类成员变量的值不会发生变化
- 只要抽象方法被调用,就会执行lambda 表达式中的代码,类成员变量的值就会被修改。
Iambda表达式与异常处理
lambda 表达式中并有抛出异常的语法,这是因为lambda表达式会默认抛出抽象方法原有的异常,当此方法被调用时则要进行异常处理。
lambda 表达式中并有抛出异常的语法,这是因为lambda表达式会默认抛出抽象方法原有的异常,当此方法被调用时则要进行异常处理。
方法的引用
引用静态方法
语法:类名 :: 静态方法名,这里出现的新操作符 :: 这是英文状态的冒号组成的。
需要注意的是这个语法中方法名是没有圆括号的。例如:
语法:类名 :: 静态方法名,这里出现的新操作符 :: 这是英文状态的冒号组成的。
需要注意的是这个语法中方法名是没有圆括号的。例如:
List<String> list = Arrays.asList("A", "B", "C");
list.forEach(System.out::println);
list.forEach(System.out::println);
在上面的例子中,System.out::println 是一个方法引用。它引用了 System.out 类的静态方法 println。这个方法接受一个参数(在这个例子中是 String 类型),并打印这个参数。
引用静态方法规则:
1.方法名和参数:在引用静态方法时,你需要提供方法的名称以及所需要的任何参数。例如,如果你有一个名为greet的静态方法,它需要一个字符串和一个整数作为参数,你可以这样引用它:ClassName.greet(参数1, 参数2)。
2.作用域:你可以在任何地方引用静态方法,包括在类的内部,其他类的内部,或者作为表达式的一部分。
3.访问权限:如果静态方法是私有的,那么你不能从类的外部访问它。如果它是受保护的,那么你可以从同一个包中的类或者其他包中的子类访问它。如果它是默认的(即没有修饰符),那么你可以从同一个包中的类访问它。
4.重载:你可以在一个类中拥有多个同名的静态方法,只要他们的参数列表不同(即形成了重载)。在引用时,Java会根据你提供的参数类型和数量来确定应该调用哪个方法。
5.泛型:虽然静态方法可以使用泛型,但是他们在引用时并不需要提供具体的类型参数。例如,如果你有一个名为calculate的静态方法,它需要一个List作为参数,你可以这样调用它:ClassName.calculate(myList),而不必提供类型参数。
6.访问静态方法的方式:你可以通过类名来访问静态方法,或者通过实例来访问(虽然这有些混淆,因为通常我们通过实例来调用实例方法,而不是静态方法)。例如,ClassName.methodName()或者instanceName.methodName()。
7.链式调用:如果一个静态方法的返回值是返回类型的实例,那么我们可以连续地调用这个实例的方法。例如,如果add方法返回一个Calculator实例,并且这个实例有一个calculate方法,那么我们可以这样使用:ClassName.add(1, 2).calculate()。
1.方法名和参数:在引用静态方法时,你需要提供方法的名称以及所需要的任何参数。例如,如果你有一个名为greet的静态方法,它需要一个字符串和一个整数作为参数,你可以这样引用它:ClassName.greet(参数1, 参数2)。
2.作用域:你可以在任何地方引用静态方法,包括在类的内部,其他类的内部,或者作为表达式的一部分。
3.访问权限:如果静态方法是私有的,那么你不能从类的外部访问它。如果它是受保护的,那么你可以从同一个包中的类或者其他包中的子类访问它。如果它是默认的(即没有修饰符),那么你可以从同一个包中的类访问它。
4.重载:你可以在一个类中拥有多个同名的静态方法,只要他们的参数列表不同(即形成了重载)。在引用时,Java会根据你提供的参数类型和数量来确定应该调用哪个方法。
5.泛型:虽然静态方法可以使用泛型,但是他们在引用时并不需要提供具体的类型参数。例如,如果你有一个名为calculate的静态方法,它需要一个List作为参数,你可以这样调用它:ClassName.calculate(myList),而不必提供类型参数。
6.访问静态方法的方式:你可以通过类名来访问静态方法,或者通过实例来访问(虽然这有些混淆,因为通常我们通过实例来调用实例方法,而不是静态方法)。例如,ClassName.methodName()或者instanceName.methodName()。
7.链式调用:如果一个静态方法的返回值是返回类型的实例,那么我们可以连续地调用这个实例的方法。例如,如果add方法返回一个Calculator实例,并且这个实例有一个calculate方法,那么我们可以这样使用:ClassName.add(1, 2).calculate()。
引用成员方法
与引用静态方法语法不同,这里操作符左侧的必须是一个对象名,而不是类名。这种语法也可以达到抽象方法按照类成员方法逻辑来实现的目的。
与引用静态方法语法不同,这里操作符左侧的必须是一个对象名,而不是类名。这种语法也可以达到抽象方法按照类成员方法逻辑来实现的目的。
引用带泛型的方法
"::"操作符支持引用带泛型的方法。除方法外,"::"操作符支持引用带泛型的类。
"::"操作符支持引用带泛型的方法。除方法外,"::"操作符支持引用带泛型的类。
引用构造方法
lambda表达式有3种引用构造方法的语法,分别是引用无参构造方法、引用有参构造方法和引用数组构造方法。
lambda表达式有3种引用构造方法的语法,分别是引用无参构造方法、引用有参构造方法和引用数组构造方法。
无参数构造方法
引用构造方法的语法:
类名::new
因为构造方法与类名相同,如果操作符左右都写类名,会让操作符误以为是在引用与类名相同的静态方法,这样会导致程序出现bug,所以引用构造方法的语法使用了new关键字。操作符右侧的写new关键字,表示引用构造方法。
引用构造方法的语法:
类名::new
因为构造方法与类名相同,如果操作符左右都写类名,会让操作符误以为是在引用与类名相同的静态方法,这样会导致程序出现bug,所以引用构造方法的语法使用了new关键字。操作符右侧的写new关键字,表示引用构造方法。
引用有参构造方法
引用有参构造方法的语法与引用无参数构造方法一样。区别就是函数式接口的抽象方法是有参数的。
引用有参构造方法的语法与引用无参数构造方法一样。区别就是函数式接口的抽象方法是有参数的。
引用数组构造方法
把数组类型当作泛型。如果方法返回值是泛型,在这种特殊场景下,方法就应该返回一个数组类型的结果。如果要求抽象方法即引用构造方法,又要返回数组类型结果,这种场景下抽象方法的参数就有了另外一个含义:数组个数。抽象方法的参数可以决定返回的数组长度,但数组中的元素并不是有值的,还需要再次赋值。引用数组构造方法的语法也会有所不同,语法如下:
类名[]::new
把数组类型当作泛型。如果方法返回值是泛型,在这种特殊场景下,方法就应该返回一个数组类型的结果。如果要求抽象方法即引用构造方法,又要返回数组类型结果,这种场景下抽象方法的参数就有了另外一个含义:数组个数。抽象方法的参数可以决定返回的数组长度,但数组中的元素并不是有值的,还需要再次赋值。引用数组构造方法的语法也会有所不同,语法如下:
类名[]::new
Fuction接口
这个接口有以下两个泛型:
T:被操作的类型,可以理解为方法参数类型。
R:操作结果类型,可以理解为方法的返回类型。
Function 接口是函数式接口,所以只有一个抽象方法,但是Function 接口还提供方法以方便开发者对函数逻辑进行更深层的处理。
这个接口有以下两个泛型:
T:被操作的类型,可以理解为方法参数类型。
R:操作结果类型,可以理解为方法的返回类型。
Function 接口是函数式接口,所以只有一个抽象方法,但是Function 接口还提供方法以方便开发者对函数逻辑进行更深层的处理。
流处理
流处理有点类似数据库的 SQL 语句,可以执行非常复杂的过滤、映射、查找和收集功能,并且代码量很少。唯一的缺点是代码可读性不高。
流处理的接口都定义在java.uil.stream 包下。BaseStream接口是最基础的接口,但最常用的是BaseStream 接口的一个子接口——Stream 接口,基本上绝大多数的流处理都是在Stream 接口上实现的。Stream 接口是泛型接口,所以流中操作的元素可以是任何类的对象。
Collection 接口新增两个可以获取流对象的方法。第一个方法最常用,可以获取集合的顺序流,方法如下:
Stream stream();
第二个方法可以获取集合的并行流,方法如下:
Stream parallelstream();
因为所有集合类都是Collection 接口的子类,如ArrayList类、HashSet类等,所以这些类都可以进行流处理。例如:
List list = new ArrayList(); //创建集合
Streamcinteger> s = list.stream();//获取集合流对象
Collection 接口新增两个可以获取流对象的方法。第一个方法最常用,可以获取集合的顺序流,方法如下:
Stream stream();
第二个方法可以获取集合的并行流,方法如下:
Stream parallelstream();
因为所有集合类都是Collection 接口的子类,如ArrayList类、HashSet类等,所以这些类都可以进行流处理。例如:
List list = new ArrayList(); //创建集合
Streamcinteger> s = list.stream();//获取集合流对象
Optional类
Optional 类是用final 修饰的,所以不能有子类。Optional类是带有泛型的类,所以该类可以保存
任何对象的值。
从Optional类的声明代码中就可以看出这些特性,JDK中的部分代码如下
public final class Optional<T>{
private final T value;
}
Optional 类中有一个叫作value的成员属性,这个属性就是用来保存具体值的。value 是用泛型了
修饰的,并且还用了final 修饰,这表示一个 Optional 对象只能保存一个值。
Optional 类是用final 修饰的,所以不能有子类。Optional类是带有泛型的类,所以该类可以保存
任何对象的值。
从Optional类的声明代码中就可以看出这些特性,JDK中的部分代码如下
public final class Optional<T>{
private final T value;
}
Optional 类中有一个叫作value的成员属性,这个属性就是用来保存具体值的。value 是用泛型了
修饰的,并且还用了final 修饰,这表示一个 Optional 对象只能保存一个值。
Collectors类
Collectors类为收集类,该类实现了java.util.Collector接口。
Collectors类为收集类,该类实现了java.util.Collector接口。
数据过滤
数据过滤就是在杂乱的数据中筛选出需要的数据,类似 SQL 语句中的WHERE 关键字,给出一定的条件,将符合条件的数据过滤并展示出来。
数据过滤就是在杂乱的数据中筛选出需要的数据,类似 SQL 语句中的WHERE 关键字,给出一定的条件,将符合条件的数据过滤并展示出来。
filter()方法
filterO方法是Stream 接口提供的过滤方法。该方法可以将lambda表达式作为参数,然后按照lambda表达式的逻辑过滤流中的元素。过滤出想要的流元素后,还需使用Stream 提供的collectO方法按照指定方法重新封装。
filterO方法是Stream 接口提供的过滤方法。该方法可以将lambda表达式作为参数,然后按照lambda表达式的逻辑过滤流中的元素。过滤出想要的流元素后,还需使用Stream 提供的collectO方法按照指定方法重新封装。
distinct()方法
该方法可以去除流中的重复元素,效果与SQL语句中的DISTINCT关键字一样
该方法可以去除流中的重复元素,效果与SQL语句中的DISTINCT关键字一样
Iimit()方法
Iimit()方法是Stream接口提供的方法,该方法可以获取流中前N个元素
Iimit()方法是Stream接口提供的方法,该方法可以获取流中前N个元素
skip方法
skip()方法是Stream接口提供的方法,该方法可以忽略流中的前N个元素
skip()方法是Stream接口提供的方法,该方法可以忽略流中的前N个元素
数据映射
数据的映射和过滤概念不同:过滤是在流中找到符合条件的元素,映射是在流中获得具体的数据。 Stream 接口提供了map0方法用来实现数据映射,map()方法会按照参数中的函数逻辑获取新的流对象,新的流对象中元素类型可能与旧流对象元素类型不相同。
数据的映射和过滤概念不同:过滤是在流中找到符合条件的元素,映射是在流中获得具体的数据。 Stream 接口提供了map0方法用来实现数据映射,map()方法会按照参数中的函数逻辑获取新的流对象,新的流对象中元素类型可能与旧流对象元素类型不相同。
数据查找
allMatch()方法
该方法会判断流中的元素是否全部符合某一条件,返回结果是boolean值
anyMatch()方法
该方法会判断流中的元素是否有符合某一条件
noneMatch()方法
该方法会判断流中的所有元素是否都不符合某一条件
findFirst()方法
这个方法会返回符合条件的第一个元素
数据收集
数据统计
不仅可以筛选出特殊元素,还可以对元素的属性进行统计计算
数据分组
将流中元素按照指定的条件分开保存
数据分组有一级分组和多级分组
一级分组:将所有数据按照一个条件进行分类
数据分组有一级分组和多级分组
一级分组:将所有数据按照一个条件进行分类
I/O(输入/输出)
输入/输出流
文件类:File
字节流:InputStream:入 OutputStream:出
字符流:Reader:入 Writer:出
输入流
InputStream类是字节输入流的抽象类,所有字节流的父类。该类中所有方法遇到错误会引发IOException异常。
该类中有一些方法
read()
read(byte[] b)
mark(int readlimit)
reset()
skip()
输出流
字节流:InputStream:入 OutputStream:出
字符流:Reader:入 Writer:出
输入流
InputStream类是字节输入流的抽象类,所有字节流的父类。该类中所有方法遇到错误会引发IOException异常。
该类中有一些方法
read()
read(byte[] b)
mark(int readlimit)
reset()
skip()
输出流
File类
操作:创建或者删除
状态:文件是否存在或者隐藏
创建文件对象
new File(String Pathnname)
File file = new File(“d:/1.txt”)
new File(String parent,String child)
new File(File parent,String child)
状态:文件是否存在或者隐藏
创建文件对象
new File(String Pathnname)
File file = new File(“d:/1.txt”)
new File(String parent,String child)
new File(File parent,String child)
文件输入/输出流
FileInputStream与FileOutputStream类
FileInputStream类与FileOutputStream类都用来操作磁盘文件。如果用户的文件读取需求比较简单,则可以使用FileInputStream类,该类继承自InputStream类。FileOutputStream类与FileInputStream类对应,提供了基本的文件写入能力。FileOutputStream类是OutputStream类的子类。
FileInputStream类常用的构造方法如下:
FileOutputStream类有与FileInputStream类相同的参数构造方法,创建一个FileOutputStream对象时,可以指定不存在的文件名,但此文件不能是一个已被其他程序打开的文件。下面的实例就是使用FileInputStream类与FileOutputStream类实现文件的读取与写入功能的。
FileInputStream类常用的构造方法如下:
- FileInputStream(String name)。
- FileInputStream(File file)。
FileOutputStream类有与FileInputStream类相同的参数构造方法,创建一个FileOutputStream对象时,可以指定不存在的文件名,但此文件不能是一个已被其他程序打开的文件。下面的实例就是使用FileInputStream类与FileOutputStream类实现文件的读取与写入功能的。
说明
虽然Java在程序结束时自动关闭所有打开的流,但是当使用完流后,显式地关闭所有打开的流仍是一个好习惯。一个被打开的流有可能会用尽系统资源,这取决于平台和实现。如果没有将打开的流关闭,当另一个程序试图打开另一个流时,可能会得不到需要的资源。
虽然Java在程序结束时自动关闭所有打开的流,但是当使用完流后,显式地关闭所有打开的流仍是一个好习惯。一个被打开的流有可能会用尽系统资源,这取决于平台和实现。如果没有将打开的流关闭,当另一个程序试图打开另一个流时,可能会得不到需要的资源。
FileReader和FileWriter类
使用FileOutputStream类向文件中写入数据与使用FileInputStream类从文件中将内容读出来,都存在一点不足,即这两个类都只提供了对字节或字节数组的读取方法。由于汉字在文件中占用两个字节,如果使用字节流,读取不好可能会出现乱码现象,此时采用字符流FileReader类或FileWriter类即可避免这种现象。
FileReader类和FileWriter类对应了FileInputStream类和FileOutputStream类。FileReader类顺序地读取文件,只要不关闭流,每次调用read()方法就顺序地读取源中其余的内容,直到源的末尾或流被关闭。
FileReader类和FileWriter类对应了FileInputStream类和FileOutputStream类。FileReader类顺序地读取文件,只要不关闭流,每次调用read()方法就顺序地读取源中其余的内容,直到源的末尾或流被关闭。
带缓存的输入/输出流
缓存是I/O的一种性能优化。缓存流为I/O流增加了内存缓存区,使得在流上执行skip()、mark()和reset()方法都成为可能。
BufferedInputStream与BufferedOutputStream类
BufferedInputStream类可以对所有InputStream类进行带缓存区的包装以达到性能的优化。BufferedInputStream类有两个构造方法:
BufferedInputStream(InputStream in)。
BufferedInputStream(InputStream in,int size)。
BufferedInputStream(InputStream in)。
BufferedInputStream(InputStream in,int size)。
BufferedReader与BufferedWriter类
BufferedReader类与BufferedWriter类分别继承Reader类与Writer类。这两个类同样具有内部缓存机制,并能够以行为单位进行输入/输出。
BufferedReader类常用的方法如下:
- read()方法:读取单个字符。
- readLine()方法:读取一个文本行,并将其返回为字符串。若无数据可读,则返回null。
- write(String s, int off, int len)方法:写入字符串的某一部分。
- flush()方法:刷新该流的缓存。
- newLine()方法:写入一个行分隔符。
反射与注释
反射
class类
访问构造方法
在通过下列一组方法访问构造方法时,将返回Constructor类型的对象或数组。每个Constructor对象代表一个构造方法,利用Constructor对象可以操纵相应的构造方法:
- getConstructors()。
- getConstructor(Class<?>...parameterTypes)。
- getDeclaredConstructors()。
- getDeclaredConstructor(Class<?>...parameterTypes)。
如果是访问指定的构造方法,需要根据该构造方法的入口参数的类型来访问。例如,访问一个入口参数类型依次为String型和int型的构造方法,通过下面两种方式均可实现:
objectClass.getDeclaredConstructor(String.class, int.class);
objectClass.getDeclaredConstructor(new Class[] { String.class, int.class });
objectClass.getDeclaredConstructor(new Class[] { String.class, int.class });
访问成员变量
在通过下列一组方法访问成员变量时,将返回Field类型的对象或数组。每个Field对象代表一个成员变量,利用Field对象可以操纵相应的成员变量:
- getFields()。
- getField(String name)。
- getDeclaredFields()。
- getDeclaredField(String name)。
如果是访问指定的成员变量,可以通过该成员变量的名称来访问。例如,访问一个名称为birthday的成员变量,访问方法如下:
object. getDeclaredField("birthday");
构造成员方法
在通过下列一组方法访问成员方法时,将返回Method类型的对象或数组。每个Method对象代表一个方法,利用Method对象可以操纵相应的方法:
getMethods()。
getMethod(String name, Class<?>...parameterTypes)。
getDeclaredMethods()。
getDeclaredMethod(String name, Class<?>...parameterTypes)。
getMethod(String name, Class<?>...parameterTypes)。
getDeclaredMethods()。
getDeclaredMethod(String name, Class<?>...parameterTypes)。
如果是访问指定的方法,需要根据该方法的名称和入口参数的类型来访问。例如,访问一个名称为print、入口参数类型依次为String型和int型的方法,通过下面两种方式均可实现:
objectClass.getDeclaredMethod("print", String.class, int.class);
objectClass.getDeclaredMethod("print", new Class[] {String.class, int.class });
objectClass.getDeclaredMethod("print", new Class[] {String.class, int.class });
案例:
Annotation注解功能
定义Annotation类型
public @interface NoMemberAnnotation { }
定义Annotation类型
在定义Annotation类型时,也需要用到用来定义接口的interface关键字,但需要在interface关键字前加一个“@”符号,即定义Annotation类型的关键字为@interface,这个关键字的隐含意思是继承了java.lang.annotation.Annotation接口。例如,下面的代码就定义了一个Annotation类型:
在定义Annotation类型时,也需要用到用来定义接口的interface关键字,但需要在interface关键字前加一个“@”符号,即定义Annotation类型的关键字为@interface,这个关键字的隐含意思是继承了java.lang.annotation.Annotation接口。例如,下面的代码就定义了一个Annotation类型:
public @interface NoMemberAnnotation { }
上面定义的Annotation类型@NoMemberAnnotation未包含任何成员,这样的Annotation类型被称为marker annotation。下面的代码定义了一个只包含一个成员的Annotation类型:
public @interface OneMemberAnnotation{
String value();
}
String value();
}
- String:成员类型。可用的成员类型有String、Class、primitive、enumerated和annotation,以及所列类型的数组。
- value:成员名称。如果在所定义的Annotation类型中只包含一个成员,通常将成员名称命名为value。
下面的代码定义了一个包含多个成员的Annotation类型:
public @interface MoreMemberAnnotation{
String desctibe();
Class type();
}
String desctibe();
Class type();
}
在为Annotation类型定义成员时,也可以为成员设置默认值。例如,下面的代码在定义Annotation类型时就为成员设置了默认值:
public @interface DefaultValueAnnotation{
String describe() default "<默认值>";
Class type() default void.class;
}
String describe() default "<默认值>";
Class type() default void.class;
}
在定义Annotation类型时,还可以通过Annotation类型@Target来设置Annotation类型适用的程序元素种类。如果未设置@Target,则表示适用于所有程序元素。枚举类ElementType中的枚举常量用来设置@Targer。
通过Annotation类型@Retention可以设置Annotation的有效范围。枚举类RetentionPolicy中的枚举常量用来设置@Retention,如表16.7所示。如果未设置@Retention,Annotation的有效范围为枚举常量CLASS表示的范围。
访问Annotation信息
如果在定义Annotation类型时将@Retention设置为RetentionPolicy.RUNTIME,那么在运行程序时通过反射就可以获取到相关的Annotation信息,如获取构造方法、字段和方法的Annotation信息。
Constructor类、Field类和Method类均继承了AccessibleObject类,在AccessibleObject中定义了3个关于Annotation的方法。其中,方法isAnnotationPresent(Class<? extends Annotation> annotationClass)用来查看是否添加了指定类型的Annotation,如果是则返回true,否则返回false;方法getAnnotation(Class<T> annotationClass)用来获得指定类型的Annotation,如果存在则返回相应的对象,否则返回null;方法getAnnotations()用来获得所有的Annotation,该方法将返回一个Annotation数组。
在Constructor类和Method类中还定义了方法getParameterAnnotations(),用来获得为所有参数添加的Annotation,将以Annotation类型的二维数组返回,在数组中的顺序与声明的顺序相同。如果没有参数则返回一个长度为0的数组;如果存在未添加Annotation的参数,将用一个长度为0的嵌套数组占位。
Constructor类、Field类和Method类均继承了AccessibleObject类,在AccessibleObject中定义了3个关于Annotation的方法。其中,方法isAnnotationPresent(Class<? extends Annotation> annotationClass)用来查看是否添加了指定类型的Annotation,如果是则返回true,否则返回false;方法getAnnotation(Class<T> annotationClass)用来获得指定类型的Annotation,如果存在则返回相应的对象,否则返回null;方法getAnnotations()用来获得所有的Annotation,该方法将返回一个Annotation数组。
在Constructor类和Method类中还定义了方法getParameterAnnotations(),用来获得为所有参数添加的Annotation,将以Annotation类型的二维数组返回,在数组中的顺序与声明的顺序相同。如果没有参数则返回一个长度为0的数组;如果存在未添加Annotation的参数,将用一个长度为0的嵌套数组占位。
数据库操作
第一步:注册驱动
第二步:获取连接
接下来,你需要创建一个连接到你的MySQL数据库的连接对象。这需要你的数据库的URL,用户名和密码。下面是一个例子:
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "root";
String password = "1234";
Connection conn = DriverManager.getConnection(url, user, password);
String user = "root";
String password = "1234";
Connection conn = DriverManager.getConnection(url, user, password);
在上面的例子中,“localhost:3306”是你的MySQL服务器的地址和端口(通常为3306),“mydatabase”是你的数据库名,“root”和“1234”是你的用户名和密码。
第三步:获取statement对象
第四步:执行SOL语句返回结果集
查询操作
下面这个例子中代码会连接到一个名为"testDB"的MySQL数据库,并从"Users"表中查询所有的记录。查询的结果会显示每条记录的"name"和"email"字段。
下面这个例子中代码会连接到一个名为"testDB"的MySQL数据库,并从"Users"表中查询所有的记录。查询的结果会显示每条记录的"name"和"email"字段。
import java.sql.*;
public class Main {
public static void main(String[] args) {
// 创建连接
String url = "jdbc:mysql://localhost:3306/testDB";
String username = "root";
String password = "password";
Connection conn = null;
Statement stmt = null;
try {
conn = DriverManager.getConnection(url, username, password);
stmt = conn.createStatement();
// 执行查询
String sql = "SELECT * FROM Users"; // Users是表的名称
ResultSet rs = stmt.executeQuery(sql);
while(rs.next()) {
// 通过列名获取相应的值
String name = rs.getString("name");
String email = rs.getString("email");
System.out.println("Name: " + name + ", Email: " + email);
}
rs.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(stmt != null) stmt.close();
if(conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
// 创建连接
String url = "jdbc:mysql://localhost:3306/testDB";
String username = "root";
String password = "password";
Connection conn = null;
Statement stmt = null;
try {
conn = DriverManager.getConnection(url, username, password);
stmt = conn.createStatement();
// 执行查询
String sql = "SELECT * FROM Users"; // Users是表的名称
ResultSet rs = stmt.executeQuery(sql);
while(rs.next()) {
// 通过列名获取相应的值
String name = rs.getString("name");
String email = rs.getString("email");
System.out.println("Name: " + name + ", Email: " + email);
}
rs.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(stmt != null) stmt.close();
if(conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
.增、删、改操作
对于插入、更新和删除操作(增删改操作),我们首先需要创建一个PreparedStatement对象,然后设置SQL语句中的参数,并使用executeUpdate()方法执行这个语句。且增删改操作的不同点在于使用不同的SQL语句来创建PreparedStatement对象,其余并无差距。
对于插入、更新和删除操作(增删改操作),我们首先需要创建一个PreparedStatement对象,然后设置SQL语句中的参数,并使用executeUpdate()方法执行这个语句。且增删改操作的不同点在于使用不同的SQL语句来创建PreparedStatement对象,其余并无差距。
遍历结果集
关闭连接释放资源
在执行查询后,你需要处理查询的结果,并且清理你的资源。下面是一个例子:
while(rs.next()) {
String column1 = rs.getString("column1");
int column2 = rs.getInt("column2");
//...处理结果...
}
rs.close(); // 关闭ResultSet
stmt.close(); // 关闭Statement
conn.close(); // 关闭Connection
String column1 = rs.getString("column1");
int column2 = rs.getInt("column2");
//...处理结果...
}
rs.close(); // 关闭ResultSet
stmt.close(); // 关闭Statement
conn.close(); // 关闭Connection
Swing程序设计
Swing概述
String包的层次结构和继承关系如下 :
常用的Swing组件如下表:
Swing常用窗体
JFrame 类的常用构造方法包括以下两种形式:
JFrame jf = new JFrame(“登录系统”);
Container container = jf.getContentPane();
在创建窗体后,先调用getContentPaneO方法将窗体转换为容器,再调用addO方法或者removeO方法向容器中添加组件或者删除容器中的组件。向容器中添加按钮,关键代码如下:
JButton okBtn= new JButton(“确定“);
container.add(okBtn);
删除容器中的按钮,关键代码如下:
container.remove(okBtn);
创建窗体后,要对窗体进行设置,如设置窗体的位置、大小、是否可见等。JFrame 类提供的相应方法可实现上述设置操作,具体如下:
setBounds(int x, int y, int width, int leight):设置窗体左上角在屏幕中的坐标为(x,y),窗体的宽度为width,窗体的高度为height。
1.setLocation(int x,int y):设置窗体左上角在屏幕中的坐标为(x,y)。
2.setSize(int width, int height):设置窗体的宽度为width,高度为height。
3.setVisibale(boolean b):设置窗体是否可见。b为true时,表示可见;b为false时,表示不可见。
4.setDefaultCloseOperation(int operation):设置窗体的关闭方式,默认值为DISPOSE_ON_CLOSE 。
Java 语言提供了多种窗体的关闭方式,常用的有4种,如图所示。
- public JFrame():创建一个初始不可见、没有标题的窗体。
- public JFrame(String title):创建一个不可见、具有标题的窗体。
JFrame jf = new JFrame(“登录系统”);
Container container = jf.getContentPane();
在创建窗体后,先调用getContentPaneO方法将窗体转换为容器,再调用addO方法或者removeO方法向容器中添加组件或者删除容器中的组件。向容器中添加按钮,关键代码如下:
JButton okBtn= new JButton(“确定“);
container.add(okBtn);
删除容器中的按钮,关键代码如下:
container.remove(okBtn);
创建窗体后,要对窗体进行设置,如设置窗体的位置、大小、是否可见等。JFrame 类提供的相应方法可实现上述设置操作,具体如下:
setBounds(int x, int y, int width, int leight):设置窗体左上角在屏幕中的坐标为(x,y),窗体的宽度为width,窗体的高度为height。
1.setLocation(int x,int y):设置窗体左上角在屏幕中的坐标为(x,y)。
2.setSize(int width, int height):设置窗体的宽度为width,高度为height。
3.setVisibale(boolean b):设置窗体是否可见。b为true时,表示可见;b为false时,表示不可见。
4.setDefaultCloseOperation(int operation):设置窗体的关闭方式,默认值为DISPOSE_ON_CLOSE 。
Java 语言提供了多种窗体的关闭方式,常用的有4种,如图所示。
结果如下
JDialog对话框
JDialog 类常用的构造方法如下:
- public JDialogO:创建一个没有标题和父窗体的对话框。
- public JDialog(Frame f):创建一个没有标题,但指定父窗体的对话框。
- public JDialog(Frame f, boolean model):创建一个没有标题,但指定父窗体和模式的对话框。如果model为true,那么弹出对话框后,用户无法操作父窗体。
- public JDialog(Frame f, String title):创建一个指定标题和父窗体的对话框。
- public JDialog(Frame f, String title, boolean model):创建一个指定标题、父窗体和模式的对话框。
结果如下
JOptionPane 小型对话框
JOptionPane提供了4种创建对话框的方法,如下:
.自定义对话框
参数说明如下:
- parentComponent:指明对话框在哪个窗体上显示,如果传入具体的窗体对象,对话框会在该窗体居中位置显示,如果传入null则在屏幕中间弹出对话框。
- message:提示的信息。
- optionType:指定可用于对话框的选项的整数:DEFAULT_OPTION、YES NO_OPTION.YES NO_CANCEL_OPTION 或 OK_CANCEL_OPTION。
- messageType:指定消息种类的整数,主要用于确定来自可插入外观的图标ERRORMESSAGE、INFORMATION_MESSAGE、WARNING_MESSAGE、QUESTION_MESSAGE 或 PLAIN_MESSAGE。
- icon:在对话框中显示的图标。
- options:指示用户可能选择的对象组成的数组。如果对象是组件,则可以正确呈现,非String对象使用其toString方法呈现;如果此参数为null,则由外观确定选项。
- initialValue:表示对话框的默认选择的对象,只有在使用options 时才有意义,可以为null。
结果如下
确认框
static int showConfirmDialog(Component parentComponent, Object message)
static int showConfirmDialog(Component parentComponent, Object message, String title, int optionType)
调用一个由optionType参数确定其中选项数的对话框,messageType参数确定要显示的图标。
static int showConfirmDialog(Component parentComponent,
Object message,
String title,
int optionType,
int messageType)
- 调出带有选项 Yes、No 和Cancel的对话框;标题为 Select an Option。
static int showConfirmDialog(Component parentComponent, Object message)
- 调出一个由optionType参数确定其中选项数的对话框。
static int showConfirmDialog(Component parentComponent, Object message, String title, int optionType)
调用一个由optionType参数确定其中选项数的对话框,messageType参数确定要显示的图标。
static int showConfirmDialog(Component parentComponent,
Object message,
String title,
int optionType,
int messageType)
结果如下
.输入框
输入框已经封装好了一套外观样式,弹出后要求用户在文本框中输入文本,用户完成输入操作后,输入框可以返回用户输入的结果。创建输入框的方法有以下几种重载形式:
static String showlnputDialog(Component parentComponent, Object message)
static String showlnputDialog(Component parentComponent, Object message, Object initialSelectionValue)
static String showlnputDialog(Component parentComponent, Object message, String title, int messageType)
static Object showInputDialog(Component parentComponent,
Object message,
String title,
int messageType,
Icon icon,
Objectü selectionValues,
Object initialSelectionValue)
static String showInputDialog(Object message)
static String showInputDialog(Object message, Object initialSelectionValue)
- 显示请求用户输入内容的问题消息对话框,它以parentComponent为父级。
static String showlnputDialog(Component parentComponent, Object message)
- 显示请求用户输入内容的问题消息对话框,它以parentComponent为父级。
static String showlnputDialog(Component parentComponent, Object message, Object initialSelectionValue)
- 显示请求用户输入内容的对话框,它以parentComponent为父级,该对话框的标题为title,消息类型为messageType。
static String showlnputDialog(Component parentComponent, Object message, String title, int messageType)
- 提示用户在可以指定初始选择、可能选择及其他所有选项的模块化的对话框中输入内容。
static Object showInputDialog(Component parentComponent,
Object message,
String title,
int messageType,
Icon icon,
Objectü selectionValues,
Object initialSelectionValue)
- 显示请求用户输入的问题消息对话框。
static String showInputDialog(Object message)
- 显示请求用户输入的问题消息对话框,它带有已初始化为initialSelectionValue的输入值。
static String showInputDialog(Object message, Object initialSelectionValue)
.通知框
创建通知框方法有以下几种重载形式:
Object message,
String title,
int messageType)
- 调出标题为Message的信息消息对话框。
- 调出对话框,它显示使用由messageType 参数确定的默认图标的message。
Object message,
String title,
int messageType)
- 调出一个显示信息的对话框,为其指定了所有参数。
static void showMessageDialog(Component parentComponent,
Object message,
String title,
int messageType,
Icon icon)
常用布局管理器
null绝对布局
绝对布局也叫null布局,其特点是硬性指定组件在容器中的位置和大小,组件的位置通过绝对坐标的方式来指定。使用绝对布局首先要使用Container.setLayout(null)方法取消容器的布局管理器,然后再使用Component.setBounds(int x, int y, int width, int height)方法设置每个组件在容器中的位置和大小。
FlowLayout 流布局管理器
FlowLayout类具有以下常用的构造方法:
- public FlowLayout()。
- public FlowLayout(int alignment)。
- public FlowLayout(int alignment,int horizGap,int vertGap)。
BorderLayout 边界布局管理器
addo方法被用于实现向容器中添加组件的功能,它可以设置组件的摆放位置。addO方法常用的语
法格式如下:
public void add(Component comp, Object constraints)
法格式如下:
public void add(Component comp, Object constraints)
- comp:被添加的组件。
- constraints:被添加组件的布局约束对象。
GridLayout 网络布局管理器
网格布局(GridLayout)管理器能够把容器划分为网格,组件可以按行、列进行排列。在网格布局管理器中,网格的个数由行数和列数决定,且每个网格的大小都相同。例如,一个两行两列的网格布局管理器能够产生4个大小相等的网格。组件从网格的左上角开始,按照从左到右、从上到下的顺序被添加到网格中,且每个组件都会填满整个网格。改变窗体大小时,组件的大小也会随之改变。
网格布局管理器主要有以下两个常用的构造方法:
public GridLayout(int rows, int columns)。
public GridLayout(int rows, int columns, int horizGap, int vertGap)。
其中,参数rows 和columns分别代表网格的行数和列数,这两个参数只允许有一个参数可以为0,被用于表示一行或一列可以排列任意多个组件;参数horizGap和vertGap分别代表网格之间的水平间距和垂直间距。
网格布局管理器主要有以下两个常用的构造方法:
public GridLayout(int rows, int columns)。
public GridLayout(int rows, int columns, int horizGap, int vertGap)。
其中,参数rows 和columns分别代表网格的行数和列数,这两个参数只允许有一个参数可以为0,被用于表示一行或一列可以排列任意多个组件;参数horizGap和vertGap分别代表网格之间的水平间距和垂直间距。
常用面板
JPanel 面板
JPanel面板继承java.awt.Container类。JPanel面板必须在窗体容器中使用,无法脱离窗体显示。
JScrollPane 滚动面板
ScrollPane面板是带滚动条的面板,被用于在较小的窗体中显示较大篇幅的内容。需要注意的是,JScrollPane滚动面板不能使用布局管理器,且只能容纳一个组件。如果需要向JScrollPane面板中添加多个组件,那么需要先将多个组件添加到JPanel面板,再将JPanel面板添加到JScrollPane滚动面板。
文字标签组件与图标
JLable 标签
JLabel类常用的构造方法如下:
- public JLabel0:创建一个不带图标或文本的标签。
- public JLabel(Icon icon):创建一个带图标的标签。
- public JLabel(Icon icon, int aligment):创建一个带图标的标签,并设置图标的水平对齐方式。
- public JLabel(String text, int aligment):创建一个带文本的标签,并设置文本的水平对齐方式。
- public JLabel(String text, Icon icon, int aligment):创建一个带文本和图标的JLabel对象,并设置文本和图标的水平对齐方式。
图标的使用
在Swing 程序设计中,图标经常被添加到标签、按钮等组件,使用javax.swing.Imagelcon类可以依据现有的图片创建图标。ImageIcon类实现了Icon接口,它有多个构造方法,常用的如下:
- public ImagelconO:创建一个 Imagelcon 对象,创建 ImageIcon对象后,使用其调用 setImage(Image image)方法设置图片。
- public Imagelcon(Image image):依据现有的图片创建图标。
- public ImageIcon(URL url):依据现有图片的路径创建图标。
按钮组件
JButton 按钮
Swing 按钮由JButton对象表示,JButton常用的构造方法如下:
创建JButon 对象后,如果要对JButton 对象进行设置,那么可以使用JButton类提供的方法。
- public JButtonO:创建一个不带文本或图标的按钮。
- public JButton(String text):创建一个带文本的按钮。
- public JButton(Icon icon):创建一个带图标的按钮。
- public JButton(String text, Icon icon):创建一个带文本和图标的按钮。
创建JButon 对象后,如果要对JButton 对象进行设置,那么可以使用JButton类提供的方法。
JRadinButton 单选按钮
单选按钮
创建JRadioButton 对象需要使用JRadioButton 类的构造方法。JRadioButton类常用的构造方法如下:
public JRadioButton():创建一个未被选中、文本未被设定的单选按钮。
public JRadioButton(Icon icon):创建一个未被选中、文本未被设定,但具有指定图标的单选按钮。
public JRadioButton(Icon icon, boolean selected):创建一个具有指定图标、选择状态,但文本区未被设定的单选按钮。
public JRadioButton(String text):创建一个具有指定文本,但未被选中的单选按钮。
public JRadioButton(String text, Icon icon):创建一个具有指定文本、指定图标,但未被选中的
单选按钮。
public JRadioButton(String text, Icon icon, boolean selected):创建一个具有指定的文本、指定图标和选择状态的单选按钮。
根据上述构造方法的相关介绍,不难发现,单选按钮的图标、文本和选择状态等属性能够被同时设定。例如,使用JRadioButton 类的构造方法创建一个文本为“选项 A”的单选按钮,关键代码如下:
JRadioButton rbtn = new JRadioButton("选项 A");
public JRadioButton():创建一个未被选中、文本未被设定的单选按钮。
public JRadioButton(Icon icon):创建一个未被选中、文本未被设定,但具有指定图标的单选按钮。
public JRadioButton(Icon icon, boolean selected):创建一个具有指定图标、选择状态,但文本区未被设定的单选按钮。
public JRadioButton(String text):创建一个具有指定文本,但未被选中的单选按钮。
public JRadioButton(String text, Icon icon):创建一个具有指定文本、指定图标,但未被选中的
单选按钮。
public JRadioButton(String text, Icon icon, boolean selected):创建一个具有指定的文本、指定图标和选择状态的单选按钮。
根据上述构造方法的相关介绍,不难发现,单选按钮的图标、文本和选择状态等属性能够被同时设定。例如,使用JRadioButton 类的构造方法创建一个文本为“选项 A”的单选按钮,关键代码如下:
JRadioButton rbtn = new JRadioButton("选项 A");
按钮组
Swing 按钮组由 ButtonGroup对象表示,多个单选按钮被添加到按钮组后,能够实现“选项有多个,
但只能选中一个”的效果。ButtonGroup 对象被创建后,可以使用addO方法把多个单选按钮添加到
ButtonGroup对象中。
但只能选中一个”的效果。ButtonGroup 对象被创建后,可以使用addO方法把多个单选按钮添加到
ButtonGroup对象中。
JCheckBox 复选框
JCheckBox的常用构造方法如下:
- public JCheckBox():创建一个文本、图标未被设定且默认未被选中的复选框。
- public JCheckBox(Icon icon, Boolean checked):创建一个具有指定图标、指定初始时是否被选中,但文本未被设定的复选框。
- public JCheckBox(String text, Boolean checked):创建一个具有指定文本、指定初始时是否被选中,但图标未被设定的复选框。
列表组件
JComboBox 下拉列表框
JComboBox 类的常用构造方法如下:
public JComboBox(ComboBoxModeldataModel):创建一个 JComboBox对象,下拉列表中的列表项使用ComboBoxModel中的列表项,ComboBoxModel 是一个用于组合框的数据模型。
public JComboBox(Object[]arrayData):创建一个包含指定数组中的元素的JComboBox对象。
public JComboBox(Vector vector):创建一个包含指定 Vector 对象中的元素的JComboBox 对象.Voetor对象中的元素可以通过整数索引进行访问,而且 Vector 对象中的元素可以根据需求被添加或者移除。
public JComboBox(ComboBoxModeldataModel):创建一个 JComboBox对象,下拉列表中的列表项使用ComboBoxModel中的列表项,ComboBoxModel 是一个用于组合框的数据模型。
public JComboBox(Object[]arrayData):创建一个包含指定数组中的元素的JComboBox对象。
public JComboBox(Vector vector):创建一个包含指定 Vector 对象中的元素的JComboBox 对象.Voetor对象中的元素可以通过整数索引进行访问,而且 Vector 对象中的元素可以根据需求被添加或者移除。
JComboBox类的常用方法及其说明如表所示。
JList 列表框
JList类的常用构造方法如下:
- public void JList():创建一个空的JList对象。
- public void JList(Object[] listData):创建一个显示指定数组中的元素的JList对象。
- public void JList(Vector listData):创建一个显示指定 Vector 中的元素的JList对象。
- public void JList(ListModel dataModel):创建一个显示指定的非 null模型的元素的JList对象。
文本组件
JTextField文本框
文本框组件由JTextField对象表示。JTextField类的常用构造方法如下:
public JTextFieldO:创建一个文本未被指定的文本框。
public JTextField(String text):创建一个指定文本的文本框。
public JTextField(int fieldwidth):创建一个指定列宽的文本框。
public JTextField(String text, int fieldwidth):创建一个指定文本和列宽的文本框。
public JTextField(Document docModel, String text, int fieldWidth):创建一个指定文本模型、本内容和列宽的文本框。
如果要为一个文本未被指定的文本框设置文本内容,那么需要使用 setTextO方法。setText0方法的语法如下:
public void setText(String t)
其中,t表示文本框要显示的文本内容。
public JTextFieldO:创建一个文本未被指定的文本框。
public JTextField(String text):创建一个指定文本的文本框。
public JTextField(int fieldwidth):创建一个指定列宽的文本框。
public JTextField(String text, int fieldwidth):创建一个指定文本和列宽的文本框。
public JTextField(Document docModel, String text, int fieldWidth):创建一个指定文本模型、本内容和列宽的文本框。
如果要为一个文本未被指定的文本框设置文本内容,那么需要使用 setTextO方法。setText0方法的语法如下:
public void setText(String t)
其中,t表示文本框要显示的文本内容。
JPasswordField 密码框
密码框组件由JPasswordField对象表示,其作用是把用户输入的字符串以某种符号进行加密。JPasswordField类的常用构造方法如下:
public JPasswordFieldO:创建一个文本未被指定的密码框。
public JPasswordFiled(String text):创建一个指定文本的密码框。
public JPasswordField(int fieldwidth):创建一个指定列宽的密码框。
public JPasswordField(String text, int fieldwidth):创建一个指定文本和列宽的密码框。
public JPasswordField(Document docModel, String text, int fieldWidth):创建一个指定文本模型和列宽的密码框。
JPasswordField 类提供了setEchoCharO方法,这个方法被用于改变密码框的回显字符。setEchoCharO方法的语法如下:
public void setEchoChar(char c)
其中,c表示密码框要显示的回显字符
public JPasswordFieldO:创建一个文本未被指定的密码框。
public JPasswordFiled(String text):创建一个指定文本的密码框。
public JPasswordField(int fieldwidth):创建一个指定列宽的密码框。
public JPasswordField(String text, int fieldwidth):创建一个指定文本和列宽的密码框。
public JPasswordField(Document docModel, String text, int fieldWidth):创建一个指定文本模型和列宽的密码框。
JPasswordField 类提供了setEchoCharO方法,这个方法被用于改变密码框的回显字符。setEchoCharO方法的语法如下:
public void setEchoChar(char c)
其中,c表示密码框要显示的回显字符
JTextArea 文本域
文本城组件曲 JTextArea 对象表示,其作用是接受用户的多行文本输入。JTextArea类的常用构造方法如下:
patie TextArea0:创建一个文本未被指定的文本域。
publie NTtextArea(String text):创建一个指定文本的文本域。
pabic leatAesfint rows,int columns):创建一个指定行高和列宽,但文本未被指定的文本域。
public JTextArea(Document doc): 创建一个指定文档模型的文本域。
public JTextArea(Document doc,String Text,int rows,int columns):创建内容以及行高和列宽的文本域。
patie TextArea0:创建一个文本未被指定的文本域。
publie NTtextArea(String text):创建一个指定文本的文本域。
pabic leatAesfint rows,int columns):创建一个指定行高和列宽,但文本未被指定的文本域。
public JTextArea(Document doc): 创建一个指定文档模型的文本域。
public JTextArea(Document doc,String Text,int rows,int columns):创建内容以及行高和列宽的文本域。
表格组件
创建表格
JTable 类除提供了默认的构造方法外,还提供了被用于显示二维数组中的元素的构造方法,这个
构造方法的语法如下:
JTable(Object[][] rowData, Objectl columnNames)
构造方法的语法如下:
JTable(Object[][] rowData, Objectl columnNames)
- rowData:存储表格数据的二维数组。
- columnNames:存储表格列名的一维数组。
DefaultTableModel 表格数据模型
Swing 使用 TableModel 接口定义了一个表格模型,AbstractTableModel 抽象类实现了 TableModel接口的大部分方法,只有以下3个抽象方法没有实现:
public int getRowCountO);
public int getColumnCountO);
public Object getValueAt(int rowIndex, int columnIndex);
为了实现使用表格模型创建表格的功能,Swing 提供了表格模型类,即DefaultTableModel类。DefaultTableModel 类继承了 AbstractTableModel 抽象类且实现了上述3个抽象方法。DefaultTableModel类提供的常用构造方法如表18.8所示。
————————————————
版权声明:本文为CSDN博主「s1133333」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/s1133333/article/details/134283096
public int getRowCountO);
public int getColumnCountO);
public Object getValueAt(int rowIndex, int columnIndex);
为了实现使用表格模型创建表格的功能,Swing 提供了表格模型类,即DefaultTableModel类。DefaultTableModel 类继承了 AbstractTableModel 抽象类且实现了上述3个抽象方法。DefaultTableModel类提供的常用构造方法如表18.8所示。
————————————————
版权声明:本文为CSDN博主「s1133333」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/s1133333/article/details/134283096
维护表格模型
表格中的数据内容需要予以维护,如使用getValueAt()方法获得表格中某一个单元格的值,使用addRow()方法向表格中添加新的行,使用setValueAt()方法修改表格中某一个单元格的值,使用removeRow()方法从表格中删除指定行等。
注意
当删除表格模型中的指定行时,每删除一行,其后所有行的索引值将相应地减1,所以当连续删除多行时,需要注意对删除行索引的处理。
注意
当删除表格模型中的指定行时,每删除一行,其后所有行的索引值将相应地减1,所以当连续删除多行时,需要注意对删除行索引的处理。
事件监听器
ActionEvent动作事件
动作事件(ActionEvent)监听器是Swing中比较常用的事件监听器,很多组件的动作都会使用它监听,如按钮被单击等。表18.9描述了动作事件监听器的接口与事件源等。
KeyEvent键盘事件
当向文本框中输入内容时,将发生键盘事件。KeyEvent类负责捕获键盘事件,可以通过为组件添加实现了KeyListener接口的监听器类来处理相应的键盘事件。
MouseEvent鼠标事件
所有组件都能发生鼠标事件,MouseEvent类负责捕获鼠标事件,可以通过为组件添加实现了MouseListener接口的监听器类来处理相应的鼠标事件。
Java绘图
Java绘图类
绘图是高级程序设计中非常重要的技术。例如,应用程序可以绘制闪屏图片、背景图片、组件外观等,Web程序可以绘制统计图、数据库存储的图片资源等。正所谓“一图胜千言”,使用图片能够更好地表达程序运行结果,并且能够进行细致的数据分析与保存等。
Graphics类
Graphics类是所有图形上下文的抽象基类,它允许应用程序在组件以及闭屏图像上进行绘制。Graphics类封装了Java支持的基本绘图操作所需的状态信息,主要包括颜色、字体、画笔、文本、图像等。
Graphics类提供了绘图常用的方法,利用这些方法可以实现直线、矩形、多边形、椭圆、圆弧等形状和文本、图片的绘制操作。另外,在执行这些操作之前,还可以使用相应的方法设置绘图的颜色和字体等状态属性。
Graphics2D类
使用Graphics类可以完成简单的图形绘制任务,但是它所实现的功能非常有限,如无法改变线条的粗细、不能对图片使用旋转和模糊等过滤效果。
Graphics2D类继承Graphics类,实现了功能更加强大的绘图操作的集合。由于Graphics2D类是Graphics类的扩展,也是推荐使用的Java绘图类
Graphics2D类继承Graphics类,实现了功能更加强大的绘图操作的集合。由于Graphics2D类是Graphics类的扩展,也是推荐使用的Java绘图类
绘制图形
Java可以分别使用Graphics类和Graphics2D类绘制图形,Graphics类使用不同的方法实现不同图形的绘制。例如,drawLine()方法可以绘制直线,drawRect()方法用于绘制矩形,drawOval()方法用于绘制椭圆形等。
Graphics2D类是在继承Graphics类的基础上编写的,它包含了Graphics类的绘图方法并添加了更强的功能,在创建绘图类时推荐使用该类。Graphics2D类可以分别使用不同的类来表示不同的形状,如Line2D类、Rectangle2D类等。
要绘制指定形状的图形,需要先创建并初始化该图形类的对象,且这些图形类必须是Shape接口的实现类;然后使用Graphics2D类的draw()方法绘制该图形对象,或者使用fill()方法填充该图形对象。语法格式如下:
draw(Shape form)或fill(Shape form)
其中,form是指实现Shape接口的对象。
java.awt.geom包中提供了如下常用的图形类,这些图形类都实现了Shape接口:
draw(Shape form)或fill(Shape form)
其中,form是指实现Shape接口的对象。
java.awt.geom包中提供了如下常用的图形类,这些图形类都实现了Shape接口:
- Arc2D类。
- CubicCurve2D类。
- Ellipse2D类。
- Line2D类。
- Point2D类。
- QuadCurve2D类。
- Rectangle2D类。
- RoundRectangle2D类。
绘图颜色与画笔属性
Java语言使用Color类封装颜色的各种属性,并对颜色进行管理。另外,在绘制图形时还可以指定线的粗细和虚实等画笔属性。
设置颜色
使用Color类可以创建任意颜色的对象,不用担心平台是否支持该颜色,因为Java以跨平台和与硬件无关的方式支持颜色管理。创建Color对象的构造方法有如下两种:
Color col = new Color(int r, int g, int b)
Color col = new Color(int rgb)
rgb:颜色值,该值是红、绿、蓝三原色的总和。
r: 该参数是三原色中红色的取值。
g:该参数是三原色中绿色的取值。
b:该参数是三原色中蓝色的取值。
Color col = new Color(int r, int g, int b)
Color col = new Color(int rgb)
rgb:颜色值,该值是红、绿、蓝三原色的总和。
r: 该参数是三原色中红色的取值。
g:该参数是三原色中绿色的取值。
b:该参数是三原色中蓝色的取值。
Color类定义了常用色彩的常量值,如表19.2所示。这些常量都是静态的Color对象,可以直接使用这些常量值定义的颜色对象。
设置画笔
默认情况下,Graphics类使用的画笔属性是粗细为1个像素的正方形,而Graphics2D类可以调用setStroke()方法设置画笔的属性,如改变线条的粗细、虚实,定义线段端点的形状、风格等。语法格式如下:
setStroke(Stroke stroke)
其中,参数stroke是Stroke接口的实现类对象。
setStroke()方法必须接受一个Stroke接口的实现类对象作参数,java.awt包中提供了BasicStroke类,它实现了Stroke接口,并且通过不同的构造方法创建画笔属性不同的对象。这些构造方法如下:
setStroke(Stroke stroke)
其中,参数stroke是Stroke接口的实现类对象。
setStroke()方法必须接受一个Stroke接口的实现类对象作参数,java.awt包中提供了BasicStroke类,它实现了Stroke接口,并且通过不同的构造方法创建画笔属性不同的对象。这些构造方法如下:
- BasicStroke()。
- BasicStroke(float width)。
- BasicStroke(float width, int cap, int join)。
- BasicStroke(float width, int cap, int join, float miterlimit)。
- BasicStroke(float width, int cap, int join, float miterlimit, float[] dash, float dash_phase)。
BasicStroke类构造方法的参数说明:
绘制文本
Java绘图类也可以用来绘制文本内容,且可以在绘制前设置字体的样式、大小等。
设置字体
Java使用Font类封装了字体的大小、样式等属性,该类在java.awt包中定义,其构造方法可以指定字体的名称、大小和样式3个属性。语法如下:
Font(String name, int style, int size)
name:字体的名称。
style:字体的样式。
size:字体的大小。
Font(String name, int style, int size)
name:字体的名称。
style:字体的样式。
size:字体的大小。
其中,字体样式可以使用Font类的PLAIN、BOLD和ITALIC常量
显示文字
Graphics2D类提供了drawString()方法,使用该方法可以实现图形上下文的文本绘制,从而实现在图片上显示文字的功能。语法格式有如下两种:
drawString(String str, int x, int y)
drawString(String str, float x, float y)
str:要绘制的文本字符串。
x:绘制字符串的水平起始位置。
y:绘制字符串的垂直起始位置。
这两个方法唯一不同的就是x和y的参数类型不同。
drawString(String str, int x, int y)
drawString(String str, float x, float y)
str:要绘制的文本字符串。
x:绘制字符串的水平起始位置。
y:绘制字符串的垂直起始位置。
这两个方法唯一不同的就是x和y的参数类型不同。
显示图片
绘图类不仅可以绘制图形和文本,还可以使用drawImage()方法将图片资源显示到绘图上下文中,而且可以实现各种特效处理,如图片的缩放、翻转等。
该方法将img图片显示在x、y指定的位置上。方法中涉及的参数说明
drawImage()方法的使用与绘制文本的drawString()方法类似,唯一不同的是该方法需要指定要通知的图像观察者。
图像处理
开发高级的桌面应用程序,必须掌握一些图像处理与动画制作的技术,如在程序中显示统计图、销售趋势图、动态按钮等。
放大与缩小
使用了drawImage()方法将图片以原始大小显示在窗体中,要想实现图片的放大与缩小,则需要使用它的重载方法。语法如下:
drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)
该方法将img图片显示在x、y指定的位置上,并指定图片的宽度和高度属性
drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)
该方法将img图片显示在x、y指定的位置上,并指定图片的宽度和高度属性
图像翻转
图像的翻转需要使用drawImage()方法的另一个重载方法。语法如下:
drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer)
此方法总是用非缩放的图像来呈现缩放的矩形,并动态地执行所需的缩放。此操作不使用缓存的缩放图像。执行图像从源到目标的缩放,要将源矩形的第一个坐标映射到目标矩形的第一个坐标,源矩形的第二个坐标映射到目标矩形的第二个坐标,按需要缩放和翻转子图像,以保持这些映射关系。
drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer)
此方法总是用非缩放的图像来呈现缩放的矩形,并动态地执行所需的缩放。此操作不使用缓存的缩放图像。执行图像从源到目标的缩放,要将源矩形的第一个坐标映射到目标矩形的第一个坐标,源矩形的第二个坐标映射到目标矩形的第二个坐标,按需要缩放和翻转子图像,以保持这些映射关系。
图像旋转
图像旋转需要调用Graphics2D类的rotate()方法,该方法将根据指定的弧度旋转图像。语法如下:
rotate(double theta)
其中,theta是指旋转的弧度。
rotate(double theta)
其中,theta是指旋转的弧度。
图像倾斜
可以使用Graphics2D类提供的shear()方法设置绘图的倾斜方向,从而使图像实现倾斜的效果。语法如下:
shear(double shx, double shy)
shx:水平方向的倾斜量。
shy:垂直方向的倾斜量。
shear(double shx, double shy)
shx:水平方向的倾斜量。
shy:垂直方向的倾斜量。
多线程
Windows操作系统是多任务操作系统,它以进程为单位。一个进程是一个包含有自身地址的程序,每个独立执行的程序都称为进程。也就是说每个正在执行的程序都是一个进程。系统可以分配给每一个进程有一段有限的使用CPU的时间(也可以称为CPU时间片),CPU在这段时间中执行某个进程,然后下一个时间片又跳至另一个进程中去执行。由于CPU转换比较快,所以使得每个进程好像是同时执行一样。
下图表明了Windows操作系统的执行模式。
下图表明了Windows操作系统的执行模式。
创建线程
继承Thread类
Thread 类是java.lang包中的一个类,从这个类中实例化的对象代表线程,程序员启动一个新线程需要建立Thread实例。Thread类中常用的两个构造方法如下:
public Thread():创建一个新的线程对象。
public Thread(String threadName):创建一个名称为threadName的线程对象。
继承Thread类创建一个新的线程的语法如下:
public class ThreadTest extends Threadf {
}
public Thread():创建一个新的线程对象。
public Thread(String threadName):创建一个名称为threadName的线程对象。
继承Thread类创建一个新的线程的语法如下:
public class ThreadTest extends Threadf {
}
实现Runnable接口
实现Runnable接口的语法如下:
public class Thread extends Object implements Runnable
实现Runnable 接口的程序会创建一个Thread对象,并将 Runnable 对象与Thread对象相关联。Thread类中有以下两个构造方法:
public class Thread extends Object implements Runnable
实现Runnable 接口的程序会创建一个Thread对象,并将 Runnable 对象与Thread对象相关联。Thread类中有以下两个构造方法:
- public Thread(Runnable target)
- public Thread(Runnable target,String name)
图20.2表明了实现Runnable接口创建线程的流程。
线程的生命周期
虽然多线程看起来像同时执行,但事实上在同一时间点上只有一个线程被执行,只是线程之间切换较快,所以才会使人产生线程是同时进行的假象。在Windows操作系统中,系统会为每个线程分配一小段CPU时间片,一旦 CPU时间片结束就会将当前线程换为下一个线程,即使该线程没有结束。要使线程处于就绪状态,有以下几种方法:
- 调用sleep()方法
- 调用wait()方法。
- 等待输入/输出完成。
操作线程的方法
操作线程有很多方法,这些方法可以使线程从某一种状态过渡到另一种状态
.线程的休眠
一种能控制线程行为的方法是调用sleep()方法,sleep()方法需要一个参数用于指定该线程休眠的时间,该时间以毫秒为单位。sleep()方法语法如下:
try{
Thread.sleep(2000);
}catch(InterruptedException e){
e.printStackTrace(();
}
try{
Thread.sleep(2000);
}catch(InterruptedException e){
e.printStackTrace(();
}
线程的加入
当某一个线程使用join()方法加入另外一个线程时,另一个线程会等待该线程执行完毕后再继续执行。
线程的中断
以往有的时候会使用stop()方法停止线程,但当前版本的JDK早己废除了stop()方法,不建议使用stop()方法来停止一个线程的运行。现在提倡在run()方法中使用无限循环的形式,然后使用一个布尔型标记控制循环的停止。
如果线程是因为使用了slcep()或wait()方法进入了就绪状态,可以使用Thread 类中 interrupt()方法使线程离开run()方法,同时结束线程,但程序会抛出InterruptedException异常,用户可以在处理该异常时完成线程的中断业务处理,如终止while循环。
如果线程是因为使用了slcep()或wait()方法进入了就绪状态,可以使用Thread 类中 interrupt()方法使线程离开run()方法,同时结束线程,但程序会抛出InterruptedException异常,用户可以在处理该异常时完成线程的中断业务处理,如终止while循环。
线程的优先级
每个线程都具有各自的优先级,线程的优先级可以表明在程序中该线程的重要性,如果有很多线程处于就绪状态,系统会根据优先级来决定首先使哪个线程进入运行状态。但这并不意味着低优先级的线程得不到运行,而只是它运行的概率比较小,如垃圾回收线程的优先级就较低。
Thread类中包含的成员变量代表了线程的某些优先级,如Thread.MIN_PRIORITY(常数1)、Thread.MAXPRIORITY(常数10)、Thread.NORM PRIORITY(常数5)。其中,每个线程的优先级都在 Thread.MIN_PRIORITY~Thread.MAX_PRIORITY,在默认情况下其优先级都是Thread.NORM PRIORITY。每个新产生的线程都继承了父线程的优先级。
在多任务操作系统中,每个线程都会得到一小段CPU时间片运行,在时间结束时,将轮换另一个线程进入运行状态,这时系统会选择与当前线程优先级相同的线程予以运行。系统始终选择就绪状态下优先级较高的线程进入运行状态。处于各个优先级状态下的线程的运行顺序如图20.8所示。
Thread类中包含的成员变量代表了线程的某些优先级,如Thread.MIN_PRIORITY(常数1)、Thread.MAXPRIORITY(常数10)、Thread.NORM PRIORITY(常数5)。其中,每个线程的优先级都在 Thread.MIN_PRIORITY~Thread.MAX_PRIORITY,在默认情况下其优先级都是Thread.NORM PRIORITY。每个新产生的线程都继承了父线程的优先级。
在多任务操作系统中,每个线程都会得到一小段CPU时间片运行,在时间结束时,将轮换另一个线程进入运行状态,这时系统会选择与当前线程优先级相同的线程予以运行。系统始终选择就绪状态下优先级较高的线程进入运行状态。处于各个优先级状态下的线程的运行顺序如图20.8所示。
在图20.8中,优先级为5的线程A首先得到CPU时间片;当该时间结束后,轮换到与线程A相同优先级的线程B;当线程 B的运行时间结束后,会继续轮换到线程A,直到线程A与线程B都执行完毕,才会轮换到线程C;当线程C结束后,才会轮换到线程D。
线程的优先级可以使用 setPriority()方法调整,如果使用该方法设置的优先级不在1~10,将产生IllegalArgumentException异常。
线程的优先级可以使用 setPriority()方法调整,如果使用该方法设置的优先级不在1~10,将产生IllegalArgumentException异常。
线程的礼让
Thread 类中提供了一种礼让方法,使用yield()方法表示,它只是给当前正处于运行状态的线程个提醒,告知它可以将资源礼让给其他线程,但这仅是一种暗示,没有任何一种机制保证当前线程会将资源礼让。
yield()方法使具有同样优先级的线程有进入可执行状态的机会,在当前线程放弃执行权时会再度回到就绪状体。对于支持多任务的操作系统来说,不需要调用yield()方法,因为操作系统会为线程自动分配CPU时间片来执行。
yield()方法使具有同样优先级的线程有进入可执行状态的机会,在当前线程放弃执行权时会再度回到就绪状体。对于支持多任务的操作系统来说,不需要调用yield()方法,因为操作系统会为线程自动分配CPU时间片来执行。
线程同步
在单线程程序中,每次只能做一件事情,后面的事情需要等待前面的事情完成后才可以进行,但是如果使用多线程程序,就会发生两个线程抢占资源的问题,如两个人同时说话、两个人同时过同个独木桥等。所以,在多线程编程中需要防止这些资源访问的冲突。Java 提供了线程同步的机制来防止资源访问的冲突。
线程安全
实际开发中,使用多线程程序的情况很多,如银行排号系统、火车站售票系统等。这种多线程的程序通常会发生问题,以火车站售票系统为例,在代码中判断当前票数是否大于0,如果大于0则执行将该票出售给乘客的功能,但当两个线程同时访问这段代码时(假如这时只剩下一张票),第一个线程将票售出,与此同时第二个线程也已经执行完成判断是否有票的操作,并得出票数大于0的结论,于是它也执行售出操作,这样就会产生负数。所以,在编写多线程程序时,应该考虑到线程安全问题。实质上线程安全问题来源于两个线程同时存取单一对象的数据。
线程同步机制
同步块
Java 中提供了同步机制,可以有效地防止资源冲突。同步机制使用synchronized关键字,使用该关键字包含的代码块称为同步块,也称为临界区,语法如下:
synchronized(Object) {
}
通常将共享资源的操作放置在 synchronized 定义的区域内,这样当其他线程获取到这个锁时,就必须等待锁被释放后才可以进入该区域。Object 为任意一个对象,每个对象都存在一个标志位,并具有两个值,分别为0和1。一个线程运行到同步块时首先检查该对象的标志位,如果为0状态,表明此同步块内存在其他线程,这时当期线程处于就绪状态,直到处于同步块中的线程执行完同步块中的代码后,这时该对象的标识位设置为1,当期线程才能开始执行同步块中的代码,并将Object对象的标识位设置为0,以防止其他线程执行同步块中的代码。
synchronized(Object) {
}
通常将共享资源的操作放置在 synchronized 定义的区域内,这样当其他线程获取到这个锁时,就必须等待锁被释放后才可以进入该区域。Object 为任意一个对象,每个对象都存在一个标志位,并具有两个值,分别为0和1。一个线程运行到同步块时首先检查该对象的标志位,如果为0状态,表明此同步块内存在其他线程,这时当期线程处于就绪状态,直到处于同步块中的线程执行完同步块中的代码后,这时该对象的标识位设置为1,当期线程才能开始执行同步块中的代码,并将Object对象的标识位设置为0,以防止其他线程执行同步块中的代码。
同步方法
同步方法就是在方法前面用synchronized关键字修饰的方法,其语法如下:
synchronized void f(){}
当某个对象调用了同步方法时,该对象上的其他同步方法必须等待该同步方法执行完毕后才能被执行。必须将每个能访问共享资源的方法修饰为synchronized,否则就会出错。
synchronized void f(){}
当某个对象调用了同步方法时,该对象上的其他同步方法必须等待该同步方法执行完毕后才能被执行。必须将每个能访问共享资源的方法修饰为synchronized,否则就会出错。
网络通信
计算机网络实现了多台计算机间的互联,使得它们彼此之间能够进行数据交流。网络应用程序就是在已连接的不同计算机上运行的程序,这些程序借助于网络协议,相互之间可以交换数据。编写网络应用程序前,首先必须明确所要使用的网络协议。TCP/IP协议是网络应用程序的首选
网络程序设计基础
网络程序设计编写的是与其他计算机进行通信的程序。Java已经将网络程序所需要的元素封装成不同的类,用户只要创建这些类的对象,使用相应的方法,即使不具备有关的网络知识,也可以编写出高质量的网络通信程序。
局域网与互联网
为了实现两台计算机的通信,必须用一个网络线路连接两台计算机
服务器是指提供信息的计算机或程序,客户机是指请求信息的计算机或程序。网络用于连接服务器与客户机,实现两者间的相互通信。但是,有时在某个网络中很难将服务器与客户机区分开。我们通常所说的局域网(Local Area Network,LAN),就是一群通过一定形式连接起来的计算机,它可以由两台计算机组成,也可以由同一区域内的上千台计算机组成。将LAN延伸到更大的范围,这样的网络称为广域网(Wide Area Network,WAN)。我们熟悉的互联网(Internet),就是由无数的LAN和WAN组成的。
网络协议
网络协议规定了计算机之间连接的物理、机械(网线与网卡的连接规定)、电气(有效的电平范围)等特征,计算机之间的相互寻址规则,数据发送冲突的解决方式,长数据如何分段传送与接收等内容。就像不同的国家有不同的法律一样,目前网络协议也有多种。下面简单地介绍几个常用的网络协议。
IP协议
IP是Internet Protocol的简称,是一种网络协议。Internet网络采用的协议是TCP/IP协议,其全称是Transmission Control Protocol/Internet Protocol。Internet依靠TCP/IP协议,在全球范围内实现了不同硬件结构、不同操作系统、不同网络系统间的互联。在Internet网络上存在着数以亿计的主机,每台主机都用网络为其分配的Internet地址代表自己,这个地址就是IP地址。到目前为止,IP地址用4个字节,也就是32位的二进制数来表示,称为IPv4。为了便于使用,通常取用每个字节的十进制数,并且每个字节之间用圆点隔开来表示IP地址,如192.168.1.1。现在人们正在试验使用16个字节来表示IP地址,这就是IPv6,但IPv6还没有投入使用。
|TCP/IP模式是一种层次结构,共分为4层,分别为应用层、传输层、互联网层和网络层。各层实现特定的功能,提供特定的服务和访问接口,并具有相对的独立性。
|TCP/IP模式是一种层次结构,共分为4层,分别为应用层、传输层、互联网层和网络层。各层实现特定的功能,提供特定的服务和访问接口,并具有相对的独立性。
TCP与UDP协议
在TCP/IP协议栈中,有两个高级协议是网络应用程序编写者应该了解的,即传输控制协议(Transmission Control Protocol,TCP)与用户数据报协议(User Datagram Protocol,UDP)。
TCP协议是一种以固接连线为基础的协议,它提供两台计算机间可靠的数据传送。TCP可以保证数据从一端送至连接的另一端时,能够确实送达,而且抵达的数据的排列顺序和送出时的顺序相同。因此,TCP协议适合可靠性要求比较高的场合。就像拨打电话,必须先拨号给对方,等两端确定连接后,相互才能听到对方说话,也知道对方回应的是什么。
HTTP、FTP和Telnet等都需要使用可靠的通信频道。例如,HTTP从某个URL读取数据时,如果收到的数据顺序与发送时不相同,可能就会出现一个混乱的HTML文件或是一些无效的信息。
UDP是无连接通信协议,不保证数据的可靠传输,但能够向若干个目标发送数据,或接收来自若干个源的数据。UDP以独立发送数据包的方式进行。这种方式就像邮递员送信给收信人,可以寄出很多信给同一个人,且每一封信都是相对独立的,各封信送达的顺序并不重要,收信人接收信件的顺序也不能保证与寄出信件的顺序相同。
UDP协议适合于一些对数据准确性要求不高,但对传输速度和时效性要求非常高的网站,如网络聊天室、在线影片等。这是由于TCP协议在认证上存在额外耗费,可能使传输速度减慢,而UDP协议即使有一小部分数据包遗失或传送顺序有所不同,也不会严重危害该项通信。
TCP协议是一种以固接连线为基础的协议,它提供两台计算机间可靠的数据传送。TCP可以保证数据从一端送至连接的另一端时,能够确实送达,而且抵达的数据的排列顺序和送出时的顺序相同。因此,TCP协议适合可靠性要求比较高的场合。就像拨打电话,必须先拨号给对方,等两端确定连接后,相互才能听到对方说话,也知道对方回应的是什么。
HTTP、FTP和Telnet等都需要使用可靠的通信频道。例如,HTTP从某个URL读取数据时,如果收到的数据顺序与发送时不相同,可能就会出现一个混乱的HTML文件或是一些无效的信息。
UDP是无连接通信协议,不保证数据的可靠传输,但能够向若干个目标发送数据,或接收来自若干个源的数据。UDP以独立发送数据包的方式进行。这种方式就像邮递员送信给收信人,可以寄出很多信给同一个人,且每一封信都是相对独立的,各封信送达的顺序并不重要,收信人接收信件的顺序也不能保证与寄出信件的顺序相同。
UDP协议适合于一些对数据准确性要求不高,但对传输速度和时效性要求非常高的网站,如网络聊天室、在线影片等。这是由于TCP协议在认证上存在额外耗费,可能使传输速度减慢,而UDP协议即使有一小部分数据包遗失或传送顺序有所不同,也不会严重危害该项通信。
端口与套接字
一般而言,一台计算机只有单一的连到网络的物理连接(Physical Connection),所有的数据都通过此连接对内、对外送达特定的计算机,这就是端口。网络程序设计中的端口(port)并非真实的物理存在,而是一个假想的连接装置。端口被规定为一个在0~65535的整数。HTTP服务一般使用80端口,FTP服务使用21端口。假如一台计算机提供了HTTP、FTP等多种服务,那么客户机会通过不同的端口来确定连接到服务器的哪项服务上,如图21.3所示。
通常,0~1023的端口数用于一些知名的网络服务和应用,用户的普通网络应用程序应该使用1024以上的端口数,以避免端口号与另一个应用或系统服务所用端口冲突。
网络程序中的套接字(Socket)用于将应用程序与端口连接起来。套接字是一个假想的连接装置,就像插座一样可连接电器与电线,如图21.4所示。Java将套接字抽象化为类,程序设计者只需创建Socket类对象,即可使用套接字。
通常,0~1023的端口数用于一些知名的网络服务和应用,用户的普通网络应用程序应该使用1024以上的端口数,以避免端口号与另一个应用或系统服务所用端口冲突。
网络程序中的套接字(Socket)用于将应用程序与端口连接起来。套接字是一个假想的连接装置,就像插座一样可连接电器与电线,如图21.4所示。Java将套接字抽象化为类,程序设计者只需创建Socket类对象,即可使用套接字。
TCP程序
TCP网络程序设计是指利用Socket类编写通信程序。利用TCP协议进行通信的两个应用程序是有主次之分的,一个称为服务器程序,另一个称为客户机程序,两者的功能和编写方法大不一样。
InetAddress类
java.net包中的InetAddress类是与IP地址相关的类,利用该类可以获取IP地址、主机地址等信息。
ServerSocket类
ServerSocket类的构造方法通常会抛出IOException异常,具体有以下几种形式:
调用ServerSocket类的accept()方法,会返回一个和客户端Socket对象相连接的Socket对象。服务器端的Socket对象使用getOutputStream()方法获得的输出流,将指向客户端Socket对象使用getInputStream()方法获得的那个输入流;同样,服务器端的Socket对象使用getInputStream()方法获得的输入流,将指向客户端Socket对象使用getOutputStream()方法获得的那个输出流。也就是说,当服务器向输出流写入信息时,客户端通过相应的输入流就能读取,反之亦然。
- ServerSocket():创建非绑定服务器套接字。
- ServerSocket(int port):创建绑定到特定端口的服务器套接字。
- ServerSocket(int port, int backlog):利用指定的backlog创建服务器套接字,并将其绑定到指定的本地端口号上。
- ServerSocket(int port, int backlog, InetAddress bindAddress):使用指定的端口、侦听backlog和要绑定到的本地IP地址创建服务器。这种情况适用于计算机上有多块网卡和多个IP地址的情况,用户可以明确规定ServerSocket在哪块网卡或哪个IP地址上等待客户的连接请求。
调用ServerSocket类的accept()方法,会返回一个和客户端Socket对象相连接的Socket对象。服务器端的Socket对象使用getOutputStream()方法获得的输出流,将指向客户端Socket对象使用getInputStream()方法获得的那个输入流;同样,服务器端的Socket对象使用getInputStream()方法获得的输入流,将指向客户端Socket对象使用getOutputStream()方法获得的那个输出流。也就是说,当服务器向输出流写入信息时,客户端通过相应的输入流就能读取,反之亦然。
java.net包中的ServerSocket类用于表示服务器套接字,其主要功能是等待来自网络上的“请求”,它可通过指定的端口来等待连接的套接字。服务器套接字一次可以与一个套接字连接。如果多台客户机同时提出连接请求,服务器套接字会将请求连接的客户机存入列队中,然后从中取出一个套接字,与服务器新建的套接字连接起来。若请求连接数大于最大容纳数,则多出的连接请求被拒绝。队列的默认大小是50。
TCP网络程序设计
明白了TCP程序工作的过程,就可以编写TCP服务器程序了。在网络编程中,如果只要求客户机向服务器发送消息,不要求服务器向客户机发送消息,称为单向通信。客户机套接字
和服务器套接字连接成功后,客户机通过输出流发送数据,服务器则通过输入流接收数据。
和服务器套接字连接成功后,客户机通过输出流发送数据,服务器则通过输入流接收数据。
UDP程序
用户数据报协议(UDP)是网络信息传输的另一种形式。基于UDP的通信和基于TCP的通信不同,基于UDP的信息传递更快,但不提供可靠性保证。使用UDP传递数据时,用户无法知道数据能否正确地到达主机,也不能确定到达目的地的顺序是否和发送的顺序相同。虽然UDP是一种不可靠的协议,但如果需要较快地传输信息,并能容忍小的错误,可以考虑使用UDP。
基于UDP通信的基本模式如下:
(1)使用DatagramSocket()创建一个数据包套接字。
(2)使用DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)创建要发送的数据包。
(3)使用DatagramSocket类的send()方法发送数据包。
接收数据包的步骤如下:
- 将数据打包(称为数据包),然后将数据包发往目的地。
- 接收别人发来的数据包,然后查看数据包。
(1)使用DatagramSocket()创建一个数据包套接字。
(2)使用DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)创建要发送的数据包。
(3)使用DatagramSocket类的send()方法发送数据包。
接收数据包的步骤如下:
- 使用DatagramSocket(int port)创建数据包套接字,绑定到指定的端口。
- 使用DatagramPacket(byte[] buf, int length)创建字节数组来接收数据包。
- 使用DatagramPacket类的receive()方法接收UDP包。
DatagramPacket类
java.net包的DatagramPacket类用来表示数据包。DatagramPacket类的构造方法如下:
DatagramPacket(byte[] buf, int length)。
DatagramPacket(byte[] buf, int length, InetAddress address, int port)。
第一种构造方法在创建DatagramPacket对象时,指定了数据包的内存空间和大小。第二种构造方法不仅指定了数据包的内存空间和大小,还指定了数据包的目标地址和端口。在发
送数据时,必须指定接收方的Socket地址和端口号,因此使用第二种构造方法可创建发送数据的DatagramPacket对象。
DatagramPacket(byte[] buf, int length)。
DatagramPacket(byte[] buf, int length, InetAddress address, int port)。
第一种构造方法在创建DatagramPacket对象时,指定了数据包的内存空间和大小。第二种构造方法不仅指定了数据包的内存空间和大小,还指定了数据包的目标地址和端口。在发
送数据时,必须指定接收方的Socket地址和端口号,因此使用第二种构造方法可创建发送数据的DatagramPacket对象。
DatagramSocket类
java.net包中的DatagramSocket类用于表示发送和接收数据包的套接字。该类的构造方法如下:
如果接收数据时必须指定一个端口号,不允许系统随机产生,此时可以使用第二种构造方法。比如有个朋友要你给他写信,那他的地址就必须确定,不确定是不行的。在发送数据时通常使用第一种构造方法,不指定端口号,而是系统为我们分配一个端口号,就像寄信不需要到指定的邮局去寄一样。
- DatagramSocket()。
- DatagramSocket(int port)。
- DatagramSocket(int port, InetAddress addr)。
如果接收数据时必须指定一个端口号,不允许系统随机产生,此时可以使用第二种构造方法。比如有个朋友要你给他写信,那他的地址就必须确定,不确定是不行的。在发送数据时通常使用第一种构造方法,不指定端口号,而是系统为我们分配一个端口号,就像寄信不需要到指定的邮局去寄一样。
UDP网络程序设计
根据前面所讲的网络编程的基本知识以及UDP网络编程的特点,下面创建一个广播数据报程序。广播数据报是一项较新的技术,其原理类似于电台广播。广播电台需要在指定的波段和频率上广播信息,收听者也要将收音机调到指定的波段、频率,才可以收听广播内容。
创建UDP协议广播电台程序
package wanluo;
import java.io.IOException;
import java.net.*;
public class Notification extends Thread{
String weather = "节日预报:八点有大型晚会,请收听";//发送的消息
int port = 9898;//端口
InetAddress iaddress = null;
MulticastSocket socket = null;//多点广播套接字
@SuppressWarnings("deprecation")
Notification(){
try {
iaddress = InetAddress.getByName("224.255.10.0");//广播组地址
socket = new MulticastSocket(port);//实例化多点广播套接字
socket.setTimeToLive(1);//指定发送范围是本地网络
socket.joinGroup(iaddress);//加入广播组
}catch(IOException e) {
e.printStackTrace();//输出异常信息
}
}
public void run() {
while(true) {
DatagramPacket packet = null;//数据包
byte data[] = weather.getBytes();//字符串消息的字节数组
packet = new DatagramPacket(data,data.length,iaddress,port);//将数据打包
System.out.println(weather);//控制台打印消息
try {
socket.send(packet);//发送数据
sleep(3000);//线程休眠
}catch(IOException e){
e.printStackTrace();
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Notification w = new Notification();
w.start();//启动线程
}
}
package wanluo;
import java.io.IOException;
import java.net.*;
public class Notification extends Thread{
String weather = "节日预报:八点有大型晚会,请收听";//发送的消息
int port = 9898;//端口
InetAddress iaddress = null;
MulticastSocket socket = null;//多点广播套接字
@SuppressWarnings("deprecation")
Notification(){
try {
iaddress = InetAddress.getByName("224.255.10.0");//广播组地址
socket = new MulticastSocket(port);//实例化多点广播套接字
socket.setTimeToLive(1);//指定发送范围是本地网络
socket.joinGroup(iaddress);//加入广播组
}catch(IOException e) {
e.printStackTrace();//输出异常信息
}
}
public void run() {
while(true) {
DatagramPacket packet = null;//数据包
byte data[] = weather.getBytes();//字符串消息的字节数组
packet = new DatagramPacket(data,data.length,iaddress,port);//将数据打包
System.out.println(weather);//控制台打印消息
try {
socket.send(packet);//发送数据
sleep(3000);//线程休眠
}catch(IOException e){
e.printStackTrace();
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Notification w = new Notification();
w.start();//启动线程
}
}
接收广播程序。单击“开始接收”按钮,系统开始接收主机播出的信息;单击“停止接收”按钮,系统停止接收广播主机播出的信息。代码如下:
package wanluo;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.net.*;
import javax.swing.*;
public class Receive extends JFrame implements Runnable,ActionListener{
int port;//端口
InetAddress group = null;//广播组地址
MulticastSocket socket = null;//多点广播套接字对象
JButton inceBtn = new JButton("开始接收");
JButton stopBtn = new JButton("停止接收");
JTextArea inceAr = new JTextArea(10,10);//显示接收广播的文本域
JTextArea inced = new JTextArea(10,10);
Thread thread;
boolean stop = false;//停止接收信息状态
public Receive() {
setTitle("广播数据报");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
thread = new Thread(this);
inceBtn.addActionListener(this);//绑定按钮ince的单击事件
stopBtn.addActionListener(this);//绑定按钮stop的单击事件
inceAr.setForeground(Color.blue);//指定文本域中文字的颜色
JPanel north = new JPanel();
north.add(inceBtn);//将按钮添加到面板north上
north.add(stopBtn);
add(north,BorderLayout.NORTH);//将north放置在窗体的上部
JPanel center = new JPanel();//创建面板对象center
center.setLayout(new GridLayout(1,2));//设置面板布局
center.add(inceAr);//将文本域添加到面板上
center.add(inced);
add(center,BorderLayout.CENTER);//设置面板布局
validate();//刷新
port = 9898;//设置端口号
try {
group = InetAddress.getByName("224.255.10.0");//指定接收地址
socket = new MulticastSocket(port);//绑定多点广播套接字
socket.joinGroup(group);//加入广播组
}catch(IOException e){
e.printStackTrace();//输出异常信息
}
setBounds(100,50,360,380);//设置布局
setVisible(true);//将窗体设置为显示状态
}
public void run() {//run()方法
while(!stop) {
byte data[] = new byte[1024];//创建缓存字节数组
DatagramPacket packet = null;
packet = new DatagramPacket(data,data.length,group,port);//待接收的数据包
try {
socket.receive(packet);//接收数据包
String message = new String(packet.getData(),0,packet.getLength());
inceAr.setText("正在接收的内容:\n" +message);//将接收内容显示在文本域中
inceAr.append(message + "\n");//每条信息为一行
}catch(IOException e){
e.printStackTrace();//输出异常信息
}
}
}
public void actionPerformed(ActionEvent e) {//单击按钮ince触发的事件
if(e.getSource() == inceBtn) {
inceBtn.setBackground(Color.red);//设置按钮颜色
stopBtn.setBackground(Color.yellow);
if(!(thread.isAlive())) {//如线程不处于"新建状态"
thread = new Thread(this);//实例化Thread对象
}
thread.start();//启动线程
stop = false;//开始接收信息
}
if(e.getSource() == stopBtn) {//单击按钮stop触发的事件
inceBtn.setBackground(Color.yellow);//设置按钮颜色
inceBtn.setBackground(Color.red);
stop = true;//停止接收信息
}
}
public static void main(String[] args) {
Receive rec = new Receive();
rec.setSize(460,200);
}
}
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.net.*;
import javax.swing.*;
public class Receive extends JFrame implements Runnable,ActionListener{
int port;//端口
InetAddress group = null;//广播组地址
MulticastSocket socket = null;//多点广播套接字对象
JButton inceBtn = new JButton("开始接收");
JButton stopBtn = new JButton("停止接收");
JTextArea inceAr = new JTextArea(10,10);//显示接收广播的文本域
JTextArea inced = new JTextArea(10,10);
Thread thread;
boolean stop = false;//停止接收信息状态
public Receive() {
setTitle("广播数据报");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
thread = new Thread(this);
inceBtn.addActionListener(this);//绑定按钮ince的单击事件
stopBtn.addActionListener(this);//绑定按钮stop的单击事件
inceAr.setForeground(Color.blue);//指定文本域中文字的颜色
JPanel north = new JPanel();
north.add(inceBtn);//将按钮添加到面板north上
north.add(stopBtn);
add(north,BorderLayout.NORTH);//将north放置在窗体的上部
JPanel center = new JPanel();//创建面板对象center
center.setLayout(new GridLayout(1,2));//设置面板布局
center.add(inceAr);//将文本域添加到面板上
center.add(inced);
add(center,BorderLayout.CENTER);//设置面板布局
validate();//刷新
port = 9898;//设置端口号
try {
group = InetAddress.getByName("224.255.10.0");//指定接收地址
socket = new MulticastSocket(port);//绑定多点广播套接字
socket.joinGroup(group);//加入广播组
}catch(IOException e){
e.printStackTrace();//输出异常信息
}
setBounds(100,50,360,380);//设置布局
setVisible(true);//将窗体设置为显示状态
}
public void run() {//run()方法
while(!stop) {
byte data[] = new byte[1024];//创建缓存字节数组
DatagramPacket packet = null;
packet = new DatagramPacket(data,data.length,group,port);//待接收的数据包
try {
socket.receive(packet);//接收数据包
String message = new String(packet.getData(),0,packet.getLength());
inceAr.setText("正在接收的内容:\n" +message);//将接收内容显示在文本域中
inceAr.append(message + "\n");//每条信息为一行
}catch(IOException e){
e.printStackTrace();//输出异常信息
}
}
}
public void actionPerformed(ActionEvent e) {//单击按钮ince触发的事件
if(e.getSource() == inceBtn) {
inceBtn.setBackground(Color.red);//设置按钮颜色
stopBtn.setBackground(Color.yellow);
if(!(thread.isAlive())) {//如线程不处于"新建状态"
thread = new Thread(this);//实例化Thread对象
}
thread.start();//启动线程
stop = false;//开始接收信息
}
if(e.getSource() == stopBtn) {//单击按钮stop触发的事件
inceBtn.setBackground(Color.yellow);//设置按钮颜色
inceBtn.setBackground(Color.red);
stop = true;//停止接收信息
}
}
public static void main(String[] args) {
Receive rec = new Receive();
rec.setSize(460,200);
}
}
结果如下
发出广播和接收广播的主机地址必须位于同一个组内,地址范围为224.0.0.0~224.255.255.255,该地址并不代表某个特定主机的位置。加入同一个组的主机可以在某个端口上广播信息,也可以在某个端口上接收信息。
收藏
0 条评论
下一页