【计理01组04号】JDK基础入门

java.lang包

java.lang包装类

我们都知道 java 是一门面向对象的语言,类将方法和属性封装起来,这样就可以创建和处理相同方法和属性的对象了。但是 java 中的基本数据类型却不是面向对象的,不能定义基本类型的对象。那如果我们想像处理对象的方式处理基本类型的数据,调用一些方法怎么办呢?

其实 java 为每个基本类型都提供了包装类:

原始数据类型 包装类
byte(字节) Byte
char(字符) Character
int(整型) Integer
long (长整型) Long
float(浮点型) Float
double (双精度) Double
boolean (布尔) Boolean
short(短整型) Short

在这八个类名中,除了 Integer 和 Character 类以后,其它六个类的类名和基本数据类型一致,只是类名的第一个字母大写。

 

Integer 类

java.lang 包中的 Integer 类、Long 类和 Short 类都是 Number 的子类,他们的区别在于不同子类里面封装着不同的数据类型,比如 Integer 类包装了一个基本类型 int。其包含的方法基本相同。

我们以 Integer 类为例。 Integer 构造方法有两种:

  1. Integer(int value),以 int 型变量作为参数创建 Integer 对象。例如Integer a = new Integer(10);
  2. Integer(String s),以 String 型变量作为参数创建 Integer 对象,例如Integer a = new Integer("10")

下面列举一下 Integer 的常用方法:

方法 返回值 功能描述
byteValue() byte 以 byte 类型返回该 Integer 的值
compareTo(Integer anotherInteger) int 在数字上比较 Integer 对象。如果这两个值相等,则返回 0;如果调用对象的数值小于 anotherInteger 的数值,则返回负值;如果调用对象的数值大于 anotherInteger 的数值,则返回正值
equals(Object IntegerObj) boolean 比较此对象与指定对象是否相等
intValue() int 以 int 型返回此 Integer 对象
shortValue() short 以 short 型返回此 Integer 对象
longValue() long 以 long 型返回此 Integer 对象
floatValue() float 以 float 型返回此 Integer 对象
doubleValue() double 以 double 型返回此 Integer 对象
toString() String 返回一个表示该 Integer 值的 String 对象
valueOf(String str) Integer 返回保存指定的 String 值的 Integer 对象
parseInt(String str) int 将字符串参数作为有符号的十进制整数进行解析

 

Character 类

Character 类在对象中包装一个基本类型 char 的值。Character 类型的对象包含类型为 char 的单个字段。

Character 包装类的常用方法:

方法 返回值 说明
isDigit(char ch) boolean 确定字符是否为数字
isLetter(char ch) boolean 确定字符是否为字母
isLowerCase(char ch) boolean 确定字符是否为小写字母
isUpperCase(char ch) boolean 确定字符是否为大写字母
isWhitespace(char ch) boolean 确定字符是否为空白字符
isUnicodeIdentifierStart(char ch) boolean 确定是否允许将指定字符作为 Unicode 标识符中的首字符

 

Boolean 类

Boolean 类将基本类型为 boolean 的值包装在一个对象中。一个 Boolean 类型的对象只包含一个类型为 boolean 的字段。

Boolean 类的构造方法也有两个:

  1. Boolean(boolean value),创建一个表示 value 参数的 Boolean 对象,如 Boolean b = new Boolean(true)
  2. Boolean(String s),如果 String 参数不为 null 且在忽略大小写时等于 "true", 创建一个表示 true 值的 Boolean 对象,如 Boolean b = new Boolean("ok"),为 false。

Boolean 包装类的常用方法:

方法 返回值 说明
booleanValue() boolean 将 Boolean 对象的值以对应的 boolean 值返回
equals(Object obj) boolean 判断调用该方法的对象与 obj 是否相等。当且仅当参数不是 null,而且与调用该方法的对象一样都表示同一个 boolean 值的 Boolean 对象时,才返回 true
parseBoolean(String s) boolean 将字符串参数解析为 boolean 值
toString() String 返回表示该 boolean 值的 String 对象
valueOf(String s) Boolean 返回一个用指定得字符串表示值的 boolean 值

java.lang类String

计算字符串长度

length() 方法

字符串比较

equals() 方法,该方法的作用是判断两个字符串对象的内容是否相同。如果相同则返回 true,否则返回 false。

equals() 方法比较是从第一个字符开始,一个字符一个字符依次比较。

那如果我想忽略掉大小写关系,比如:java 和 Java 是一样的,我们怎么办呢?我们可以调用equalsIgnoreCase()方法,其用法与 equals 一致,不过它会忽视大小写。

字符串连接

字符串连接有两种方法:

  1. 使用+,比如 String s = "Hello " + "World!"
  2. 使用 String 类的 concat() 方法。

而且使用+进行连接,不仅可以连接字符串,也可以连接其他类型。但是要求进行连接时至少有一个参与连接的内容是字符串类型。

charAt() 方法

charAt() 方法的作用是按照索引值(规定字符串中第一个字符的索引值是 0,第二个字符的索引值是 1,依次类推),获得字符串中的指定字符。

字符串常用提取方法

方法 返回值 功能描述
indexOf(int ch) int 搜索字符 ch 第一次出现的索引
indexOf(String value) int 搜索字符串 value 第一次出现的索引
lastIndexOf(int ch) int 搜索字符 ch 最后一次出现的索引
lastIndexOf(String value) int 搜索字符串 value 最后一次出现的索引
substring(int index) String 提取从位置索引开始到结束的字符串
substring(int beginindex, int endindex) String 提取 beginindex 和 endindex 之间的字符串部分
trim() String 返回一个前后不含任何空格的调用字符串的副本

说明:在字符串中,第一个字符的索引为 0,子字符串包含 beginindex 的字符,但不包含 endindex 的字符。

StringBuffer

String 的不变性的机制显然会在 String 常量内有大量的冗余。比如我创建一个循环,使字符'1'依次连接到'n',那么系统就得创建 n+(n-1) 个 String 对象。那有没有可变的 String 类呢?

StringBuffer 类是可变的。它是 String 的对等类,它可以增加和编写字符的可变序列,并且能够将字符插入到字符串中间或附加到字符串末尾,当然是不用创建其他对象的,这里建议大家去看一看 String 类与 StringBuffer 类的区别,理解一下他们在内存中的存储情况。

先上 StringBuffer 的构造方法:

构造方法 说明
StringBuffer() 构造一个其中不带字符的字符串缓冲区,其初始容量为 16 个字符
StringBuffer(CharSequence seq) 构造一个字符串缓冲区,它包含与指定的 CharSequence 相同的字符
StringBuffer(int capacity) 构造一个不带字符,但具有指定初始容量的字符串缓冲区
StringBuffer(String str) 构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容

StringBuffer 类的常用方法:

方法 返回值 功能描述
insert(int offsetm,Object s) StringBuffer 在 offsetm 的位置插入字符串 s
append(Object s) StringBuffer 在字符串末尾追加字符串 s
length() int 确定 StringBuffer 对象的长度
setCharAt(int pos,char ch) void 使用 ch 指定的新值设置 pos 指定的位置上的字符
toString() String 转换为字符串形式
reverse() StringBuffer 反转字符串
delete(int start, int end) StringBuffer 删除调用对象中从 start 位置开始直到 end 指定的索引(end-1)位置的字符序列
replace(int start, int end, String s) StringBuffer 使用一组字符替换另一组字符。将用替换字符串从 start 指定的位置开始替换,直到 end 指定的位置结束

java.lang类Math

Math 类的简单使用

我们在编程的过程中,经常对一些数字进行数学操作,比如我们想要求绝对值或余弦什么的。那这些方法是否需要我们自己实现吗?其实是 java.lang 里的 Math 类。Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。我们就来学习一下吧。

先认识一些方法吧:

方法 返回值 功能描述
sin(double numvalue) double 计算角 numvalue 的正弦值
cos(double numvalue) double 计算角 numvalue 的余弦值
acos(double numvalue) double 计算 numvalue 的反余弦
asin(double numvalue) double 计算 numvalue 的反正弦
atan(double numvalue) double 计算 numvalue 的反正切
pow(double a, double b) double 计算 a 的 b 次方
sqrt(double numvalue) double 计算给定值的正平方根
abs(int numvalue) int 计算 int 类型值 numvalue 的绝对值,也接收 long、float 和 double 类型的参数
ceil(double numvalue) double 返回大于等于 numvalue 的最小整数值
floor(double numvalue) double 返回小于等于 numvalue 的最大整数值
max(int a, int b) int 返回 int 型 a 和 b 中的较大值,也接收 long、float 和 double 类型的参数
min(int a, int b) int 返回 a 和 b 中的较小值,也可接受 long、float 和 double 类型的参数
rint(double numvalue) double 返回最接近 numvalue 的整数值
round(T arg) arg 为 double 时返回 long,为 float 时返回 int 返回最接近 arg 的整数值
random() double 返回带正号的 double 值,该值大于等于 0.0 且小于 1.0

上面都是一些常用的方法,如果同学们以后还会用到极坐标、对数等,就去查一查手册吧。

java.lang类Class

Class 类的实例表示正在运行的 Java 应用程序中的类或接口。在 Java 中,每个 Class 都有一个相应的 Class 对象,即每一个类,在生成的 .class 文件中,就会产生一个 Class 对象,用于表示这个类的类型信息。我们获取 Class 实例有三种方法:

  1. 利用对象调用 getClass() 方法获取该对象的 Class 实例

  2. 使用 Class 类的静态方法 forName(String className),用类的名字获取一个 Class 实例

  3. 运用.class的方式来获取 Class 实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的 Class 实例

java.lang类Object

Object 类是所有类的父类,所有对象(包括数组)都实现这个类的方法。所以在默认的情况下,我们定义的类扩展自 Object 类,那我们当然可以调用和重写 Object 类里的所有方法了。

我们看一下 Object 类里都定义了哪些方法。

方法 返回值 功能描述
equals(Objectobj) boolean 将当前对象实例与给定的对象进行比较,检查它们是否相等
finalize() throws Throwable void 当垃圾回收器确定不存在对象的更多引用时,由对象的垃圾回收器调用此方法。通常被子类重写
getClass() Class 返回当前对象的 Class 对象
toString() String 返回此对象的字符串表示
wait() throws InterruptedException void 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,使当前线程进入等待状态

日期和随机数

Date类

Date 类表示日期和时间,里面封装了操作日期和时间的方法。Date 类经常用来获取系统当前时间。

我们来看看类 Date 中定义的未过时的构造方法:

构造方法 说明
Date() 构造一个 Date 对象并对其进行初始化以反映当前时间
Date(long date) 构造一个 Date 对象,并根据相对于 GMT 1970 年 1 月 1 日 00:00:00 的毫秒数对其进行初始化

Calendar类

在早期的 JDK 版本中,Date 类附有两大功能:

  1. 允许用年、月、日、时、分、秒来解释日期
  2. 允许对表示日期的字符串进行格式化和句法分析

在 JDK1.1 中提供了类 Calendar 来完成第一种功能,类 DateFormat 来完成第二项功能。DateFormat 是 java.text 包中的一个类。与 Date 类有所不同的是,DateFormat 类可以接受用各种语言和不同习惯表示的日期字符串。

但是 Calendar 类是一个抽象类,它完成 Date 类与普通日期表示法之间的转换,而我们更多的是使用 Calendar 类的子类 GregorianCalendar 类。它实现了世界上普遍使用的公历系统。当然我们也可以继承 Calendar 类,然后自己定义实现日历方法。

先来看一看 GregorianCalendar 类的构造函数:

构造方法 说明
GregorianCalendar() 创建的对象中的相关值被设置成指定时区,缺省地点的当前时间,即程序运行时所处的时区、地点的当前时间
GregorianCalendar(TimeZone zone) 创建的对象中的相关值被设置成指定时区 zone,缺省地点的当前时间
GregorianCalendar(Locale aLocale) 创建的对象中的相关值被设置成缺省时区,指定地点 aLocale 的当前时间
GregorianCalendar(TimeZone zone,Locale aLocale) year - 创建的对象中的相关值被设置成指定时区,指定地点的当前时间

TimeZone 是 java.util 包中的一个类,其中封装了有关时区的信息。每一个时区对应一组 ID。类 TimeZone 提供了一些方法完成时区与对应 ID 两者之间的转换。

java.time包

因为 java8 之前的日期和时间 api 饱受诟病,比如线程安全问题,比如 Date 的月份是从 0 开始的!而 java.time 包中将月份封装成为了枚举类型。接下来来看看如何使用这个新的时间报。

首先了解一下 LocalTime 类,LocalTime 类是一个不可变类(也就是用 final 修饰的类),和 String 类一样,所以它是线程安全的。除了 LocalTime 还有 LocalDate(日期)、LocalDateTime(日期和时间)等,他们的使用方式都差不多。

Random类

Java 实用工具类库中的类 java.util.Random 提供了产生各种类型随机数的方法。它可以产生 int、long、float、double 以及 Gaussian 等类型的随机数。这也是它与 java.lang.Math 中的方法 random() 最大的不同之处,后者只产生 double 型的随机数。

构造方法 说明
Random() 产生一个随机数需要基值,这里将系统时间作为 seed
Random(long seed) 使用单个 long 种子创建一个新的随机数生成器

集合框架

Collection接口

因为集合框架中的很多类功能是相似的,所以我们用接口来规范类。Collection 接口是 java 集合框架里的一个根接口。它也是 List、Set 和 Queue 接口的父接口。Collection 接口中定义了可用于操作 List、Set 和 Queue 的方法——增删改查。

方法 返回值 说明
add(E e) boolean 向 collection 的尾部追加指定的元素(可选操作)
addAll(Collection c) boolean 将指定 collection 中的所有元素都添加到此 collection 中(可选操作)
clear() void 移除此 collection 中的所有元素(可选操作)
contains(Object o) boolean 如果此 collection 包含指定的元素,则返回 true
containsAll(Collection c) boolean 如果此 collection 包含指定 collection 的所有元素,则返回 true
equals(Object o) boolean 比较此 collection 与指定对象是否相等
hashCode() int 返回此 collection 的哈希码值
isEmpty() boolean 如果此 collection 不包含元素,则返回 true
iterator() Iterator 返回在此 collection 的元素上进行迭代的迭代器
remove(Object o) boolean 移除此 collection 中出现的首个指定元素(可选操作)
removeAll(Collection c) boolean 移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)
retainAll(Collection c) boolean 仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)
size() int 返回此 collection 中的元素数
toArray() Object[] 返回包含此 collection 中所有元素的数组
toArray(T[] a) T[] 返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同

Map接口

Map 接口也是一个非常重要的集合接口,用于存储键/值对。Map 中的元素都是成对出现的,键值对就像数组的索引与数组的内容的关系一样,将一个键映射到一个值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。我们可以通过键去找到相应的值。

value 可以存储任意类型的对象,我们可以根据 key 键快速查找 value。Map 中的键/值对以 Entry 类型的对象实例形式存在。

看一看 Map 中的方法吧

方法 返回值 说明
clear() void 从此映射中移除所用映射关系(可选操作)
containsKey(Object key) boolean 如果此映射包含指定键的映射关系,则返回 true
containsValue(Object value) boolean 如果此映射将一个或多个键映射到指定值,则返回 true
entrySet() Set> 返回此映射中包含的映射关系的 Set 视图
equals(Object o) boolean 比较指定的对象与此映射是否相等
get(Object key) V 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null
hashCode() int 返回此映射的哈希码值
isEmpty() boolean 如果此映射未包含键-值映射关系,则返回 true
keySet() Set 返回此映射中包含的键的 Set 视图
put(K key, V value) V 将指定的值与此映射中的指定键关联(可选操作)
putAll(Map m) void 从指定映射中将所有映射关系复制到此映射中(可选操作)
remove(Object key) V 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)
size int 返回此映射中的键-值映射关系数
values() Collection 返回此映射中包含的值的 Collection 视图

List 接口与 ArrayList 类

List 是一个接口,不能实例化,需要一个具体类来实现实例化。List 集合中的对象按照一定的顺序排放,里面的内容可以重复。List 接口实现的类有:ArrayList(实现动态数组),Vector(实现动态数组),LinkedList(实现链表),Stack(实现堆栈)。

List 在 Collection 基础上增加的方法:

方法 返回值 说明
add(int index, E element) void 在列表的指定位置插入指定元素(可选操作)
addAll(int index, Collection c) boolean 将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)
get(int index) E 返回列表中指定位置的元素
indexOf(Object o) int 返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1
lastIndexOf(Object o) int 返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1
listIterator() ListIterator 返回此列表元素的列表迭代器(按适当顺序)
listIterator(int index) ListIterator 返回此列表元素的列表迭代器(按适当顺序),从列表的指定位置开始
remove(int index) E 移除列表中指定位置的元素(可选操作)
set(int index, E element) E 用指定元素替换列表中指定位置的元素(可选操作)
subList(int fromIndex, int toIndex) List 返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图

今天我们主要来学习 java.util.ArrayList,ArrayList 类实现一个可增长的动态数组,它可以存储不同类型的对象,而数组则只能存放特定数据类型的值。

Set和HashSet

Set 接口也是 Collection 接口的子接口,它有一个很重要也是很常用的实现类——HashSet,Set 是元素无序并且不包含重复元素的 collection(List 可以重复),被称为集。

HashSet 由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。

HashMap

HashMap 是基于哈希表的 Map 接口的一个重要实现类。HashMap 中的 Entry 对象是无序排列的,Key 值和 value 值都可以为 null,但是一个 HashMap 只能有一个 key 值为 null 的映射(key 值不可重复)。

字节流

I/O 流

大部分程序都需要进行输入/输出处理,比如从键盘读取数据、从屏幕中输出数据、从文件中写数据等等。在 Java 中,把这些不同类型的输入、输出源抽象为流(Stream),而其中输入或输出的数据则称为数据流(Data Stream),用统一的接口表示,从而使程序设计简单明了。

【计理01组04号】JDK基础入门_第1张图片

流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。

流一般分为输入流(Input Stream)和输出流(Output Stream)两类,但这种划分并不是绝对的。比如一个文件,当向其中写数据时,它就是一个输出流;当从其中读取数据时,它就是一个输入流。当然,键盘只是一个输入流,而屏幕则只是一个输出流。(其实我们可以通过一个非常简单的方法来判断,只要是向内存中写入就是输入流,从内存中写出就是输出流)。

【计理01组04号】JDK基础入门_第2张图片

字节流

基类:InputStream 和 OutputStream

字节流主要操作 byte 类型数据,以 byte 数组为准,java 中每一种字节流的基本功能依赖于基本类 InputStream 和 Outputstream,他们是抽象类,不能直接使用。字节流能处理所有类型的数据(如图片、avi 等)。

InputStream

InputStream 是所有表示字节输入流类的基类,继承它的子类要重新定义其中所定义的抽象方法。InputStream 是从装置来源地读取数据的抽象表示,例如 System 中的标准输入流 in 对象就是一个 InputStream 类型的实例。

我们先来看看 InputStream 类的方法:

方法 说明
read()throws IOException 从输入流中读取数据的下一个字节(抽象方法)
skip(long n) throws IOException 跳过和丢弃此输入流中数据的 n 个字节
available()throws IOException 返回流中可用字节数
mark(int readlimit)throws IOException 在此输入流中标记当前的位置
reset()throws IOException 将此流重新定位到最后一次对此输入流调用 mark 方法时的位置
markSupport()throws IOException 测试此输入流是否支持 mark 和 reset 方法
close()throws IOException 关闭流

在 InputStream 类中,方法 read() 提供了三种从流中读数据的方法:

  1. int read():从输入流中读一个字节,形成一个 0~255 之间的整数返回(是一个抽象方法)。
  2. int read(byte b[]):从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
  3. int read(byte b[],int off,int len):从输入流中读取长度为 len 的数据,写入数组 b 中从索引 off 开始的位置,并返回读取得字节数。

对于这三个方法,若返回 -1,表明流结束,否则,返回实际读取的字符数。

OutputStream

OutputStream 是所有表示字节输出流类的基类。子类要重新定义其中所定义的抽象方法,OutputStream 是用于将数据写入目的地的抽象表示。例如 System 中的标准输出流对象 out 其类型是 java.io.PrintStream,这个类是 OutputStream 的子类。

OutputStream 类方法:

方法 说明
write(int b)throws IOException 将指定的字节写入此输出流(抽象方法)
write(byte b[])throws IOException 将字节数组中的数据输出到流中
write(byte b[], int off, int len)throws IOException 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流
flush()throws IOException 刷新此输出流并强制写出所有缓冲的输出字节
close()throws IOException 关闭流
文件流

在 I/O 处理中,最常见的就是对文件的操作。java.io 包中所提供的文件操作类包括:

  1. 用于读写本地文件系统中的文件:FileInputStream 和 FileOutputStream
  2. 描述本地文件系统中的文件或目录:File、FileDescriptor 和 FilenameFilter
  3. 提供对本地文件系统中文件的随机访问支持:RandomAccessFile

今天我们来学习文件流的 FileInputStream 和 FileOutputStream。

FileInputStream 类用于打开一个输入文件,若要打开的文件不存在,则会产生异常 FileNotFoundException,这是一个非运行时异常,必须捕获或声明抛弃。 FileOutputStream 类用来打开一个输出文件,若要打开的文件不存在,则会创建一个新的文件,否则原文件的内容会被新写入的内容所覆盖。

在进行文件的读/写操作时,会产生非运行时异常 IOException,必须捕获或声明抛弃(其他的输入/输出流处理时也同样需要进行输入/输出异常处理)。

缓冲流

类 BufferedInputStream 和 BufferedOutputStream 实现了带缓冲的过滤流,它提供了缓冲机制,把任意的 I/O 流“捆绑”到缓冲流上,可以提高 I/O 流的读取效率。

在初始化时,除了要指定所连接的 I/O 流之外,还可以指定缓冲区的大小。缺省时是用 32 字节大小的缓冲区;最优的缓冲区大小常依赖于主机操作系统、可使用的内存空间以及机器的配置等;一般缓冲区的大小为内存页或磁盘块等的整数倍。

BufferedInputStream 的数据成员 buf 是一个位数组,默认为 2048 字节。当读取数据来源时例如文件,BufferedInputStream 会尽量将 buf 填满。当使用 read () 方法时,实际上是先读取 buf 中的数据,而不是直接对数据来源作读取。当 buf 中的数据不足时,BufferedInputStream 才会再实现给定的 InputStream 对象的 read() 方法,从指定的装置中提取数据。

BufferedOutputStream 的数据成员 buf 是一个位数组,默认为 512 字节。当使用 write() 方法写入数据时,实际上会先将数据写至 buf 中,当 buf 已满时才会实现给定的 OutputStream 对象的 write() 方法,将 buf 数据写至目的地,而不是每次都对目的地作写入的动作。

【计理01组04号】JDK基础入门_第3张图片

数据流

接口 DataInput 和 DataOutput,设计了一种较为高级的数据输入输出方式:除了可处理字节和字节数组外,还可以处理 int、float、boolean 等基本数据类型,这些数据在文件中的表示方式和它们在内存中的一样,无须转换,如 read()readInt()readByte()...; write()writeChar()writeBoolean()... 此外,还可以用 readLine() 方法读取一行信息。

方法:

方法 返回值 说明
readBoolean() boolean  
readByte() byte  
readShort() short  
readChar() char  
readInt() int  
readLong() long  
readDouble() double  
readFloat() float  
readUnsignedByte() int  
readUnsignedShort() int  
readFully(byte[] b) void 从输入流中读取一些字节,并将它们存储在缓冲区数组 b 中
readFully(byte[] b, int off,int len) void 从输入流中读取 len 个字节
skipBytes(int n) int 与 InputStream.skip 等价
readUTF() String 按照 UTF-8 形式从输入中读取字符串
readLine() String 按回车 (\r) 换行 (\n) 为分割符读取一行字符串,不完全支持 UNICODE
writeBoolean(boolean v) void  
writeByte(int v) void  
writeShort(int v) void  
writeChar(int v) void  
writeInt(int v) void  
writeLong(long v) void  
writeFloat(float v) void  
writeDouble(double v) void  
write(byte[] b) void 与 OutputStream.write 同义
write(byte[] b, int off, int len) void 与 OutputStream.write 同义
write(int b) void 与 OutputStream.write 同义
writeBytes(String s) void 只输出每个字符的低 8 位;不完全支持 UNICODE
writeChars(String s) void 每个字符在输出中都占两个字节

数据流类 DataInputStream 和 DataOutputStream 的处理对象除了是字节或字节数组外,还可以实现对文件的不同数据类型的读写:

  1. 分别实现了 DataInput 和 DataOutput 接口。
  2. 在提供字节流的读写手段同时,以统一的形式向输入流中写入 boolean,int,long,double 等基本数据类型,并可以再次把基本数据类型的值读取回来。
  3. 提供了字符串读写的手段。
标准流、内存读写流、顺序输入流

标准流

语言包 java.lang 中的 System 类管理标准输入/输出流和错误流。

System.in 从 InputStream 中继承而来,用于从标准输入设备中获取输入数据(通常是键盘) System.out 从 PrintStream 中继承而来,把输入送到缺省的显示设备(通常是显示器) System.err 也是从 PrintStream 中继承而来,把错误信息送到缺省的显示设备(通常是显示器)

每当 main 方法被执行时,就会自动生产上述三个对象。这里就不再写代码验证了。

内存读写流

为了支持在内存上的 I/O,java.io 中提供了类:ByteArrayInputStream、ByteArrayOutputStream 和 StringBufferInputStream

  1. ByteArrayInputStream 可以从指定的字节数组中读取数据。
  2. ByteArrayOutputStream 中提供了缓冲区可以存放数据(缓冲区大小可以在构造方法中设定,缺省为 32),可以用 write() 方法向其中写入数据,然后用 toByteArray() 方法将缓冲区中的有效字节写到字节数组中去。size() 方法可以知道写入的字节数,reset() 可以丢弃所有内容。
  3. StringBufferInputStream 与 ByteArrayInputStream 相类似,不同点在于它是从字符缓冲区 StringBuffer 中读取 16 位的 Unicode 数据,而不是 8 位的字节数据(已被 StringReader 取代)。

这里只做简要的介绍,有兴趣的同学可以查看一下这些类里具体的方法。

顺序输入流

java.io 中提供了类 SequenceInputStream,使应用程序可以将几个输入流顺序连接起来。顺序输入流提供了将多个不同的输入流统一为一个输入流的功能,这使得程序可能变得更加简洁。

字符流

字符流基类

java.io 包中专门用于字符流处理的类,是以 Reader 和 Writer 为基础派生的一系列类。

字符流以字符为单位,根据码表映射字符,一次可能读多个字节,只能处理字符类型的数据。

同类 InputStream 和 OutputStream 一样,Reader 和 Writer 也是抽象类,只提供了一系列用于字符流处理的接口。它们的方法与类 InputStream 和 OutputStream 类似,只不过其中的参数换成字符或字符数组。

Reader 是所有的输入字符流的父类,它是一个抽象类。

我们先来看一看基类 Reader 的方法,其用法与作用都与 InputStream 和 OutputStream 类似,就不做过多的说明了。

方法 返回值
close() void
mark (int readAheadLimit) void
markSupported() boolean
read() int
read(char[] cbuf, int off,int len) int
ready() boolean
reset() void
skip(long n) long

Writer 是所有的输出字符流的父类,它是一个抽象类。 Writer 的方法:

方法 返回值
close() void
flush() void
write(char[] cbuf) void
write(char[] cbuf, int off,int len) void
write(int c) void
write(String str) void
write(String str, int off, int len) void

InputStreamReader 和 OutputStreamWriter 是 java.io 包中用于处理字符流的最基本的类,用来在字节流和字符流之间作为中介:从字节输入流读入字节,并按编码规范转换为字符;往字节输出流写字符时先将字符按编码规范转换为字节。使用这两者进行字符处理时,在构造方法中应指定一定的平台规范,以便把以字节方式表示的流转换为特定平台上的字符表示。

缓存流

同样的,为了提高字符流处理的效率,java.io 中也提供了缓冲流 BufferedReader 和 BufferedWriter。其构造方法与 BufferedInputStream 和 BufferedOutPutStream 相类似。另外,除了 read() 和 write() 方法外,它还提供了整行字符处理方法:

  1. public String readLine():BufferedReader 的方法,从输入流中读取一行字符,行结束标志\n\r或者两者一起(这是根据系统而定的)。
  2. public void newLine():BufferedWriter 的方法,向输出流中写入一个行结束标志,它不是简单地换行符\n\r,而是系统定义的行隔离标志(line separator)。
其它字符流类

在这里我们就列举一下有哪些类,具体的就不再讲解了。

  1. 对字符数组进行处理: CharArrayReader、CharArrayWrite
  2. 对文本文件进行处理:FileReader、FileWriter
  3. 对字符串进行处理:StringReader、StringWriter
  4. 过滤字符流:FilterReader、FilterWriter
  5. 管道字符流:PipedReader、PipedWriter
  6. 行处理字符流:LineNumberReader
  7. 打印字符流:PrintWriter

类有千万,方法更是不计其数,所以没有必要去掌握所有的方法和类,只需要知道常见常用的就行了,而大多数的类和方法,希望大家有一个印象,当我们在实际开发的时候,能够想到,并且借助其他工具去查询我们需要的方法的应用方式就可以了。

文件操作

文件操作

文件操作

java.io 定义的大多数类都是流式操作,但 File 类不是。它直接处理文件和文件系统。File 类没有指定信息怎样从文件读取或向文件存储;它描述了文件本身的属性。File 对象用来获取或处理与磁盘文件相关的信息,例如权限,时间,日期和目录路径。此外,File 还浏览子目录层次结构。Java 中的目录当成 File 对待,它具有附加的属性——一个可以被 list() 方法检测的文件名列表。

先看一看 File 的构造方法:

//根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
File(File parent, String child)

//通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例
File(String pathname)

// 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例
File(String parent, String child)

//通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例
File(URI uri)

例如:

//一个目录路径参数
File f1 = new File("/Users/mumutongxue/");

//对象有两个参数——路径和文件名
File f2 = new File("/Users/mumutongxue/","a.bat");

//指向 f1 文件的路径及文件名
File f3 = new File(f1,"a.bat");

再来看看 File 的一些方法

方法 说明
boolean canExecute() 测试应用程序是否可以执行此抽象路径名表示的文件
boolean canRead() 测试应用程序是否可以读取此抽象路径名表示的文件
boolean canWrite() 测试应用程序是否可以修改此抽象路径名表示的文件
int compareTo(File pathname) 按字母顺序比较两个抽象路径名
boolean createNewFile() 当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件
static File createTempFile(String prefix, String suffix) 在默认临时文件目录中创建一个空文件,使用给定前缀和后缀生成其名称
static File createTempFile(String prefix, String suffix, File directory) 在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称
boolean delete() 删除此抽象路径名表示的文件或目录
void deleteOnExit() 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录
boolean equals(Object obj) 测试此抽象路径名与给定对象是否相等
boolean exists() 测试此抽象路径名表示的文件或目录是否存在
File getAbsoluteFile() 返回此抽象路径名的绝对路径名形式
String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串
File getCanonicalFile() 返回此抽象路径名的规范形式
String getCanonicalPath() 返回此抽象路径名的规范路径名字符串
long getFreeSpace() 返回此抽象路径名指定的分区中未分配的字节数
String getName() 返回由此抽象路径名表示的文件或目录的名称
String getParent() 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null
File getParentFile() 返回此抽象路径名父目录的抽象路径名;如果此路径名没有指定父目录,则返回 null
String getPath() 将此抽象路径名转换为一个路径名字符串
long getTotalSpace() 返回此抽象路径名指定的分区大小
long getUsableSpace() 返回此抽象路径名指定的分区上可用于此虚拟机的字节数
int hashCode() 计算此抽象路径名的哈希码
boolean isAbsolute() 测试此抽象路径名是否为绝对路径名
boolean isDirectory() 测试此抽象路径名表示的文件是否是一个目录
boolean isFile() 测试此抽象路径名表示的文件是否是一个标准文件
boolean isHidden() 测试此抽象路径名指定的文件是否是一个隐藏文件
long lastModified() 返回此抽象路径名表示的文件最后一次被修改的时间
long length() 返回由此抽象路径名表示的文件的长度
String[] list() 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录
String[] list(FilenameFilter filter) 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录
File[] listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件
File[] listFiles(FileFilter filter) 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录
File[] listFiles(FilenameFilter filter) 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录
static File[] listRoots() 列出可用的文件系统根
boolean mkdir() 创建此抽象路径名指定的目录
boolean mkdirs() 创建此抽象路径名指定的目录,包括所有必需但不存在的父目录
boolean renameTo(File dest) 重新命名此抽象路径名表示的文件
boolean setExecutable(boolean executable) 设置此抽象路径名所有者执行权限的一个便捷方法
boolean setExecutable(boolean executable, boolean ownerOnly) 设置此抽象路径名的所有者或所有用户的执行权限
boolean setLastModified(long time) 设置此抽象路径名指定的文件或目录的最后一次修改时间
boolean setReadable(boolean readable) 设置此抽象路径名所有者读权限的一个便捷方法
boolean setReadable(boolean readable, boolean ownerOnly) 设置此抽象路径名的所有者或所有用户的读权限
boolean setReadOnly() 标记此抽象路径名指定的文件或目录,从而只能对其进行读操作
boolean setWritable(boolean writable) 设置此抽象路径名所有者写权限的一个便捷方法
boolean setWritable(boolean writable, boolean ownerOnly) 设置此抽象路径名的所有者或所有用户的写权限
String toString() 返回此抽象路径名的路径名字符串
URI toURI() 构造一个表示此抽象路径名的 file: URI

随机访问文件

对于 FileInputStream/FileOutputStream、FileReader/FileWriter 来说,它们的实例都是顺序访问流,即只能进行顺序读/写。而类 RandomAccessFile 则允许文件内容同时完成读和写操作,它直接继承 object,并且同时实现了接口 DataInput 和 DataOutput。

随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针。输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用。输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。

RandomAccessFile 提供了支持随机文件操作的方法:

  1. readXXX() 或者 writeXXX(): 如 ReadInt(),ReadLine(),WriteChar(),WriteDouble() 等
  2. int skipBytes(int n): 将指针向下移动若干字节
  3. length(): 返回文件长度
  4. long getFilePointer(): 返回指针当前位置
  5. void seek(long pos): 将指针调用所需位置

在生成一个随机文件对象时,除了要指明文件对象和文件名之外,还需要指明访问文件的模式。

我们来看看 RandomAccessFile 的构造方法:

RandomAccessFile(File file,String mode)
RandomAccessFile(String name,String mode)

mode 的取值:

  • r: 只读,任何写操作都将抛出 IOException。
  • rw: 读写,文件不存在时会创建该文件,文件存在时,原文件内容不变,通过写操作改变文件内容。
  • rws: 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
  • rwd: 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。

Swing入门

注意:WEB IDE 下不支持显示图形界面,所以本章节的内容请同学们在本地使用 IDE 练习,如 IDEA,Eclipse 等

先聊聊 GUI 和 MVC

图形用户界面(Graphical User Interface,简称 GUI,又称图形用户接口)是指采用图形方式显示的计算机操作用户界面。 ——来自 百度百科 · GUI

你在系统中按下的每一个按钮、浏览器中输入网址的地址栏、以及无数个被打开和关闭的窗口,它们都是 GUI 的组成部分。这与我们在前面章节提到的模块化思想不谋而合。Swing 便是 Java 中的一个 GUI,它是基于MVC(模型-视图-控制器)设计模式来进行设计的,通过事件对用户的输入进行反应。即使是最简单的一个按钮,也是包含了它的外观(什么颜色,有多大)、内容(按钮上面显示什么文字等)以及行为(对于用户按下时的反应)这三个要素。你可以 进一步了解设计模式、MVC 框架 和 事件 这三个方面的知识来作为学习 Swing 的准备。

比如,你在实验楼进行实验的时候,摆在你面前、在你的电脑屏幕上显示的内容,就是视图;你在实验环境中每一次的鼠标点击、输入的内容,都有专门的模块来负责处理你的这些输入,可以理解为控制器;而你写的代码、实验时的环境,这些内容,都称之为模型

下图表示了 MVC 组件类型的关系和功能。

【计理01组04号】JDK基础入门_第4张图片

GUI

MVC 基础

Swing 编程

Swing 是在 抽象窗口工具箱(AWT) 的架构上发展而来的一个用户界面库,整个可视组件库的基础构造块是 JComponent。它是所有组件的父类,为所有组件提供了绘制的基础架构。换言之,所有的 Swing 组件都是由它派生而来。

基于 Swing 制作的 Java 程序就是由一个一个的组件构成的,开发的过程有点像组装乐高积木。下面我们就通过实验来熟悉一些基本的组件。

请在 Eclipse 中新建项目 HelloSwing,创建包 com.shiyanlou.course,新建一个包含主方法的类 MySwingWindow。在创建类时,你可以使用 Eclipse 来帮你生成此类的主方法,就像下图那样。

【计理01组04号】JDK基础入门_第5张图片

这样,在创建好指定的类之后,你可以在代码中看到自动生成的 main() 方法,然后就能增添更多功能模块了。

【计理01组04号】JDK基础入门_第6张图片

盛放控件的盘子——JFrame

JFrame 类就是一个容器,允许你把其他组件添加到它里面,把它们组织起来,并把它们呈现给用户。JFrame 在大多数操作系统中是以窗口的形式注册的,这意味着你可以对其进行最小化和最大化,甚至移动这个窗口。

如果要打个比方的话,你的脸就是一个容器,眼睛、耳朵、鼻子和嘴巴这些“控件”需要放在脸上,别人看到你这个“界面”实际上就是你的脸,以及上面的“控件”。

不同的教材对于 JFrame 的称呼是有差别的。为了帮助你理解,在本实验中,我们称之为“窗体”。

下面是它包含的一些方法,你最好在 Java SE 官方 API 中去查阅它们的用法和详细说明:

方法 说明
get/setTitle() 获取/设置窗体的标题
get/setState() 获取/设置窗体的最小化、最大化等状态
is/setVisible() 获取/设置窗体的可视状态,换言之,是否在屏幕上显示
get/setLocation() 获取/设置窗体在屏幕上在什么位置出现
get/setSize() 获取/设置窗体的大小
add() 将组件添加到窗体中,这个过程把各个控件形成了一个整体

对于 Swing 应用程序,我们如果要将组件放在 JFrame 上,则需要继承 JFrame 类。我们来尝试创建一个窗体吧。

主要的代码如下:

package com.shiyanlou.course;

import javax.swing.JFrame;

public class MySwingWindow extends JFrame {
    //此处通过继承 JFrame 类来使我们自己的 MySwingWindow 具有窗体的一些属性和方法

    public MySwingWindow(){
        //在窗体的构造方法中设置窗体的各项属性

        super();
        //使用 super() 来引用父类的成分,用 this 来引用当前对象

        this.setSize(400, 300);
        //设置窗体的大小

        this.getContentPane().setLayout(null);
        //返回此窗体的 contentPane 对象,设置其布局
        //这一句不太懂的话也不用担心,先写着

        this.setTitle("My First Swing Window");
        //设置窗体的标题
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        MySwingWindow window = new MySwingWindow();
        //声明一个窗体对象 window

        window.setVisible(true);
        //设置这个窗体是可见的
    }
}

你不必一开始就写 import 相关代码,通常在需要引入相应的包时,使用自动提示给出的方案即可,就像下面这样:

【计理01组04号】JDK基础入门_第7张图片

和所有的 Java 程序一样,都需要 main() 方法才能让程序“跑”起来,所以我们只需要在其中创建好一个窗体对象并设置其可见就行了。

不妨编译并运行一下,你应该可以看到一个最原始的窗体程序。

【计理01组04号】JDK基础入门_第8张图片

个性化 Swing 应用程序

有了最基础的容器,我们就可以在上面添加各式各样的控件。Swing 中的控件数量是巨大的,但它们的使用方法都是相通的,你可以在 API 文档中查阅每种控件的属性及其设置方法、获取属性和数据的方法等等。

我们在本实验中先为大家介绍一些常用的控件,在上一步的基础上继续个性化我们的 MySwingWindow

首先添加 Swing 库中最基础的组件——标签 JLabel。JLabel 可以用作文本描述和图片描述,常用的方法如下:

方法 说明
get/setText() 获取/设置标签的文本
get/setIcon() 获取/设置标签的图片

你需要先声明这个控件对象。

private JLabel myLabel;

然后编写一个getJLabel()方法,用于返回一个 JLabel 实例。

private JLabel getJLabel() {
    //此处的返回值类型为 JLabel

    if(myLabel == null) {

        myLabel = new JLabel();
        //实例化 myLabel 对象

        myLabel.setBounds(5, 10, 250, 30);
        //使用 setBounds() 方法设置尺寸
        //四个参数的分别是 x,y,width,height
        //代表了横向、纵向的位置,以及标签自身的宽和高

        myLabel.setText("Hello! Welcome to shiyanlou.com");
        //使用 setText() 方法设置要显示的文本
    }

    return myLabel;
    //返回创建的实例
}

同样,我们来添加一个文本框 JTextFiled,它包含的方法与 JLabel 类似,你可以在 API 文档中查阅更多的方法。

private JTextField myTextField;

private JTextField getJTextField() {
    //此处的返回值类型为 JTextField

    if(myTextField == null) {
    //加上这个判断是为了防止出错

        myTextField = new JTextField();
        //实例化 myTextField 对象

        myTextField.setBounds(5, 45, 200, 30);
        //设置它的位置和尺寸

        myTextField.setText("Shi Yan Lou");
        //设定它要显示的字符串

    }

    return myTextField;
    //返回这个实例
}

再来做一个按钮 JButton。与上述控件不同的是,我们在这里为它添加一个事件响应,当你按下按钮的时候它就能做一些事情了。

private JButton myButton;

private JButton getJButton() {
    //此处的返回值类型为 JButton

    if(myButton == null) {

        myButton = new JButton();
        //实例化 myTextField 对象
        myButton.setBounds(5, 80, 100, 40);
        //设置它的位置和尺寸
        myButton.setText("Click me!");
        //设定它要显示的字符串
        myButton.addActionListener(new ActionListener() {
            //为其添加一个事件监听,从而使这个按钮可以响应用户的点击操作
            //ActionListener 是用于接收操作事件的侦听器接口。
            //对处理操作事件感兴趣的类可以实现此接口,而使用该类创建的对
            //可使用组件的 addActionListener 方法向该组件注册。
            //在发生操作事件时,调用该对象的 actionPerformed 方法。

            public void actionPerformed(ActionEvent e) {
                //该方法会在发生操作时被调用,我们要做的事情就可以写在这里面
                //比如我们下面要做的事情就是改变之前两个控件里面的文字颜色和背景色

                myLabel.setForeground(Color.RED);
                //设置此组件的前景色。

                myTextField.setBackground(Color.BLUE);
                //设置此组件的背景色。
            }
        });
    }

    return myButton;
    //返回这个实例
}

代码 myButton.addActionListener(new ActionListener(){ ... }); 中的 new ActionListener(){ ... } 是一种名为 匿名类 的用法。

最后,我们在这个窗体的构造方法 public MySwingWindow() 中,将这三个控件的获取方法添加进去。

this.add(getJLabel(),null);
this.add(getJTextField(), null);
this.add(getJButton(),null);
//在自定义的 JFrame 构造方法中使用 add() 方法来添加控件
//add() 方法可以将指定组件添加到此容器的给定位置上
//第一个参数为待添加的组件,这里的组件来自我们的返回值
//第二个参数为描述组件的布局限制的对象,我们不加限制,所以填 null

如果你不是很清楚整个代码的结构,可以参考下面的完整代码:

package com.shiyanlou.course;

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

public class MySwingWindow extends JFrame {
    //此处通过继承 JFrame 类来使我们自己的 MySwingWindow 具有窗体的一些属性和方法

    /**
     *
     */
    private static final long serialVersionUID = 8978037719568897634L;

    //首先需要声明各个控件
    private JLabel myLabel;
    private JTextField myTextField;
    private JButton myButton;

    public MySwingWindow(){
        //在窗体的构造方法中设置窗体的各项属性

        super();
        //使用 super() 来引用父类的成分,用 this 来引用当前对象

        this.setSize(400, 300);
        //设置窗体的大小

        this.getContentPane().setLayout(null);
        //返回此窗体的 contentPane 对象,设置其布局
        //这一句不太懂的话也不用担心,先写着

        this.setTitle("My First Swing Window");
        //设置窗体的标题

        this.add(getJLabel(),null);
        this.add(getJTextField(), null);
        this.add(getJButton(),null);
        //在自定义的 JFrame 构造方法中使用 add() 方法来添加控件
        //add() 方法可以将指定组件添加到此容器的给定位置上
        //第一个参数为待添加的组件,这里的组件来自我们的返回值
        //第二个参数为描述组件的布局限制的对象,我们不加限制,所以填 null
    }

    private JLabel getJLabel() {
        //此处的返回值类型为 JLabel

        if(myLabel == null) {

            myLabel = new JLabel();
            //实例化 myLabel 对象

            myLabel.setBounds(5, 10, 250, 30);
            //使用 setBounds 方法设置其位置和尺寸
            //四个参数的分别是 x,y,width,height
            //代表了横向、纵向的位置,以及标签自身的宽和高

            myLabel.setText("Hello! Welcome to shiyanlou.com");
            //使用 setText 方法设置要显示的文本
        }

        return myLabel;
        //返回创建的实例
    }

    private JTextField getJTextField() {
        //此处的返回值类型为 JTextField

        if(myTextField == null) {
        //加上这个判断是为了防止出错

            myTextField = new JTextField();
            //实例化 myTextField 对象

            myTextField.setBounds(5, 45, 200, 30);
            //设置它的位置和尺寸

            myTextField.setText("Shi Yan Lou");
            //设定它要显示的字符串

        }

        return myTextField;
        //返回这个实例
    }

    private JButton getJButton() {
        //此处的返回值类型为 JButton

        if(myButton == null) {

            myButton = new JButton();
            //实例化 myTextField 对象
            myButton.setBounds(5, 80, 100, 40);
            //设置它的位置和尺寸
            myButton.setText("Click me!");
            //设定它要显示的字符串
            myButton.addActionListener(new ActionListener() {
                //为其添加一个事件监听,从而使这个按钮可以响应用户的点击操作
                //ActionListener 是用于接收操作事件的侦听器接口。
                //对处理操作事件感兴趣的类可以实现此接口,而使用该类创建的对
                //可使用组件的 addActionListener 方法向该组件注册。
                //在发生操作事件时,调用该对象的 actionPerformed 方法。

                public void actionPerformed(ActionEvent e) {
                    //该方法会在发生操作时被调用,我们要做的事情就可以写在这里面
                    //比如我们下面要做的事情就是改变之前两个控件里面的文字颜色和背景色

                    myLabel.setForeground(Color.RED);
                    //设置此组件的前景色。

                    myTextField.setBackground(Color.BLUE);
                    //设置此组件的背景色。
                }
            });
        }

        return myButton;
        //返回这个实例
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        MySwingWindow window = new MySwingWindow();
        //声明一个窗体对象 window

        window.setVisible(true);
        //设置这个窗体是可见的
    }
}

检查一下代码,确认无误后点击编译并运行,自己动手做的第一个窗体程序就出来了。

【计理01组04号】JDK基础入门_第9张图片

多线程编程

从线程到多线程

首先你应该知道什么是线程:

线程:程序执行流的最小单元。它是进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派 CPU 的基本单位。

如同大自然中的万物,线程也有“生老病死”的过程,下图表示了一个线程从创建到消亡的过程,以及过程中的状态。

【计理01组04号】JDK基础入门_第10张图片

结合线程的生命周期,我们再来看看多线程的定义:

多线程:从软件或者硬件上实现多个线程并发执行的技术。在单个程序中同时运行多个线程完成不同的工作。

在 Java 中,垃圾回收机制 就是通过一个线程在后台实现的,这样做的好处在于:开发者通常不需要为内存管理投入太多的精力。反映到我们现实生活中,在浏览网页时,浏览器能够同时下载多张图片;实验楼的服务器能够容纳多个用户同时进行在线实验,这些都是多线程带来的好处。

从专业的角度来看,多线程编程是为了最大限度地利用 CPU 资源——当处理某个线程不需要占用 CPU 而只需要利用 IO 资源时,允许其他的那些需要 CPU 资源的线程有机会利用 CPU。这或许就是多线程编程的最终目的。当然,你也可以进一步了解 为什么使用多线程。

对于多线程和线程之间的关系,你可以这样理解:一个使用了多线程技术的程序,包含了两条或两条以上并发运行的线程(Thread)。

Java 中的 Thread 类就是专门用来创建线程和操作线程的类,我们来具体学习一下。

使用 Thread 类

创建线程

根据我们前面所学,我们可以自定义一个类,然后继承 Thread 类来使其成为一个线程类。

那么我们要把线程要做的事情放在哪里呢?在 Java 中,run() 方法为线程指明了它要完成的任务,你可以通过下面两种方式来为线程提供 run 方法:

  1. 继承 Thread 类并重写它的 run() 方法,然后用这个子类来创建对象并调用 start() 方法。
  2. 通过定义一个类,实现 Runnable 接口,实现 run() 方法。

概括一下,启动线程的唯一的方法便是 start(),而你需要把待完成的工作(功能代码)放入到 run() 方法中。

我们来创建两个线程试试。新建一个带有主方法的类 CreateThread

代码片段如下,我们在注释中继续讲解:

public class CreateThread {

    public static void main(String[] args)
    {
        Thread1 thread1 = new Thread1();
        //声明一个 Thread1 对象,这个 Thread1 类继承自 Thread 类的

        Thread thread2 = new Thread(new Thread2());
        //传递一个匿名对象作为参数

        thread1.start();
        thread2.start();
        //启动线程
    }
}

class Thread1 extends Thread
{
    public void run()
    {
        //在 run() 方法中放入线程要完成的工作

        //这里我们把两个线程各自的工作设置为打印 100 次信息
        for (int i = 0; i < 100; ++i)
        {
            System.out.println("Hello! This is " + i);
        }

        //在这个循环结束后,线程便会自动结束
    }
}

class Thread2 implements Runnable {
    //与 Thread1 不同,如果当一个线程已经继承了另一个类时,就建议你通过实现 Runnable 接口来构造

    public void run()
    {
        for (int i = 0; i < 100; ++i)
        {
            System.out.println("Thanks. There is " + i);
        }
    }
}

编译并运行此程序

javac CreateThread.java
java CreateThread

你在控制台就可以看到下面这样的输出信息。两个线程近似交替地在输出信息。受到系统调度的影响,两个线程输出信息的先后顺序可能不同。

查看线程运行状态

线程的状态共有 6 种,分别是:新建 New、运行(可运行)Runnable、阻塞 Blocked、计时等待 Timed Waiting、等待 Waiting 和终止 Terminate

当你声明一个线程对象时,线程处于新建状态,系统不会为它分配资源,它只是一个空的线程对象。 调用 start() 方法时,线程就成为了可运行状态,至于是否是运行状态,则要看系统的调度了。 调用了 sleep() 方法、调用 wait() 方法和 IO 阻塞时,线程处于等待、计时等待或阻塞状态。 当 run() 方法执行结束后,线程也就终止了。

我们通过一个例子来加深对于这些状态的理解。新建 ThreadState 类,用于自定义线程的状态。

主要的代码如下:

public class ThreadState implements Runnable {

    public synchronized void waitForAMoment() throws InterruptedException {

        wait(500);
        //使用 wait() 方法使当前线程等待 500 毫秒
        //或者等待其他线程调用 notify() 或 notifyAll() 方法来唤醒
    }

    public synchronized void waitForever() throws InterruptedException {

        wait();
        //不填入时间就意味着使当前线程永久等待,
        //只能等到其他线程调用 notify() 或 notifyAll() 方法才能唤醒
    }

    public synchronized void notifyNow() throws InterruptedException {

        notify();
        //使用 notify() 方法来唤醒那些因为调用了 wait() 方法而进入等待状态的线程
    }

    public void run() {

        //这里用异常处理是为了防止可能的中断异常
        //如果任何线程中断了当前线程,则抛出该异常

        try {
            waitForAMoment();
            // 在新线程中运行 waitMoment() 方法

            waitForever();
            // 在新线程中运行 waitForever() 方法

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

然后再新建一个测试类 ThreadTest,用于输出这些状态。

接下来会用到 sleep() 方法,下面给出了这个方法的使用方法。

sleep(),在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。填入的参数为休眠的时间(单位:毫秒)。

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        ThreadState state = new ThreadState();
        //声明并实例化一个 ThreadState 对象

        Thread thread = new Thread(state);
        //利用这个名为 state 的 ThreadState 对象来创建 Thread 对象

        System.out.println("Create new thread: " + thread.getState());
        //使用 getState() 方法来获得线程的状态,并进行输出

        thread.start();
        //使用 thread 对象的 start() 方法来启动新的线程

        System.out.println("Start the thread: " + thread.getState());
        //输出线程的状态

        Thread.sleep(100);
        //通过调用 sleep() 方法使当前这个线程休眠 100 毫秒,从而使新的线程运行 waitForAMoment() 方法

        System.out.println("Waiting for a moment (time): " + thread.getState());
        //输出线程的状态

        Thread.sleep(1000);
        //使当前这个线程休眠 1000 毫秒,从而使新的线程运行 waitForever() 方法

        System.out.println("Waiting for a moment: " + thread.getState());
        //输出线程的状态

        state.notifyNow();
        // 调用 state 的 notifyNow() 方法

        System.out.println("Wake up the thread: " + thread.getState());
        //输出线程的状态

        Thread.sleep(1000);
        //使当前线程休眠 1000 毫秒,使新线程结束

        System.out.println("Terminate the thread: " + thread.getState());
        //输出线程的状态
    }
}

 

你可能感兴趣的:(【计理01组04号】JDK基础入门)